index.js 29 KB

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