index.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. import { _optionalChain } from '@sentry/utils';
  2. import { defineIntegration, convertIntegrationFnToClass } from '@sentry/core';
  3. var NodeType;
  4. (function (NodeType) {
  5. NodeType[NodeType["Document"] = 0] = "Document";
  6. NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
  7. NodeType[NodeType["Element"] = 2] = "Element";
  8. NodeType[NodeType["Text"] = 3] = "Text";
  9. NodeType[NodeType["CDATA"] = 4] = "CDATA";
  10. NodeType[NodeType["Comment"] = 5] = "Comment";
  11. })(NodeType || (NodeType = {}));
  12. function elementClassMatchesRegex(el, regex) {
  13. for (let eIndex = el.classList.length; eIndex--;) {
  14. const className = el.classList[eIndex];
  15. if (regex.test(className)) {
  16. return true;
  17. }
  18. }
  19. return false;
  20. }
  21. function distanceToMatch(node, matchPredicate, limit = Infinity, distance = 0) {
  22. if (!node)
  23. return -1;
  24. if (node.nodeType !== node.ELEMENT_NODE)
  25. return -1;
  26. if (distance > limit)
  27. return -1;
  28. if (matchPredicate(node))
  29. return distance;
  30. return distanceToMatch(node.parentNode, matchPredicate, limit, distance + 1);
  31. }
  32. function createMatchPredicate(className, selector) {
  33. return (node) => {
  34. const el = node;
  35. if (el === null)
  36. return false;
  37. try {
  38. if (className) {
  39. if (typeof className === 'string') {
  40. if (el.matches(`.${className}`))
  41. return true;
  42. }
  43. else if (elementClassMatchesRegex(el, className)) {
  44. return true;
  45. }
  46. }
  47. if (selector && el.matches(selector))
  48. return true;
  49. return false;
  50. }
  51. catch (e2) {
  52. return false;
  53. }
  54. };
  55. }
  56. const DEPARTED_MIRROR_ACCESS_WARNING = 'Please stop import mirror directly. Instead of that,' +
  57. '\r\n' +
  58. 'now you can use replayer.getMirror() to access the mirror instance of a replayer,' +
  59. '\r\n' +
  60. 'or you can use record.mirror to access the mirror instance during recording.';
  61. let _mirror = {
  62. map: {},
  63. getId() {
  64. console.error(DEPARTED_MIRROR_ACCESS_WARNING);
  65. return -1;
  66. },
  67. getNode() {
  68. console.error(DEPARTED_MIRROR_ACCESS_WARNING);
  69. return null;
  70. },
  71. removeNodeFromMap() {
  72. console.error(DEPARTED_MIRROR_ACCESS_WARNING);
  73. },
  74. has() {
  75. console.error(DEPARTED_MIRROR_ACCESS_WARNING);
  76. return false;
  77. },
  78. reset() {
  79. console.error(DEPARTED_MIRROR_ACCESS_WARNING);
  80. },
  81. };
  82. if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
  83. _mirror = new Proxy(_mirror, {
  84. get(target, prop, receiver) {
  85. if (prop === 'map') {
  86. console.error(DEPARTED_MIRROR_ACCESS_WARNING);
  87. }
  88. return Reflect.get(target, prop, receiver);
  89. },
  90. });
  91. }
  92. function hookSetter(target, key, d, isRevoked, win = window) {
  93. const original = win.Object.getOwnPropertyDescriptor(target, key);
  94. win.Object.defineProperty(target, key, isRevoked
  95. ? d
  96. : {
  97. set(value) {
  98. setTimeout(() => {
  99. d.set.call(this, value);
  100. }, 0);
  101. if (original && original.set) {
  102. original.set.call(this, value);
  103. }
  104. },
  105. });
  106. return () => hookSetter(target, key, original || {}, true);
  107. }
  108. function patch(source, name, replacement) {
  109. try {
  110. if (!(name in source)) {
  111. return () => {
  112. };
  113. }
  114. const original = source[name];
  115. const wrapped = replacement(original);
  116. if (typeof wrapped === 'function') {
  117. wrapped.prototype = wrapped.prototype || {};
  118. Object.defineProperties(wrapped, {
  119. __rrweb_original__: {
  120. enumerable: false,
  121. value: original,
  122. },
  123. });
  124. }
  125. source[name] = wrapped;
  126. return () => {
  127. source[name] = original;
  128. };
  129. }
  130. catch (e2) {
  131. return () => {
  132. };
  133. }
  134. }
  135. if (!(/[1-9][0-9]{12}/.test(Date.now().toString()))) ;
  136. function isBlocked(node, blockClass, blockSelector, unblockSelector, checkAncestors) {
  137. if (!node) {
  138. return false;
  139. }
  140. const el = node.nodeType === node.ELEMENT_NODE
  141. ? node
  142. : node.parentElement;
  143. if (!el)
  144. return false;
  145. const blockedPredicate = createMatchPredicate(blockClass, blockSelector);
  146. if (!checkAncestors) {
  147. const isUnblocked = unblockSelector && el.matches(unblockSelector);
  148. return blockedPredicate(el) && !isUnblocked;
  149. }
  150. const blockDistance = distanceToMatch(el, blockedPredicate);
  151. let unblockDistance = -1;
  152. if (blockDistance < 0) {
  153. return false;
  154. }
  155. if (unblockSelector) {
  156. unblockDistance = distanceToMatch(el, createMatchPredicate(null, unblockSelector));
  157. }
  158. if (blockDistance > -1 && unblockDistance < 0) {
  159. return true;
  160. }
  161. return blockDistance < unblockDistance;
  162. }
  163. let cachedRequestAnimationFrameImplementation;
  164. function getRequestAnimationFrameImplementation() {
  165. if (cachedRequestAnimationFrameImplementation) {
  166. return cachedRequestAnimationFrameImplementation;
  167. }
  168. const document = window.document;
  169. let requestAnimationFrameImplementation = window.requestAnimationFrame;
  170. if (document && typeof document.createElement === 'function') {
  171. try {
  172. const sandbox = document.createElement('iframe');
  173. sandbox.hidden = true;
  174. document.head.appendChild(sandbox);
  175. const contentWindow = sandbox.contentWindow;
  176. if (contentWindow && contentWindow.requestAnimationFrame) {
  177. requestAnimationFrameImplementation =
  178. contentWindow.requestAnimationFrame;
  179. }
  180. document.head.removeChild(sandbox);
  181. }
  182. catch (e) {
  183. }
  184. }
  185. return (cachedRequestAnimationFrameImplementation =
  186. requestAnimationFrameImplementation.bind(window));
  187. }
  188. function onRequestAnimationFrame(...rest) {
  189. return getRequestAnimationFrameImplementation()(...rest);
  190. }
  191. var CanvasContext = /* @__PURE__ */ ((CanvasContext2) => {
  192. CanvasContext2[CanvasContext2["2D"] = 0] = "2D";
  193. CanvasContext2[CanvasContext2["WebGL"] = 1] = "WebGL";
  194. CanvasContext2[CanvasContext2["WebGL2"] = 2] = "WebGL2";
  195. return CanvasContext2;
  196. })(CanvasContext || {});
  197. let errorHandler;
  198. function registerErrorHandler(handler) {
  199. errorHandler = handler;
  200. }
  201. const callbackWrapper = (cb) => {
  202. if (!errorHandler) {
  203. return cb;
  204. }
  205. const rrwebWrapped = ((...rest) => {
  206. try {
  207. return cb(...rest);
  208. }
  209. catch (error) {
  210. if (errorHandler && errorHandler(error) === true) {
  211. return () => {
  212. };
  213. }
  214. throw error;
  215. }
  216. });
  217. return rrwebWrapped;
  218. };
  219. /*
  220. * base64-arraybuffer 1.0.1 <https://github.com/niklasvh/base64-arraybuffer>
  221. * Copyright (c) 2021 Niklas von Hertzen <https://hertzen.com>
  222. * Released under MIT License
  223. */
  224. var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  225. // Use a lookup table to find the index.
  226. var lookup = typeof Uint8Array === 'undefined' ? [] : new Uint8Array(256);
  227. for (var i = 0; i < chars.length; i++) {
  228. lookup[chars.charCodeAt(i)] = i;
  229. }
  230. var encode = function (arraybuffer) {
  231. var bytes = new Uint8Array(arraybuffer), i, len = bytes.length, base64 = '';
  232. for (i = 0; i < len; i += 3) {
  233. base64 += chars[bytes[i] >> 2];
  234. base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
  235. base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
  236. base64 += chars[bytes[i + 2] & 63];
  237. }
  238. if (len % 3 === 2) {
  239. base64 = base64.substring(0, base64.length - 1) + '=';
  240. }
  241. else if (len % 3 === 1) {
  242. base64 = base64.substring(0, base64.length - 2) + '==';
  243. }
  244. return base64;
  245. };
  246. const canvasVarMap = new Map();
  247. function variableListFor(ctx, ctor) {
  248. let contextMap = canvasVarMap.get(ctx);
  249. if (!contextMap) {
  250. contextMap = new Map();
  251. canvasVarMap.set(ctx, contextMap);
  252. }
  253. if (!contextMap.has(ctor)) {
  254. contextMap.set(ctor, []);
  255. }
  256. return contextMap.get(ctor);
  257. }
  258. const saveWebGLVar = (value, win, ctx) => {
  259. if (!value ||
  260. !(isInstanceOfWebGLObject(value, win) || typeof value === 'object'))
  261. return;
  262. const name = value.constructor.name;
  263. const list = variableListFor(ctx, name);
  264. let index = list.indexOf(value);
  265. if (index === -1) {
  266. index = list.length;
  267. list.push(value);
  268. }
  269. return index;
  270. };
  271. function serializeArg(value, win, ctx) {
  272. if (value instanceof Array) {
  273. return value.map((arg) => serializeArg(arg, win, ctx));
  274. }
  275. else if (value === null) {
  276. return value;
  277. }
  278. else if (value instanceof Float32Array ||
  279. value instanceof Float64Array ||
  280. value instanceof Int32Array ||
  281. value instanceof Uint32Array ||
  282. value instanceof Uint8Array ||
  283. value instanceof Uint16Array ||
  284. value instanceof Int16Array ||
  285. value instanceof Int8Array ||
  286. value instanceof Uint8ClampedArray) {
  287. const name = value.constructor.name;
  288. return {
  289. rr_type: name,
  290. args: [Object.values(value)],
  291. };
  292. }
  293. else if (value instanceof ArrayBuffer) {
  294. const name = value.constructor.name;
  295. const base64 = encode(value);
  296. return {
  297. rr_type: name,
  298. base64,
  299. };
  300. }
  301. else if (value instanceof DataView) {
  302. const name = value.constructor.name;
  303. return {
  304. rr_type: name,
  305. args: [
  306. serializeArg(value.buffer, win, ctx),
  307. value.byteOffset,
  308. value.byteLength,
  309. ],
  310. };
  311. }
  312. else if (value instanceof HTMLImageElement) {
  313. const name = value.constructor.name;
  314. const { src } = value;
  315. return {
  316. rr_type: name,
  317. src,
  318. };
  319. }
  320. else if (value instanceof HTMLCanvasElement) {
  321. const name = 'HTMLImageElement';
  322. const src = value.toDataURL();
  323. return {
  324. rr_type: name,
  325. src,
  326. };
  327. }
  328. else if (value instanceof ImageData) {
  329. const name = value.constructor.name;
  330. return {
  331. rr_type: name,
  332. args: [serializeArg(value.data, win, ctx), value.width, value.height],
  333. };
  334. }
  335. else if (isInstanceOfWebGLObject(value, win) || typeof value === 'object') {
  336. const name = value.constructor.name;
  337. const index = saveWebGLVar(value, win, ctx);
  338. return {
  339. rr_type: name,
  340. index: index,
  341. };
  342. }
  343. return value;
  344. }
  345. const serializeArgs = (args, win, ctx) => {
  346. return args.map((arg) => serializeArg(arg, win, ctx));
  347. };
  348. const isInstanceOfWebGLObject = (value, win) => {
  349. const webGLConstructorNames = [
  350. 'WebGLActiveInfo',
  351. 'WebGLBuffer',
  352. 'WebGLFramebuffer',
  353. 'WebGLProgram',
  354. 'WebGLRenderbuffer',
  355. 'WebGLShader',
  356. 'WebGLShaderPrecisionFormat',
  357. 'WebGLTexture',
  358. 'WebGLUniformLocation',
  359. 'WebGLVertexArrayObject',
  360. 'WebGLVertexArrayObjectOES',
  361. ];
  362. const supportedWebGLConstructorNames = webGLConstructorNames.filter((name) => typeof win[name] === 'function');
  363. return Boolean(supportedWebGLConstructorNames.find((name) => value instanceof win[name]));
  364. };
  365. function initCanvas2DMutationObserver(cb, win, blockClass, blockSelector, unblockSelector) {
  366. const handlers = [];
  367. const props2D = Object.getOwnPropertyNames(win.CanvasRenderingContext2D.prototype);
  368. for (const prop of props2D) {
  369. try {
  370. if (typeof win.CanvasRenderingContext2D.prototype[prop] !== 'function') {
  371. continue;
  372. }
  373. const restoreHandler = patch(win.CanvasRenderingContext2D.prototype, prop, function (original) {
  374. return function (...args) {
  375. if (!isBlocked(this.canvas, blockClass, blockSelector, unblockSelector, true)) {
  376. setTimeout(() => {
  377. const recordArgs = serializeArgs(args, win, this);
  378. cb(this.canvas, {
  379. type: CanvasContext['2D'],
  380. property: prop,
  381. args: recordArgs,
  382. });
  383. }, 0);
  384. }
  385. return original.apply(this, args);
  386. };
  387. });
  388. handlers.push(restoreHandler);
  389. }
  390. catch (e) {
  391. const hookHandler = hookSetter(win.CanvasRenderingContext2D.prototype, prop, {
  392. set(v) {
  393. cb(this.canvas, {
  394. type: CanvasContext['2D'],
  395. property: prop,
  396. args: [v],
  397. setter: true,
  398. });
  399. },
  400. });
  401. handlers.push(hookHandler);
  402. }
  403. }
  404. return () => {
  405. handlers.forEach((h) => h());
  406. };
  407. }
  408. function getNormalizedContextName(contextType) {
  409. return contextType === 'experimental-webgl' ? 'webgl' : contextType;
  410. }
  411. function initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector, setPreserveDrawingBufferToTrue) {
  412. const handlers = [];
  413. try {
  414. const restoreHandler = patch(win.HTMLCanvasElement.prototype, 'getContext', function (original) {
  415. return function (contextType, ...args) {
  416. if (!isBlocked(this, blockClass, blockSelector, unblockSelector, true)) {
  417. const ctxName = getNormalizedContextName(contextType);
  418. if (!('__context' in this))
  419. this.__context = ctxName;
  420. if (setPreserveDrawingBufferToTrue &&
  421. ['webgl', 'webgl2'].includes(ctxName)) {
  422. if (args[0] && typeof args[0] === 'object') {
  423. const contextAttributes = args[0];
  424. if (!contextAttributes.preserveDrawingBuffer) {
  425. contextAttributes.preserveDrawingBuffer = true;
  426. }
  427. }
  428. else {
  429. args.splice(0, 1, {
  430. preserveDrawingBuffer: true,
  431. });
  432. }
  433. }
  434. }
  435. return original.apply(this, [contextType, ...args]);
  436. };
  437. });
  438. handlers.push(restoreHandler);
  439. }
  440. catch (e) {
  441. console.error('failed to patch HTMLCanvasElement.prototype.getContext');
  442. }
  443. return () => {
  444. handlers.forEach((h) => h());
  445. };
  446. }
  447. function patchGLPrototype(prototype, type, cb, blockClass, blockSelector, unblockSelector, mirror, win) {
  448. const handlers = [];
  449. const props = Object.getOwnPropertyNames(prototype);
  450. for (const prop of props) {
  451. if ([
  452. 'isContextLost',
  453. 'canvas',
  454. 'drawingBufferWidth',
  455. 'drawingBufferHeight',
  456. ].includes(prop)) {
  457. continue;
  458. }
  459. try {
  460. if (typeof prototype[prop] !== 'function') {
  461. continue;
  462. }
  463. const restoreHandler = patch(prototype, prop, function (original) {
  464. return function (...args) {
  465. const result = original.apply(this, args);
  466. saveWebGLVar(result, win, this);
  467. if ('tagName' in this.canvas &&
  468. !isBlocked(this.canvas, blockClass, blockSelector, unblockSelector, true)) {
  469. const recordArgs = serializeArgs(args, win, this);
  470. const mutation = {
  471. type,
  472. property: prop,
  473. args: recordArgs,
  474. };
  475. cb(this.canvas, mutation);
  476. }
  477. return result;
  478. };
  479. });
  480. handlers.push(restoreHandler);
  481. }
  482. catch (e) {
  483. const hookHandler = hookSetter(prototype, prop, {
  484. set(v) {
  485. cb(this.canvas, {
  486. type,
  487. property: prop,
  488. args: [v],
  489. setter: true,
  490. });
  491. },
  492. });
  493. handlers.push(hookHandler);
  494. }
  495. }
  496. return handlers;
  497. }
  498. function initCanvasWebGLMutationObserver(cb, win, blockClass, blockSelector, unblockSelector, mirror) {
  499. const handlers = [];
  500. handlers.push(...patchGLPrototype(win.WebGLRenderingContext.prototype, CanvasContext.WebGL, cb, blockClass, blockSelector, unblockSelector, mirror, win));
  501. if (typeof win.WebGL2RenderingContext !== 'undefined') {
  502. handlers.push(...patchGLPrototype(win.WebGL2RenderingContext.prototype, CanvasContext.WebGL2, cb, blockClass, blockSelector, unblockSelector, mirror, win));
  503. }
  504. return () => {
  505. handlers.forEach((h) => h());
  506. };
  507. }
  508. var r = `for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",e="undefined"==typeof Uint8Array?[]:new Uint8Array(256),n=0;n<64;n++)e[t.charCodeAt(n)]=n;var a=function(e){var n,a=new Uint8Array(e),s=a.length,r="";for(n=0;n<s;n+=3)r+=t[a[n]>>2],r+=t[(3&a[n])<<4|a[n+1]>>4],r+=t[(15&a[n+1])<<2|a[n+2]>>6],r+=t[63&a[n+2]];return s%3==2?r=r.substring(0,r.length-1)+"=":s%3==1&&(r=r.substring(0,r.length-2)+"=="),r};const s=new Map,r=new Map;const i=self;i.onmessage=async function(t){if(!("OffscreenCanvas"in globalThis))return i.postMessage({id:t.data.id});{const{id:e,bitmap:n,width:o,height:f,dataURLOptions:c}=t.data,g=async function(t,e,n){const s=t+"-"+e;if("OffscreenCanvas"in globalThis){if(r.has(s))return r.get(s);const i=new OffscreenCanvas(t,e);i.getContext("2d");const o=await i.convertToBlob(n),f=await o.arrayBuffer(),c=a(f);return r.set(s,c),c}return""}(o,f,c),d=new OffscreenCanvas(o,f);d.getContext("2d").drawImage(n,0,0),n.close();const u=await d.convertToBlob(c),h=u.type,w=await u.arrayBuffer(),l=a(w);if(!s.has(e)&&await g===l)return s.set(e,l),i.postMessage({id:e});if(s.get(e)===l)return i.postMessage({id:e});i.postMessage({id:e,type:h,base64:l,width:o,height:f}),s.set(e,l)}};`;
  509. function t(){const t=new Blob([r]);return URL.createObjectURL(t)}
  510. class CanvasManager {
  511. reset() {
  512. this.pendingCanvasMutations.clear();
  513. this.resetObservers && this.resetObservers();
  514. }
  515. freeze() {
  516. this.frozen = true;
  517. }
  518. unfreeze() {
  519. this.frozen = false;
  520. }
  521. lock() {
  522. this.locked = true;
  523. }
  524. unlock() {
  525. this.locked = false;
  526. }
  527. constructor(options) {
  528. this.pendingCanvasMutations = new Map();
  529. this.rafStamps = { latestId: 0, invokeId: null };
  530. this.frozen = false;
  531. this.locked = false;
  532. this.processMutation = (target, mutation) => {
  533. const newFrame = this.rafStamps.invokeId &&
  534. this.rafStamps.latestId !== this.rafStamps.invokeId;
  535. if (newFrame || !this.rafStamps.invokeId)
  536. this.rafStamps.invokeId = this.rafStamps.latestId;
  537. if (!this.pendingCanvasMutations.has(target)) {
  538. this.pendingCanvasMutations.set(target, []);
  539. }
  540. this.pendingCanvasMutations.get(target).push(mutation);
  541. };
  542. const { sampling = 'all', win, blockClass, blockSelector, unblockSelector, recordCanvas, dataURLOptions, errorHandler, } = options;
  543. this.mutationCb = options.mutationCb;
  544. this.mirror = options.mirror;
  545. this.options = options;
  546. if (errorHandler) {
  547. registerErrorHandler(errorHandler);
  548. }
  549. if (options.enableManualSnapshot) {
  550. return;
  551. }
  552. callbackWrapper(() => {
  553. if (recordCanvas && sampling === 'all')
  554. this.initCanvasMutationObserver(win, blockClass, blockSelector, unblockSelector);
  555. if (recordCanvas && typeof sampling === 'number')
  556. this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector, unblockSelector, {
  557. dataURLOptions,
  558. });
  559. })();
  560. }
  561. initCanvasFPSObserver(fps, win, blockClass, blockSelector, unblockSelector, options) {
  562. const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector, true);
  563. const rafId = this.takeSnapshot(false, fps, win, blockClass, blockSelector, unblockSelector, options.dataURLOptions);
  564. this.resetObservers = () => {
  565. canvasContextReset();
  566. cancelAnimationFrame(rafId);
  567. };
  568. }
  569. initCanvasMutationObserver(win, blockClass, blockSelector, unblockSelector) {
  570. this.startRAFTimestamping();
  571. this.startPendingCanvasMutationFlusher();
  572. const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector, false);
  573. const canvas2DReset = initCanvas2DMutationObserver(this.processMutation.bind(this), win, blockClass, blockSelector, unblockSelector);
  574. const canvasWebGL1and2Reset = initCanvasWebGLMutationObserver(this.processMutation.bind(this), win, blockClass, blockSelector, unblockSelector, this.mirror);
  575. this.resetObservers = () => {
  576. canvasContextReset();
  577. canvas2DReset();
  578. canvasWebGL1and2Reset();
  579. };
  580. }
  581. snapshot(canvasElement) {
  582. const { options } = this;
  583. const rafId = this.takeSnapshot(true, options.sampling === 'all' ? 2 : options.sampling || 2, options.win, options.blockClass, options.blockSelector, options.unblockSelector, options.dataURLOptions, canvasElement);
  584. this.resetObservers = () => {
  585. cancelAnimationFrame(rafId);
  586. };
  587. }
  588. takeSnapshot(isManualSnapshot, fps, win, blockClass, blockSelector, unblockSelector, dataURLOptions, canvasElement) {
  589. const snapshotInProgressMap = new Map();
  590. const worker = new Worker(t());
  591. worker.onmessage = (e) => {
  592. const data = e.data;
  593. const { id } = data;
  594. snapshotInProgressMap.set(id, false);
  595. if (!('base64' in data))
  596. return;
  597. const { base64, type, width, height } = data;
  598. this.mutationCb({
  599. id,
  600. type: CanvasContext['2D'],
  601. commands: [
  602. {
  603. property: 'clearRect',
  604. args: [0, 0, width, height],
  605. },
  606. {
  607. property: 'drawImage',
  608. args: [
  609. {
  610. rr_type: 'ImageBitmap',
  611. args: [
  612. {
  613. rr_type: 'Blob',
  614. data: [{ rr_type: 'ArrayBuffer', base64 }],
  615. type,
  616. },
  617. ],
  618. },
  619. 0,
  620. 0,
  621. ],
  622. },
  623. ],
  624. });
  625. };
  626. const timeBetweenSnapshots = 1000 / fps;
  627. let lastSnapshotTime = 0;
  628. let rafId;
  629. const getCanvas = (canvasElement) => {
  630. if (canvasElement) {
  631. return [canvasElement];
  632. }
  633. const matchedCanvas = [];
  634. win.document.querySelectorAll('canvas').forEach((canvas) => {
  635. if (!isBlocked(canvas, blockClass, blockSelector, unblockSelector, true)) {
  636. matchedCanvas.push(canvas);
  637. }
  638. });
  639. return matchedCanvas;
  640. };
  641. const takeCanvasSnapshots = (timestamp) => {
  642. if (lastSnapshotTime &&
  643. timestamp - lastSnapshotTime < timeBetweenSnapshots) {
  644. rafId = onRequestAnimationFrame(takeCanvasSnapshots);
  645. return;
  646. }
  647. lastSnapshotTime = timestamp;
  648. getCanvas(canvasElement).forEach((canvas) => {
  649. const id = this.mirror.getId(canvas);
  650. if (snapshotInProgressMap.get(id))
  651. return;
  652. snapshotInProgressMap.set(id, true);
  653. if (!isManualSnapshot &&
  654. ['webgl', 'webgl2'].includes(canvas.__context)) {
  655. const context = canvas.getContext(canvas.__context);
  656. if (_optionalChain([context, 'optionalAccess', _ => _.getContextAttributes, 'call', _2 => _2(), 'optionalAccess', _3 => _3.preserveDrawingBuffer]) === false) {
  657. context.clear(context.COLOR_BUFFER_BIT);
  658. }
  659. }
  660. createImageBitmap(canvas)
  661. .then((bitmap) => {
  662. worker.postMessage({
  663. id,
  664. bitmap,
  665. width: canvas.width,
  666. height: canvas.height,
  667. dataURLOptions,
  668. }, [bitmap]);
  669. })
  670. .catch((error) => {
  671. callbackWrapper(() => {
  672. throw error;
  673. })();
  674. });
  675. });
  676. rafId = onRequestAnimationFrame(takeCanvasSnapshots);
  677. };
  678. rafId = onRequestAnimationFrame(takeCanvasSnapshots);
  679. return rafId;
  680. }
  681. startPendingCanvasMutationFlusher() {
  682. onRequestAnimationFrame(() => this.flushPendingCanvasMutations());
  683. }
  684. startRAFTimestamping() {
  685. const setLatestRAFTimestamp = (timestamp) => {
  686. this.rafStamps.latestId = timestamp;
  687. onRequestAnimationFrame(setLatestRAFTimestamp);
  688. };
  689. onRequestAnimationFrame(setLatestRAFTimestamp);
  690. }
  691. flushPendingCanvasMutations() {
  692. this.pendingCanvasMutations.forEach((values, canvas) => {
  693. const id = this.mirror.getId(canvas);
  694. this.flushPendingCanvasMutationFor(canvas, id);
  695. });
  696. onRequestAnimationFrame(() => this.flushPendingCanvasMutations());
  697. }
  698. flushPendingCanvasMutationFor(canvas, id) {
  699. if (this.frozen || this.locked) {
  700. return;
  701. }
  702. const valuesWithType = this.pendingCanvasMutations.get(canvas);
  703. if (!valuesWithType || id === -1)
  704. return;
  705. const values = valuesWithType.map((value) => {
  706. const { type, ...rest } = value;
  707. return rest;
  708. });
  709. const { type } = valuesWithType[0];
  710. this.mutationCb({ id, type, commands: values });
  711. this.pendingCanvasMutations.delete(canvas);
  712. }
  713. }
  714. const CANVAS_QUALITY = {
  715. low: {
  716. sampling: {
  717. canvas: 1,
  718. },
  719. dataURLOptions: {
  720. type: 'image/webp',
  721. quality: 0.25,
  722. },
  723. },
  724. medium: {
  725. sampling: {
  726. canvas: 2,
  727. },
  728. dataURLOptions: {
  729. type: 'image/webp',
  730. quality: 0.4,
  731. },
  732. },
  733. high: {
  734. sampling: {
  735. canvas: 4,
  736. },
  737. dataURLOptions: {
  738. type: 'image/webp',
  739. quality: 0.5,
  740. },
  741. },
  742. };
  743. const INTEGRATION_NAME = 'ReplayCanvas';
  744. /** Exported only for type safe tests. */
  745. const _replayCanvasIntegration = ((options = {}) => {
  746. const _canvasOptions = {
  747. quality: options.quality || 'medium',
  748. enableManualSnapshot: options.enableManualSnapshot,
  749. };
  750. let canvasManagerResolve;
  751. const _canvasManager = new Promise(resolve => (canvasManagerResolve = resolve));
  752. return {
  753. name: INTEGRATION_NAME,
  754. // eslint-disable-next-line @typescript-eslint/no-empty-function
  755. setupOnce() {},
  756. getOptions() {
  757. const { quality, enableManualSnapshot } = _canvasOptions;
  758. return {
  759. enableManualSnapshot,
  760. recordCanvas: true,
  761. getCanvasManager: (options) => {
  762. const manager = new CanvasManager({ ...options, enableManualSnapshot });
  763. canvasManagerResolve(manager);
  764. return manager;
  765. },
  766. ...(CANVAS_QUALITY[quality || 'medium'] || CANVAS_QUALITY.medium),
  767. };
  768. },
  769. async snapshot(canvasElement) {
  770. const canvasManager = await _canvasManager;
  771. canvasManager.snapshot(canvasElement);
  772. },
  773. };
  774. }) ;
  775. /**
  776. * Add this in addition to `replayIntegration()` to enable canvas recording.
  777. */
  778. const replayCanvasIntegration = defineIntegration(_replayCanvasIntegration);
  779. /**
  780. * @deprecated Use `replayCanvasIntegration()` instead
  781. */
  782. // eslint-disable-next-line deprecation/deprecation
  783. const ReplayCanvas = convertIntegrationFnToClass(INTEGRATION_NAME, replayCanvasIntegration)
  784. ;
  785. export { ReplayCanvas, replayCanvasIntegration };
  786. //# sourceMappingURL=index.js.map