@sentry/replay 7.66.0 → 7.67.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/cjs/index.js +2281 -1084
  2. package/cjs/index.js.map +1 -1
  3. package/esm/index.js +2281 -1084
  4. package/esm/index.js.map +1 -1
  5. package/package.json +7 -7
  6. package/types/constants.d.ts +2 -0
  7. package/types/constants.d.ts.map +1 -1
  8. package/types/coreHandlers/handleClick.d.ts.map +1 -1
  9. package/types/coreHandlers/handleDom.d.ts +1 -2
  10. package/types/coreHandlers/handleDom.d.ts.map +1 -1
  11. package/types/index.d.ts +2 -1
  12. package/types/index.d.ts.map +1 -1
  13. package/types/integration.d.ts +1 -1
  14. package/types/integration.d.ts.map +1 -1
  15. package/types/replay.d.ts +7 -1
  16. package/types/replay.d.ts.map +1 -1
  17. package/types/session/Session.d.ts.map +1 -1
  18. package/types/session/loadOrCreateSession.d.ts +2 -1
  19. package/types/session/loadOrCreateSession.d.ts.map +1 -1
  20. package/types/session/shouldRefreshSession.d.ts +4 -0
  21. package/types/session/shouldRefreshSession.d.ts.map +1 -0
  22. package/types/types/deprecated.d.ts +16 -0
  23. package/types/types/deprecated.d.ts.map +1 -0
  24. package/types/types/replay.d.ts +9 -9
  25. package/types/types/replay.d.ts.map +1 -1
  26. package/types/types/replayFrame.d.ts +45 -45
  27. package/types/types/replayFrame.d.ts.map +1 -1
  28. package/types/types/rrweb.d.ts +14 -16
  29. package/types/types/rrweb.d.ts.map +1 -1
  30. package/types/util/createBreadcrumb.d.ts +2 -2
  31. package/types/util/createBreadcrumb.d.ts.map +1 -1
  32. package/types/util/getPrivacyOptions.d.ts +0 -2
  33. package/types/util/getPrivacyOptions.d.ts.map +1 -1
  34. package/types/util/handleRecordingEmit.d.ts +2 -2
  35. package/types/util/handleRecordingEmit.d.ts.map +1 -1
  36. package/types/util/maskAttribute.d.ts +15 -0
  37. package/types/util/maskAttribute.d.ts.map +1 -0
  38. package/types/session/maybeRefreshSession.d.ts +0 -11
  39. package/types/session/maybeRefreshSession.d.ts.map +0 -1
package/cjs/index.js CHANGED
@@ -63,44 +63,135 @@ var NodeType$1;
63
63
  NodeType[NodeType["Comment"] = 5] = "Comment";
64
64
  })(NodeType$1 || (NodeType$1 = {}));
65
65
 
66
- function isElement(n) {
66
+ function isElement$1(n) {
67
67
  return n.nodeType === n.ELEMENT_NODE;
68
68
  }
69
69
  function isShadowRoot(n) {
70
70
  const host = n === null || n === void 0 ? void 0 : n.host;
71
- return Boolean(host && host.shadowRoot && host.shadowRoot === n);
71
+ return Boolean((host === null || host === void 0 ? void 0 : host.shadowRoot) === n);
72
72
  }
73
- function isInputTypeMasked({ maskInputOptions, tagName, type, }) {
74
- if (tagName.toLowerCase() === 'option') {
75
- tagName = 'select';
73
+ function isNativeShadowDom(shadowRoot) {
74
+ return Object.prototype.toString.call(shadowRoot) === '[object ShadowRoot]';
75
+ }
76
+ function fixBrowserCompatibilityIssuesInCSS(cssText) {
77
+ if (cssText.includes(' background-clip: text;') &&
78
+ !cssText.includes(' -webkit-background-clip: text;')) {
79
+ cssText = cssText.replace(' background-clip: text;', ' -webkit-background-clip: text; background-clip: text;');
76
80
  }
77
- const actualType = typeof type === 'string' ? type.toLowerCase() : undefined;
78
- return (maskInputOptions[tagName.toLowerCase()] ||
79
- (actualType && maskInputOptions[actualType]) ||
80
- actualType === 'password' ||
81
- (tagName === 'input' && !type && maskInputOptions['text']));
81
+ return cssText;
82
82
  }
83
- function hasInputMaskOptions({ tagName, type, maskInputOptions, maskInputSelector, }) {
84
- return (maskInputSelector || isInputTypeMasked({ maskInputOptions, tagName, type }));
83
+ function getCssRulesString(s) {
84
+ try {
85
+ const rules = s.rules || s.cssRules;
86
+ return rules
87
+ ? fixBrowserCompatibilityIssuesInCSS(Array.from(rules).map(getCssRuleString).join(''))
88
+ : null;
89
+ }
90
+ catch (error) {
91
+ return null;
92
+ }
85
93
  }
86
- function maskInputValue({ input, maskInputSelector, unmaskInputSelector, maskInputOptions, tagName, type, value, maskInputFn, }) {
87
- let text = value || '';
88
- if (unmaskInputSelector && input.matches(unmaskInputSelector)) {
89
- return text;
94
+ function getCssRuleString(rule) {
95
+ let cssStringified = rule.cssText;
96
+ if (isCSSImportRule(rule)) {
97
+ try {
98
+ cssStringified = getCssRulesString(rule.styleSheet) || cssStringified;
99
+ }
100
+ catch (_a) {
101
+ }
102
+ }
103
+ return validateStringifiedCssRule(cssStringified);
104
+ }
105
+ function validateStringifiedCssRule(cssStringified) {
106
+ if (cssStringified.includes(':')) {
107
+ const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
108
+ return cssStringified.replace(regex, '$1\\$2');
109
+ }
110
+ return cssStringified;
111
+ }
112
+ function isCSSImportRule(rule) {
113
+ return 'styleSheet' in rule;
114
+ }
115
+ class Mirror {
116
+ constructor() {
117
+ this.idNodeMap = new Map();
118
+ this.nodeMetaMap = new WeakMap();
90
119
  }
91
- if (input.hasAttribute('data-rr-is-password')) {
92
- type = 'password';
120
+ getId(n) {
121
+ var _a;
122
+ if (!n)
123
+ return -1;
124
+ const id = (_a = this.getMeta(n)) === null || _a === void 0 ? void 0 : _a.id;
125
+ return id !== null && id !== void 0 ? id : -1;
126
+ }
127
+ getNode(id) {
128
+ return this.idNodeMap.get(id) || null;
129
+ }
130
+ getIds() {
131
+ return Array.from(this.idNodeMap.keys());
132
+ }
133
+ getMeta(n) {
134
+ return this.nodeMetaMap.get(n) || null;
93
135
  }
94
- if (isInputTypeMasked({ maskInputOptions, tagName, type }) ||
95
- (maskInputSelector && input.matches(maskInputSelector))) {
96
- if (maskInputFn) {
97
- text = maskInputFn(text);
136
+ removeNodeFromMap(n) {
137
+ const id = this.getId(n);
138
+ this.idNodeMap.delete(id);
139
+ if (n.childNodes) {
140
+ n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode));
98
141
  }
99
- else {
100
- text = '*'.repeat(text.length);
142
+ }
143
+ has(id) {
144
+ return this.idNodeMap.has(id);
145
+ }
146
+ hasNode(node) {
147
+ return this.nodeMetaMap.has(node);
148
+ }
149
+ add(n, meta) {
150
+ const id = meta.id;
151
+ this.idNodeMap.set(id, n);
152
+ this.nodeMetaMap.set(n, meta);
153
+ }
154
+ replace(id, n) {
155
+ const oldNode = this.getNode(id);
156
+ if (oldNode) {
157
+ const meta = this.nodeMetaMap.get(oldNode);
158
+ if (meta)
159
+ this.nodeMetaMap.set(n, meta);
101
160
  }
161
+ this.idNodeMap.set(id, n);
162
+ }
163
+ reset() {
164
+ this.idNodeMap = new Map();
165
+ this.nodeMetaMap = new WeakMap();
166
+ }
167
+ }
168
+ function createMirror() {
169
+ return new Mirror();
170
+ }
171
+ function shouldMaskInput({ maskInputOptions, tagName, type, }) {
172
+ if (tagName === 'OPTION') {
173
+ tagName = 'SELECT';
174
+ }
175
+ return Boolean(maskInputOptions[tagName.toLowerCase()] ||
176
+ (type && maskInputOptions[type]) ||
177
+ type === 'password' ||
178
+ (tagName === 'INPUT' && !type && maskInputOptions['text']));
179
+ }
180
+ function maskInputValue({ isMasked, element, value, maskInputFn, }) {
181
+ let text = value || '';
182
+ if (!isMasked) {
183
+ return text;
184
+ }
185
+ if (maskInputFn) {
186
+ text = maskInputFn(text, element);
102
187
  }
103
- return text;
188
+ return '*'.repeat(text.length);
189
+ }
190
+ function toLowerCase(str) {
191
+ return str.toLowerCase();
192
+ }
193
+ function toUpperCase(str) {
194
+ return str.toUpperCase();
104
195
  }
105
196
  const ORIGINAL_ATTRIBUTE_NAME = '__rrweb_original__';
106
197
  function is2DCanvasBlank(canvas) {
@@ -126,11 +217,11 @@ function getInputType(element) {
126
217
  return element.hasAttribute('data-rr-is-password')
127
218
  ? 'password'
128
219
  : type
129
- ? type.toLowerCase()
220
+ ?
221
+ toLowerCase(type)
130
222
  : null;
131
223
  }
132
224
  function getInputValue(el, tagName, type) {
133
- typeof type === 'string' ? type.toLowerCase() : '';
134
225
  if (tagName === 'INPUT' && (type === 'radio' || type === 'checkbox')) {
135
226
  return el.getAttribute('value') || '';
136
227
  }
@@ -140,9 +231,6 @@ function getInputValue(el, tagName, type) {
140
231
  let _id = 1;
141
232
  const tagNameRegex = new RegExp('[^a-z0-9-_:]');
142
233
  const IGNORED_NODE = -2;
143
- function defaultMaskFn(str) {
144
- return str ? str.replace(/[\S]/g, '*') : '';
145
- }
146
234
  function genId() {
147
235
  return _id++;
148
236
  }
@@ -150,42 +238,12 @@ function getValidTagName(element) {
150
238
  if (element instanceof HTMLFormElement) {
151
239
  return 'form';
152
240
  }
153
- const processedTagName = element.tagName.toLowerCase().trim();
241
+ const processedTagName = toLowerCase(element.tagName);
154
242
  if (tagNameRegex.test(processedTagName)) {
155
243
  return 'div';
156
244
  }
157
245
  return processedTagName;
158
246
  }
159
- function getCssRulesString(s) {
160
- try {
161
- const rules = s.rules || s.cssRules;
162
- return rules ? Array.from(rules).map(getCssRuleString).join('') : null;
163
- }
164
- catch (error) {
165
- return null;
166
- }
167
- }
168
- function getCssRuleString(rule) {
169
- let cssStringified = rule.cssText;
170
- if (isCSSImportRule(rule)) {
171
- try {
172
- cssStringified = getCssRulesString(rule.styleSheet) || cssStringified;
173
- }
174
- catch (_a) {
175
- }
176
- }
177
- return validateStringifiedCssRule(cssStringified);
178
- }
179
- function validateStringifiedCssRule(cssStringified) {
180
- if (cssStringified.indexOf(':') > -1) {
181
- const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
182
- return cssStringified.replace(regex, '$1\\$2');
183
- }
184
- return cssStringified;
185
- }
186
- function isCSSImportRule(rule) {
187
- return 'styleSheet' in rule;
188
- }
189
247
  function stringifyStyleSheet(sheet) {
190
248
  return sheet.cssRules
191
249
  ? Array.from(sheet.cssRules)
@@ -207,7 +265,8 @@ function extractOrigin(url) {
207
265
  let canvasService;
208
266
  let canvasCtx;
209
267
  const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm;
210
- const RELATIVE_PATH = /^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/|#).*/;
268
+ const URL_PROTOCOL_MATCH = /^(?:[a-z+]+:)?\/\//i;
269
+ const URL_WWW_MATCH = /^www\..*/i;
211
270
  const DATA_URI = /^(data:)([^,]*),(.*)/i;
212
271
  function absoluteToStylesheet(cssText, href) {
213
272
  return (cssText || '').replace(URL_IN_CSS_REF, (origin, quote1, path1, quote2, path2, path3) => {
@@ -216,7 +275,7 @@ function absoluteToStylesheet(cssText, href) {
216
275
  if (!filePath) {
217
276
  return origin;
218
277
  }
219
- if (!RELATIVE_PATH.test(filePath)) {
278
+ if (URL_PROTOCOL_MATCH.test(filePath) || URL_WWW_MATCH.test(filePath)) {
220
279
  return `url(${maybeQuote}${filePath}${maybeQuote})`;
221
280
  }
222
281
  if (DATA_URI.test(filePath)) {
@@ -251,7 +310,7 @@ function getAbsoluteSrcsetString(doc, attributeValue) {
251
310
  let pos = 0;
252
311
  function collectCharacters(regEx) {
253
312
  let chars;
254
- let match = regEx.exec(attributeValue.substring(pos));
313
+ const match = regEx.exec(attributeValue.substring(pos));
255
314
  if (match) {
256
315
  chars = match[0];
257
316
  pos += chars.length;
@@ -259,7 +318,7 @@ function getAbsoluteSrcsetString(doc, attributeValue) {
259
318
  }
260
319
  return '';
261
320
  }
262
- let output = [];
321
+ const output = [];
263
322
  while (true) {
264
323
  collectCharacters(SRCSET_COMMAS_OR_SPACES);
265
324
  if (pos >= attributeValue.length) {
@@ -275,7 +334,7 @@ function getAbsoluteSrcsetString(doc, attributeValue) {
275
334
  url = absoluteToDoc(doc, url);
276
335
  let inParens = false;
277
336
  while (true) {
278
- let c = attributeValue.charAt(pos);
337
+ const c = attributeValue.charAt(pos);
279
338
  if (c === '') {
280
339
  output.push((url + descriptorsStr).trim());
281
340
  break;
@@ -318,13 +377,12 @@ function getHref() {
318
377
  a.href = '';
319
378
  return a.href;
320
379
  }
321
- function transformAttribute(doc, element, _tagName, _name, value, maskAllText, unmaskTextSelector, maskTextFn) {
380
+ function transformAttribute(doc, tagName, name, value, element, maskAttributeFn) {
322
381
  if (!value) {
323
382
  return value;
324
383
  }
325
- const name = _name.toLowerCase();
326
- const tagName = _tagName.toLowerCase();
327
- if (name === 'src' || name === 'href') {
384
+ if (name === 'src' ||
385
+ (name === 'href' && !(tagName === 'use' && value[0] === '#'))) {
328
386
  return absoluteToDoc(doc, value);
329
387
  }
330
388
  else if (name === 'xlink:href' && value[0] !== '#') {
@@ -343,79 +401,113 @@ function transformAttribute(doc, element, _tagName, _name, value, maskAllText, u
343
401
  else if (tagName === 'object' && name === 'data') {
344
402
  return absoluteToDoc(doc, value);
345
403
  }
346
- else if (maskAllText &&
347
- _shouldMaskAttribute(element, name, tagName, unmaskTextSelector)) {
348
- return maskTextFn ? maskTextFn(value) : defaultMaskFn(value);
404
+ if (typeof maskAttributeFn === 'function') {
405
+ return maskAttributeFn(name, value, element);
349
406
  }
350
407
  return value;
351
408
  }
352
- function _shouldMaskAttribute(element, attribute, tagName, unmaskTextSelector) {
353
- if (unmaskTextSelector && element.matches(unmaskTextSelector)) {
354
- return false;
355
- }
356
- return (['placeholder', 'title', 'aria-label'].indexOf(attribute) > -1 ||
357
- (tagName === 'input' &&
358
- attribute === 'value' &&
359
- element.hasAttribute('type') &&
360
- ['submit', 'button'].indexOf(element.getAttribute('type').toLowerCase()) > -1));
409
+ function ignoreAttribute(tagName, name, _value) {
410
+ return (tagName === 'video' || tagName === 'audio') && name === 'autoplay';
361
411
  }
362
412
  function _isBlockedElement(element, blockClass, blockSelector, unblockSelector) {
363
- if (unblockSelector && element.matches(unblockSelector)) {
364
- return false;
365
- }
366
- if (typeof blockClass === 'string') {
367
- if (element.classList.contains(blockClass)) {
368
- return true;
413
+ try {
414
+ if (unblockSelector && element.matches(unblockSelector)) {
415
+ return false;
369
416
  }
370
- }
371
- else {
372
- for (let eIndex = 0; eIndex < element.classList.length; eIndex++) {
373
- const className = element.classList[eIndex];
374
- if (blockClass.test(className)) {
417
+ if (typeof blockClass === 'string') {
418
+ if (element.classList.contains(blockClass)) {
375
419
  return true;
376
420
  }
377
421
  }
422
+ else {
423
+ for (let eIndex = element.classList.length; eIndex--;) {
424
+ const className = element.classList[eIndex];
425
+ if (blockClass.test(className)) {
426
+ return true;
427
+ }
428
+ }
429
+ }
430
+ if (blockSelector) {
431
+ return element.matches(blockSelector);
432
+ }
378
433
  }
379
- if (blockSelector) {
380
- return element.matches(blockSelector);
434
+ catch (e) {
381
435
  }
382
436
  return false;
383
437
  }
384
- function needMaskingText(node, maskTextClass, maskTextSelector, unmaskTextSelector, maskAllText) {
385
- if (!node) {
386
- return false;
387
- }
388
- if (node.nodeType !== node.ELEMENT_NODE) {
389
- return needMaskingText(node.parentNode, maskTextClass, maskTextSelector, unmaskTextSelector, maskAllText);
390
- }
391
- if (unmaskTextSelector) {
392
- if (node.matches(unmaskTextSelector) ||
393
- node.closest(unmaskTextSelector)) {
394
- return false;
395
- }
396
- }
397
- if (maskAllText) {
398
- return true;
399
- }
400
- if (typeof maskTextClass === 'string') {
401
- if (node.classList.contains(maskTextClass)) {
438
+ function elementClassMatchesRegex(el, regex) {
439
+ for (let eIndex = el.classList.length; eIndex--;) {
440
+ const className = el.classList[eIndex];
441
+ if (regex.test(className)) {
402
442
  return true;
403
443
  }
404
444
  }
405
- else {
406
- for (let eIndex = 0; eIndex < node.classList.length; eIndex++) {
407
- const className = node.classList[eIndex];
408
- if (maskTextClass.test(className)) {
445
+ return false;
446
+ }
447
+ function distanceToMatch(node, matchPredicate, limit = Infinity, distance = 0) {
448
+ if (!node)
449
+ return -1;
450
+ if (node.nodeType !== node.ELEMENT_NODE)
451
+ return -1;
452
+ if (distance > limit)
453
+ return -1;
454
+ if (matchPredicate(node))
455
+ return distance;
456
+ return distanceToMatch(node.parentNode, matchPredicate, limit, distance + 1);
457
+ }
458
+ function createMatchPredicate(className, selector) {
459
+ return (node) => {
460
+ const el = node;
461
+ if (el === null)
462
+ return false;
463
+ if (className) {
464
+ if (typeof className === 'string') {
465
+ if (el.matches(`.${className}`))
466
+ return true;
467
+ }
468
+ else if (elementClassMatchesRegex(el, className)) {
409
469
  return true;
410
470
  }
411
471
  }
412
- }
413
- if (maskTextSelector) {
414
- if (node.matches(maskTextSelector)) {
472
+ if (selector && el.matches(selector))
415
473
  return true;
474
+ return false;
475
+ };
476
+ }
477
+ function needMaskingText(node, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, maskAllText) {
478
+ try {
479
+ const el = node.nodeType === node.ELEMENT_NODE
480
+ ? node
481
+ : node.parentElement;
482
+ if (el === null)
483
+ return false;
484
+ let maskDistance = -1;
485
+ let unmaskDistance = -1;
486
+ if (maskAllText) {
487
+ unmaskDistance = distanceToMatch(el, createMatchPredicate(unmaskTextClass, unmaskTextSelector));
488
+ if (unmaskDistance < 0) {
489
+ return true;
490
+ }
491
+ maskDistance = distanceToMatch(el, createMatchPredicate(maskTextClass, maskTextSelector), unmaskDistance >= 0 ? unmaskDistance : Infinity);
492
+ }
493
+ else {
494
+ maskDistance = distanceToMatch(el, createMatchPredicate(maskTextClass, maskTextSelector));
495
+ if (maskDistance < 0) {
496
+ return false;
497
+ }
498
+ unmaskDistance = distanceToMatch(el, createMatchPredicate(unmaskTextClass, unmaskTextSelector), maskDistance >= 0 ? maskDistance : Infinity);
416
499
  }
500
+ return maskDistance >= 0
501
+ ? unmaskDistance >= 0
502
+ ? maskDistance <= unmaskDistance
503
+ : true
504
+ : unmaskDistance >= 0
505
+ ? false
506
+ : !!maskAllText;
507
+ }
508
+ catch (e) {
417
509
  }
418
- return needMaskingText(node.parentNode, maskTextClass, maskTextSelector, unmaskTextSelector, maskAllText);
510
+ return !!maskAllText;
419
511
  }
420
512
  function onceIframeLoaded(iframeEl, listener, iframeLoadTimeout) {
421
513
  const win = iframeEl.contentWindow;
@@ -449,18 +541,36 @@ function onceIframeLoaded(iframeEl, listener, iframeLoadTimeout) {
449
541
  iframeEl.src === blankUrl ||
450
542
  iframeEl.src === '') {
451
543
  setTimeout(listener, 0);
452
- return;
544
+ return iframeEl.addEventListener('load', listener);
453
545
  }
454
546
  iframeEl.addEventListener('load', listener);
455
547
  }
456
- function serializeNode(n, options) {
457
- var _a;
458
- const { doc, blockClass, blockSelector, unblockSelector, maskTextClass, maskTextSelector, unmaskTextSelector, inlineStylesheet, maskInputSelector, unmaskInputSelector, maskAllText, maskInputOptions = {}, maskTextFn, maskInputFn, dataURLOptions = {}, inlineImages, recordCanvas, keepIframeSrcFn, } = options;
459
- let rootId;
460
- if (doc.__sn) {
461
- const docId = doc.__sn.id;
462
- rootId = docId === 1 ? undefined : docId;
548
+ function onceStylesheetLoaded(link, listener, styleSheetLoadTimeout) {
549
+ let fired = false;
550
+ let styleSheetLoaded;
551
+ try {
552
+ styleSheetLoaded = link.sheet;
553
+ }
554
+ catch (error) {
555
+ return;
463
556
  }
557
+ if (styleSheetLoaded)
558
+ return;
559
+ const timer = setTimeout(() => {
560
+ if (!fired) {
561
+ listener();
562
+ fired = true;
563
+ }
564
+ }, styleSheetLoadTimeout);
565
+ link.addEventListener('load', () => {
566
+ clearTimeout(timer);
567
+ fired = true;
568
+ listener();
569
+ });
570
+ }
571
+ function serializeNode(n, options) {
572
+ const { doc, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskAttributeFn, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, inlineStylesheet, maskInputOptions = {}, maskTextFn, maskInputFn, dataURLOptions = {}, inlineImages, recordCanvas, keepIframeSrcFn, newlyAddedElement = false, } = options;
573
+ const rootId = getRootId(doc, mirror);
464
574
  switch (n.nodeType) {
465
575
  case n.DOCUMENT_NODE:
466
576
  if (n.compatMode !== 'CSS1Compat') {
@@ -468,14 +578,12 @@ function serializeNode(n, options) {
468
578
  type: NodeType$1.Document,
469
579
  childNodes: [],
470
580
  compatMode: n.compatMode,
471
- rootId,
472
581
  };
473
582
  }
474
583
  else {
475
584
  return {
476
585
  type: NodeType$1.Document,
477
586
  childNodes: [],
478
- rootId,
479
587
  };
480
588
  }
481
589
  case n.DOCUMENT_TYPE_NODE:
@@ -487,202 +595,39 @@ function serializeNode(n, options) {
487
595
  rootId,
488
596
  };
489
597
  case n.ELEMENT_NODE:
490
- const needBlock = _isBlockedElement(n, blockClass, blockSelector, unblockSelector);
491
- const tagName = getValidTagName(n);
492
- let attributes = {};
493
- for (const { name, value } of Array.from(n.attributes)) {
494
- if (!skipAttribute(tagName, name)) {
495
- attributes[name] = transformAttribute(doc, n, tagName, name, value, maskAllText, unmaskTextSelector, maskTextFn);
496
- }
497
- }
498
- if (tagName === 'link' && inlineStylesheet) {
499
- const stylesheet = Array.from(doc.styleSheets).find((s) => {
500
- return s.href === n.href;
501
- });
502
- let cssText = null;
503
- if (stylesheet) {
504
- cssText = getCssRulesString(stylesheet);
505
- }
506
- if (cssText) {
507
- delete attributes.rel;
508
- delete attributes.href;
509
- attributes._cssText = absoluteToStylesheet(cssText, stylesheet.href);
510
- }
511
- }
512
- if (tagName === 'style' &&
513
- n.sheet &&
514
- !(n.innerText ||
515
- n.textContent ||
516
- '').trim().length) {
517
- const cssText = getCssRulesString(n.sheet);
518
- if (cssText) {
519
- attributes._cssText = absoluteToStylesheet(cssText, getHref());
520
- }
521
- }
522
- if (tagName === 'input' ||
523
- tagName === 'textarea' ||
524
- tagName === 'select' ||
525
- tagName === 'option') {
526
- const el = n;
527
- const type = getInputType(el);
528
- const value = getInputValue(el, tagName.toUpperCase(), type);
529
- const checked = n.checked;
530
- if (type !== 'submit' &&
531
- type !== 'button' &&
532
- value) {
533
- attributes.value = maskInputValue({
534
- input: el,
535
- type,
536
- tagName,
537
- value,
538
- maskInputSelector,
539
- unmaskInputSelector,
540
- maskInputOptions,
541
- maskInputFn,
542
- });
543
- }
544
- if (checked) {
545
- attributes.checked = checked;
546
- }
547
- }
548
- if (tagName === 'option') {
549
- if (n.selected && !maskInputOptions['select']) {
550
- attributes.selected = true;
551
- }
552
- else {
553
- delete attributes.selected;
554
- }
555
- }
556
- if (tagName === 'canvas' && recordCanvas) {
557
- if (n.__context === '2d') {
558
- if (!is2DCanvasBlank(n)) {
559
- attributes.rr_dataURL = n.toDataURL(dataURLOptions.type, dataURLOptions.quality);
560
- }
561
- }
562
- else if (!('__context' in n)) {
563
- const canvasDataURL = n.toDataURL(dataURLOptions.type, dataURLOptions.quality);
564
- const blankCanvas = document.createElement('canvas');
565
- blankCanvas.width = n.width;
566
- blankCanvas.height = n.height;
567
- const blankCanvasDataURL = blankCanvas.toDataURL(dataURLOptions.type, dataURLOptions.quality);
568
- if (canvasDataURL !== blankCanvasDataURL) {
569
- attributes.rr_dataURL = canvasDataURL;
570
- }
571
- }
572
- }
573
- if (tagName === 'img' && inlineImages) {
574
- if (!canvasService) {
575
- canvasService = doc.createElement('canvas');
576
- canvasCtx = canvasService.getContext('2d');
577
- }
578
- const image = n;
579
- const oldValue = image.crossOrigin;
580
- image.crossOrigin = 'anonymous';
581
- const recordInlineImage = () => {
582
- try {
583
- canvasService.width = image.naturalWidth;
584
- canvasService.height = image.naturalHeight;
585
- canvasCtx.drawImage(image, 0, 0);
586
- attributes.rr_dataURL = canvasService.toDataURL(dataURLOptions.type, dataURLOptions.quality);
587
- }
588
- catch (err) {
589
- console.warn(`Cannot inline img src=${image.currentSrc}! Error: ${err}`);
590
- }
591
- oldValue
592
- ? (attributes.crossOrigin = oldValue)
593
- : delete attributes.crossOrigin;
594
- };
595
- if (image.complete && image.naturalWidth !== 0)
596
- recordInlineImage();
597
- else
598
- image.onload = recordInlineImage;
599
- }
600
- if (tagName === 'audio' || tagName === 'video') {
601
- attributes.rr_mediaState = n.paused
602
- ? 'paused'
603
- : 'played';
604
- attributes.rr_mediaCurrentTime = n.currentTime;
605
- }
606
- if (n.scrollLeft) {
607
- attributes.rr_scrollLeft = n.scrollLeft;
608
- }
609
- if (n.scrollTop) {
610
- attributes.rr_scrollTop = n.scrollTop;
611
- }
612
- if (needBlock) {
613
- const { width, height } = n.getBoundingClientRect();
614
- attributes = {
615
- class: attributes.class,
616
- rr_width: `${width}px`,
617
- rr_height: `${height}px`,
618
- };
619
- }
620
- if (tagName === 'iframe' && !keepIframeSrcFn(attributes.src)) {
621
- if (!n.contentDocument) {
622
- attributes.rr_src = attributes.src;
623
- }
624
- delete attributes.src;
625
- }
626
- return {
627
- type: NodeType$1.Element,
628
- tagName,
629
- attributes,
630
- childNodes: [],
631
- isSVG: isSVGElement(n) || undefined,
632
- needBlock,
598
+ return serializeElementNode(n, {
599
+ doc,
600
+ blockClass,
601
+ blockSelector,
602
+ unblockSelector,
603
+ inlineStylesheet,
604
+ maskAttributeFn,
605
+ maskInputOptions,
606
+ maskInputFn,
607
+ dataURLOptions,
608
+ inlineImages,
609
+ recordCanvas,
610
+ keepIframeSrcFn,
611
+ newlyAddedElement,
633
612
  rootId,
634
- };
613
+ maskAllText,
614
+ maskTextClass,
615
+ unmaskTextClass,
616
+ maskTextSelector,
617
+ unmaskTextSelector,
618
+ });
635
619
  case n.TEXT_NODE:
636
- const parentTagName = n.parentNode && n.parentNode.tagName;
637
- let textContent = n.textContent;
638
- const isStyle = parentTagName === 'STYLE' ? true : undefined;
639
- const isScript = parentTagName === 'SCRIPT' ? true : undefined;
640
- if (isStyle && textContent) {
641
- try {
642
- if (n.nextSibling || n.previousSibling) {
643
- }
644
- else if ((_a = n.parentNode.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) {
645
- textContent = stringifyStyleSheet(n.parentNode.sheet);
646
- }
647
- }
648
- catch (err) {
649
- console.warn(`Cannot get CSS styles from text's parentNode. Error: ${err}`, n);
650
- }
651
- textContent = absoluteToStylesheet(textContent, getHref());
652
- }
653
- if (isScript) {
654
- textContent = 'SCRIPT_PLACEHOLDER';
655
- }
656
- if (parentTagName === 'TEXTAREA' && textContent) {
657
- textContent = '';
658
- }
659
- else if (parentTagName === 'OPTION' && textContent) {
660
- const option = n.parentNode;
661
- textContent = maskInputValue({
662
- input: option,
663
- type: null,
664
- tagName: parentTagName,
665
- value: textContent,
666
- maskInputSelector,
667
- unmaskInputSelector,
668
- maskInputOptions,
669
- maskInputFn,
670
- });
671
- }
672
- else if (!isStyle &&
673
- !isScript &&
674
- needMaskingText(n, maskTextClass, maskTextSelector, unmaskTextSelector, maskAllText) &&
675
- textContent) {
676
- textContent = maskTextFn
677
- ? maskTextFn(textContent)
678
- : defaultMaskFn(textContent);
679
- }
680
- return {
681
- type: NodeType$1.Text,
682
- textContent: textContent || '',
683
- isStyle,
620
+ return serializeTextNode(n, {
621
+ maskAllText,
622
+ maskTextClass,
623
+ unmaskTextClass,
624
+ maskTextSelector,
625
+ unmaskTextSelector,
626
+ maskTextFn,
627
+ maskInputOptions,
628
+ maskInputFn,
684
629
  rootId,
685
- };
630
+ });
686
631
  case n.CDATA_SECTION_NODE:
687
632
  return {
688
633
  type: NodeType$1.CDATA,
@@ -699,30 +644,249 @@ function serializeNode(n, options) {
699
644
  return false;
700
645
  }
701
646
  }
702
- function lowerIfExists(maybeAttr) {
703
- if (maybeAttr === undefined || maybeAttr === null) {
704
- return '';
705
- }
706
- else {
707
- return maybeAttr.toLowerCase();
647
+ function getRootId(doc, mirror) {
648
+ if (!mirror.hasNode(doc))
649
+ return undefined;
650
+ const docId = mirror.getId(doc);
651
+ return docId === 1 ? undefined : docId;
652
+ }
653
+ function serializeTextNode(n, options) {
654
+ var _a;
655
+ const { maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, maskTextFn, maskInputOptions, maskInputFn, rootId, } = options;
656
+ const parentTagName = n.parentNode && n.parentNode.tagName;
657
+ let textContent = n.textContent;
658
+ const isStyle = parentTagName === 'STYLE' ? true : undefined;
659
+ const isScript = parentTagName === 'SCRIPT' ? true : undefined;
660
+ const isTextarea = parentTagName === 'TEXTAREA' ? true : undefined;
661
+ if (isStyle && textContent) {
662
+ try {
663
+ if (n.nextSibling || n.previousSibling) {
664
+ }
665
+ else if ((_a = n.parentNode.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) {
666
+ textContent = stringifyStyleSheet(n.parentNode.sheet);
667
+ }
668
+ }
669
+ catch (err) {
670
+ console.warn(`Cannot get CSS styles from text's parentNode. Error: ${err}`, n);
671
+ }
672
+ textContent = absoluteToStylesheet(textContent, getHref());
673
+ }
674
+ if (isScript) {
675
+ textContent = 'SCRIPT_PLACEHOLDER';
676
+ }
677
+ const forceMask = needMaskingText(n, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, maskAllText);
678
+ if (!isStyle && !isScript && !isTextarea && textContent && forceMask) {
679
+ textContent = maskTextFn
680
+ ? maskTextFn(textContent)
681
+ : textContent.replace(/[\S]/g, '*');
682
+ }
683
+ if (isTextarea && textContent && (maskInputOptions.textarea || forceMask)) {
684
+ textContent = maskInputFn
685
+ ? maskInputFn(textContent, n.parentNode)
686
+ : textContent.replace(/[\S]/g, '*');
687
+ }
688
+ if (parentTagName === 'OPTION' && textContent) {
689
+ const isInputMasked = shouldMaskInput({
690
+ type: null,
691
+ tagName: parentTagName,
692
+ maskInputOptions,
693
+ });
694
+ textContent = maskInputValue({
695
+ isMasked: needMaskingText(n, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, isInputMasked),
696
+ element: n,
697
+ value: textContent,
698
+ maskInputFn,
699
+ });
708
700
  }
701
+ return {
702
+ type: NodeType$1.Text,
703
+ textContent: textContent || '',
704
+ isStyle,
705
+ rootId,
706
+ };
709
707
  }
710
- function slimDOMExcluded(sn, slimDOMOptions) {
711
- if (slimDOMOptions.comment && sn.type === NodeType$1.Comment) {
712
- return true;
708
+ function serializeElementNode(n, options) {
709
+ const { doc, blockClass, blockSelector, unblockSelector, inlineStylesheet, maskInputOptions = {}, maskAttributeFn, maskInputFn, dataURLOptions = {}, inlineImages, recordCanvas, keepIframeSrcFn, newlyAddedElement = false, rootId, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, } = options;
710
+ const needBlock = _isBlockedElement(n, blockClass, blockSelector, unblockSelector);
711
+ const tagName = getValidTagName(n);
712
+ let attributes = {};
713
+ const len = n.attributes.length;
714
+ for (let i = 0; i < len; i++) {
715
+ const attr = n.attributes[i];
716
+ if (!ignoreAttribute(tagName, attr.name, attr.value)) {
717
+ attributes[attr.name] = transformAttribute(doc, tagName, toLowerCase(attr.name), attr.value, n, maskAttributeFn);
718
+ }
713
719
  }
714
- else if (sn.type === NodeType$1.Element) {
715
- if (slimDOMOptions.script &&
716
- (sn.tagName === 'script' ||
717
- (sn.tagName === 'link' &&
718
- (sn.attributes.rel === 'preload' ||
719
- sn.attributes.rel === 'modulepreload') &&
720
- sn.attributes.as === 'script') ||
721
- (sn.tagName === 'link' &&
722
- sn.attributes.rel === 'prefetch' &&
723
- typeof sn.attributes.href === 'string' &&
724
- sn.attributes.href.endsWith('.js')))) {
725
- return true;
720
+ if (tagName === 'link' && inlineStylesheet) {
721
+ const stylesheet = Array.from(doc.styleSheets).find((s) => {
722
+ return s.href === n.href;
723
+ });
724
+ let cssText = null;
725
+ if (stylesheet) {
726
+ cssText = getCssRulesString(stylesheet);
727
+ }
728
+ if (cssText) {
729
+ delete attributes.rel;
730
+ delete attributes.href;
731
+ attributes._cssText = absoluteToStylesheet(cssText, stylesheet.href);
732
+ }
733
+ }
734
+ if (tagName === 'style' &&
735
+ n.sheet &&
736
+ !(n.innerText || n.textContent || '').trim().length) {
737
+ const cssText = getCssRulesString(n.sheet);
738
+ if (cssText) {
739
+ attributes._cssText = absoluteToStylesheet(cssText, getHref());
740
+ }
741
+ }
742
+ if (tagName === 'input' ||
743
+ tagName === 'textarea' ||
744
+ tagName === 'select' ||
745
+ tagName === 'option') {
746
+ const el = n;
747
+ const type = getInputType(el);
748
+ const value = getInputValue(el, toUpperCase(tagName), type);
749
+ const checked = el.checked;
750
+ if (type !== 'submit' && type !== 'button' && value) {
751
+ const forceMask = needMaskingText(el, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, shouldMaskInput({
752
+ type,
753
+ tagName: toUpperCase(tagName),
754
+ maskInputOptions,
755
+ }));
756
+ attributes.value = maskInputValue({
757
+ isMasked: forceMask,
758
+ element: el,
759
+ value,
760
+ maskInputFn,
761
+ });
762
+ }
763
+ if (checked) {
764
+ attributes.checked = checked;
765
+ }
766
+ }
767
+ if (tagName === 'option') {
768
+ if (n.selected && !maskInputOptions['select']) {
769
+ attributes.selected = true;
770
+ }
771
+ else {
772
+ delete attributes.selected;
773
+ }
774
+ }
775
+ if (tagName === 'canvas' && recordCanvas) {
776
+ if (n.__context === '2d') {
777
+ if (!is2DCanvasBlank(n)) {
778
+ attributes.rr_dataURL = n.toDataURL(dataURLOptions.type, dataURLOptions.quality);
779
+ }
780
+ }
781
+ else if (!('__context' in n)) {
782
+ const canvasDataURL = n.toDataURL(dataURLOptions.type, dataURLOptions.quality);
783
+ const blankCanvas = document.createElement('canvas');
784
+ blankCanvas.width = n.width;
785
+ blankCanvas.height = n.height;
786
+ const blankCanvasDataURL = blankCanvas.toDataURL(dataURLOptions.type, dataURLOptions.quality);
787
+ if (canvasDataURL !== blankCanvasDataURL) {
788
+ attributes.rr_dataURL = canvasDataURL;
789
+ }
790
+ }
791
+ }
792
+ if (tagName === 'img' && inlineImages) {
793
+ if (!canvasService) {
794
+ canvasService = doc.createElement('canvas');
795
+ canvasCtx = canvasService.getContext('2d');
796
+ }
797
+ const image = n;
798
+ const oldValue = image.crossOrigin;
799
+ image.crossOrigin = 'anonymous';
800
+ const recordInlineImage = () => {
801
+ image.removeEventListener('load', recordInlineImage);
802
+ try {
803
+ canvasService.width = image.naturalWidth;
804
+ canvasService.height = image.naturalHeight;
805
+ canvasCtx.drawImage(image, 0, 0);
806
+ attributes.rr_dataURL = canvasService.toDataURL(dataURLOptions.type, dataURLOptions.quality);
807
+ }
808
+ catch (err) {
809
+ console.warn(`Cannot inline img src=${image.currentSrc}! Error: ${err}`);
810
+ }
811
+ oldValue
812
+ ? (attributes.crossOrigin = oldValue)
813
+ : image.removeAttribute('crossorigin');
814
+ };
815
+ if (image.complete && image.naturalWidth !== 0)
816
+ recordInlineImage();
817
+ else
818
+ image.addEventListener('load', recordInlineImage);
819
+ }
820
+ if (tagName === 'audio' || tagName === 'video') {
821
+ attributes.rr_mediaState = n.paused
822
+ ? 'paused'
823
+ : 'played';
824
+ attributes.rr_mediaCurrentTime = n.currentTime;
825
+ }
826
+ if (!newlyAddedElement) {
827
+ if (n.scrollLeft) {
828
+ attributes.rr_scrollLeft = n.scrollLeft;
829
+ }
830
+ if (n.scrollTop) {
831
+ attributes.rr_scrollTop = n.scrollTop;
832
+ }
833
+ }
834
+ if (needBlock) {
835
+ const { width, height } = n.getBoundingClientRect();
836
+ attributes = {
837
+ class: attributes.class,
838
+ rr_width: `${width}px`,
839
+ rr_height: `${height}px`,
840
+ };
841
+ }
842
+ if (tagName === 'iframe' && !keepIframeSrcFn(attributes.src)) {
843
+ if (!n.contentDocument) {
844
+ attributes.rr_src = attributes.src;
845
+ }
846
+ delete attributes.src;
847
+ }
848
+ let isCustomElement;
849
+ try {
850
+ if (customElements.get(tagName))
851
+ isCustomElement = true;
852
+ }
853
+ catch (e) {
854
+ }
855
+ return {
856
+ type: NodeType$1.Element,
857
+ tagName,
858
+ attributes,
859
+ childNodes: [],
860
+ isSVG: isSVGElement(n) || undefined,
861
+ needBlock,
862
+ rootId,
863
+ isCustom: isCustomElement,
864
+ };
865
+ }
866
+ function lowerIfExists(maybeAttr) {
867
+ if (maybeAttr === undefined || maybeAttr === null) {
868
+ return '';
869
+ }
870
+ else {
871
+ return maybeAttr.toLowerCase();
872
+ }
873
+ }
874
+ function slimDOMExcluded(sn, slimDOMOptions) {
875
+ if (slimDOMOptions.comment && sn.type === NodeType$1.Comment) {
876
+ return true;
877
+ }
878
+ else if (sn.type === NodeType$1.Element) {
879
+ if (slimDOMOptions.script &&
880
+ (sn.tagName === 'script' ||
881
+ (sn.tagName === 'link' &&
882
+ (sn.attributes.rel === 'preload' ||
883
+ sn.attributes.rel === 'modulepreload') &&
884
+ sn.attributes.as === 'script') ||
885
+ (sn.tagName === 'link' &&
886
+ sn.attributes.rel === 'prefetch' &&
887
+ typeof sn.attributes.href === 'string' &&
888
+ sn.attributes.href.endsWith('.js')))) {
889
+ return true;
726
890
  }
727
891
  else if (slimDOMOptions.headFavicon &&
728
892
  ((sn.tagName === 'link' && sn.attributes.rel === 'shortcut icon') ||
@@ -780,35 +944,37 @@ function slimDOMExcluded(sn, slimDOMOptions) {
780
944
  return false;
781
945
  }
782
946
  function serializeNodeWithId(n, options) {
783
- const { doc, map, blockClass, blockSelector, unblockSelector, maskTextClass, maskTextSelector, unmaskTextSelector, skipChild = false, inlineStylesheet = true, maskInputSelector, unmaskInputSelector, maskAllText, maskInputOptions = {}, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions = {}, inlineImages = false, recordCanvas = false, onSerialize, onIframeLoad, iframeLoadTimeout = 5000, keepIframeSrcFn = () => false, } = options;
947
+ const { doc, mirror, blockClass, blockSelector, unblockSelector, maskAllText, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, skipChild = false, inlineStylesheet = true, maskInputOptions = {}, maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, dataURLOptions = {}, inlineImages = false, recordCanvas = false, onSerialize, onIframeLoad, iframeLoadTimeout = 5000, onStylesheetLoad, stylesheetLoadTimeout = 5000, keepIframeSrcFn = () => false, newlyAddedElement = false, } = options;
784
948
  let { preserveWhiteSpace = true } = options;
785
949
  const _serializedNode = serializeNode(n, {
786
950
  doc,
951
+ mirror,
787
952
  blockClass,
788
953
  blockSelector,
954
+ maskAllText,
789
955
  unblockSelector,
790
956
  maskTextClass,
957
+ unmaskTextClass,
791
958
  maskTextSelector,
792
959
  unmaskTextSelector,
793
960
  inlineStylesheet,
794
- maskInputSelector,
795
- unmaskInputSelector,
796
- maskAllText,
797
961
  maskInputOptions,
962
+ maskAttributeFn,
798
963
  maskTextFn,
799
964
  maskInputFn,
800
965
  dataURLOptions,
801
966
  inlineImages,
802
967
  recordCanvas,
803
968
  keepIframeSrcFn,
969
+ newlyAddedElement,
804
970
  });
805
971
  if (!_serializedNode) {
806
972
  console.warn(n, 'not serialized');
807
973
  return null;
808
974
  }
809
975
  let id;
810
- if ('__sn' in n) {
811
- id = n.__sn.id;
976
+ if (mirror.hasNode(n)) {
977
+ id = mirror.getId(n);
812
978
  }
813
979
  else if (slimDOMExcluded(_serializedNode, slimDOMOptions) ||
814
980
  (!preserveWhiteSpace &&
@@ -821,11 +987,10 @@ function serializeNodeWithId(n, options) {
821
987
  id = genId();
822
988
  }
823
989
  const serializedNode = Object.assign(_serializedNode, { id });
824
- n.__sn = serializedNode;
990
+ mirror.add(n, serializedNode);
825
991
  if (id === IGNORED_NODE) {
826
992
  return null;
827
993
  }
828
- map[id] = n;
829
994
  if (onSerialize) {
830
995
  onSerialize(n);
831
996
  }
@@ -833,32 +998,33 @@ function serializeNodeWithId(n, options) {
833
998
  if (serializedNode.type === NodeType$1.Element) {
834
999
  recordChild = recordChild && !serializedNode.needBlock;
835
1000
  delete serializedNode.needBlock;
836
- if (n.shadowRoot)
1001
+ const shadowRoot = n.shadowRoot;
1002
+ if (shadowRoot && isNativeShadowDom(shadowRoot))
837
1003
  serializedNode.isShadowHost = true;
838
1004
  }
839
1005
  if ((serializedNode.type === NodeType$1.Document ||
840
1006
  serializedNode.type === NodeType$1.Element) &&
841
1007
  recordChild) {
842
1008
  if (slimDOMOptions.headWhitespace &&
843
- _serializedNode.type === NodeType$1.Element &&
844
- _serializedNode.tagName === 'head') {
1009
+ serializedNode.type === NodeType$1.Element &&
1010
+ serializedNode.tagName === 'head') {
845
1011
  preserveWhiteSpace = false;
846
1012
  }
847
1013
  const bypassOptions = {
848
1014
  doc,
849
- map,
1015
+ mirror,
850
1016
  blockClass,
851
1017
  blockSelector,
1018
+ maskAllText,
852
1019
  unblockSelector,
853
1020
  maskTextClass,
1021
+ unmaskTextClass,
854
1022
  maskTextSelector,
855
1023
  unmaskTextSelector,
856
1024
  skipChild,
857
1025
  inlineStylesheet,
858
- maskInputSelector,
859
- unmaskInputSelector,
860
- maskAllText,
861
1026
  maskInputOptions,
1027
+ maskAttributeFn,
862
1028
  maskTextFn,
863
1029
  maskInputFn,
864
1030
  slimDOMOptions,
@@ -869,6 +1035,8 @@ function serializeNodeWithId(n, options) {
869
1035
  onSerialize,
870
1036
  onIframeLoad,
871
1037
  iframeLoadTimeout,
1038
+ onStylesheetLoad,
1039
+ stylesheetLoadTimeout,
872
1040
  keepIframeSrcFn,
873
1041
  };
874
1042
  for (const childN of Array.from(n.childNodes)) {
@@ -877,17 +1045,20 @@ function serializeNodeWithId(n, options) {
877
1045
  serializedNode.childNodes.push(serializedChildNode);
878
1046
  }
879
1047
  }
880
- if (isElement(n) && n.shadowRoot) {
1048
+ if (isElement$1(n) && n.shadowRoot) {
881
1049
  for (const childN of Array.from(n.shadowRoot.childNodes)) {
882
1050
  const serializedChildNode = serializeNodeWithId(childN, bypassOptions);
883
1051
  if (serializedChildNode) {
884
- serializedChildNode.isShadow = true;
1052
+ isNativeShadowDom(n.shadowRoot) &&
1053
+ (serializedChildNode.isShadow = true);
885
1054
  serializedNode.childNodes.push(serializedChildNode);
886
1055
  }
887
1056
  }
888
1057
  }
889
1058
  }
890
- if (n.parentNode && isShadowRoot(n.parentNode)) {
1059
+ if (n.parentNode &&
1060
+ isShadowRoot(n.parentNode) &&
1061
+ isNativeShadowDom(n.parentNode)) {
891
1062
  serializedNode.isShadow = true;
892
1063
  }
893
1064
  if (serializedNode.type === NodeType$1.Element &&
@@ -897,19 +1068,19 @@ function serializeNodeWithId(n, options) {
897
1068
  if (iframeDoc && onIframeLoad) {
898
1069
  const serializedIframeNode = serializeNodeWithId(iframeDoc, {
899
1070
  doc: iframeDoc,
900
- map,
1071
+ mirror,
901
1072
  blockClass,
902
1073
  blockSelector,
903
1074
  unblockSelector,
1075
+ maskAllText,
904
1076
  maskTextClass,
1077
+ unmaskTextClass,
905
1078
  maskTextSelector,
906
1079
  unmaskTextSelector,
907
1080
  skipChild: false,
908
1081
  inlineStylesheet,
909
- maskInputSelector,
910
- unmaskInputSelector,
911
- maskAllText,
912
1082
  maskInputOptions,
1083
+ maskAttributeFn,
913
1084
  maskTextFn,
914
1085
  maskInputFn,
915
1086
  slimDOMOptions,
@@ -920,6 +1091,8 @@ function serializeNodeWithId(n, options) {
920
1091
  onSerialize,
921
1092
  onIframeLoad,
922
1093
  iframeLoadTimeout,
1094
+ onStylesheetLoad,
1095
+ stylesheetLoadTimeout,
923
1096
  keepIframeSrcFn,
924
1097
  });
925
1098
  if (serializedIframeNode) {
@@ -928,11 +1101,50 @@ function serializeNodeWithId(n, options) {
928
1101
  }
929
1102
  }, iframeLoadTimeout);
930
1103
  }
1104
+ if (serializedNode.type === NodeType$1.Element &&
1105
+ serializedNode.tagName === 'link' &&
1106
+ serializedNode.attributes.rel === 'stylesheet') {
1107
+ onceStylesheetLoaded(n, () => {
1108
+ if (onStylesheetLoad) {
1109
+ const serializedLinkNode = serializeNodeWithId(n, {
1110
+ doc,
1111
+ mirror,
1112
+ blockClass,
1113
+ blockSelector,
1114
+ unblockSelector,
1115
+ maskAllText,
1116
+ maskTextClass,
1117
+ unmaskTextClass,
1118
+ maskTextSelector,
1119
+ unmaskTextSelector,
1120
+ skipChild: false,
1121
+ inlineStylesheet,
1122
+ maskInputOptions,
1123
+ maskAttributeFn,
1124
+ maskTextFn,
1125
+ maskInputFn,
1126
+ slimDOMOptions,
1127
+ dataURLOptions,
1128
+ inlineImages,
1129
+ recordCanvas,
1130
+ preserveWhiteSpace,
1131
+ onSerialize,
1132
+ onIframeLoad,
1133
+ iframeLoadTimeout,
1134
+ onStylesheetLoad,
1135
+ stylesheetLoadTimeout,
1136
+ keepIframeSrcFn,
1137
+ });
1138
+ if (serializedLinkNode) {
1139
+ onStylesheetLoad(n, serializedLinkNode);
1140
+ }
1141
+ }
1142
+ }, stylesheetLoadTimeout);
1143
+ }
931
1144
  return serializedNode;
932
1145
  }
933
1146
  function snapshot(n, options) {
934
- const { blockClass = 'rr-block', blockSelector = null, unblockSelector = null, maskTextClass = 'rr-mask', maskTextSelector = null, unmaskTextSelector = null, inlineStylesheet = true, inlineImages = false, recordCanvas = false, maskInputSelector = null, unmaskInputSelector = null, maskAllText = false, maskAllInputs = false, maskTextFn, maskInputFn, slimDOM = false, dataURLOptions, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, keepIframeSrcFn = () => false, } = options || {};
935
- const idNodeMap = {};
1147
+ const { mirror = new Mirror(), blockClass = 'rr-block', blockSelector = null, unblockSelector = null, maskAllText = false, maskTextClass = 'rr-mask', unmaskTextClass = null, maskTextSelector = null, unmaskTextSelector = null, inlineStylesheet = true, inlineImages = false, recordCanvas = false, maskAllInputs = false, maskAttributeFn, maskTextFn, maskInputFn, slimDOM = false, dataURLOptions, preserveWhiteSpace, onSerialize, onIframeLoad, iframeLoadTimeout, onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn = () => false, } = options || {};
936
1148
  const maskInputOptions = maskAllInputs === true
937
1149
  ? {
938
1150
  color: true,
@@ -971,147 +1183,43 @@ function snapshot(n, options) {
971
1183
  : slimDOM === false
972
1184
  ? {}
973
1185
  : slimDOM;
974
- return [
975
- serializeNodeWithId(n, {
976
- doc: n,
977
- map: idNodeMap,
978
- blockClass,
979
- blockSelector,
980
- unblockSelector,
981
- maskTextClass,
982
- maskTextSelector,
983
- unmaskTextSelector,
984
- skipChild: false,
985
- inlineStylesheet,
986
- maskInputSelector,
987
- unmaskInputSelector,
988
- maskAllText,
989
- maskInputOptions,
990
- maskTextFn,
991
- maskInputFn,
992
- slimDOMOptions,
993
- dataURLOptions,
994
- inlineImages,
995
- recordCanvas,
996
- preserveWhiteSpace,
997
- onSerialize,
998
- onIframeLoad,
999
- iframeLoadTimeout,
1000
- keepIframeSrcFn,
1001
- }),
1002
- idNodeMap,
1003
- ];
1186
+ return serializeNodeWithId(n, {
1187
+ doc: n,
1188
+ mirror,
1189
+ blockClass,
1190
+ blockSelector,
1191
+ unblockSelector,
1192
+ maskAllText,
1193
+ maskTextClass,
1194
+ unmaskTextClass,
1195
+ maskTextSelector,
1196
+ unmaskTextSelector,
1197
+ skipChild: false,
1198
+ inlineStylesheet,
1199
+ maskInputOptions,
1200
+ maskAttributeFn,
1201
+ maskTextFn,
1202
+ maskInputFn,
1203
+ slimDOMOptions,
1204
+ dataURLOptions,
1205
+ inlineImages,
1206
+ recordCanvas,
1207
+ preserveWhiteSpace,
1208
+ onSerialize,
1209
+ onIframeLoad,
1210
+ iframeLoadTimeout,
1211
+ onStylesheetLoad,
1212
+ stylesheetLoadTimeout,
1213
+ keepIframeSrcFn,
1214
+ newlyAddedElement: false,
1215
+ });
1004
1216
  }
1005
- function skipAttribute(tagName, attributeName, value) {
1006
- return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
1007
- }
1008
-
1009
- var EventType;
1010
- (function (EventType) {
1011
- EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
1012
- EventType[EventType["Load"] = 1] = "Load";
1013
- EventType[EventType["FullSnapshot"] = 2] = "FullSnapshot";
1014
- EventType[EventType["IncrementalSnapshot"] = 3] = "IncrementalSnapshot";
1015
- EventType[EventType["Meta"] = 4] = "Meta";
1016
- EventType[EventType["Custom"] = 5] = "Custom";
1017
- EventType[EventType["Plugin"] = 6] = "Plugin";
1018
- })(EventType || (EventType = {}));
1019
- var IncrementalSource;
1020
- (function (IncrementalSource) {
1021
- IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
1022
- IncrementalSource[IncrementalSource["MouseMove"] = 1] = "MouseMove";
1023
- IncrementalSource[IncrementalSource["MouseInteraction"] = 2] = "MouseInteraction";
1024
- IncrementalSource[IncrementalSource["Scroll"] = 3] = "Scroll";
1025
- IncrementalSource[IncrementalSource["ViewportResize"] = 4] = "ViewportResize";
1026
- IncrementalSource[IncrementalSource["Input"] = 5] = "Input";
1027
- IncrementalSource[IncrementalSource["TouchMove"] = 6] = "TouchMove";
1028
- IncrementalSource[IncrementalSource["MediaInteraction"] = 7] = "MediaInteraction";
1029
- IncrementalSource[IncrementalSource["StyleSheetRule"] = 8] = "StyleSheetRule";
1030
- IncrementalSource[IncrementalSource["CanvasMutation"] = 9] = "CanvasMutation";
1031
- IncrementalSource[IncrementalSource["Font"] = 10] = "Font";
1032
- IncrementalSource[IncrementalSource["Log"] = 11] = "Log";
1033
- IncrementalSource[IncrementalSource["Drag"] = 12] = "Drag";
1034
- IncrementalSource[IncrementalSource["StyleDeclaration"] = 13] = "StyleDeclaration";
1035
- })(IncrementalSource || (IncrementalSource = {}));
1036
- var MouseInteractions;
1037
- (function (MouseInteractions) {
1038
- MouseInteractions[MouseInteractions["MouseUp"] = 0] = "MouseUp";
1039
- MouseInteractions[MouseInteractions["MouseDown"] = 1] = "MouseDown";
1040
- MouseInteractions[MouseInteractions["Click"] = 2] = "Click";
1041
- MouseInteractions[MouseInteractions["ContextMenu"] = 3] = "ContextMenu";
1042
- MouseInteractions[MouseInteractions["DblClick"] = 4] = "DblClick";
1043
- MouseInteractions[MouseInteractions["Focus"] = 5] = "Focus";
1044
- MouseInteractions[MouseInteractions["Blur"] = 6] = "Blur";
1045
- MouseInteractions[MouseInteractions["TouchStart"] = 7] = "TouchStart";
1046
- MouseInteractions[MouseInteractions["TouchMove_Departed"] = 8] = "TouchMove_Departed";
1047
- MouseInteractions[MouseInteractions["TouchEnd"] = 9] = "TouchEnd";
1048
- MouseInteractions[MouseInteractions["TouchCancel"] = 10] = "TouchCancel";
1049
- })(MouseInteractions || (MouseInteractions = {}));
1050
- var CanvasContext;
1051
- (function (CanvasContext) {
1052
- CanvasContext[CanvasContext["2D"] = 0] = "2D";
1053
- CanvasContext[CanvasContext["WebGL"] = 1] = "WebGL";
1054
- CanvasContext[CanvasContext["WebGL2"] = 2] = "WebGL2";
1055
- })(CanvasContext || (CanvasContext = {}));
1056
- var MediaInteractions;
1057
- (function (MediaInteractions) {
1058
- MediaInteractions[MediaInteractions["Play"] = 0] = "Play";
1059
- MediaInteractions[MediaInteractions["Pause"] = 1] = "Pause";
1060
- MediaInteractions[MediaInteractions["Seeked"] = 2] = "Seeked";
1061
- MediaInteractions[MediaInteractions["VolumeChange"] = 3] = "VolumeChange";
1062
- })(MediaInteractions || (MediaInteractions = {}));
1063
- var ReplayerEvents;
1064
- (function (ReplayerEvents) {
1065
- ReplayerEvents["Start"] = "start";
1066
- ReplayerEvents["Pause"] = "pause";
1067
- ReplayerEvents["Resume"] = "resume";
1068
- ReplayerEvents["Resize"] = "resize";
1069
- ReplayerEvents["Finish"] = "finish";
1070
- ReplayerEvents["FullsnapshotRebuilded"] = "fullsnapshot-rebuilded";
1071
- ReplayerEvents["LoadStylesheetStart"] = "load-stylesheet-start";
1072
- ReplayerEvents["LoadStylesheetEnd"] = "load-stylesheet-end";
1073
- ReplayerEvents["SkipStart"] = "skip-start";
1074
- ReplayerEvents["SkipEnd"] = "skip-end";
1075
- ReplayerEvents["MouseInteraction"] = "mouse-interaction";
1076
- ReplayerEvents["EventCast"] = "event-cast";
1077
- ReplayerEvents["CustomEvent"] = "custom-event";
1078
- ReplayerEvents["Flush"] = "flush";
1079
- ReplayerEvents["StateChange"] = "state-change";
1080
- ReplayerEvents["PlayBack"] = "play-back";
1081
- })(ReplayerEvents || (ReplayerEvents = {}));
1082
1217
 
1083
1218
  function on(type, fn, target = document) {
1084
1219
  const options = { capture: true, passive: true };
1085
1220
  target.addEventListener(type, fn, options);
1086
1221
  return () => target.removeEventListener(type, fn, options);
1087
1222
  }
1088
- function createMirror() {
1089
- return {
1090
- map: {},
1091
- getId(n) {
1092
- if (!n || !n.__sn) {
1093
- return -1;
1094
- }
1095
- return n.__sn.id;
1096
- },
1097
- getNode(id) {
1098
- return this.map[id] || null;
1099
- },
1100
- removeNodeFromMap(n) {
1101
- const id = n.__sn && n.__sn.id;
1102
- delete this.map[id];
1103
- if (n.childNodes) {
1104
- n.childNodes.forEach((child) => this.removeNodeFromMap(child));
1105
- }
1106
- },
1107
- has(id) {
1108
- return this.map.hasOwnProperty(id);
1109
- },
1110
- reset() {
1111
- this.map = {};
1112
- },
1113
- };
1114
- }
1115
1223
  const DEPARTED_MIRROR_ACCESS_WARNING = 'Please stop import mirror directly. Instead of that,' +
1116
1224
  '\r\n' +
1117
1225
  'now you can use replayer.getMirror() to access the mirror instance of a replayer,' +
@@ -1151,14 +1259,13 @@ if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
1151
1259
  function throttle$1(func, wait, options = {}) {
1152
1260
  let timeout = null;
1153
1261
  let previous = 0;
1154
- return function (arg) {
1155
- let now = Date.now();
1262
+ return function (...args) {
1263
+ const now = Date.now();
1156
1264
  if (!previous && options.leading === false) {
1157
1265
  previous = now;
1158
1266
  }
1159
- let remaining = wait - (now - previous);
1160
- let context = this;
1161
- let args = arguments;
1267
+ const remaining = wait - (now - previous);
1268
+ const context = this;
1162
1269
  if (remaining <= 0 || remaining > wait) {
1163
1270
  if (timeout) {
1164
1271
  clearTimeout(timeout);
@@ -1195,7 +1302,8 @@ function hookSetter(target, key, d, isRevoked, win = window) {
1195
1302
  function patch(source, name, replacement) {
1196
1303
  try {
1197
1304
  if (!(name in source)) {
1198
- return () => { };
1305
+ return () => {
1306
+ };
1199
1307
  }
1200
1308
  const original = source[name];
1201
1309
  const wrapped = replacement(original);
@@ -1214,9 +1322,36 @@ function patch(source, name, replacement) {
1214
1322
  };
1215
1323
  }
1216
1324
  catch (_a) {
1217
- return () => { };
1325
+ return () => {
1326
+ };
1218
1327
  }
1219
1328
  }
1329
+ let nowTimestamp = Date.now;
1330
+ if (!(/[1-9][0-9]{12}/.test(Date.now().toString()))) {
1331
+ nowTimestamp = () => new Date().getTime();
1332
+ }
1333
+ function getWindowScroll(win) {
1334
+ var _a, _b, _c, _d, _e, _f;
1335
+ const doc = win.document;
1336
+ return {
1337
+ left: doc.scrollingElement
1338
+ ? doc.scrollingElement.scrollLeft
1339
+ : win.pageXOffset !== undefined
1340
+ ? win.pageXOffset
1341
+ : (doc === null || doc === void 0 ? void 0 : doc.documentElement.scrollLeft) ||
1342
+ ((_b = (_a = doc === null || doc === void 0 ? void 0 : doc.body) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.scrollLeft) ||
1343
+ ((_c = doc === null || doc === void 0 ? void 0 : doc.body) === null || _c === void 0 ? void 0 : _c.scrollLeft) ||
1344
+ 0,
1345
+ top: doc.scrollingElement
1346
+ ? doc.scrollingElement.scrollTop
1347
+ : win.pageYOffset !== undefined
1348
+ ? win.pageYOffset
1349
+ : (doc === null || doc === void 0 ? void 0 : doc.documentElement.scrollTop) ||
1350
+ ((_e = (_d = doc === null || doc === void 0 ? void 0 : doc.body) === null || _d === void 0 ? void 0 : _d.parentElement) === null || _e === void 0 ? void 0 : _e.scrollTop) ||
1351
+ ((_f = doc === null || doc === void 0 ? void 0 : doc.body) === null || _f === void 0 ? void 0 : _f.scrollTop) ||
1352
+ 0,
1353
+ };
1354
+ }
1220
1355
  function getWindowHeight() {
1221
1356
  return (window.innerHeight ||
1222
1357
  (document.documentElement && document.documentElement.clientHeight) ||
@@ -1227,48 +1362,38 @@ function getWindowWidth() {
1227
1362
  (document.documentElement && document.documentElement.clientWidth) ||
1228
1363
  (document.body && document.body.clientWidth));
1229
1364
  }
1230
- function isBlocked(node, blockClass, blockSelector, unblockSelector) {
1365
+ function isBlocked(node, blockClass, blockSelector, unblockSelector, checkAncestors) {
1231
1366
  if (!node) {
1232
1367
  return false;
1233
1368
  }
1234
- if (node.nodeType === node.ELEMENT_NODE) {
1235
- let needBlock = false;
1236
- const needUnblock = unblockSelector && node.matches(unblockSelector);
1237
- if (typeof blockClass === 'string') {
1238
- if (node.closest !== undefined) {
1239
- needBlock =
1240
- !needUnblock &&
1241
- node.closest('.' + blockClass) !== null;
1242
- }
1243
- else {
1244
- needBlock =
1245
- !needUnblock && node.classList.contains(blockClass);
1246
- }
1247
- }
1248
- else {
1249
- !needUnblock &&
1250
- node.classList.forEach((className) => {
1251
- if (blockClass.test(className)) {
1252
- needBlock = true;
1253
- }
1254
- });
1255
- }
1256
- if (!needBlock && blockSelector) {
1257
- needBlock = node.matches(blockSelector);
1258
- }
1259
- return ((!needUnblock && needBlock) ||
1260
- isBlocked(node.parentNode, blockClass, blockSelector, unblockSelector));
1369
+ const el = node.nodeType === node.ELEMENT_NODE
1370
+ ? node
1371
+ : node.parentElement;
1372
+ if (!el)
1373
+ return false;
1374
+ const blockedPredicate = createMatchPredicate(blockClass, blockSelector);
1375
+ if (!checkAncestors) {
1376
+ const isUnblocked = unblockSelector && el.matches(unblockSelector);
1377
+ return blockedPredicate(el) && !isUnblocked;
1378
+ }
1379
+ const blockDistance = distanceToMatch(el, blockedPredicate);
1380
+ let unblockDistance = -1;
1381
+ if (blockDistance < 0) {
1382
+ return false;
1261
1383
  }
1262
- if (node.nodeType === node.TEXT_NODE) {
1263
- return isBlocked(node.parentNode, blockClass, blockSelector, unblockSelector);
1384
+ if (unblockSelector) {
1385
+ unblockDistance = distanceToMatch(el, createMatchPredicate(null, unblockSelector));
1264
1386
  }
1265
- return isBlocked(node.parentNode, blockClass, blockSelector, unblockSelector);
1266
- }
1267
- function isIgnored(n) {
1268
- if ('__sn' in n) {
1269
- return n.__sn.id === IGNORED_NODE;
1387
+ if (blockDistance > -1 && unblockDistance < 0) {
1388
+ return true;
1270
1389
  }
1271
- return false;
1390
+ return blockDistance < unblockDistance;
1391
+ }
1392
+ function isSerialized(n, mirror) {
1393
+ return mirror.getId(n) !== -1;
1394
+ }
1395
+ function isIgnored(n, mirror) {
1396
+ return mirror.getId(n) === IGNORED_NODE;
1272
1397
  }
1273
1398
  function isAncestorRemoved(target, mirror) {
1274
1399
  if (isShadowRoot(target)) {
@@ -1287,7 +1412,7 @@ function isAncestorRemoved(target, mirror) {
1287
1412
  }
1288
1413
  return isAncestorRemoved(target.parentNode, mirror);
1289
1414
  }
1290
- function isTouchEvent(event) {
1415
+ function legacy_isTouchEvent(event) {
1291
1416
  return Boolean(event.changedTouches);
1292
1417
  }
1293
1418
  function polyfill(win = window) {
@@ -1300,8 +1425,9 @@ function polyfill(win = window) {
1300
1425
  .forEach;
1301
1426
  }
1302
1427
  if (!Node.prototype.contains) {
1303
- Node.prototype.contains = function contains(node) {
1304
- if (!(0 in arguments)) {
1428
+ Node.prototype.contains = (...args) => {
1429
+ let node = args[0];
1430
+ if (!(0 in args)) {
1305
1431
  throw new TypeError('1 argument is required');
1306
1432
  }
1307
1433
  do {
@@ -1313,15 +1439,142 @@ function polyfill(win = window) {
1313
1439
  };
1314
1440
  }
1315
1441
  }
1316
- function isIframeINode(node) {
1317
- if ('__sn' in node) {
1318
- return (node.__sn.type === NodeType$1.Element && node.__sn.tagName === 'iframe');
1319
- }
1320
- return false;
1442
+ function isSerializedIframe(n, mirror) {
1443
+ return Boolean(n.nodeName === 'IFRAME' && mirror.getMeta(n));
1444
+ }
1445
+ function isSerializedStylesheet(n, mirror) {
1446
+ return Boolean(n.nodeName === 'LINK' &&
1447
+ n.nodeType === n.ELEMENT_NODE &&
1448
+ n.getAttribute &&
1449
+ n.getAttribute('rel') === 'stylesheet' &&
1450
+ mirror.getMeta(n));
1321
1451
  }
1322
1452
  function hasShadowRoot(n) {
1323
1453
  return Boolean(n === null || n === void 0 ? void 0 : n.shadowRoot);
1324
1454
  }
1455
+ class StyleSheetMirror {
1456
+ constructor() {
1457
+ this.id = 1;
1458
+ this.styleIDMap = new WeakMap();
1459
+ this.idStyleMap = new Map();
1460
+ }
1461
+ getId(stylesheet) {
1462
+ var _a;
1463
+ return (_a = this.styleIDMap.get(stylesheet)) !== null && _a !== void 0 ? _a : -1;
1464
+ }
1465
+ has(stylesheet) {
1466
+ return this.styleIDMap.has(stylesheet);
1467
+ }
1468
+ add(stylesheet, id) {
1469
+ if (this.has(stylesheet))
1470
+ return this.getId(stylesheet);
1471
+ let newId;
1472
+ if (id === undefined) {
1473
+ newId = this.id++;
1474
+ }
1475
+ else
1476
+ newId = id;
1477
+ this.styleIDMap.set(stylesheet, newId);
1478
+ this.idStyleMap.set(newId, stylesheet);
1479
+ return newId;
1480
+ }
1481
+ getStyle(id) {
1482
+ return this.idStyleMap.get(id) || null;
1483
+ }
1484
+ reset() {
1485
+ this.styleIDMap = new WeakMap();
1486
+ this.idStyleMap = new Map();
1487
+ this.id = 1;
1488
+ }
1489
+ generateId() {
1490
+ return this.id++;
1491
+ }
1492
+ }
1493
+ function getShadowHost(n) {
1494
+ var _a, _b;
1495
+ let shadowHost = null;
1496
+ if (((_b = (_a = n.getRootNode) === null || _a === void 0 ? void 0 : _a.call(n)) === null || _b === void 0 ? void 0 : _b.nodeType) === Node.DOCUMENT_FRAGMENT_NODE &&
1497
+ n.getRootNode().host)
1498
+ shadowHost = n.getRootNode().host;
1499
+ return shadowHost;
1500
+ }
1501
+ function getRootShadowHost(n) {
1502
+ let rootShadowHost = n;
1503
+ let shadowHost;
1504
+ while ((shadowHost = getShadowHost(rootShadowHost)))
1505
+ rootShadowHost = shadowHost;
1506
+ return rootShadowHost;
1507
+ }
1508
+ function shadowHostInDom(n) {
1509
+ const doc = n.ownerDocument;
1510
+ if (!doc)
1511
+ return false;
1512
+ const shadowHost = getRootShadowHost(n);
1513
+ return doc.contains(shadowHost);
1514
+ }
1515
+ function inDom(n) {
1516
+ const doc = n.ownerDocument;
1517
+ if (!doc)
1518
+ return false;
1519
+ return doc.contains(n) || shadowHostInDom(n);
1520
+ }
1521
+
1522
+ var EventType = /* @__PURE__ */ ((EventType2) => {
1523
+ EventType2[EventType2["DomContentLoaded"] = 0] = "DomContentLoaded";
1524
+ EventType2[EventType2["Load"] = 1] = "Load";
1525
+ EventType2[EventType2["FullSnapshot"] = 2] = "FullSnapshot";
1526
+ EventType2[EventType2["IncrementalSnapshot"] = 3] = "IncrementalSnapshot";
1527
+ EventType2[EventType2["Meta"] = 4] = "Meta";
1528
+ EventType2[EventType2["Custom"] = 5] = "Custom";
1529
+ EventType2[EventType2["Plugin"] = 6] = "Plugin";
1530
+ return EventType2;
1531
+ })(EventType || {});
1532
+ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
1533
+ IncrementalSource2[IncrementalSource2["Mutation"] = 0] = "Mutation";
1534
+ IncrementalSource2[IncrementalSource2["MouseMove"] = 1] = "MouseMove";
1535
+ IncrementalSource2[IncrementalSource2["MouseInteraction"] = 2] = "MouseInteraction";
1536
+ IncrementalSource2[IncrementalSource2["Scroll"] = 3] = "Scroll";
1537
+ IncrementalSource2[IncrementalSource2["ViewportResize"] = 4] = "ViewportResize";
1538
+ IncrementalSource2[IncrementalSource2["Input"] = 5] = "Input";
1539
+ IncrementalSource2[IncrementalSource2["TouchMove"] = 6] = "TouchMove";
1540
+ IncrementalSource2[IncrementalSource2["MediaInteraction"] = 7] = "MediaInteraction";
1541
+ IncrementalSource2[IncrementalSource2["StyleSheetRule"] = 8] = "StyleSheetRule";
1542
+ IncrementalSource2[IncrementalSource2["CanvasMutation"] = 9] = "CanvasMutation";
1543
+ IncrementalSource2[IncrementalSource2["Font"] = 10] = "Font";
1544
+ IncrementalSource2[IncrementalSource2["Log"] = 11] = "Log";
1545
+ IncrementalSource2[IncrementalSource2["Drag"] = 12] = "Drag";
1546
+ IncrementalSource2[IncrementalSource2["StyleDeclaration"] = 13] = "StyleDeclaration";
1547
+ IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
1548
+ IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
1549
+ IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
1550
+ return IncrementalSource2;
1551
+ })(IncrementalSource || {});
1552
+ var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
1553
+ MouseInteractions2[MouseInteractions2["MouseUp"] = 0] = "MouseUp";
1554
+ MouseInteractions2[MouseInteractions2["MouseDown"] = 1] = "MouseDown";
1555
+ MouseInteractions2[MouseInteractions2["Click"] = 2] = "Click";
1556
+ MouseInteractions2[MouseInteractions2["ContextMenu"] = 3] = "ContextMenu";
1557
+ MouseInteractions2[MouseInteractions2["DblClick"] = 4] = "DblClick";
1558
+ MouseInteractions2[MouseInteractions2["Focus"] = 5] = "Focus";
1559
+ MouseInteractions2[MouseInteractions2["Blur"] = 6] = "Blur";
1560
+ MouseInteractions2[MouseInteractions2["TouchStart"] = 7] = "TouchStart";
1561
+ MouseInteractions2[MouseInteractions2["TouchMove_Departed"] = 8] = "TouchMove_Departed";
1562
+ MouseInteractions2[MouseInteractions2["TouchEnd"] = 9] = "TouchEnd";
1563
+ MouseInteractions2[MouseInteractions2["TouchCancel"] = 10] = "TouchCancel";
1564
+ return MouseInteractions2;
1565
+ })(MouseInteractions || {});
1566
+ var PointerTypes = /* @__PURE__ */ ((PointerTypes2) => {
1567
+ PointerTypes2[PointerTypes2["Mouse"] = 0] = "Mouse";
1568
+ PointerTypes2[PointerTypes2["Pen"] = 1] = "Pen";
1569
+ PointerTypes2[PointerTypes2["Touch"] = 2] = "Touch";
1570
+ return PointerTypes2;
1571
+ })(PointerTypes || {});
1572
+ var CanvasContext = /* @__PURE__ */ ((CanvasContext2) => {
1573
+ CanvasContext2[CanvasContext2["2D"] = 0] = "2D";
1574
+ CanvasContext2[CanvasContext2["WebGL"] = 1] = "WebGL";
1575
+ CanvasContext2[CanvasContext2["WebGL2"] = 2] = "WebGL2";
1576
+ return CanvasContext2;
1577
+ })(CanvasContext || {});
1325
1578
 
1326
1579
  function isNodeInLinkedList(n) {
1327
1580
  return '__ln' in n;
@@ -1330,6 +1583,7 @@ class DoubleLinkedList {
1330
1583
  constructor() {
1331
1584
  this.length = 0;
1332
1585
  this.head = null;
1586
+ this.tail = null;
1333
1587
  }
1334
1588
  get(position) {
1335
1589
  if (position >= this.length) {
@@ -1375,6 +1629,9 @@ class DoubleLinkedList {
1375
1629
  node.next = this.head;
1376
1630
  this.head = node;
1377
1631
  }
1632
+ if (node.next === null) {
1633
+ this.tail = node;
1634
+ }
1378
1635
  this.length++;
1379
1636
  }
1380
1637
  removeNode(n) {
@@ -1387,12 +1644,18 @@ class DoubleLinkedList {
1387
1644
  if (this.head) {
1388
1645
  this.head.previous = null;
1389
1646
  }
1647
+ else {
1648
+ this.tail = null;
1649
+ }
1390
1650
  }
1391
1651
  else {
1392
1652
  current.previous.next = current.next;
1393
1653
  if (current.next) {
1394
1654
  current.next.previous = current.previous;
1395
1655
  }
1656
+ else {
1657
+ this.tail = current.previous;
1658
+ }
1396
1659
  }
1397
1660
  if (n.__ln) {
1398
1661
  delete n.__ln;
@@ -1401,9 +1664,6 @@ class DoubleLinkedList {
1401
1664
  }
1402
1665
  }
1403
1666
  const moveKey = (id, parentId) => `${id}@${parentId}`;
1404
- function isINode(n) {
1405
- return '__sn' in n;
1406
- }
1407
1667
  class MutationBuffer {
1408
1668
  constructor() {
1409
1669
  this.frozen = false;
@@ -1425,6 +1685,7 @@ class MutationBuffer {
1425
1685
  return;
1426
1686
  }
1427
1687
  const adds = [];
1688
+ const addedIds = new Set();
1428
1689
  const addList = new DoubleLinkedList();
1429
1690
  const getNextId = (n) => {
1430
1691
  let ns = n;
@@ -1436,59 +1697,56 @@ class MutationBuffer {
1436
1697
  return nextId;
1437
1698
  };
1438
1699
  const pushAdd = (n) => {
1439
- var _a, _b, _c, _d, _e;
1440
- const shadowHost = n.getRootNode
1441
- ? (_a = n.getRootNode()) === null || _a === void 0 ? void 0 : _a.host
1442
- : null;
1443
- let rootShadowHost = shadowHost;
1444
- while ((_c = (_b = rootShadowHost === null || rootShadowHost === void 0 ? void 0 : rootShadowHost.getRootNode) === null || _b === void 0 ? void 0 : _b.call(rootShadowHost)) === null || _c === void 0 ? void 0 : _c.host)
1445
- rootShadowHost =
1446
- ((_e = (_d = rootShadowHost === null || rootShadowHost === void 0 ? void 0 : rootShadowHost.getRootNode) === null || _d === void 0 ? void 0 : _d.call(rootShadowHost)) === null || _e === void 0 ? void 0 : _e.host) ||
1447
- null;
1448
- const notInDoc = !this.doc.contains(n) &&
1449
- (!rootShadowHost || !this.doc.contains(rootShadowHost));
1450
- if (!n.parentNode || notInDoc) {
1700
+ if (!n.parentNode || !inDom(n)) {
1451
1701
  return;
1452
1702
  }
1453
1703
  const parentId = isShadowRoot(n.parentNode)
1454
- ? this.mirror.getId(shadowHost)
1704
+ ? this.mirror.getId(getShadowHost(n))
1455
1705
  : this.mirror.getId(n.parentNode);
1456
1706
  const nextId = getNextId(n);
1457
1707
  if (parentId === -1 || nextId === -1) {
1458
1708
  return addList.addNode(n);
1459
1709
  }
1460
- let sn = serializeNodeWithId(n, {
1710
+ const sn = serializeNodeWithId(n, {
1461
1711
  doc: this.doc,
1462
- map: this.mirror.map,
1712
+ mirror: this.mirror,
1463
1713
  blockClass: this.blockClass,
1464
1714
  blockSelector: this.blockSelector,
1715
+ maskAllText: this.maskAllText,
1465
1716
  unblockSelector: this.unblockSelector,
1466
1717
  maskTextClass: this.maskTextClass,
1718
+ unmaskTextClass: this.unmaskTextClass,
1467
1719
  maskTextSelector: this.maskTextSelector,
1468
1720
  unmaskTextSelector: this.unmaskTextSelector,
1469
- maskInputSelector: this.maskInputSelector,
1470
- unmaskInputSelector: this.unmaskInputSelector,
1471
1721
  skipChild: true,
1722
+ newlyAddedElement: true,
1472
1723
  inlineStylesheet: this.inlineStylesheet,
1473
- maskAllText: this.maskAllText,
1474
1724
  maskInputOptions: this.maskInputOptions,
1725
+ maskAttributeFn: this.maskAttributeFn,
1475
1726
  maskTextFn: this.maskTextFn,
1476
1727
  maskInputFn: this.maskInputFn,
1477
1728
  slimDOMOptions: this.slimDOMOptions,
1729
+ dataURLOptions: this.dataURLOptions,
1478
1730
  recordCanvas: this.recordCanvas,
1479
1731
  inlineImages: this.inlineImages,
1480
1732
  onSerialize: (currentN) => {
1481
- if (isIframeINode(currentN)) {
1733
+ if (isSerializedIframe(currentN, this.mirror)) {
1482
1734
  this.iframeManager.addIframe(currentN);
1483
1735
  }
1736
+ if (isSerializedStylesheet(currentN, this.mirror)) {
1737
+ this.stylesheetManager.trackLinkElement(currentN);
1738
+ }
1484
1739
  if (hasShadowRoot(n)) {
1485
- this.shadowDomManager.addShadowRoot(n.shadowRoot, document);
1740
+ this.shadowDomManager.addShadowRoot(n.shadowRoot, this.doc);
1486
1741
  }
1487
1742
  },
1488
1743
  onIframeLoad: (iframe, childSn) => {
1489
1744
  this.iframeManager.attachIframe(iframe, childSn);
1490
1745
  this.shadowDomManager.observeAttachShadow(iframe);
1491
1746
  },
1747
+ onStylesheetLoad: (link, childSn) => {
1748
+ this.stylesheetManager.attachLinkElement(link, childSn);
1749
+ },
1492
1750
  });
1493
1751
  if (sn) {
1494
1752
  adds.push({
@@ -1496,6 +1754,7 @@ class MutationBuffer {
1496
1754
  nextId,
1497
1755
  node: sn,
1498
1756
  });
1757
+ addedIds.add(sn.id);
1499
1758
  }
1500
1759
  };
1501
1760
  while (this.mapRemoves.length) {
@@ -1531,15 +1790,33 @@ class MutationBuffer {
1531
1790
  }
1532
1791
  }
1533
1792
  if (!node) {
1534
- for (let index = addList.length - 1; index >= 0; index--) {
1535
- const _node = addList.get(index);
1793
+ let tailNode = addList.tail;
1794
+ while (tailNode) {
1795
+ const _node = tailNode;
1796
+ tailNode = tailNode.previous;
1536
1797
  if (_node) {
1537
1798
  const parentId = this.mirror.getId(_node.value.parentNode);
1538
1799
  const nextId = getNextId(_node.value);
1539
- if (parentId !== -1 && nextId !== -1) {
1800
+ if (nextId === -1)
1801
+ continue;
1802
+ else if (parentId !== -1) {
1540
1803
  node = _node;
1541
1804
  break;
1542
1805
  }
1806
+ else {
1807
+ const unhandledNode = _node.value;
1808
+ if (unhandledNode.parentNode &&
1809
+ unhandledNode.parentNode.nodeType ===
1810
+ Node.DOCUMENT_FRAGMENT_NODE) {
1811
+ const shadowHost = unhandledNode.parentNode
1812
+ .host;
1813
+ const parentId = this.mirror.getId(shadowHost);
1814
+ if (parentId !== -1) {
1815
+ node = _node;
1816
+ break;
1817
+ }
1818
+ }
1819
+ }
1543
1820
  }
1544
1821
  }
1545
1822
  }
@@ -1559,12 +1836,27 @@ class MutationBuffer {
1559
1836
  id: this.mirror.getId(text.node),
1560
1837
  value: text.value,
1561
1838
  }))
1839
+ .filter((text) => !addedIds.has(text.id))
1562
1840
  .filter((text) => this.mirror.has(text.id)),
1563
1841
  attributes: this.attributes
1564
- .map((attribute) => ({
1565
- id: this.mirror.getId(attribute.node),
1566
- attributes: attribute.attributes,
1567
- }))
1842
+ .map((attribute) => {
1843
+ const { attributes } = attribute;
1844
+ if (typeof attributes.style === 'string') {
1845
+ const diffAsStr = JSON.stringify(attribute.styleDiff);
1846
+ const unchangedAsStr = JSON.stringify(attribute._unchangedStyles);
1847
+ if (diffAsStr.length < attributes.style.length) {
1848
+ if ((diffAsStr + unchangedAsStr).split('var(').length ===
1849
+ attributes.style.split('var(').length) {
1850
+ attributes.style = attribute.styleDiff;
1851
+ }
1852
+ }
1853
+ }
1854
+ return {
1855
+ id: this.mirror.getId(attribute.node),
1856
+ attributes: attributes,
1857
+ };
1858
+ })
1859
+ .filter((attribute) => !addedIds.has(attribute.id))
1568
1860
  .filter((attribute) => this.mirror.has(attribute.id)),
1569
1861
  removes: this.removes,
1570
1862
  adds,
@@ -1585,15 +1877,23 @@ class MutationBuffer {
1585
1877
  this.mutationCb(payload);
1586
1878
  };
1587
1879
  this.processMutation = (m) => {
1588
- if (isIgnored(m.target)) {
1880
+ if (isIgnored(m.target, this.mirror)) {
1589
1881
  return;
1590
1882
  }
1883
+ let unattachedDoc;
1884
+ try {
1885
+ unattachedDoc = document.implementation.createHTMLDocument();
1886
+ }
1887
+ catch (e) {
1888
+ unattachedDoc = this.doc;
1889
+ }
1591
1890
  switch (m.type) {
1592
1891
  case 'characterData': {
1593
1892
  const value = m.target.textContent;
1594
- if (!isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector) && value !== m.oldValue) {
1893
+ if (!isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector, false) &&
1894
+ value !== m.oldValue) {
1595
1895
  this.texts.push({
1596
- value: needMaskingText(m.target, this.maskTextClass, this.maskTextSelector, this.unmaskTextSelector, this.maskAllText) && value
1896
+ value: needMaskingText(m.target, this.maskTextClass, this.maskTextSelector, this.unmaskTextClass, this.unmaskTextSelector, this.maskAllText) && value
1597
1897
  ? this.maskTextFn
1598
1898
  ? this.maskTextFn(value)
1599
1899
  : value.replace(/[\S]/g, '*')
@@ -1605,83 +1905,99 @@ class MutationBuffer {
1605
1905
  }
1606
1906
  case 'attributes': {
1607
1907
  const target = m.target;
1608
- let value = target.getAttribute(m.attributeName);
1609
- if (m.attributeName === 'value') {
1610
- value = maskInputValue({
1611
- input: target,
1612
- maskInputSelector: this.maskInputSelector,
1613
- unmaskInputSelector: this.unmaskInputSelector,
1908
+ let attributeName = m.attributeName;
1909
+ let value = m.target.getAttribute(attributeName);
1910
+ if (attributeName === 'value') {
1911
+ const type = getInputType(target);
1912
+ const tagName = target.tagName;
1913
+ value = getInputValue(target, tagName, type);
1914
+ const isInputMasked = shouldMaskInput({
1614
1915
  maskInputOptions: this.maskInputOptions,
1615
- tagName: target.tagName,
1616
- type: target.getAttribute('type'),
1916
+ tagName,
1917
+ type,
1918
+ });
1919
+ const forceMask = needMaskingText(m.target, this.maskTextClass, this.maskTextSelector, this.unmaskTextClass, this.unmaskTextSelector, isInputMasked);
1920
+ value = maskInputValue({
1921
+ isMasked: forceMask,
1922
+ element: target,
1617
1923
  value,
1618
1924
  maskInputFn: this.maskInputFn,
1619
1925
  });
1620
1926
  }
1621
- if (isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector) || value === m.oldValue) {
1927
+ if (isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector, false) ||
1928
+ value === m.oldValue) {
1622
1929
  return;
1623
1930
  }
1624
1931
  let item = this.attributes.find((a) => a.node === m.target);
1932
+ if (target.tagName === 'IFRAME' &&
1933
+ attributeName === 'src' &&
1934
+ !this.keepIframeSrcFn(value)) {
1935
+ if (!target.contentDocument) {
1936
+ attributeName = 'rr_src';
1937
+ }
1938
+ else {
1939
+ return;
1940
+ }
1941
+ }
1625
1942
  if (!item) {
1626
1943
  item = {
1627
1944
  node: m.target,
1628
1945
  attributes: {},
1946
+ styleDiff: {},
1947
+ _unchangedStyles: {},
1629
1948
  };
1630
1949
  this.attributes.push(item);
1631
1950
  }
1632
- if (m.attributeName === 'type' &&
1951
+ if (attributeName === 'type' &&
1633
1952
  target.tagName === 'INPUT' &&
1634
1953
  (m.oldValue || '').toLowerCase() === 'password') {
1635
1954
  target.setAttribute('data-rr-is-password', 'true');
1636
1955
  }
1637
- if (m.attributeName === 'style') {
1638
- const old = this.doc.createElement('span');
1639
- if (m.oldValue) {
1640
- old.setAttribute('style', m.oldValue);
1641
- }
1642
- if (item.attributes.style === undefined ||
1643
- item.attributes.style === null) {
1644
- item.attributes.style = {};
1645
- }
1646
- try {
1647
- const styleObj = item.attributes.style;
1956
+ if (!ignoreAttribute(target.tagName, attributeName)) {
1957
+ item.attributes[attributeName] = transformAttribute(this.doc, toLowerCase(target.tagName), toLowerCase(attributeName), value, target, this.maskAttributeFn);
1958
+ if (attributeName === 'style') {
1959
+ const old = unattachedDoc.createElement('span');
1960
+ if (m.oldValue) {
1961
+ old.setAttribute('style', m.oldValue);
1962
+ }
1648
1963
  for (const pname of Array.from(target.style)) {
1649
1964
  const newValue = target.style.getPropertyValue(pname);
1650
1965
  const newPriority = target.style.getPropertyPriority(pname);
1651
1966
  if (newValue !== old.style.getPropertyValue(pname) ||
1652
1967
  newPriority !== old.style.getPropertyPriority(pname)) {
1653
1968
  if (newPriority === '') {
1654
- styleObj[pname] = newValue;
1969
+ item.styleDiff[pname] = newValue;
1655
1970
  }
1656
1971
  else {
1657
- styleObj[pname] = [newValue, newPriority];
1972
+ item.styleDiff[pname] = [newValue, newPriority];
1658
1973
  }
1659
1974
  }
1975
+ else {
1976
+ item._unchangedStyles[pname] = [newValue, newPriority];
1977
+ }
1660
1978
  }
1661
1979
  for (const pname of Array.from(old.style)) {
1662
1980
  if (target.style.getPropertyValue(pname) === '') {
1663
- styleObj[pname] = false;
1981
+ item.styleDiff[pname] = false;
1664
1982
  }
1665
1983
  }
1666
1984
  }
1667
- catch (error) {
1668
- console.warn('[rrweb] Error when parsing update to style attribute:', error);
1669
- }
1670
- }
1671
- else {
1672
- const element = m.target;
1673
- item.attributes[m.attributeName] = transformAttribute(this.doc, element, element.tagName, m.attributeName, value, this.maskAllText, this.unmaskTextSelector, this.maskTextFn);
1674
1985
  }
1675
1986
  break;
1676
1987
  }
1677
1988
  case 'childList': {
1989
+ if (isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector, true)) {
1990
+ return;
1991
+ }
1678
1992
  m.addedNodes.forEach((n) => this.genAdds(n, m.target));
1679
1993
  m.removedNodes.forEach((n) => {
1680
1994
  const nodeId = this.mirror.getId(n);
1681
1995
  const parentId = isShadowRoot(m.target)
1682
1996
  ? this.mirror.getId(m.target.host)
1683
1997
  : this.mirror.getId(m.target);
1684
- if (isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector) || isIgnored(n)) {
1998
+ if (isBlocked(m.target, this.blockClass, this.blockSelector, this.unblockSelector, false) ||
1999
+ isIgnored(n, this.mirror) ||
2000
+ !isSerialized(n, this.mirror)) {
1685
2001
  return;
1686
2002
  }
1687
2003
  if (this.addedSet.has(n)) {
@@ -1698,7 +2014,9 @@ class MutationBuffer {
1698
2014
  this.removes.push({
1699
2015
  parentId,
1700
2016
  id: nodeId,
1701
- isShadow: isShadowRoot(m.target) ? true : undefined,
2017
+ isShadow: isShadowRoot(m.target) && isNativeShadowDom(m.target)
2018
+ ? true
2019
+ : undefined,
1702
2020
  });
1703
2021
  }
1704
2022
  this.mapRemoves.push(n);
@@ -1708,28 +2026,36 @@ class MutationBuffer {
1708
2026
  }
1709
2027
  };
1710
2028
  this.genAdds = (n, target) => {
1711
- if (target && isBlocked(target, this.blockClass, this.blockSelector, this.unblockSelector)) {
2029
+ if (this.processedNodeManager.inOtherBuffer(n, this))
1712
2030
  return;
1713
- }
1714
- if (isINode(n)) {
1715
- if (isIgnored(n)) {
2031
+ if (this.addedSet.has(n) || this.movedSet.has(n))
2032
+ return;
2033
+ if (this.mirror.hasNode(n)) {
2034
+ if (isIgnored(n, this.mirror)) {
1716
2035
  return;
1717
2036
  }
1718
2037
  this.movedSet.add(n);
1719
2038
  let targetId = null;
1720
- if (target && isINode(target)) {
1721
- targetId = target.__sn.id;
2039
+ if (target && this.mirror.hasNode(target)) {
2040
+ targetId = this.mirror.getId(target);
1722
2041
  }
1723
- if (targetId) {
1724
- this.movedMap[moveKey(n.__sn.id, targetId)] = true;
2042
+ if (targetId && targetId !== -1) {
2043
+ this.movedMap[moveKey(this.mirror.getId(n), targetId)] = true;
1725
2044
  }
1726
2045
  }
1727
2046
  else {
1728
2047
  this.addedSet.add(n);
1729
2048
  this.droppedSet.delete(n);
1730
2049
  }
1731
- if (!isBlocked(n, this.blockClass, this.blockSelector, this.unblockSelector))
2050
+ if (!isBlocked(n, this.blockClass, this.blockSelector, this.unblockSelector, false)) {
1732
2051
  n.childNodes.forEach((childN) => this.genAdds(childN));
2052
+ if (hasShadowRoot(n)) {
2053
+ n.shadowRoot.childNodes.forEach((childN) => {
2054
+ this.processedNodeManager.add(childN, this);
2055
+ this.genAdds(childN, n);
2056
+ });
2057
+ }
2058
+ }
1733
2059
  };
1734
2060
  }
1735
2061
  init(options) {
@@ -1738,24 +2064,28 @@ class MutationBuffer {
1738
2064
  'blockClass',
1739
2065
  'blockSelector',
1740
2066
  'unblockSelector',
2067
+ 'maskAllText',
1741
2068
  'maskTextClass',
2069
+ 'unmaskTextClass',
1742
2070
  'maskTextSelector',
1743
2071
  'unmaskTextSelector',
1744
- 'maskInputSelector',
1745
- 'unmaskInputSelector',
1746
2072
  'inlineStylesheet',
1747
- 'maskAllText',
1748
2073
  'maskInputOptions',
2074
+ 'maskAttributeFn',
1749
2075
  'maskTextFn',
1750
2076
  'maskInputFn',
2077
+ 'keepIframeSrcFn',
1751
2078
  'recordCanvas',
1752
2079
  'inlineImages',
1753
2080
  'slimDOMOptions',
2081
+ 'dataURLOptions',
1754
2082
  'doc',
1755
2083
  'mirror',
1756
2084
  'iframeManager',
2085
+ 'stylesheetManager',
1757
2086
  'shadowDomManager',
1758
2087
  'canvasManager',
2088
+ 'processedNodeManager',
1759
2089
  ].forEach((key) => {
1760
2090
  this[key] = options[key];
1761
2091
  });
@@ -1791,6 +2121,11 @@ function deepDelete(addsSet, n) {
1791
2121
  n.childNodes.forEach((childN) => deepDelete(addsSet, childN));
1792
2122
  }
1793
2123
  function isParentRemoved(removes, n, mirror) {
2124
+ if (removes.length === 0)
2125
+ return false;
2126
+ return _isParentRemoved(removes, n, mirror);
2127
+ }
2128
+ function _isParentRemoved(removes, n, mirror) {
1794
2129
  const { parentNode } = n;
1795
2130
  if (!parentNode) {
1796
2131
  return false;
@@ -1799,9 +2134,14 @@ function isParentRemoved(removes, n, mirror) {
1799
2134
  if (removes.some((r) => r.id === parentId)) {
1800
2135
  return true;
1801
2136
  }
1802
- return isParentRemoved(removes, parentNode, mirror);
2137
+ return _isParentRemoved(removes, parentNode, mirror);
1803
2138
  }
1804
2139
  function isAncestorInSet(set, n) {
2140
+ if (set.size === 0)
2141
+ return false;
2142
+ return _isAncestorInSet(set, n);
2143
+ }
2144
+ function _isAncestorInSet(set, n) {
1805
2145
  const { parentNode } = n;
1806
2146
  if (!parentNode) {
1807
2147
  return false;
@@ -1809,23 +2149,31 @@ function isAncestorInSet(set, n) {
1809
2149
  if (set.has(parentNode)) {
1810
2150
  return true;
1811
2151
  }
1812
- return isAncestorInSet(set, parentNode);
2152
+ return _isAncestorInSet(set, parentNode);
1813
2153
  }
1814
2154
 
2155
+ let errorHandler;
2156
+ function registerErrorHandler(handler) {
2157
+ errorHandler = handler;
2158
+ }
2159
+ function unregisterErrorHandler() {
2160
+ errorHandler = undefined;
2161
+ }
1815
2162
  const callbackWrapper = (cb) => {
1816
- const rrwebWrapped = (...rest) => {
2163
+ if (!errorHandler) {
2164
+ return cb;
2165
+ }
2166
+ const rrwebWrapped = ((...rest) => {
1817
2167
  try {
1818
2168
  return cb(...rest);
1819
2169
  }
1820
2170
  catch (error) {
1821
- try {
1822
- error.__rrweb__ = true;
1823
- }
1824
- catch (_a) {
2171
+ if (errorHandler && errorHandler(error) === true) {
2172
+ return;
1825
2173
  }
1826
2174
  throw error;
1827
2175
  }
1828
- };
2176
+ });
1829
2177
  return rrwebWrapped;
1830
2178
  };
1831
2179
 
@@ -1842,7 +2190,8 @@ function getEventTarget(event) {
1842
2190
  return event.path[0];
1843
2191
  }
1844
2192
  }
1845
- catch (_a) { }
2193
+ catch (_a) {
2194
+ }
1846
2195
  return event && event.target;
1847
2196
  }
1848
2197
  function initMutationObserver(options, rootEl) {
@@ -1861,7 +2210,7 @@ function initMutationObserver(options, rootEl) {
1861
2210
  if (options.onMutation && options.onMutation(mutations) === false) {
1862
2211
  return;
1863
2212
  }
1864
- mutationBuffer.processMutations(mutations);
2213
+ mutationBuffer.processMutations.bind(mutationBuffer)(mutations);
1865
2214
  }));
1866
2215
  observer.observe(rootEl, {
1867
2216
  attributes: true,
@@ -1875,7 +2224,8 @@ function initMutationObserver(options, rootEl) {
1875
2224
  }
1876
2225
  function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
1877
2226
  if (sampling.mousemove === false) {
1878
- return () => { };
2227
+ return () => {
2228
+ };
1879
2229
  }
1880
2230
  const threshold = typeof sampling.mousemove === 'number' ? sampling.mousemove : 50;
1881
2231
  const callbackThreshold = typeof sampling.mousemoveCallback === 'number'
@@ -1883,41 +2233,41 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
1883
2233
  : 500;
1884
2234
  let positions = [];
1885
2235
  let timeBaseline;
1886
- const wrappedCb = throttle$1((source) => {
2236
+ const wrappedCb = throttle$1(callbackWrapper((source) => {
1887
2237
  const totalOffset = Date.now() - timeBaseline;
1888
- callbackWrapper(mousemoveCb)(positions.map((p) => {
2238
+ mousemoveCb(positions.map((p) => {
1889
2239
  p.timeOffset -= totalOffset;
1890
2240
  return p;
1891
2241
  }), source);
1892
2242
  positions = [];
1893
2243
  timeBaseline = null;
1894
- }, callbackThreshold);
1895
- const updatePosition = throttle$1((evt) => {
2244
+ }), callbackThreshold);
2245
+ const updatePosition = callbackWrapper(throttle$1(callbackWrapper((evt) => {
1896
2246
  const target = getEventTarget(evt);
1897
- const { clientX, clientY } = isTouchEvent(evt)
2247
+ const { clientX, clientY } = legacy_isTouchEvent(evt)
1898
2248
  ? evt.changedTouches[0]
1899
2249
  : evt;
1900
2250
  if (!timeBaseline) {
1901
- timeBaseline = Date.now();
2251
+ timeBaseline = nowTimestamp();
1902
2252
  }
1903
2253
  positions.push({
1904
2254
  x: clientX,
1905
2255
  y: clientY,
1906
2256
  id: mirror.getId(target),
1907
- timeOffset: Date.now() - timeBaseline,
2257
+ timeOffset: nowTimestamp() - timeBaseline,
1908
2258
  });
1909
2259
  wrappedCb(typeof DragEvent !== 'undefined' && evt instanceof DragEvent
1910
2260
  ? IncrementalSource.Drag
1911
2261
  : evt instanceof MouseEvent
1912
2262
  ? IncrementalSource.MouseMove
1913
2263
  : IncrementalSource.TouchMove);
1914
- }, threshold, {
2264
+ }), threshold, {
1915
2265
  trailing: false,
1916
- });
2266
+ }));
1917
2267
  const handlers = [
1918
- on('mousemove', callbackWrapper(updatePosition), doc),
1919
- on('touchmove', callbackWrapper(updatePosition), doc),
1920
- on('drag', callbackWrapper(updatePosition), doc),
2268
+ on('mousemove', updatePosition, doc),
2269
+ on('touchmove', updatePosition, doc),
2270
+ on('drag', updatePosition, doc),
1921
2271
  ];
1922
2272
  return callbackWrapper(() => {
1923
2273
  handlers.forEach((h) => h());
@@ -1925,31 +2275,68 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
1925
2275
  }
1926
2276
  function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockClass, blockSelector, unblockSelector, sampling, }) {
1927
2277
  if (sampling.mouseInteraction === false) {
1928
- return () => { };
1929
- }
2278
+ return () => {
2279
+ };
2280
+ }
1930
2281
  const disableMap = sampling.mouseInteraction === true ||
1931
2282
  sampling.mouseInteraction === undefined
1932
2283
  ? {}
1933
2284
  : sampling.mouseInteraction;
1934
2285
  const handlers = [];
2286
+ let currentPointerType = null;
1935
2287
  const getHandler = (eventKey) => {
1936
2288
  return (event) => {
1937
2289
  const target = getEventTarget(event);
1938
- if (isBlocked(target, blockClass, blockSelector, unblockSelector)) {
2290
+ if (isBlocked(target, blockClass, blockSelector, unblockSelector, true)) {
1939
2291
  return;
1940
2292
  }
1941
- const e = isTouchEvent(event) ? event.changedTouches[0] : event;
2293
+ let pointerType = null;
2294
+ let thisEventKey = eventKey;
2295
+ if ('pointerType' in event) {
2296
+ switch (event.pointerType) {
2297
+ case 'mouse':
2298
+ pointerType = PointerTypes.Mouse;
2299
+ break;
2300
+ case 'touch':
2301
+ pointerType = PointerTypes.Touch;
2302
+ break;
2303
+ case 'pen':
2304
+ pointerType = PointerTypes.Pen;
2305
+ break;
2306
+ }
2307
+ if (pointerType === PointerTypes.Touch) {
2308
+ if (MouseInteractions[eventKey] === MouseInteractions.MouseDown) {
2309
+ thisEventKey = 'TouchStart';
2310
+ }
2311
+ else if (MouseInteractions[eventKey] === MouseInteractions.MouseUp) {
2312
+ thisEventKey = 'TouchEnd';
2313
+ }
2314
+ }
2315
+ else if (pointerType === PointerTypes.Pen) ;
2316
+ }
2317
+ else if (legacy_isTouchEvent(event)) {
2318
+ pointerType = PointerTypes.Touch;
2319
+ }
2320
+ if (pointerType !== null) {
2321
+ currentPointerType = pointerType;
2322
+ if ((thisEventKey.startsWith('Touch') &&
2323
+ pointerType === PointerTypes.Touch) ||
2324
+ (thisEventKey.startsWith('Mouse') &&
2325
+ pointerType === PointerTypes.Mouse)) {
2326
+ pointerType = null;
2327
+ }
2328
+ }
2329
+ else if (MouseInteractions[eventKey] === MouseInteractions.Click) {
2330
+ pointerType = currentPointerType;
2331
+ currentPointerType = null;
2332
+ }
2333
+ const e = legacy_isTouchEvent(event) ? event.changedTouches[0] : event;
1942
2334
  if (!e) {
1943
2335
  return;
1944
2336
  }
1945
2337
  const id = mirror.getId(target);
1946
2338
  const { clientX, clientY } = e;
1947
- callbackWrapper(mouseInteractionCb)({
1948
- type: MouseInteractions[eventKey],
1949
- id,
1950
- x: clientX,
1951
- y: clientY,
1952
- });
2339
+ callbackWrapper(mouseInteractionCb)(Object.assign({ type: MouseInteractions[thisEventKey], id, x: clientX, y: clientY }, (pointerType !== null && { pointerType })));
1953
2340
  };
1954
2341
  };
1955
2342
  Object.keys(MouseInteractions)
@@ -1957,8 +2344,19 @@ function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockCl
1957
2344
  !key.endsWith('_Departed') &&
1958
2345
  disableMap[key] !== false)
1959
2346
  .forEach((eventKey) => {
1960
- const eventName = eventKey.toLowerCase();
1961
- const handler = callbackWrapper(getHandler(eventKey));
2347
+ let eventName = toLowerCase(eventKey);
2348
+ const handler = getHandler(eventKey);
2349
+ if (window.PointerEvent) {
2350
+ switch (MouseInteractions[eventKey]) {
2351
+ case MouseInteractions.MouseDown:
2352
+ case MouseInteractions.MouseUp:
2353
+ eventName = eventName.replace('mouse', 'pointer');
2354
+ break;
2355
+ case MouseInteractions.TouchStart:
2356
+ case MouseInteractions.TouchEnd:
2357
+ return;
2358
+ }
2359
+ }
1962
2360
  handlers.push(on(eventName, handler, doc));
1963
2361
  });
1964
2362
  return callbackWrapper(() => {
@@ -1966,47 +2364,47 @@ function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockCl
1966
2364
  });
1967
2365
  }
1968
2366
  function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector, unblockSelector, sampling, }) {
1969
- const updatePosition = throttle$1((evt) => {
2367
+ const updatePosition = callbackWrapper(throttle$1(callbackWrapper((evt) => {
1970
2368
  const target = getEventTarget(evt);
1971
2369
  if (!target ||
1972
- isBlocked(target, blockClass, blockSelector, unblockSelector)) {
2370
+ isBlocked(target, blockClass, blockSelector, unblockSelector, true)) {
1973
2371
  return;
1974
2372
  }
1975
2373
  const id = mirror.getId(target);
1976
- if (target === doc) {
1977
- const scrollEl = (doc.scrollingElement || doc.documentElement);
1978
- callbackWrapper(scrollCb)({
2374
+ if (target === doc && doc.defaultView) {
2375
+ const scrollLeftTop = getWindowScroll(doc.defaultView);
2376
+ scrollCb({
1979
2377
  id,
1980
- x: scrollEl.scrollLeft,
1981
- y: scrollEl.scrollTop,
2378
+ x: scrollLeftTop.left,
2379
+ y: scrollLeftTop.top,
1982
2380
  });
1983
2381
  }
1984
2382
  else {
1985
- callbackWrapper(scrollCb)({
2383
+ scrollCb({
1986
2384
  id,
1987
2385
  x: target.scrollLeft,
1988
2386
  y: target.scrollTop,
1989
2387
  });
1990
2388
  }
1991
- }, sampling.scroll || 100);
1992
- return on('scroll', callbackWrapper(updatePosition), doc);
2389
+ }), sampling.scroll || 100));
2390
+ return on('scroll', updatePosition, doc);
1993
2391
  }
1994
- function initViewportResizeObserver({ viewportResizeCb, }) {
2392
+ function initViewportResizeObserver({ viewportResizeCb }, { win }) {
1995
2393
  let lastH = -1;
1996
2394
  let lastW = -1;
1997
- const updateDimension = throttle$1(() => {
2395
+ const updateDimension = callbackWrapper(throttle$1(callbackWrapper(() => {
1998
2396
  const height = getWindowHeight();
1999
2397
  const width = getWindowWidth();
2000
2398
  if (lastH !== height || lastW !== width) {
2001
- callbackWrapper(viewportResizeCb)({
2399
+ viewportResizeCb({
2002
2400
  width: Number(width),
2003
2401
  height: Number(height),
2004
2402
  });
2005
2403
  lastH = height;
2006
2404
  lastW = width;
2007
2405
  }
2008
- }, 200);
2009
- return on('resize', callbackWrapper(updateDimension), window);
2406
+ }), 200));
2407
+ return on('resize', updateDimension, win);
2010
2408
  }
2011
2409
  function wrapEventWithUserTriggeredFlag(v, enable) {
2012
2410
  const value = Object.assign({}, v);
@@ -2016,47 +2414,42 @@ function wrapEventWithUserTriggeredFlag(v, enable) {
2016
2414
  }
2017
2415
  const INPUT_TAGS = ['INPUT', 'TEXTAREA', 'SELECT'];
2018
2416
  const lastInputValueMap = new WeakMap();
2019
- function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, unblockSelector, ignoreClass, ignoreSelector, maskInputSelector, unmaskInputSelector, maskInputOptions, maskInputFn, sampling, userTriggeredOnInput, }) {
2417
+ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, unblockSelector, ignoreClass, ignoreSelector, maskInputOptions, maskInputFn, sampling, userTriggeredOnInput, maskTextClass, unmaskTextClass, maskTextSelector, unmaskTextSelector, }) {
2020
2418
  function eventHandler(event) {
2021
2419
  let target = getEventTarget(event);
2022
- const tagName = target && target.tagName;
2023
2420
  const userTriggered = event.isTrusted;
2421
+ const tagName = target && toUpperCase(target.tagName);
2024
2422
  if (tagName === 'OPTION')
2025
2423
  target = target.parentElement;
2026
2424
  if (!target ||
2027
2425
  !tagName ||
2028
2426
  INPUT_TAGS.indexOf(tagName) < 0 ||
2029
- isBlocked(target, blockClass, blockSelector, unblockSelector)) {
2427
+ isBlocked(target, blockClass, blockSelector, unblockSelector, true)) {
2030
2428
  return;
2031
2429
  }
2032
2430
  const el = target;
2033
- const type = getInputType(el);
2034
2431
  if (el.classList.contains(ignoreClass) ||
2035
2432
  (ignoreSelector && el.matches(ignoreSelector))) {
2036
2433
  return;
2037
2434
  }
2435
+ const type = getInputType(target);
2038
2436
  let text = getInputValue(el, tagName, type);
2039
2437
  let isChecked = false;
2040
- if (type === 'radio' || type === 'checkbox') {
2041
- isChecked = target.checked;
2042
- }
2043
- if (hasInputMaskOptions({
2438
+ const isInputMasked = shouldMaskInput({
2044
2439
  maskInputOptions,
2045
- maskInputSelector,
2046
2440
  tagName,
2047
2441
  type,
2048
- })) {
2049
- text = maskInputValue({
2050
- input: el,
2051
- maskInputOptions,
2052
- maskInputSelector,
2053
- unmaskInputSelector,
2054
- tagName,
2055
- type,
2056
- value: text,
2057
- maskInputFn,
2058
- });
2442
+ });
2443
+ const forceMask = needMaskingText(target, maskTextClass, maskTextSelector, unmaskTextClass, unmaskTextSelector, isInputMasked);
2444
+ if (type === 'radio' || type === 'checkbox') {
2445
+ isChecked = target.checked;
2059
2446
  }
2447
+ text = maskInputValue({
2448
+ isMasked: forceMask,
2449
+ element: target,
2450
+ value: text,
2451
+ maskInputFn,
2452
+ });
2060
2453
  cbWithDedup(target, callbackWrapper(wrapEventWithUserTriggeredFlag)({ text, isChecked, userTriggered }, userTriggeredOnInput));
2061
2454
  const name = target.name;
2062
2455
  if (type === 'radio' && name && isChecked) {
@@ -2065,12 +2458,8 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
2065
2458
  .forEach((el) => {
2066
2459
  if (el !== target) {
2067
2460
  const text = maskInputValue({
2068
- input: el,
2069
- maskInputOptions,
2070
- maskInputSelector,
2071
- unmaskInputSelector,
2072
- tagName,
2073
- type,
2461
+ isMasked: forceMask,
2462
+ element: el,
2074
2463
  value: getInputValue(el, tagName, type),
2075
2464
  maskInputFn,
2076
2465
  });
@@ -2090,26 +2479,35 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
2090
2479
  lastInputValue.isChecked !== v.isChecked) {
2091
2480
  lastInputValueMap.set(target, v);
2092
2481
  const id = mirror.getId(target);
2093
- inputCb(Object.assign(Object.assign({}, v), { id }));
2482
+ callbackWrapper(inputCb)(Object.assign(Object.assign({}, v), { id }));
2094
2483
  }
2095
2484
  }
2096
2485
  const events = sampling.input === 'last' ? ['change'] : ['input', 'change'];
2097
2486
  const handlers = events.map((eventName) => on(eventName, callbackWrapper(eventHandler), doc));
2098
- const propertyDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
2487
+ const currentWindow = doc.defaultView;
2488
+ if (!currentWindow) {
2489
+ return () => {
2490
+ handlers.forEach((h) => h());
2491
+ };
2492
+ }
2493
+ const propertyDescriptor = currentWindow.Object.getOwnPropertyDescriptor(currentWindow.HTMLInputElement.prototype, 'value');
2099
2494
  const hookProperties = [
2100
- [HTMLInputElement.prototype, 'value'],
2101
- [HTMLInputElement.prototype, 'checked'],
2102
- [HTMLSelectElement.prototype, 'value'],
2103
- [HTMLTextAreaElement.prototype, 'value'],
2104
- [HTMLSelectElement.prototype, 'selectedIndex'],
2105
- [HTMLOptionElement.prototype, 'selected'],
2495
+ [currentWindow.HTMLInputElement.prototype, 'value'],
2496
+ [currentWindow.HTMLInputElement.prototype, 'checked'],
2497
+ [currentWindow.HTMLSelectElement.prototype, 'value'],
2498
+ [currentWindow.HTMLTextAreaElement.prototype, 'value'],
2499
+ [currentWindow.HTMLSelectElement.prototype, 'selectedIndex'],
2500
+ [currentWindow.HTMLOptionElement.prototype, 'selected'],
2106
2501
  ];
2107
2502
  if (propertyDescriptor && propertyDescriptor.set) {
2108
2503
  handlers.push(...hookProperties.map((p) => hookSetter(p[0], p[1], {
2109
2504
  set() {
2110
- callbackWrapper(eventHandler)({ target: this });
2505
+ callbackWrapper(eventHandler)({
2506
+ target: this,
2507
+ isTrusted: false,
2508
+ });
2111
2509
  },
2112
- })));
2510
+ }, false, currentWindow)));
2113
2511
  }
2114
2512
  return callbackWrapper(() => {
2115
2513
  handlers.forEach((h) => h());
@@ -2130,7 +2528,7 @@ function getNestedCSSRulePositions(rule) {
2130
2528
  const index = rules.indexOf(childRule);
2131
2529
  pos.unshift(index);
2132
2530
  }
2133
- else {
2531
+ else if (childRule.parentStyleSheet) {
2134
2532
  const rules = Array.from(childRule.parentStyleSheet.cssRules);
2135
2533
  const index = rules.indexOf(childRule);
2136
2534
  pos.unshift(index);
@@ -2139,18 +2537,33 @@ function getNestedCSSRulePositions(rule) {
2139
2537
  }
2140
2538
  return recurse(rule, positions);
2141
2539
  }
2142
- function initStyleSheetObserver({ styleSheetRuleCb, mirror }, { win }) {
2540
+ function getIdAndStyleId(sheet, mirror, styleMirror) {
2541
+ let id, styleId;
2542
+ if (!sheet)
2543
+ return {};
2544
+ if (sheet.ownerNode)
2545
+ id = mirror.getId(sheet.ownerNode);
2546
+ else
2547
+ styleId = styleMirror.getId(sheet);
2548
+ return {
2549
+ styleId,
2550
+ id,
2551
+ };
2552
+ }
2553
+ function initStyleSheetObserver({ styleSheetRuleCb, mirror, stylesheetManager }, { win }) {
2143
2554
  if (!win.CSSStyleSheet || !win.CSSStyleSheet.prototype) {
2144
- return () => { };
2555
+ return () => {
2556
+ };
2145
2557
  }
2146
2558
  const insertRule = win.CSSStyleSheet.prototype.insertRule;
2147
2559
  win.CSSStyleSheet.prototype.insertRule = new Proxy(insertRule, {
2148
2560
  apply: callbackWrapper((target, thisArg, argumentsList) => {
2149
2561
  const [rule, index] = argumentsList;
2150
- const id = mirror.getId(thisArg.ownerNode);
2151
- if (id !== -1) {
2562
+ const { id, styleId } = getIdAndStyleId(thisArg, mirror, stylesheetManager.styleMirror);
2563
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2152
2564
  styleSheetRuleCb({
2153
2565
  id,
2566
+ styleId,
2154
2567
  adds: [{ rule, index }],
2155
2568
  });
2156
2569
  }
@@ -2161,16 +2574,53 @@ function initStyleSheetObserver({ styleSheetRuleCb, mirror }, { win }) {
2161
2574
  win.CSSStyleSheet.prototype.deleteRule = new Proxy(deleteRule, {
2162
2575
  apply: callbackWrapper((target, thisArg, argumentsList) => {
2163
2576
  const [index] = argumentsList;
2164
- const id = mirror.getId(thisArg.ownerNode);
2165
- if (id !== -1) {
2577
+ const { id, styleId } = getIdAndStyleId(thisArg, mirror, stylesheetManager.styleMirror);
2578
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2166
2579
  styleSheetRuleCb({
2167
2580
  id,
2581
+ styleId,
2168
2582
  removes: [{ index }],
2169
2583
  });
2170
2584
  }
2171
2585
  return target.apply(thisArg, argumentsList);
2172
2586
  }),
2173
2587
  });
2588
+ let replace;
2589
+ if (win.CSSStyleSheet.prototype.replace) {
2590
+ replace = win.CSSStyleSheet.prototype.replace;
2591
+ win.CSSStyleSheet.prototype.replace = new Proxy(replace, {
2592
+ apply: callbackWrapper((target, thisArg, argumentsList) => {
2593
+ const [text] = argumentsList;
2594
+ const { id, styleId } = getIdAndStyleId(thisArg, mirror, stylesheetManager.styleMirror);
2595
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2596
+ styleSheetRuleCb({
2597
+ id,
2598
+ styleId,
2599
+ replace: text,
2600
+ });
2601
+ }
2602
+ return target.apply(thisArg, argumentsList);
2603
+ }),
2604
+ });
2605
+ }
2606
+ let replaceSync;
2607
+ if (win.CSSStyleSheet.prototype.replaceSync) {
2608
+ replaceSync = win.CSSStyleSheet.prototype.replaceSync;
2609
+ win.CSSStyleSheet.prototype.replaceSync = new Proxy(replaceSync, {
2610
+ apply: callbackWrapper((target, thisArg, argumentsList) => {
2611
+ const [text] = argumentsList;
2612
+ const { id, styleId } = getIdAndStyleId(thisArg, mirror, stylesheetManager.styleMirror);
2613
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2614
+ styleSheetRuleCb({
2615
+ id,
2616
+ styleId,
2617
+ replaceSync: text,
2618
+ });
2619
+ }
2620
+ return target.apply(thisArg, argumentsList);
2621
+ }),
2622
+ });
2623
+ }
2174
2624
  const supportedNestedCSSRuleTypes = {};
2175
2625
  if (canMonkeyPatchNestedCSSRule('CSSGroupingRule')) {
2176
2626
  supportedNestedCSSRuleTypes.CSSGroupingRule = win.CSSGroupingRule;
@@ -2195,10 +2645,11 @@ function initStyleSheetObserver({ styleSheetRuleCb, mirror }, { win }) {
2195
2645
  type.prototype.insertRule = new Proxy(unmodifiedFunctions[typeKey].insertRule, {
2196
2646
  apply: callbackWrapper((target, thisArg, argumentsList) => {
2197
2647
  const [rule, index] = argumentsList;
2198
- const id = mirror.getId(thisArg.parentStyleSheet.ownerNode);
2199
- if (id !== -1) {
2648
+ const { id, styleId } = getIdAndStyleId(thisArg.parentStyleSheet, mirror, stylesheetManager.styleMirror);
2649
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2200
2650
  styleSheetRuleCb({
2201
2651
  id,
2652
+ styleId,
2202
2653
  adds: [
2203
2654
  {
2204
2655
  rule,
@@ -2216,10 +2667,11 @@ function initStyleSheetObserver({ styleSheetRuleCb, mirror }, { win }) {
2216
2667
  type.prototype.deleteRule = new Proxy(unmodifiedFunctions[typeKey].deleteRule, {
2217
2668
  apply: callbackWrapper((target, thisArg, argumentsList) => {
2218
2669
  const [index] = argumentsList;
2219
- const id = mirror.getId(thisArg.parentStyleSheet.ownerNode);
2220
- if (id !== -1) {
2670
+ const { id, styleId } = getIdAndStyleId(thisArg.parentStyleSheet, mirror, stylesheetManager.styleMirror);
2671
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2221
2672
  styleSheetRuleCb({
2222
2673
  id,
2674
+ styleId,
2223
2675
  removes: [
2224
2676
  { index: [...getNestedCSSRulePositions(thisArg), index] },
2225
2677
  ],
@@ -2232,22 +2684,74 @@ function initStyleSheetObserver({ styleSheetRuleCb, mirror }, { win }) {
2232
2684
  return callbackWrapper(() => {
2233
2685
  win.CSSStyleSheet.prototype.insertRule = insertRule;
2234
2686
  win.CSSStyleSheet.prototype.deleteRule = deleteRule;
2687
+ replace && (win.CSSStyleSheet.prototype.replace = replace);
2688
+ replaceSync && (win.CSSStyleSheet.prototype.replaceSync = replaceSync);
2235
2689
  Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => {
2236
2690
  type.prototype.insertRule = unmodifiedFunctions[typeKey].insertRule;
2237
2691
  type.prototype.deleteRule = unmodifiedFunctions[typeKey].deleteRule;
2238
2692
  });
2239
2693
  });
2240
2694
  }
2241
- function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
2695
+ function initAdoptedStyleSheetObserver({ mirror, stylesheetManager, }, host) {
2696
+ var _a, _b, _c;
2697
+ let hostId = null;
2698
+ if (host.nodeName === '#document')
2699
+ hostId = mirror.getId(host);
2700
+ else
2701
+ hostId = mirror.getId(host.host);
2702
+ const patchTarget = host.nodeName === '#document'
2703
+ ? (_a = host.defaultView) === null || _a === void 0 ? void 0 : _a.Document
2704
+ : (_c = (_b = host.ownerDocument) === null || _b === void 0 ? void 0 : _b.defaultView) === null || _c === void 0 ? void 0 : _c.ShadowRoot;
2705
+ const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(patchTarget === null || patchTarget === void 0 ? void 0 : patchTarget.prototype, 'adoptedStyleSheets');
2706
+ if (hostId === null ||
2707
+ hostId === -1 ||
2708
+ !patchTarget ||
2709
+ !originalPropertyDescriptor)
2710
+ return () => {
2711
+ };
2712
+ Object.defineProperty(host, 'adoptedStyleSheets', {
2713
+ configurable: originalPropertyDescriptor.configurable,
2714
+ enumerable: originalPropertyDescriptor.enumerable,
2715
+ get() {
2716
+ var _a;
2717
+ return (_a = originalPropertyDescriptor.get) === null || _a === void 0 ? void 0 : _a.call(this);
2718
+ },
2719
+ set(sheets) {
2720
+ var _a;
2721
+ const result = (_a = originalPropertyDescriptor.set) === null || _a === void 0 ? void 0 : _a.call(this, sheets);
2722
+ if (hostId !== null && hostId !== -1) {
2723
+ try {
2724
+ stylesheetManager.adoptStyleSheets(sheets, hostId);
2725
+ }
2726
+ catch (e) {
2727
+ }
2728
+ }
2729
+ return result;
2730
+ },
2731
+ });
2732
+ return callbackWrapper(() => {
2733
+ Object.defineProperty(host, 'adoptedStyleSheets', {
2734
+ configurable: originalPropertyDescriptor.configurable,
2735
+ enumerable: originalPropertyDescriptor.enumerable,
2736
+ get: originalPropertyDescriptor.get,
2737
+ set: originalPropertyDescriptor.set,
2738
+ });
2739
+ });
2740
+ }
2741
+ function initStyleDeclarationObserver({ styleDeclarationCb, mirror, ignoreCSSAttributes, stylesheetManager, }, { win }) {
2242
2742
  const setProperty = win.CSSStyleDeclaration.prototype.setProperty;
2243
2743
  win.CSSStyleDeclaration.prototype.setProperty = new Proxy(setProperty, {
2244
2744
  apply: callbackWrapper((target, thisArg, argumentsList) => {
2245
- var _a, _b;
2745
+ var _a;
2246
2746
  const [property, value, priority] = argumentsList;
2247
- const id = mirror.getId((_b = (_a = thisArg.parentRule) === null || _a === void 0 ? void 0 : _a.parentStyleSheet) === null || _b === void 0 ? void 0 : _b.ownerNode);
2248
- if (id !== -1) {
2747
+ if (ignoreCSSAttributes.has(property)) {
2748
+ return setProperty.apply(thisArg, [property, value, priority]);
2749
+ }
2750
+ const { id, styleId } = getIdAndStyleId((_a = thisArg.parentRule) === null || _a === void 0 ? void 0 : _a.parentStyleSheet, mirror, stylesheetManager.styleMirror);
2751
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2249
2752
  styleDeclarationCb({
2250
2753
  id,
2754
+ styleId,
2251
2755
  set: {
2252
2756
  property,
2253
2757
  value,
@@ -2262,12 +2766,16 @@ function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
2262
2766
  const removeProperty = win.CSSStyleDeclaration.prototype.removeProperty;
2263
2767
  win.CSSStyleDeclaration.prototype.removeProperty = new Proxy(removeProperty, {
2264
2768
  apply: callbackWrapper((target, thisArg, argumentsList) => {
2265
- var _a, _b;
2769
+ var _a;
2266
2770
  const [property] = argumentsList;
2267
- const id = mirror.getId((_b = (_a = thisArg.parentRule) === null || _a === void 0 ? void 0 : _a.parentStyleSheet) === null || _b === void 0 ? void 0 : _b.ownerNode);
2268
- if (id !== -1) {
2771
+ if (ignoreCSSAttributes.has(property)) {
2772
+ return removeProperty.apply(thisArg, [property]);
2773
+ }
2774
+ const { id, styleId } = getIdAndStyleId((_a = thisArg.parentRule) === null || _a === void 0 ? void 0 : _a.parentStyleSheet, mirror, stylesheetManager.styleMirror);
2775
+ if ((id && id !== -1) || (styleId && styleId !== -1)) {
2269
2776
  styleDeclarationCb({
2270
2777
  id,
2778
+ styleId,
2271
2779
  remove: {
2272
2780
  property,
2273
2781
  },
@@ -2282,27 +2790,29 @@ function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
2282
2790
  win.CSSStyleDeclaration.prototype.removeProperty = removeProperty;
2283
2791
  });
2284
2792
  }
2285
- function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSelector, unblockSelector, mirror, sampling, }) {
2286
- const handler = (type) => throttle$1(callbackWrapper((event) => {
2793
+ function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSelector, unblockSelector, mirror, sampling, doc, }) {
2794
+ const handler = callbackWrapper((type) => throttle$1(callbackWrapper((event) => {
2287
2795
  const target = getEventTarget(event);
2288
2796
  if (!target ||
2289
- isBlocked(target, blockClass, blockSelector, unblockSelector)) {
2797
+ isBlocked(target, blockClass, blockSelector, unblockSelector, true)) {
2290
2798
  return;
2291
2799
  }
2292
- const { currentTime, volume, muted } = target;
2800
+ const { currentTime, volume, muted, playbackRate } = target;
2293
2801
  mediaInteractionCb({
2294
2802
  type,
2295
2803
  id: mirror.getId(target),
2296
2804
  currentTime,
2297
2805
  volume,
2298
2806
  muted,
2807
+ playbackRate,
2299
2808
  });
2300
- }), sampling.media || 500);
2809
+ }), sampling.media || 500));
2301
2810
  const handlers = [
2302
- on('play', handler(0)),
2303
- on('pause', handler(1)),
2304
- on('seeked', handler(2)),
2305
- on('volumechange', handler(3)),
2811
+ on('play', handler(0), doc),
2812
+ on('pause', handler(1), doc),
2813
+ on('seeked', handler(2), doc),
2814
+ on('volumechange', handler(3), doc),
2815
+ on('ratechange', handler(4), doc),
2306
2816
  ];
2307
2817
  return callbackWrapper(() => {
2308
2818
  handlers.forEach((h) => h());
@@ -2311,7 +2821,8 @@ function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSel
2311
2821
  function initFontObserver({ fontCb, doc }) {
2312
2822
  const win = doc.defaultView;
2313
2823
  if (!win) {
2314
- return () => { };
2824
+ return () => {
2825
+ };
2315
2826
  }
2316
2827
  const handlers = [];
2317
2828
  const fontMap = new WeakMap();
@@ -2324,20 +2835,19 @@ function initFontObserver({ fontCb, doc }) {
2324
2835
  descriptors,
2325
2836
  fontSource: typeof source === 'string'
2326
2837
  ? source
2327
- :
2328
- JSON.stringify(Array.from(new Uint8Array(source))),
2838
+ : JSON.stringify(Array.from(new Uint8Array(source))),
2329
2839
  });
2330
2840
  return fontFace;
2331
2841
  };
2332
2842
  const restoreHandler = patch(doc.fonts, 'add', function (original) {
2333
2843
  return function (fontFace) {
2334
- setTimeout(() => {
2844
+ setTimeout(callbackWrapper(() => {
2335
2845
  const p = fontMap.get(fontFace);
2336
2846
  if (p) {
2337
2847
  fontCb(p);
2338
2848
  fontMap.delete(fontFace);
2339
2849
  }
2340
- }, 0);
2850
+ }), 0);
2341
2851
  return original.apply(this, [fontFace]);
2342
2852
  };
2343
2853
  });
@@ -2349,8 +2859,59 @@ function initFontObserver({ fontCb, doc }) {
2349
2859
  handlers.forEach((h) => h());
2350
2860
  });
2351
2861
  }
2862
+ function initSelectionObserver(param) {
2863
+ const { doc, mirror, blockClass, blockSelector, unblockSelector, selectionCb, } = param;
2864
+ let collapsed = true;
2865
+ const updateSelection = callbackWrapper(() => {
2866
+ const selection = doc.getSelection();
2867
+ if (!selection || (collapsed && (selection === null || selection === void 0 ? void 0 : selection.isCollapsed)))
2868
+ return;
2869
+ collapsed = selection.isCollapsed || false;
2870
+ const ranges = [];
2871
+ const count = selection.rangeCount || 0;
2872
+ for (let i = 0; i < count; i++) {
2873
+ const range = selection.getRangeAt(i);
2874
+ const { startContainer, startOffset, endContainer, endOffset } = range;
2875
+ const blocked = isBlocked(startContainer, blockClass, blockSelector, unblockSelector, true) ||
2876
+ isBlocked(endContainer, blockClass, blockSelector, unblockSelector, true);
2877
+ if (blocked)
2878
+ continue;
2879
+ ranges.push({
2880
+ start: mirror.getId(startContainer),
2881
+ startOffset,
2882
+ end: mirror.getId(endContainer),
2883
+ endOffset,
2884
+ });
2885
+ }
2886
+ selectionCb({ ranges });
2887
+ });
2888
+ updateSelection();
2889
+ return on('selectionchange', updateSelection);
2890
+ }
2891
+ function initCustomElementObserver({ doc, customElementCb, }) {
2892
+ const win = doc.defaultView;
2893
+ if (!win || !win.customElements) {
2894
+ return () => {
2895
+ };
2896
+ }
2897
+ const restoreHandler = patch(win.customElements, 'define', function (original) {
2898
+ return function (name, constructor, options) {
2899
+ try {
2900
+ customElementCb({
2901
+ define: {
2902
+ name,
2903
+ },
2904
+ });
2905
+ }
2906
+ catch (e) {
2907
+ }
2908
+ return original.apply(this, [name, constructor, options]);
2909
+ };
2910
+ });
2911
+ return restoreHandler;
2912
+ }
2352
2913
  function mergeHooks(o, hooks) {
2353
- const { mutationCb, mousemoveCb, mouseInteractionCb, scrollCb, viewportResizeCb, inputCb, mediaInteractionCb, styleSheetRuleCb, styleDeclarationCb, canvasMutationCb, fontCb, } = o;
2914
+ const { mutationCb, mousemoveCb, mouseInteractionCb, scrollCb, viewportResizeCb, inputCb, mediaInteractionCb, styleSheetRuleCb, styleDeclarationCb, canvasMutationCb, fontCb, selectionCb, customElementCb, } = o;
2354
2915
  o.mutationCb = (...p) => {
2355
2916
  if (hooks.mutation) {
2356
2917
  hooks.mutation(...p);
@@ -2417,25 +2978,46 @@ function mergeHooks(o, hooks) {
2417
2978
  }
2418
2979
  fontCb(...p);
2419
2980
  };
2981
+ o.selectionCb = (...p) => {
2982
+ if (hooks.selection) {
2983
+ hooks.selection(...p);
2984
+ }
2985
+ selectionCb(...p);
2986
+ };
2987
+ o.customElementCb = (...c) => {
2988
+ if (hooks.customElement) {
2989
+ hooks.customElement(...c);
2990
+ }
2991
+ customElementCb(...c);
2992
+ };
2420
2993
  }
2421
2994
  function initObservers(o, hooks = {}) {
2422
2995
  const currentWindow = o.doc.defaultView;
2423
2996
  if (!currentWindow) {
2424
- return () => { };
2997
+ return () => {
2998
+ };
2425
2999
  }
2426
3000
  mergeHooks(o, hooks);
2427
3001
  const mutationObserver = initMutationObserver(o, o.doc);
2428
3002
  const mousemoveHandler = initMoveObserver(o);
2429
3003
  const mouseInteractionHandler = initMouseInteractionObserver(o);
2430
3004
  const scrollHandler = initScrollObserver(o);
2431
- const viewportResizeHandler = initViewportResizeObserver(o);
3005
+ const viewportResizeHandler = initViewportResizeObserver(o, {
3006
+ win: currentWindow,
3007
+ });
2432
3008
  const inputHandler = initInputObserver(o);
2433
3009
  const mediaInteractionHandler = initMediaInteractionObserver(o);
2434
3010
  const styleSheetObserver = initStyleSheetObserver(o, { win: currentWindow });
3011
+ const adoptedStyleSheetObserver = initAdoptedStyleSheetObserver(o, o.doc);
2435
3012
  const styleDeclarationObserver = initStyleDeclarationObserver(o, {
2436
3013
  win: currentWindow,
2437
3014
  });
2438
- const fontObserver = o.collectFonts ? initFontObserver(o) : () => { };
3015
+ const fontObserver = o.collectFonts
3016
+ ? initFontObserver(o)
3017
+ : () => {
3018
+ };
3019
+ const selectionObserver = initSelectionObserver(o);
3020
+ const customElementObserver = initCustomElementObserver(o);
2439
3021
  const pluginHandlers = [];
2440
3022
  for (const plugin of o.plugins) {
2441
3023
  pluginHandlers.push(plugin.observer(plugin.callback, currentWindow, plugin.options));
@@ -2449,13 +3031,12 @@ function initObservers(o, hooks = {}) {
2449
3031
  viewportResizeHandler();
2450
3032
  inputHandler();
2451
3033
  mediaInteractionHandler();
2452
- try {
2453
- styleSheetObserver();
2454
- styleDeclarationObserver();
2455
- }
2456
- catch (e) {
2457
- }
3034
+ styleSheetObserver();
3035
+ adoptedStyleSheetObserver();
3036
+ styleDeclarationObserver();
2458
3037
  fontObserver();
3038
+ selectionObserver();
3039
+ customElementObserver();
2459
3040
  pluginHandlers.forEach((h) => h());
2460
3041
  });
2461
3042
  }
@@ -2469,13 +3050,88 @@ function canMonkeyPatchNestedCSSRule(prop) {
2469
3050
  'deleteRule' in window[prop].prototype);
2470
3051
  }
2471
3052
 
3053
+ class CrossOriginIframeMirror {
3054
+ constructor(generateIdFn) {
3055
+ this.generateIdFn = generateIdFn;
3056
+ this.iframeIdToRemoteIdMap = new WeakMap();
3057
+ this.iframeRemoteIdToIdMap = new WeakMap();
3058
+ }
3059
+ getId(iframe, remoteId, idToRemoteMap, remoteToIdMap) {
3060
+ const idToRemoteIdMap = idToRemoteMap || this.getIdToRemoteIdMap(iframe);
3061
+ const remoteIdToIdMap = remoteToIdMap || this.getRemoteIdToIdMap(iframe);
3062
+ let id = idToRemoteIdMap.get(remoteId);
3063
+ if (!id) {
3064
+ id = this.generateIdFn();
3065
+ idToRemoteIdMap.set(remoteId, id);
3066
+ remoteIdToIdMap.set(id, remoteId);
3067
+ }
3068
+ return id;
3069
+ }
3070
+ getIds(iframe, remoteId) {
3071
+ const idToRemoteIdMap = this.getIdToRemoteIdMap(iframe);
3072
+ const remoteIdToIdMap = this.getRemoteIdToIdMap(iframe);
3073
+ return remoteId.map((id) => this.getId(iframe, id, idToRemoteIdMap, remoteIdToIdMap));
3074
+ }
3075
+ getRemoteId(iframe, id, map) {
3076
+ const remoteIdToIdMap = map || this.getRemoteIdToIdMap(iframe);
3077
+ if (typeof id !== 'number')
3078
+ return id;
3079
+ const remoteId = remoteIdToIdMap.get(id);
3080
+ if (!remoteId)
3081
+ return -1;
3082
+ return remoteId;
3083
+ }
3084
+ getRemoteIds(iframe, ids) {
3085
+ const remoteIdToIdMap = this.getRemoteIdToIdMap(iframe);
3086
+ return ids.map((id) => this.getRemoteId(iframe, id, remoteIdToIdMap));
3087
+ }
3088
+ reset(iframe) {
3089
+ if (!iframe) {
3090
+ this.iframeIdToRemoteIdMap = new WeakMap();
3091
+ this.iframeRemoteIdToIdMap = new WeakMap();
3092
+ return;
3093
+ }
3094
+ this.iframeIdToRemoteIdMap.delete(iframe);
3095
+ this.iframeRemoteIdToIdMap.delete(iframe);
3096
+ }
3097
+ getIdToRemoteIdMap(iframe) {
3098
+ let idToRemoteIdMap = this.iframeIdToRemoteIdMap.get(iframe);
3099
+ if (!idToRemoteIdMap) {
3100
+ idToRemoteIdMap = new Map();
3101
+ this.iframeIdToRemoteIdMap.set(iframe, idToRemoteIdMap);
3102
+ }
3103
+ return idToRemoteIdMap;
3104
+ }
3105
+ getRemoteIdToIdMap(iframe) {
3106
+ let remoteIdToIdMap = this.iframeRemoteIdToIdMap.get(iframe);
3107
+ if (!remoteIdToIdMap) {
3108
+ remoteIdToIdMap = new Map();
3109
+ this.iframeRemoteIdToIdMap.set(iframe, remoteIdToIdMap);
3110
+ }
3111
+ return remoteIdToIdMap;
3112
+ }
3113
+ }
3114
+
2472
3115
  class IframeManager {
2473
3116
  constructor(options) {
2474
3117
  this.iframes = new WeakMap();
3118
+ this.crossOriginIframeMap = new WeakMap();
3119
+ this.crossOriginIframeMirror = new CrossOriginIframeMirror(genId);
3120
+ this.crossOriginIframeRootIdMap = new WeakMap();
2475
3121
  this.mutationCb = options.mutationCb;
3122
+ this.wrappedEmit = options.wrappedEmit;
3123
+ this.stylesheetManager = options.stylesheetManager;
3124
+ this.recordCrossOriginIframes = options.recordCrossOriginIframes;
3125
+ this.crossOriginIframeStyleMirror = new CrossOriginIframeMirror(this.stylesheetManager.styleMirror.generateId.bind(this.stylesheetManager.styleMirror));
3126
+ this.mirror = options.mirror;
3127
+ if (this.recordCrossOriginIframes) {
3128
+ window.addEventListener('message', this.handleMessage.bind(this));
3129
+ }
2476
3130
  }
2477
3131
  addIframe(iframeEl) {
2478
3132
  this.iframes.set(iframeEl, true);
3133
+ if (iframeEl.contentWindow)
3134
+ this.crossOriginIframeMap.set(iframeEl.contentWindow, iframeEl);
2479
3135
  }
2480
3136
  addLoadListener(cb) {
2481
3137
  this.loadListener = cb;
@@ -2485,7 +3141,7 @@ class IframeManager {
2485
3141
  this.mutationCb({
2486
3142
  adds: [
2487
3143
  {
2488
- parentId: iframeEl.__sn.id,
3144
+ parentId: this.mirror.getId(iframeEl),
2489
3145
  nextId: null,
2490
3146
  node: childSn,
2491
3147
  },
@@ -2496,49 +3152,239 @@ class IframeManager {
2496
3152
  isAttachIframe: true,
2497
3153
  });
2498
3154
  (_a = this.loadListener) === null || _a === void 0 ? void 0 : _a.call(this, iframeEl);
3155
+ if (iframeEl.contentDocument &&
3156
+ iframeEl.contentDocument.adoptedStyleSheets &&
3157
+ iframeEl.contentDocument.adoptedStyleSheets.length > 0)
3158
+ this.stylesheetManager.adoptStyleSheets(iframeEl.contentDocument.adoptedStyleSheets, this.mirror.getId(iframeEl.contentDocument));
3159
+ }
3160
+ handleMessage(message) {
3161
+ const crossOriginMessageEvent = message;
3162
+ if (crossOriginMessageEvent.data.type !== 'rrweb' ||
3163
+ crossOriginMessageEvent.origin !== crossOriginMessageEvent.data.origin)
3164
+ return;
3165
+ const iframeSourceWindow = message.source;
3166
+ if (!iframeSourceWindow)
3167
+ return;
3168
+ const iframeEl = this.crossOriginIframeMap.get(message.source);
3169
+ if (!iframeEl)
3170
+ return;
3171
+ const transformedEvent = this.transformCrossOriginEvent(iframeEl, crossOriginMessageEvent.data.event);
3172
+ if (transformedEvent)
3173
+ this.wrappedEmit(transformedEvent, crossOriginMessageEvent.data.isCheckout);
3174
+ }
3175
+ transformCrossOriginEvent(iframeEl, e) {
3176
+ var _a;
3177
+ switch (e.type) {
3178
+ case EventType.FullSnapshot: {
3179
+ this.crossOriginIframeMirror.reset(iframeEl);
3180
+ this.crossOriginIframeStyleMirror.reset(iframeEl);
3181
+ this.replaceIdOnNode(e.data.node, iframeEl);
3182
+ const rootId = e.data.node.id;
3183
+ this.crossOriginIframeRootIdMap.set(iframeEl, rootId);
3184
+ this.patchRootIdOnNode(e.data.node, rootId);
3185
+ return {
3186
+ timestamp: e.timestamp,
3187
+ type: EventType.IncrementalSnapshot,
3188
+ data: {
3189
+ source: IncrementalSource.Mutation,
3190
+ adds: [
3191
+ {
3192
+ parentId: this.mirror.getId(iframeEl),
3193
+ nextId: null,
3194
+ node: e.data.node,
3195
+ },
3196
+ ],
3197
+ removes: [],
3198
+ texts: [],
3199
+ attributes: [],
3200
+ isAttachIframe: true,
3201
+ },
3202
+ };
3203
+ }
3204
+ case EventType.Meta:
3205
+ case EventType.Load:
3206
+ case EventType.DomContentLoaded: {
3207
+ return false;
3208
+ }
3209
+ case EventType.Plugin: {
3210
+ return e;
3211
+ }
3212
+ case EventType.Custom: {
3213
+ this.replaceIds(e.data.payload, iframeEl, ['id', 'parentId', 'previousId', 'nextId']);
3214
+ return e;
3215
+ }
3216
+ case EventType.IncrementalSnapshot: {
3217
+ switch (e.data.source) {
3218
+ case IncrementalSource.Mutation: {
3219
+ e.data.adds.forEach((n) => {
3220
+ this.replaceIds(n, iframeEl, [
3221
+ 'parentId',
3222
+ 'nextId',
3223
+ 'previousId',
3224
+ ]);
3225
+ this.replaceIdOnNode(n.node, iframeEl);
3226
+ const rootId = this.crossOriginIframeRootIdMap.get(iframeEl);
3227
+ rootId && this.patchRootIdOnNode(n.node, rootId);
3228
+ });
3229
+ e.data.removes.forEach((n) => {
3230
+ this.replaceIds(n, iframeEl, ['parentId', 'id']);
3231
+ });
3232
+ e.data.attributes.forEach((n) => {
3233
+ this.replaceIds(n, iframeEl, ['id']);
3234
+ });
3235
+ e.data.texts.forEach((n) => {
3236
+ this.replaceIds(n, iframeEl, ['id']);
3237
+ });
3238
+ return e;
3239
+ }
3240
+ case IncrementalSource.Drag:
3241
+ case IncrementalSource.TouchMove:
3242
+ case IncrementalSource.MouseMove: {
3243
+ e.data.positions.forEach((p) => {
3244
+ this.replaceIds(p, iframeEl, ['id']);
3245
+ });
3246
+ return e;
3247
+ }
3248
+ case IncrementalSource.ViewportResize: {
3249
+ return false;
3250
+ }
3251
+ case IncrementalSource.MediaInteraction:
3252
+ case IncrementalSource.MouseInteraction:
3253
+ case IncrementalSource.Scroll:
3254
+ case IncrementalSource.CanvasMutation:
3255
+ case IncrementalSource.Input: {
3256
+ this.replaceIds(e.data, iframeEl, ['id']);
3257
+ return e;
3258
+ }
3259
+ case IncrementalSource.StyleSheetRule:
3260
+ case IncrementalSource.StyleDeclaration: {
3261
+ this.replaceIds(e.data, iframeEl, ['id']);
3262
+ this.replaceStyleIds(e.data, iframeEl, ['styleId']);
3263
+ return e;
3264
+ }
3265
+ case IncrementalSource.Font: {
3266
+ return e;
3267
+ }
3268
+ case IncrementalSource.Selection: {
3269
+ e.data.ranges.forEach((range) => {
3270
+ this.replaceIds(range, iframeEl, ['start', 'end']);
3271
+ });
3272
+ return e;
3273
+ }
3274
+ case IncrementalSource.AdoptedStyleSheet: {
3275
+ this.replaceIds(e.data, iframeEl, ['id']);
3276
+ this.replaceStyleIds(e.data, iframeEl, ['styleIds']);
3277
+ (_a = e.data.styles) === null || _a === void 0 ? void 0 : _a.forEach((style) => {
3278
+ this.replaceStyleIds(style, iframeEl, ['styleId']);
3279
+ });
3280
+ return e;
3281
+ }
3282
+ }
3283
+ }
3284
+ }
3285
+ return false;
3286
+ }
3287
+ replace(iframeMirror, obj, iframeEl, keys) {
3288
+ for (const key of keys) {
3289
+ if (!Array.isArray(obj[key]) && typeof obj[key] !== 'number')
3290
+ continue;
3291
+ if (Array.isArray(obj[key])) {
3292
+ obj[key] = iframeMirror.getIds(iframeEl, obj[key]);
3293
+ }
3294
+ else {
3295
+ obj[key] = iframeMirror.getId(iframeEl, obj[key]);
3296
+ }
3297
+ }
3298
+ return obj;
3299
+ }
3300
+ replaceIds(obj, iframeEl, keys) {
3301
+ return this.replace(this.crossOriginIframeMirror, obj, iframeEl, keys);
3302
+ }
3303
+ replaceStyleIds(obj, iframeEl, keys) {
3304
+ return this.replace(this.crossOriginIframeStyleMirror, obj, iframeEl, keys);
3305
+ }
3306
+ replaceIdOnNode(node, iframeEl) {
3307
+ this.replaceIds(node, iframeEl, ['id', 'rootId']);
3308
+ if ('childNodes' in node) {
3309
+ node.childNodes.forEach((child) => {
3310
+ this.replaceIdOnNode(child, iframeEl);
3311
+ });
3312
+ }
3313
+ }
3314
+ patchRootIdOnNode(node, rootId) {
3315
+ if (node.type !== NodeType$1.Document && !node.rootId)
3316
+ node.rootId = rootId;
3317
+ if ('childNodes' in node) {
3318
+ node.childNodes.forEach((child) => {
3319
+ this.patchRootIdOnNode(child, rootId);
3320
+ });
3321
+ }
2499
3322
  }
2500
3323
  }
2501
3324
 
2502
3325
  class ShadowDomManager {
2503
3326
  constructor(options) {
2504
- this.restorePatches = [];
3327
+ this.shadowDoms = new WeakSet();
3328
+ this.restoreHandlers = [];
2505
3329
  this.mutationCb = options.mutationCb;
2506
3330
  this.scrollCb = options.scrollCb;
2507
3331
  this.bypassOptions = options.bypassOptions;
2508
3332
  this.mirror = options.mirror;
2509
- const manager = this;
2510
- this.restorePatches.push(patch(HTMLElement.prototype, 'attachShadow', function (original) {
2511
- return function () {
2512
- const shadowRoot = original.apply(this, arguments);
2513
- if (this.shadowRoot)
2514
- manager.addShadowRoot(this.shadowRoot, this.ownerDocument);
2515
- return shadowRoot;
2516
- };
2517
- }));
3333
+ this.init();
3334
+ }
3335
+ init() {
3336
+ this.reset();
3337
+ this.patchAttachShadow(Element, document);
2518
3338
  }
2519
3339
  addShadowRoot(shadowRoot, doc) {
2520
- initMutationObserver(Object.assign(Object.assign({}, this.bypassOptions), { doc, mutationCb: this.mutationCb, mirror: this.mirror, shadowDomManager: this }), shadowRoot);
2521
- initScrollObserver(Object.assign(Object.assign({}, this.bypassOptions), { scrollCb: this.scrollCb, doc: shadowRoot, mirror: this.mirror }));
3340
+ if (!isNativeShadowDom(shadowRoot))
3341
+ return;
3342
+ if (this.shadowDoms.has(shadowRoot))
3343
+ return;
3344
+ this.shadowDoms.add(shadowRoot);
3345
+ const observer = initMutationObserver(Object.assign(Object.assign({}, this.bypassOptions), { doc, mutationCb: this.mutationCb, mirror: this.mirror, shadowDomManager: this }), shadowRoot);
3346
+ this.restoreHandlers.push(() => observer.disconnect());
3347
+ this.restoreHandlers.push(initScrollObserver(Object.assign(Object.assign({}, this.bypassOptions), { scrollCb: this.scrollCb, doc: shadowRoot, mirror: this.mirror })));
3348
+ setTimeout(() => {
3349
+ if (shadowRoot.adoptedStyleSheets &&
3350
+ shadowRoot.adoptedStyleSheets.length > 0)
3351
+ this.bypassOptions.stylesheetManager.adoptStyleSheets(shadowRoot.adoptedStyleSheets, this.mirror.getId(shadowRoot.host));
3352
+ this.restoreHandlers.push(initAdoptedStyleSheetObserver({
3353
+ mirror: this.mirror,
3354
+ stylesheetManager: this.bypassOptions.stylesheetManager,
3355
+ }, shadowRoot));
3356
+ }, 0);
2522
3357
  }
2523
3358
  observeAttachShadow(iframeElement) {
2524
- if (iframeElement.contentWindow) {
2525
- const manager = this;
2526
- this.restorePatches.push(patch(iframeElement.contentWindow.HTMLElement.prototype, 'attachShadow', function (original) {
2527
- return function () {
2528
- const shadowRoot = original.apply(this, arguments);
2529
- if (this.shadowRoot)
2530
- manager.addShadowRoot(this.shadowRoot, iframeElement.contentDocument);
2531
- return shadowRoot;
2532
- };
2533
- }));
2534
- }
3359
+ if (!iframeElement.contentWindow || !iframeElement.contentDocument)
3360
+ return;
3361
+ this.patchAttachShadow(iframeElement.contentWindow.Element, iframeElement.contentDocument);
3362
+ }
3363
+ patchAttachShadow(element, doc) {
3364
+ const manager = this;
3365
+ this.restoreHandlers.push(patch(element.prototype, 'attachShadow', function (original) {
3366
+ return function (option) {
3367
+ const shadowRoot = original.call(this, option);
3368
+ if (this.shadowRoot && inDom(this))
3369
+ manager.addShadowRoot(this.shadowRoot, doc);
3370
+ return shadowRoot;
3371
+ };
3372
+ }));
2535
3373
  }
2536
3374
  reset() {
2537
- this.restorePatches.forEach((restorePatch) => restorePatch());
3375
+ this.restoreHandlers.forEach((handler) => {
3376
+ try {
3377
+ handler();
3378
+ }
3379
+ catch (e) {
3380
+ }
3381
+ });
3382
+ this.restoreHandlers = [];
3383
+ this.shadowDoms = new WeakSet();
2538
3384
  }
2539
3385
  }
2540
3386
 
2541
- /******************************************************************************
3387
+ /*! *****************************************************************************
2542
3388
  Copyright (c) Microsoft Corporation.
2543
3389
 
2544
3390
  Permission to use, copy, modify, and/or distribute this software for any
@@ -2565,85 +3411,19 @@ function __rest(s, e) {
2565
3411
  return t;
2566
3412
  }
2567
3413
 
2568
- function initCanvas2DMutationObserver(cb, win, blockClass, unblockSelector, blockSelector, mirror) {
2569
- const handlers = [];
2570
- const props2D = Object.getOwnPropertyNames(win.CanvasRenderingContext2D.prototype);
2571
- for (const prop of props2D) {
2572
- try {
2573
- if (typeof win.CanvasRenderingContext2D.prototype[prop] !== 'function') {
2574
- continue;
2575
- }
2576
- const restoreHandler = patch(win.CanvasRenderingContext2D.prototype, prop, function (original) {
2577
- return function (...args) {
2578
- if (!isBlocked(this.canvas, blockClass, blockSelector, unblockSelector)) {
2579
- setTimeout(() => {
2580
- const recordArgs = [...args];
2581
- if (prop === 'drawImage') {
2582
- if (recordArgs[0] &&
2583
- recordArgs[0] instanceof HTMLCanvasElement) {
2584
- const canvas = recordArgs[0];
2585
- const ctx = canvas.getContext('2d');
2586
- let imgd = ctx === null || ctx === void 0 ? void 0 : ctx.getImageData(0, 0, canvas.width, canvas.height);
2587
- let pix = imgd === null || imgd === void 0 ? void 0 : imgd.data;
2588
- recordArgs[0] = JSON.stringify(pix);
2589
- }
2590
- }
2591
- cb(this.canvas, {
2592
- type: CanvasContext['2D'],
2593
- property: prop,
2594
- args: recordArgs,
2595
- });
2596
- }, 0);
2597
- }
2598
- return original.apply(this, args);
2599
- };
2600
- });
2601
- handlers.push(restoreHandler);
2602
- }
2603
- catch (_a) {
2604
- const hookHandler = hookSetter(win.CanvasRenderingContext2D.prototype, prop, {
2605
- set(v) {
2606
- cb(this.canvas, {
2607
- type: CanvasContext['2D'],
2608
- property: prop,
2609
- args: [v],
2610
- setter: true,
2611
- });
2612
- },
2613
- });
2614
- handlers.push(hookHandler);
2615
- }
2616
- }
2617
- return () => {
2618
- handlers.forEach((h) => h());
2619
- };
2620
- }
2621
-
2622
- function initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector) {
2623
- const handlers = [];
2624
- try {
2625
- const restoreHandler = patch(win.HTMLCanvasElement.prototype, 'getContext', function (original) {
2626
- return function (contextType, ...args) {
2627
- if (!isBlocked(this, blockClass, blockSelector, unblockSelector)) {
2628
- if (!('__context' in this))
2629
- this.__context = contextType;
2630
- }
2631
- return original.apply(this, [contextType, ...args]);
2632
- };
2633
- });
2634
- handlers.push(restoreHandler);
2635
- }
2636
- catch (_a) {
2637
- console.error('failed to patch HTMLCanvasElement.prototype.getContext');
2638
- }
2639
- return () => {
2640
- handlers.forEach((h) => h());
2641
- };
3414
+ function __awaiter(thisArg, _arguments, P, generator) {
3415
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3416
+ return new (P || (P = Promise))(function (resolve, reject) {
3417
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
3418
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
3419
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
3420
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
3421
+ });
2642
3422
  }
2643
3423
 
2644
3424
  /*
2645
- * base64-arraybuffer 1.0.2 <https://github.com/niklasvh/base64-arraybuffer>
2646
- * Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
3425
+ * base64-arraybuffer 1.0.1 <https://github.com/niklasvh/base64-arraybuffer>
3426
+ * Copyright (c) 2021 Niklas von Hertzen <https://hertzen.com>
2647
3427
  * Released under MIT License
2648
3428
  */
2649
3429
  var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
@@ -2669,12 +3449,12 @@ var encode = function (arraybuffer) {
2669
3449
  return base64;
2670
3450
  };
2671
3451
 
2672
- const webGLVarMap = new Map();
3452
+ const canvasVarMap = new Map();
2673
3453
  function variableListFor(ctx, ctor) {
2674
- let contextMap = webGLVarMap.get(ctx);
3454
+ let contextMap = canvasVarMap.get(ctx);
2675
3455
  if (!contextMap) {
2676
3456
  contextMap = new Map();
2677
- webGLVarMap.set(ctx, contextMap);
3457
+ canvasVarMap.set(ctx, contextMap);
2678
3458
  }
2679
3459
  if (!contextMap.has(ctor)) {
2680
3460
  contextMap.set(ctor, []);
@@ -2743,6 +3523,14 @@ function serializeArg(value, win, ctx) {
2743
3523
  src,
2744
3524
  };
2745
3525
  }
3526
+ else if (value instanceof HTMLCanvasElement) {
3527
+ const name = 'HTMLImageElement';
3528
+ const src = value.toDataURL();
3529
+ return {
3530
+ rr_type: name,
3531
+ src,
3532
+ };
3533
+ }
2746
3534
  else if (value instanceof ImageData) {
2747
3535
  const name = value.constructor.name;
2748
3536
  return {
@@ -2758,33 +3546,125 @@ function serializeArg(value, win, ctx) {
2758
3546
  index: index,
2759
3547
  };
2760
3548
  }
2761
- return value;
3549
+ return value;
3550
+ }
3551
+ const serializeArgs = (args, win, ctx) => {
3552
+ return [...args].map((arg) => serializeArg(arg, win, ctx));
3553
+ };
3554
+ const isInstanceOfWebGLObject = (value, win) => {
3555
+ const webGLConstructorNames = [
3556
+ 'WebGLActiveInfo',
3557
+ 'WebGLBuffer',
3558
+ 'WebGLFramebuffer',
3559
+ 'WebGLProgram',
3560
+ 'WebGLRenderbuffer',
3561
+ 'WebGLShader',
3562
+ 'WebGLShaderPrecisionFormat',
3563
+ 'WebGLTexture',
3564
+ 'WebGLUniformLocation',
3565
+ 'WebGLVertexArrayObject',
3566
+ 'WebGLVertexArrayObjectOES',
3567
+ ];
3568
+ const supportedWebGLConstructorNames = webGLConstructorNames.filter((name) => typeof win[name] === 'function');
3569
+ return Boolean(supportedWebGLConstructorNames.find((name) => value instanceof win[name]));
3570
+ };
3571
+
3572
+ function initCanvas2DMutationObserver(cb, win, blockClass, blockSelector, unblockSelector) {
3573
+ const handlers = [];
3574
+ const props2D = Object.getOwnPropertyNames(win.CanvasRenderingContext2D.prototype);
3575
+ for (const prop of props2D) {
3576
+ try {
3577
+ if (typeof win.CanvasRenderingContext2D.prototype[prop] !== 'function') {
3578
+ continue;
3579
+ }
3580
+ const restoreHandler = patch(win.CanvasRenderingContext2D.prototype, prop, function (original) {
3581
+ return function (...args) {
3582
+ if (!isBlocked(this.canvas, blockClass, blockSelector, unblockSelector, true)) {
3583
+ setTimeout(() => {
3584
+ const recordArgs = serializeArgs([...args], win, this);
3585
+ cb(this.canvas, {
3586
+ type: CanvasContext['2D'],
3587
+ property: prop,
3588
+ args: recordArgs,
3589
+ });
3590
+ }, 0);
3591
+ }
3592
+ return original.apply(this, args);
3593
+ };
3594
+ });
3595
+ handlers.push(restoreHandler);
3596
+ }
3597
+ catch (_a) {
3598
+ const hookHandler = hookSetter(win.CanvasRenderingContext2D.prototype, prop, {
3599
+ set(v) {
3600
+ cb(this.canvas, {
3601
+ type: CanvasContext['2D'],
3602
+ property: prop,
3603
+ args: [v],
3604
+ setter: true,
3605
+ });
3606
+ },
3607
+ });
3608
+ handlers.push(hookHandler);
3609
+ }
3610
+ }
3611
+ return () => {
3612
+ handlers.forEach((h) => h());
3613
+ };
3614
+ }
3615
+
3616
+ function getNormalizedContextName(contextType) {
3617
+ return contextType === 'experimental-webgl' ? 'webgl' : contextType;
3618
+ }
3619
+ function initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector, setPreserveDrawingBufferToTrue) {
3620
+ const handlers = [];
3621
+ try {
3622
+ const restoreHandler = patch(win.HTMLCanvasElement.prototype, 'getContext', function (original) {
3623
+ return function (contextType, ...args) {
3624
+ if (!isBlocked(this, blockClass, blockSelector, unblockSelector, true)) {
3625
+ const ctxName = getNormalizedContextName(contextType);
3626
+ if (!('__context' in this))
3627
+ this.__context = ctxName;
3628
+ if (setPreserveDrawingBufferToTrue &&
3629
+ ['webgl', 'webgl2'].includes(ctxName)) {
3630
+ if (args[0] && typeof args[0] === 'object') {
3631
+ const contextAttributes = args[0];
3632
+ if (!contextAttributes.preserveDrawingBuffer) {
3633
+ contextAttributes.preserveDrawingBuffer = true;
3634
+ }
3635
+ }
3636
+ else {
3637
+ args.splice(0, 1, {
3638
+ preserveDrawingBuffer: true,
3639
+ });
3640
+ }
3641
+ }
3642
+ }
3643
+ return original.apply(this, [contextType, ...args]);
3644
+ };
3645
+ });
3646
+ handlers.push(restoreHandler);
3647
+ }
3648
+ catch (_a) {
3649
+ console.error('failed to patch HTMLCanvasElement.prototype.getContext');
3650
+ }
3651
+ return () => {
3652
+ handlers.forEach((h) => h());
3653
+ };
2762
3654
  }
2763
- const serializeArgs = (args, win, ctx) => {
2764
- return [...args].map((arg) => serializeArg(arg, win, ctx));
2765
- };
2766
- const isInstanceOfWebGLObject = (value, win) => {
2767
- const webGLConstructorNames = [
2768
- 'WebGLActiveInfo',
2769
- 'WebGLBuffer',
2770
- 'WebGLFramebuffer',
2771
- 'WebGLProgram',
2772
- 'WebGLRenderbuffer',
2773
- 'WebGLShader',
2774
- 'WebGLShaderPrecisionFormat',
2775
- 'WebGLTexture',
2776
- 'WebGLUniformLocation',
2777
- 'WebGLVertexArrayObject',
2778
- 'WebGLVertexArrayObjectOES',
2779
- ];
2780
- const supportedWebGLConstructorNames = webGLConstructorNames.filter((name) => typeof win[name] === 'function');
2781
- return Boolean(supportedWebGLConstructorNames.find((name) => value instanceof win[name]));
2782
- };
2783
3655
 
2784
- function patchGLPrototype(prototype, type, cb, blockClass, unblockSelector, blockSelector, mirror, win) {
3656
+ function patchGLPrototype(prototype, type, cb, blockClass, blockSelector, unblockSelector, mirror, win) {
2785
3657
  const handlers = [];
2786
3658
  const props = Object.getOwnPropertyNames(prototype);
2787
3659
  for (const prop of props) {
3660
+ if ([
3661
+ 'isContextLost',
3662
+ 'canvas',
3663
+ 'drawingBufferWidth',
3664
+ 'drawingBufferHeight',
3665
+ ].includes(prop)) {
3666
+ continue;
3667
+ }
2788
3668
  try {
2789
3669
  if (typeof prototype[prop] !== 'function') {
2790
3670
  continue;
@@ -2792,10 +3672,9 @@ function patchGLPrototype(prototype, type, cb, blockClass, unblockSelector, bloc
2792
3672
  const restoreHandler = patch(prototype, prop, function (original) {
2793
3673
  return function (...args) {
2794
3674
  const result = original.apply(this, args);
2795
- saveWebGLVar(result, win, prototype);
2796
- if (!isBlocked(this.canvas, blockClass, blockSelector, unblockSelector)) {
2797
- const id = mirror.getId(this.canvas);
2798
- const recordArgs = serializeArgs([...args], win, prototype);
3675
+ saveWebGLVar(result, win, this);
3676
+ if (!isBlocked(this.canvas, blockClass, blockSelector, unblockSelector, true)) {
3677
+ const recordArgs = serializeArgs([...args], win, this);
2799
3678
  const mutation = {
2800
3679
  type,
2801
3680
  property: prop,
@@ -2835,7 +3714,64 @@ function initCanvasWebGLMutationObserver(cb, win, blockClass, blockSelector, unb
2835
3714
  };
2836
3715
  }
2837
3716
 
3717
+ function decodeBase64(base64, enableUnicode) {
3718
+ var binaryString = atob(base64);
3719
+ if (enableUnicode) {
3720
+ var binaryView = new Uint8Array(binaryString.length);
3721
+ for (var i = 0, n = binaryString.length; i < n; ++i) {
3722
+ binaryView[i] = binaryString.charCodeAt(i);
3723
+ }
3724
+ return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer));
3725
+ }
3726
+ return binaryString;
3727
+ }
3728
+
3729
+ function createURL(base64, sourcemapArg, enableUnicodeArg) {
3730
+ var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;
3731
+ var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;
3732
+ var source = decodeBase64(base64, enableUnicode);
3733
+ var start = source.indexOf('\n', 10) + 1;
3734
+ var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : '');
3735
+ var blob = new Blob([body], { type: 'application/javascript' });
3736
+ return URL.createObjectURL(blob);
3737
+ }
3738
+
3739
+ function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {
3740
+ var url;
3741
+ return function WorkerFactory(options) {
3742
+ url = url || createURL(base64, sourcemapArg, enableUnicodeArg);
3743
+ return new Worker(url, options);
3744
+ };
3745
+ }
3746
+
3747
+ var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIC8qISAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KICAgIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLg0KDQogICAgUGVybWlzc2lvbiB0byB1c2UsIGNvcHksIG1vZGlmeSwgYW5kL29yIGRpc3RyaWJ1dGUgdGhpcyBzb2Z0d2FyZSBmb3IgYW55DQogICAgcHVycG9zZSB3aXRoIG9yIHdpdGhvdXQgZmVlIGlzIGhlcmVieSBncmFudGVkLg0KDQogICAgVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIgQU5EIFRIRSBBVVRIT1IgRElTQ0xBSU1TIEFMTCBXQVJSQU5USUVTIFdJVEgNCiAgICBSRUdBUkQgVE8gVEhJUyBTT0ZUV0FSRSBJTkNMVURJTkcgQUxMIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkNCiAgICBBTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsDQogICAgSU5ESVJFQ1QsIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyBPUiBBTlkgREFNQUdFUyBXSEFUU09FVkVSIFJFU1VMVElORyBGUk9NDQogICAgTE9TUyBPRiBVU0UsIERBVEEgT1IgUFJPRklUUywgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIE5FR0xJR0VOQ0UgT1INCiAgICBPVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SDQogICAgUEVSRk9STUFOQ0UgT0YgVEhJUyBTT0ZUV0FSRS4NCiAgICAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAqLw0KDQogICAgZnVuY3Rpb24gX19hd2FpdGVyKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikgew0KICAgICAgICBmdW5jdGlvbiBhZG9wdCh2YWx1ZSkgeyByZXR1cm4gdmFsdWUgaW5zdGFuY2VvZiBQID8gdmFsdWUgOiBuZXcgUChmdW5jdGlvbiAocmVzb2x2ZSkgeyByZXNvbHZlKHZhbHVlKTsgfSk7IH0NCiAgICAgICAgcmV0dXJuIG5ldyAoUCB8fCAoUCA9IFByb21pc2UpKShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7DQogICAgICAgICAgICBmdW5jdGlvbiBmdWxmaWxsZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3IubmV4dCh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9DQogICAgICAgICAgICBmdW5jdGlvbiByZWplY3RlZCh2YWx1ZSkgeyB0cnkgeyBzdGVwKGdlbmVyYXRvclsidGhyb3ciXSh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9DQogICAgICAgICAgICBmdW5jdGlvbiBzdGVwKHJlc3VsdCkgeyByZXN1bHQuZG9uZSA/IHJlc29sdmUocmVzdWx0LnZhbHVlKSA6IGFkb3B0KHJlc3VsdC52YWx1ZSkudGhlbihmdWxmaWxsZWQsIHJlamVjdGVkKTsgfQ0KICAgICAgICAgICAgc3RlcCgoZ2VuZXJhdG9yID0gZ2VuZXJhdG9yLmFwcGx5KHRoaXNBcmcsIF9hcmd1bWVudHMgfHwgW10pKS5uZXh0KCkpOw0KICAgICAgICB9KTsNCiAgICB9CgogICAgLyoKICAgICAqIGJhc2U2NC1hcnJheWJ1ZmZlciAxLjAuMSA8aHR0cHM6Ly9naXRodWIuY29tL25pa2xhc3ZoL2Jhc2U2NC1hcnJheWJ1ZmZlcj4KICAgICAqIENvcHlyaWdodCAoYykgMjAyMSBOaWtsYXMgdm9uIEhlcnR6ZW4gPGh0dHBzOi8vaGVydHplbi5jb20+CiAgICAgKiBSZWxlYXNlZCB1bmRlciBNSVQgTGljZW5zZQogICAgICovCiAgICB2YXIgY2hhcnMgPSAnQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLyc7CiAgICAvLyBVc2UgYSBsb29rdXAgdGFibGUgdG8gZmluZCB0aGUgaW5kZXguCiAgICB2YXIgbG9va3VwID0gdHlwZW9mIFVpbnQ4QXJyYXkgPT09ICd1bmRlZmluZWQnID8gW10gOiBuZXcgVWludDhBcnJheSgyNTYpOwogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjaGFycy5sZW5ndGg7IGkrKykgewogICAgICAgIGxvb2t1cFtjaGFycy5jaGFyQ29kZUF0KGkpXSA9IGk7CiAgICB9CiAgICB2YXIgZW5jb2RlID0gZnVuY3Rpb24gKGFycmF5YnVmZmVyKSB7CiAgICAgICAgdmFyIGJ5dGVzID0gbmV3IFVpbnQ4QXJyYXkoYXJyYXlidWZmZXIpLCBpLCBsZW4gPSBieXRlcy5sZW5ndGgsIGJhc2U2NCA9ICcnOwogICAgICAgIGZvciAoaSA9IDA7IGkgPCBsZW47IGkgKz0gMykgewogICAgICAgICAgICBiYXNlNjQgKz0gY2hhcnNbYnl0ZXNbaV0gPj4gMl07CiAgICAgICAgICAgIGJhc2U2NCArPSBjaGFyc1soKGJ5dGVzW2ldICYgMykgPDwgNCkgfCAoYnl0ZXNbaSArIDFdID4+IDQpXTsKICAgICAgICAgICAgYmFzZTY0ICs9IGNoYXJzWygoYnl0ZXNbaSArIDFdICYgMTUpIDw8IDIpIHwgKGJ5dGVzW2kgKyAyXSA+PiA2KV07CiAgICAgICAgICAgIGJhc2U2NCArPSBjaGFyc1tieXRlc1tpICsgMl0gJiA2M107CiAgICAgICAgfQogICAgICAgIGlmIChsZW4gJSAzID09PSAyKSB7CiAgICAgICAgICAgIGJhc2U2NCA9IGJhc2U2NC5zdWJzdHJpbmcoMCwgYmFzZTY0Lmxlbmd0aCAtIDEpICsgJz0nOwogICAgICAgIH0KICAgICAgICBlbHNlIGlmIChsZW4gJSAzID09PSAxKSB7CiAgICAgICAgICAgIGJhc2U2NCA9IGJhc2U2NC5zdWJzdHJpbmcoMCwgYmFzZTY0Lmxlbmd0aCAtIDIpICsgJz09JzsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIGJhc2U2NDsKICAgIH07CgogICAgY29uc3QgbGFzdEJsb2JNYXAgPSBuZXcgTWFwKCk7DQogICAgY29uc3QgdHJhbnNwYXJlbnRCbG9iTWFwID0gbmV3IE1hcCgpOw0KICAgIGZ1bmN0aW9uIGdldFRyYW5zcGFyZW50QmxvYkZvcih3aWR0aCwgaGVpZ2h0LCBkYXRhVVJMT3B0aW9ucykgew0KICAgICAgICByZXR1cm4gX19hd2FpdGVyKHRoaXMsIHZvaWQgMCwgdm9pZCAwLCBmdW5jdGlvbiogKCkgew0KICAgICAgICAgICAgY29uc3QgaWQgPSBgJHt3aWR0aH0tJHtoZWlnaHR9YDsNCiAgICAgICAgICAgIGlmICgnT2Zmc2NyZWVuQ2FudmFzJyBpbiBnbG9iYWxUaGlzKSB7DQogICAgICAgICAgICAgICAgaWYgKHRyYW5zcGFyZW50QmxvYk1hcC5oYXMoaWQpKQ0KICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJhbnNwYXJlbnRCbG9iTWFwLmdldChpZCk7DQogICAgICAgICAgICAgICAgY29uc3Qgb2Zmc2NyZWVuID0gbmV3IE9mZnNjcmVlbkNhbnZhcyh3aWR0aCwgaGVpZ2h0KTsNCiAgICAgICAgICAgICAgICBvZmZzY3JlZW4uZ2V0Q29udGV4dCgnMmQnKTsNCiAgICAgICAgICAgICAgICBjb25zdCBibG9iID0geWllbGQgb2Zmc2NyZWVuLmNvbnZlcnRUb0Jsb2IoZGF0YVVSTE9wdGlvbnMpOw0KICAgICAgICAgICAgICAgIGNvbnN0IGFycmF5QnVmZmVyID0geWllbGQgYmxvYi5hcnJheUJ1ZmZlcigpOw0KICAgICAgICAgICAgICAgIGNvbnN0IGJhc2U2NCA9IGVuY29kZShhcnJheUJ1ZmZlcik7DQogICAgICAgICAgICAgICAgdHJhbnNwYXJlbnRCbG9iTWFwLnNldChpZCwgYmFzZTY0KTsNCiAgICAgICAgICAgICAgICByZXR1cm4gYmFzZTY0Ow0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZSB7DQogICAgICAgICAgICAgICAgcmV0dXJuICcnOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9KTsNCiAgICB9DQogICAgY29uc3Qgd29ya2VyID0gc2VsZjsNCiAgICB3b3JrZXIub25tZXNzYWdlID0gZnVuY3Rpb24gKGUpIHsNCiAgICAgICAgcmV0dXJuIF9fYXdhaXRlcih0aGlzLCB2b2lkIDAsIHZvaWQgMCwgZnVuY3Rpb24qICgpIHsNCiAgICAgICAgICAgIGlmICgnT2Zmc2NyZWVuQ2FudmFzJyBpbiBnbG9iYWxUaGlzKSB7DQogICAgICAgICAgICAgICAgY29uc3QgeyBpZCwgYml0bWFwLCB3aWR0aCwgaGVpZ2h0LCBkYXRhVVJMT3B0aW9ucyB9ID0gZS5kYXRhOw0KICAgICAgICAgICAgICAgIGNvbnN0IHRyYW5zcGFyZW50QmFzZTY0ID0gZ2V0VHJhbnNwYXJlbnRCbG9iRm9yKHdpZHRoLCBoZWlnaHQsIGRhdGFVUkxPcHRpb25zKTsNCiAgICAgICAgICAgICAgICBjb25zdCBvZmZzY3JlZW4gPSBuZXcgT2Zmc2NyZWVuQ2FudmFzKHdpZHRoLCBoZWlnaHQpOw0KICAgICAgICAgICAgICAgIGNvbnN0IGN0eCA9IG9mZnNjcmVlbi5nZXRDb250ZXh0KCcyZCcpOw0KICAgICAgICAgICAgICAgIGN0eC5kcmF3SW1hZ2UoYml0bWFwLCAwLCAwKTsNCiAgICAgICAgICAgICAgICBiaXRtYXAuY2xvc2UoKTsNCiAgICAgICAgICAgICAgICBjb25zdCBibG9iID0geWllbGQgb2Zmc2NyZWVuLmNvbnZlcnRUb0Jsb2IoZGF0YVVSTE9wdGlvbnMpOw0KICAgICAgICAgICAgICAgIGNvbnN0IHR5cGUgPSBibG9iLnR5cGU7DQogICAgICAgICAgICAgICAgY29uc3QgYXJyYXlCdWZmZXIgPSB5aWVsZCBibG9iLmFycmF5QnVmZmVyKCk7DQogICAgICAgICAgICAgICAgY29uc3QgYmFzZTY0ID0gZW5jb2RlKGFycmF5QnVmZmVyKTsNCiAgICAgICAgICAgICAgICBpZiAoIWxhc3RCbG9iTWFwLmhhcyhpZCkgJiYgKHlpZWxkIHRyYW5zcGFyZW50QmFzZTY0KSA9PT0gYmFzZTY0KSB7DQogICAgICAgICAgICAgICAgICAgIGxhc3RCbG9iTWFwLnNldChpZCwgYmFzZTY0KTsNCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHdvcmtlci5wb3N0TWVzc2FnZSh7IGlkIH0pOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBpZiAobGFzdEJsb2JNYXAuZ2V0KGlkKSA9PT0gYmFzZTY0KQ0KICAgICAgICAgICAgICAgICAgICByZXR1cm4gd29ya2VyLnBvc3RNZXNzYWdlKHsgaWQgfSk7DQogICAgICAgICAgICAgICAgd29ya2VyLnBvc3RNZXNzYWdlKHsNCiAgICAgICAgICAgICAgICAgICAgaWQsDQogICAgICAgICAgICAgICAgICAgIHR5cGUsDQogICAgICAgICAgICAgICAgICAgIGJhc2U2NCwNCiAgICAgICAgICAgICAgICAgICAgd2lkdGgsDQogICAgICAgICAgICAgICAgICAgIGhlaWdodCwNCiAgICAgICAgICAgICAgICB9KTsNCiAgICAgICAgICAgICAgICBsYXN0QmxvYk1hcC5zZXQoaWQsIGJhc2U2NCk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBlbHNlIHsNCiAgICAgICAgICAgICAgICByZXR1cm4gd29ya2VyLnBvc3RNZXNzYWdlKHsgaWQ6IGUuZGF0YS5pZCB9KTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfSk7DQogICAgfTsKCn0pKCk7Cgo=', null, false);
3748
+
2838
3749
  class CanvasManager {
3750
+ constructor(options) {
3751
+ this.pendingCanvasMutations = new Map();
3752
+ this.rafStamps = { latestId: 0, invokeId: null };
3753
+ this.frozen = false;
3754
+ this.locked = false;
3755
+ this.processMutation = (target, mutation) => {
3756
+ const newFrame = this.rafStamps.invokeId &&
3757
+ this.rafStamps.latestId !== this.rafStamps.invokeId;
3758
+ if (newFrame || !this.rafStamps.invokeId)
3759
+ this.rafStamps.invokeId = this.rafStamps.latestId;
3760
+ if (!this.pendingCanvasMutations.has(target)) {
3761
+ this.pendingCanvasMutations.set(target, []);
3762
+ }
3763
+ this.pendingCanvasMutations.get(target).push(mutation);
3764
+ };
3765
+ const { sampling = 'all', win, blockClass, blockSelector, unblockSelector, recordCanvas, dataURLOptions, } = options;
3766
+ this.mutationCb = options.mutationCb;
3767
+ this.mirror = options.mirror;
3768
+ if (recordCanvas && sampling === 'all')
3769
+ this.initCanvasMutationObserver(win, blockClass, blockSelector, unblockSelector);
3770
+ if (recordCanvas && typeof sampling === 'number')
3771
+ this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector, unblockSelector, {
3772
+ dataURLOptions,
3773
+ });
3774
+ }
2839
3775
  reset() {
2840
3776
  this.pendingCanvasMutations.clear();
2841
3777
  this.resetObservers && this.resetObservers();
@@ -2852,31 +3788,98 @@ class CanvasManager {
2852
3788
  unlock() {
2853
3789
  this.locked = false;
2854
3790
  }
2855
- constructor(options) {
2856
- this.pendingCanvasMutations = new Map();
2857
- this.rafStamps = { latestId: 0, invokeId: null };
2858
- this.frozen = false;
2859
- this.locked = false;
2860
- this.processMutation = function (target, mutation) {
2861
- const newFrame = this.rafStamps.invokeId &&
2862
- this.rafStamps.latestId !== this.rafStamps.invokeId;
2863
- if (newFrame || !this.rafStamps.invokeId)
2864
- this.rafStamps.invokeId = this.rafStamps.latestId;
2865
- if (!this.pendingCanvasMutations.has(target)) {
2866
- this.pendingCanvasMutations.set(target, []);
3791
+ initCanvasFPSObserver(fps, win, blockClass, blockSelector, unblockSelector, options) {
3792
+ const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector, true);
3793
+ const snapshotInProgressMap = new Map();
3794
+ const worker = new WorkerFactory();
3795
+ worker.onmessage = (e) => {
3796
+ const { id } = e.data;
3797
+ snapshotInProgressMap.set(id, false);
3798
+ if (!('base64' in e.data))
3799
+ return;
3800
+ const { base64, type, width, height } = e.data;
3801
+ this.mutationCb({
3802
+ id,
3803
+ type: CanvasContext['2D'],
3804
+ commands: [
3805
+ {
3806
+ property: 'clearRect',
3807
+ args: [0, 0, width, height],
3808
+ },
3809
+ {
3810
+ property: 'drawImage',
3811
+ args: [
3812
+ {
3813
+ rr_type: 'ImageBitmap',
3814
+ args: [
3815
+ {
3816
+ rr_type: 'Blob',
3817
+ data: [{ rr_type: 'ArrayBuffer', base64 }],
3818
+ type,
3819
+ },
3820
+ ],
3821
+ },
3822
+ 0,
3823
+ 0,
3824
+ ],
3825
+ },
3826
+ ],
3827
+ });
3828
+ };
3829
+ const timeBetweenSnapshots = 1000 / fps;
3830
+ let lastSnapshotTime = 0;
3831
+ let rafId;
3832
+ const getCanvas = () => {
3833
+ const matchedCanvas = [];
3834
+ win.document.querySelectorAll('canvas').forEach((canvas) => {
3835
+ if (!isBlocked(canvas, blockClass, blockSelector, unblockSelector, true)) {
3836
+ matchedCanvas.push(canvas);
3837
+ }
3838
+ });
3839
+ return matchedCanvas;
3840
+ };
3841
+ const takeCanvasSnapshots = (timestamp) => {
3842
+ if (lastSnapshotTime &&
3843
+ timestamp - lastSnapshotTime < timeBetweenSnapshots) {
3844
+ rafId = requestAnimationFrame(takeCanvasSnapshots);
3845
+ return;
2867
3846
  }
2868
- this.pendingCanvasMutations.get(target).push(mutation);
3847
+ lastSnapshotTime = timestamp;
3848
+ getCanvas()
3849
+ .forEach((canvas) => __awaiter(this, void 0, void 0, function* () {
3850
+ var _a;
3851
+ const id = this.mirror.getId(canvas);
3852
+ if (snapshotInProgressMap.get(id))
3853
+ return;
3854
+ snapshotInProgressMap.set(id, true);
3855
+ if (['webgl', 'webgl2'].includes(canvas.__context)) {
3856
+ const context = canvas.getContext(canvas.__context);
3857
+ if (((_a = context === null || context === void 0 ? void 0 : context.getContextAttributes()) === null || _a === void 0 ? void 0 : _a.preserveDrawingBuffer) === false) {
3858
+ context.clear(context.COLOR_BUFFER_BIT);
3859
+ }
3860
+ }
3861
+ const bitmap = yield createImageBitmap(canvas);
3862
+ worker.postMessage({
3863
+ id,
3864
+ bitmap,
3865
+ width: canvas.width,
3866
+ height: canvas.height,
3867
+ dataURLOptions: options.dataURLOptions,
3868
+ }, [bitmap]);
3869
+ }));
3870
+ rafId = requestAnimationFrame(takeCanvasSnapshots);
3871
+ };
3872
+ rafId = requestAnimationFrame(takeCanvasSnapshots);
3873
+ this.resetObservers = () => {
3874
+ canvasContextReset();
3875
+ cancelAnimationFrame(rafId);
2869
3876
  };
2870
- this.mutationCb = options.mutationCb;
2871
- this.mirror = options.mirror;
2872
- if (options.recordCanvas === true)
2873
- this.initCanvasMutationObserver(options.win, options.blockClass, options.blockSelector, options.unblockSelector);
2874
3877
  }
2875
- initCanvasMutationObserver(win, blockClass, unblockSelector, blockSelector) {
3878
+ initCanvasMutationObserver(win, blockClass, blockSelector, unblockSelector) {
2876
3879
  this.startRAFTimestamping();
2877
3880
  this.startPendingCanvasMutationFlusher();
2878
- const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector);
2879
- const canvas2DReset = initCanvas2DMutationObserver(this.processMutation.bind(this), win, blockClass, blockSelector, unblockSelector, this.mirror);
3881
+ const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector, unblockSelector, false);
3882
+ const canvas2DReset = initCanvas2DMutationObserver(this.processMutation.bind(this), win, blockClass, blockSelector, unblockSelector);
2880
3883
  const canvasWebGL1and2Reset = initCanvasWebGLMutationObserver(this.processMutation.bind(this), win, blockClass, blockSelector, unblockSelector, this.mirror);
2881
3884
  this.resetObservers = () => {
2882
3885
  canvasContextReset();
@@ -2918,20 +3921,136 @@ class CanvasManager {
2918
3921
  }
2919
3922
  }
2920
3923
 
3924
+ class StylesheetManager {
3925
+ constructor(options) {
3926
+ this.trackedLinkElements = new WeakSet();
3927
+ this.styleMirror = new StyleSheetMirror();
3928
+ this.mutationCb = options.mutationCb;
3929
+ this.adoptedStyleSheetCb = options.adoptedStyleSheetCb;
3930
+ }
3931
+ attachLinkElement(linkEl, childSn) {
3932
+ if ('_cssText' in childSn.attributes)
3933
+ this.mutationCb({
3934
+ adds: [],
3935
+ removes: [],
3936
+ texts: [],
3937
+ attributes: [
3938
+ {
3939
+ id: childSn.id,
3940
+ attributes: childSn
3941
+ .attributes,
3942
+ },
3943
+ ],
3944
+ });
3945
+ this.trackLinkElement(linkEl);
3946
+ }
3947
+ trackLinkElement(linkEl) {
3948
+ if (this.trackedLinkElements.has(linkEl))
3949
+ return;
3950
+ this.trackedLinkElements.add(linkEl);
3951
+ this.trackStylesheetInLinkElement(linkEl);
3952
+ }
3953
+ adoptStyleSheets(sheets, hostId) {
3954
+ if (sheets.length === 0)
3955
+ return;
3956
+ const adoptedStyleSheetData = {
3957
+ id: hostId,
3958
+ styleIds: [],
3959
+ };
3960
+ const styles = [];
3961
+ for (const sheet of sheets) {
3962
+ let styleId;
3963
+ if (!this.styleMirror.has(sheet)) {
3964
+ styleId = this.styleMirror.add(sheet);
3965
+ const rules = Array.from(sheet.rules || CSSRule);
3966
+ styles.push({
3967
+ styleId,
3968
+ rules: rules.map((r, index) => {
3969
+ return {
3970
+ rule: getCssRuleString(r),
3971
+ index,
3972
+ };
3973
+ }),
3974
+ });
3975
+ }
3976
+ else
3977
+ styleId = this.styleMirror.getId(sheet);
3978
+ adoptedStyleSheetData.styleIds.push(styleId);
3979
+ }
3980
+ if (styles.length > 0)
3981
+ adoptedStyleSheetData.styles = styles;
3982
+ this.adoptedStyleSheetCb(adoptedStyleSheetData);
3983
+ }
3984
+ reset() {
3985
+ this.styleMirror.reset();
3986
+ this.trackedLinkElements = new WeakSet();
3987
+ }
3988
+ trackStylesheetInLinkElement(linkEl) {
3989
+ }
3990
+ }
3991
+
3992
+ class ProcessedNodeManager {
3993
+ constructor() {
3994
+ this.nodeMap = new WeakMap();
3995
+ this.loop = true;
3996
+ this.periodicallyClear();
3997
+ }
3998
+ periodicallyClear() {
3999
+ requestAnimationFrame(() => {
4000
+ this.clear();
4001
+ if (this.loop)
4002
+ this.periodicallyClear();
4003
+ });
4004
+ }
4005
+ inOtherBuffer(node, thisBuffer) {
4006
+ const buffers = this.nodeMap.get(node);
4007
+ return (buffers && Array.from(buffers).some((buffer) => buffer !== thisBuffer));
4008
+ }
4009
+ add(node, buffer) {
4010
+ this.nodeMap.set(node, (this.nodeMap.get(node) || new Set()).add(buffer));
4011
+ }
4012
+ clear() {
4013
+ this.nodeMap = new WeakMap();
4014
+ }
4015
+ destroy() {
4016
+ this.loop = false;
4017
+ }
4018
+ }
4019
+
2921
4020
  function wrapEvent(e) {
2922
- return Object.assign(Object.assign({}, e), { timestamp: Date.now() });
4021
+ return Object.assign(Object.assign({}, e), { timestamp: nowTimestamp() });
2923
4022
  }
2924
4023
  let wrappedEmit;
2925
4024
  let takeFullSnapshot;
4025
+ let canvasManager;
4026
+ let recording = false;
2926
4027
  const mirror = createMirror();
2927
4028
  function record(options = {}) {
2928
- const { emit, checkoutEveryNms, checkoutEveryNth, blockClass = 'rr-block', blockSelector = null, unblockSelector = null, ignoreClass = 'rr-ignore', ignoreSelector = null, maskTextClass = 'rr-mask', maskTextSelector = null, maskInputSelector = null, unmaskTextSelector = null, unmaskInputSelector = null, inlineStylesheet = true, maskAllText = false, maskAllInputs, maskInputOptions: _maskInputOptions, slimDOMOptions: _slimDOMOptions, maskInputFn, maskTextFn, hooks, packFn, sampling = {}, mousemoveWait, recordCanvas = false, userTriggeredOnInput = false, collectFonts = false, inlineImages = false, plugins, keepIframeSrcFn = () => false, onMutation, } = options;
2929
- if (!emit) {
4029
+ const { emit, checkoutEveryNms, checkoutEveryNth, blockClass = 'rr-block', blockSelector = null, unblockSelector = null, ignoreClass = 'rr-ignore', ignoreSelector = null, maskAllText = false, maskTextClass = 'rr-mask', unmaskTextClass = null, maskTextSelector = null, unmaskTextSelector = null, inlineStylesheet = true, maskAllInputs, maskInputOptions: _maskInputOptions, slimDOMOptions: _slimDOMOptions, maskAttributeFn, maskInputFn, maskTextFn, hooks, packFn, sampling = {}, dataURLOptions = {}, mousemoveWait, recordCanvas = false, recordCrossOriginIframes = false, recordAfter = options.recordAfter === 'DOMContentLoaded'
4030
+ ? options.recordAfter
4031
+ : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, plugins, keepIframeSrcFn = () => false, ignoreCSSAttributes = new Set([]), errorHandler, onMutation, } = options;
4032
+ registerErrorHandler(errorHandler);
4033
+ const inEmittingFrame = recordCrossOriginIframes
4034
+ ? window.parent === window
4035
+ : true;
4036
+ let passEmitsToParent = false;
4037
+ if (!inEmittingFrame) {
4038
+ try {
4039
+ if (window.parent.document) {
4040
+ passEmitsToParent = false;
4041
+ }
4042
+ }
4043
+ catch (e) {
4044
+ passEmitsToParent = true;
4045
+ }
4046
+ }
4047
+ if (inEmittingFrame && !emit) {
2930
4048
  throw new Error('emit function is required');
2931
4049
  }
2932
4050
  if (mousemoveWait !== undefined && sampling.mousemove === undefined) {
2933
4051
  sampling.mousemove = mousemoveWait;
2934
4052
  }
4053
+ mirror.reset();
2935
4054
  const maskInputOptions = maskAllInputs === true
2936
4055
  ? {
2937
4056
  color: true,
@@ -2980,7 +4099,8 @@ function record(options = {}) {
2980
4099
  e = plugin.eventProcessor(e);
2981
4100
  }
2982
4101
  }
2983
- if (packFn) {
4102
+ if (packFn &&
4103
+ !passEmitsToParent) {
2984
4104
  e = packFn(e);
2985
4105
  }
2986
4106
  return e;
@@ -2993,7 +4113,18 @@ function record(options = {}) {
2993
4113
  e.data.source === IncrementalSource.Mutation)) {
2994
4114
  mutationBuffers.forEach((buf) => buf.unfreeze());
2995
4115
  }
2996
- emit(eventProcessor(e), isCheckout);
4116
+ if (inEmittingFrame) {
4117
+ emit === null || emit === void 0 ? void 0 : emit(eventProcessor(e), isCheckout);
4118
+ }
4119
+ else if (passEmitsToParent) {
4120
+ const message = {
4121
+ type: 'rrweb',
4122
+ event: eventProcessor(e),
4123
+ origin: window.location.origin,
4124
+ isCheckout,
4125
+ };
4126
+ window.parent.postMessage(message, '*');
4127
+ }
2997
4128
  if (e.type === EventType.FullSnapshot) {
2998
4129
  lastFullSnapshotEvent = e;
2999
4130
  incrementalSnapshotCount = 0;
@@ -3026,10 +4157,31 @@ function record(options = {}) {
3026
4157
  type: EventType.IncrementalSnapshot,
3027
4158
  data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
3028
4159
  }));
4160
+ const wrappedAdoptedStyleSheetEmit = (a) => wrappedEmit(wrapEvent({
4161
+ type: EventType.IncrementalSnapshot,
4162
+ data: Object.assign({ source: IncrementalSource.AdoptedStyleSheet }, a),
4163
+ }));
4164
+ const stylesheetManager = new StylesheetManager({
4165
+ mutationCb: wrappedMutationEmit,
4166
+ adoptedStyleSheetCb: wrappedAdoptedStyleSheetEmit,
4167
+ });
3029
4168
  const iframeManager = new IframeManager({
4169
+ mirror,
3030
4170
  mutationCb: wrappedMutationEmit,
4171
+ stylesheetManager: stylesheetManager,
4172
+ recordCrossOriginIframes,
4173
+ wrappedEmit,
3031
4174
  });
3032
- const canvasManager = new CanvasManager({
4175
+ for (const plugin of plugins || []) {
4176
+ if (plugin.getMirror)
4177
+ plugin.getMirror({
4178
+ nodeMirror: mirror,
4179
+ crossOriginIframeMirror: iframeManager.crossOriginIframeMirror,
4180
+ crossOriginIframeStyleMirror: iframeManager.crossOriginIframeStyleMirror,
4181
+ });
4182
+ }
4183
+ const processedNodeManager = new ProcessedNodeManager();
4184
+ canvasManager = new CanvasManager({
3033
4185
  recordCanvas,
3034
4186
  mutationCb: wrappedCanvasMutationEmit,
3035
4187
  win: window,
@@ -3037,6 +4189,8 @@ function record(options = {}) {
3037
4189
  blockSelector,
3038
4190
  unblockSelector,
3039
4191
  mirror,
4192
+ sampling: sampling.canvas,
4193
+ dataURLOptions,
3040
4194
  });
3041
4195
  const shadowDomManager = new ShadowDomManager({
3042
4196
  mutationCb: wrappedMutationEmit,
@@ -3046,14 +4200,15 @@ function record(options = {}) {
3046
4200
  blockClass,
3047
4201
  blockSelector,
3048
4202
  unblockSelector,
4203
+ maskAllText,
3049
4204
  maskTextClass,
4205
+ unmaskTextClass,
3050
4206
  maskTextSelector,
3051
4207
  unmaskTextSelector,
3052
- maskInputSelector,
3053
- unmaskInputSelector,
3054
4208
  inlineStylesheet,
3055
- maskAllText,
3056
4209
  maskInputOptions,
4210
+ dataURLOptions,
4211
+ maskAttributeFn,
3057
4212
  maskTextFn,
3058
4213
  maskInputFn,
3059
4214
  recordCanvas,
@@ -3061,12 +4216,14 @@ function record(options = {}) {
3061
4216
  sampling,
3062
4217
  slimDOMOptions,
3063
4218
  iframeManager,
4219
+ stylesheetManager,
3064
4220
  canvasManager,
4221
+ keepIframeSrcFn,
4222
+ processedNodeManager,
3065
4223
  },
3066
4224
  mirror,
3067
4225
  });
3068
4226
  takeFullSnapshot = (isCheckout = false) => {
3069
- var _a, _b, _c, _d;
3070
4227
  wrappedEmit(wrapEvent({
3071
4228
  type: EventType.Meta,
3072
4229
  data: {
@@ -3075,27 +4232,35 @@ function record(options = {}) {
3075
4232
  height: getWindowHeight(),
3076
4233
  },
3077
4234
  }), isCheckout);
4235
+ stylesheetManager.reset();
4236
+ shadowDomManager.init();
3078
4237
  mutationBuffers.forEach((buf) => buf.lock());
3079
- const [node, idNodeMap] = snapshot(document, {
4238
+ const node = snapshot(document, {
4239
+ mirror,
3080
4240
  blockClass,
3081
4241
  blockSelector,
3082
4242
  unblockSelector,
4243
+ maskAllText,
3083
4244
  maskTextClass,
4245
+ unmaskTextClass,
3084
4246
  maskTextSelector,
3085
4247
  unmaskTextSelector,
3086
- maskInputSelector,
3087
- unmaskInputSelector,
3088
4248
  inlineStylesheet,
3089
- maskAllText,
3090
4249
  maskAllInputs: maskInputOptions,
4250
+ maskAttributeFn,
4251
+ maskInputFn,
3091
4252
  maskTextFn,
3092
4253
  slimDOM: slimDOMOptions,
4254
+ dataURLOptions,
3093
4255
  recordCanvas,
3094
4256
  inlineImages,
3095
4257
  onSerialize: (n) => {
3096
- if (isIframeINode(n)) {
4258
+ if (isSerializedIframe(n, mirror)) {
3097
4259
  iframeManager.addIframe(n);
3098
4260
  }
4261
+ if (isSerializedStylesheet(n, mirror)) {
4262
+ stylesheetManager.trackLinkElement(n);
4263
+ }
3099
4264
  if (hasShadowRoot(n)) {
3100
4265
  shadowDomManager.addShadowRoot(n.shadowRoot, document);
3101
4266
  }
@@ -3104,42 +4269,27 @@ function record(options = {}) {
3104
4269
  iframeManager.attachIframe(iframe, childSn);
3105
4270
  shadowDomManager.observeAttachShadow(iframe);
3106
4271
  },
4272
+ onStylesheetLoad: (linkEl, childSn) => {
4273
+ stylesheetManager.attachLinkElement(linkEl, childSn);
4274
+ },
3107
4275
  keepIframeSrcFn,
3108
4276
  });
3109
4277
  if (!node) {
3110
4278
  return console.warn('Failed to snapshot the document');
3111
4279
  }
3112
- mirror.map = idNodeMap;
3113
4280
  wrappedEmit(wrapEvent({
3114
4281
  type: EventType.FullSnapshot,
3115
4282
  data: {
3116
4283
  node,
3117
- initialOffset: {
3118
- left: window.pageXOffset !== undefined
3119
- ? window.pageXOffset
3120
- : (document === null || document === void 0 ? void 0 : document.documentElement.scrollLeft) ||
3121
- ((_b = (_a = document === null || document === void 0 ? void 0 : document.body) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.scrollLeft) ||
3122
- (document === null || document === void 0 ? void 0 : document.body.scrollLeft) ||
3123
- 0,
3124
- top: window.pageYOffset !== undefined
3125
- ? window.pageYOffset
3126
- : (document === null || document === void 0 ? void 0 : document.documentElement.scrollTop) ||
3127
- ((_d = (_c = document === null || document === void 0 ? void 0 : document.body) === null || _c === void 0 ? void 0 : _c.parentElement) === null || _d === void 0 ? void 0 : _d.scrollTop) ||
3128
- (document === null || document === void 0 ? void 0 : document.body.scrollTop) ||
3129
- 0,
3130
- },
4284
+ initialOffset: getWindowScroll(window),
3131
4285
  },
3132
- }));
4286
+ }), isCheckout);
3133
4287
  mutationBuffers.forEach((buf) => buf.unlock());
4288
+ if (document.adoptedStyleSheets && document.adoptedStyleSheets.length > 0)
4289
+ stylesheetManager.adoptStyleSheets(document.adoptedStyleSheets, mirror.getId(document));
3134
4290
  };
3135
4291
  try {
3136
4292
  const handlers = [];
3137
- handlers.push(on('DOMContentLoaded', () => {
3138
- wrappedEmit(wrapEvent({
3139
- type: EventType.DomContentLoaded,
3140
- data: {},
3141
- }));
3142
- }));
3143
4293
  const observe = (doc) => {
3144
4294
  var _a;
3145
4295
  return callbackWrapper(initObservers)({
@@ -3182,14 +4332,26 @@ function record(options = {}) {
3182
4332
  type: EventType.IncrementalSnapshot,
3183
4333
  data: Object.assign({ source: IncrementalSource.Font }, p),
3184
4334
  })),
4335
+ selectionCb: (p) => {
4336
+ wrappedEmit(wrapEvent({
4337
+ type: EventType.IncrementalSnapshot,
4338
+ data: Object.assign({ source: IncrementalSource.Selection }, p),
4339
+ }));
4340
+ },
4341
+ customElementCb: (c) => {
4342
+ wrappedEmit(wrapEvent({
4343
+ type: EventType.IncrementalSnapshot,
4344
+ data: Object.assign({ source: IncrementalSource.CustomElement }, c),
4345
+ }));
4346
+ },
3185
4347
  blockClass,
3186
4348
  ignoreClass,
3187
4349
  ignoreSelector,
4350
+ maskAllText,
3188
4351
  maskTextClass,
4352
+ unmaskTextClass,
3189
4353
  maskTextSelector,
3190
4354
  unmaskTextSelector,
3191
- maskInputSelector,
3192
- unmaskInputSelector,
3193
4355
  maskInputOptions,
3194
4356
  inlineStylesheet,
3195
4357
  sampling,
@@ -3198,16 +4360,21 @@ function record(options = {}) {
3198
4360
  userTriggeredOnInput,
3199
4361
  collectFonts,
3200
4362
  doc,
3201
- maskAllText,
4363
+ maskAttributeFn,
3202
4364
  maskInputFn,
3203
4365
  maskTextFn,
4366
+ keepIframeSrcFn,
3204
4367
  blockSelector,
3205
4368
  unblockSelector,
3206
4369
  slimDOMOptions,
4370
+ dataURLOptions,
3207
4371
  mirror,
3208
4372
  iframeManager,
4373
+ stylesheetManager,
3209
4374
  shadowDomManager,
4375
+ processedNodeManager,
3210
4376
  canvasManager,
4377
+ ignoreCSSAttributes,
3211
4378
  plugins: ((_a = plugins === null || plugins === void 0 ? void 0 : plugins.filter((p) => p.observer)) === null || _a === void 0 ? void 0 : _a.map((p) => ({
3212
4379
  observer: p.observer,
3213
4380
  options: p.options,
@@ -3232,22 +4399,35 @@ function record(options = {}) {
3232
4399
  const init = () => {
3233
4400
  takeFullSnapshot();
3234
4401
  handlers.push(observe(document));
4402
+ recording = true;
3235
4403
  };
3236
4404
  if (document.readyState === 'interactive' ||
3237
4405
  document.readyState === 'complete') {
3238
4406
  init();
3239
4407
  }
3240
4408
  else {
4409
+ handlers.push(on('DOMContentLoaded', () => {
4410
+ wrappedEmit(wrapEvent({
4411
+ type: EventType.DomContentLoaded,
4412
+ data: {},
4413
+ }));
4414
+ if (recordAfter === 'DOMContentLoaded')
4415
+ init();
4416
+ }));
3241
4417
  handlers.push(on('load', () => {
3242
4418
  wrappedEmit(wrapEvent({
3243
4419
  type: EventType.Load,
3244
4420
  data: {},
3245
4421
  }));
3246
- init();
4422
+ if (recordAfter === 'load')
4423
+ init();
3247
4424
  }, window));
3248
4425
  }
3249
4426
  return () => {
3250
4427
  handlers.forEach((h) => h());
4428
+ processedNodeManager.destroy();
4429
+ recording = false;
4430
+ unregisterErrorHandler();
3251
4431
  };
3252
4432
  }
3253
4433
  catch (error) {
@@ -3255,7 +4435,7 @@ function record(options = {}) {
3255
4435
  }
3256
4436
  }
3257
4437
  record.addCustomEvent = (tag, payload) => {
3258
- if (!wrappedEmit) {
4438
+ if (!recording) {
3259
4439
  throw new Error('please add custom event after start recording');
3260
4440
  }
3261
4441
  wrappedEmit(wrapEvent({
@@ -3270,7 +4450,7 @@ record.freezePage = () => {
3270
4450
  mutationBuffers.forEach((buf) => buf.freeze());
3271
4451
  };
3272
4452
  record.takeFullSnapshot = (isCheckout) => {
3273
- if (!takeFullSnapshot) {
4453
+ if (!recording) {
3274
4454
  throw new Error('please take full snapshot after start recording');
3275
4455
  }
3276
4456
  takeFullSnapshot(isCheckout);
@@ -3766,27 +4946,25 @@ const handleDomListener = (
3766
4946
 
3767
4947
  /** Get the base DOM breadcrumb. */
3768
4948
  function getBaseDomBreadcrumb(target, message) {
3769
- // `__sn` property is the serialized node created by rrweb
3770
- const serializedNode = target && isRrwebNode(target) && target.__sn.type === NodeType.Element ? target.__sn : null;
4949
+ const nodeId = record.mirror.getId(target);
4950
+ const node = nodeId && record.mirror.getNode(nodeId);
4951
+ const meta = node && record.mirror.getMeta(node);
4952
+ const element = meta && isElement(meta) ? meta : null;
3771
4953
 
3772
4954
  return {
3773
4955
  message,
3774
- data: serializedNode
4956
+ data: element
3775
4957
  ? {
3776
- nodeId: serializedNode.id,
4958
+ nodeId,
3777
4959
  node: {
3778
- id: serializedNode.id,
3779
- tagName: serializedNode.tagName,
3780
- textContent: target
3781
- ? Array.from(target.childNodes)
3782
- .map(
3783
- (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
3784
- )
3785
- .filter(Boolean) // filter out empty values
3786
- .map(text => (text ).trim())
3787
- .join('')
3788
- : '',
3789
- attributes: getAttributesToRecord(serializedNode.attributes),
4960
+ id: nodeId,
4961
+ tagName: element.tagName,
4962
+ textContent: Array.from(element.childNodes)
4963
+ .map((node) => node.type === NodeType.Text && node.textContent)
4964
+ .filter(Boolean) // filter out empty values
4965
+ .map(text => (text ).trim())
4966
+ .join(''),
4967
+ attributes: getAttributesToRecord(element.attributes),
3790
4968
  },
3791
4969
  }
3792
4970
  : {},
@@ -3823,8 +5001,8 @@ function getDomTarget(handlerData) {
3823
5001
  return { target, message };
3824
5002
  }
3825
5003
 
3826
- function isRrwebNode(node) {
3827
- return '__sn' in node;
5004
+ function isElement(node) {
5005
+ return node.type === NodeType.Element;
3828
5006
  }
3829
5007
 
3830
5008
  /** Handle keyboard events & create breadcrumbs. */
@@ -4562,7 +5740,6 @@ function makeSession(session) {
4562
5740
  const lastActivity = session.lastActivity || now;
4563
5741
  const segmentId = session.segmentId || 0;
4564
5742
  const sampled = session.sampled;
4565
- const shouldRefresh = typeof session.shouldRefresh === 'boolean' ? session.shouldRefresh : true;
4566
5743
  const previousSessionId = session.previousSessionId;
4567
5744
 
4568
5745
  return {
@@ -4571,7 +5748,6 @@ function makeSession(session) {
4571
5748
  lastActivity,
4572
5749
  segmentId,
4573
5750
  sampled,
4574
- shouldRefresh,
4575
5751
  previousSessionId,
4576
5752
  };
4577
5753
  }
@@ -4666,46 +5842,19 @@ function isSessionExpired(session, timeouts, targetTime = +new Date()) {
4666
5842
  );
4667
5843
  }
4668
5844
 
4669
- /**
4670
- * Check a session, and either return it or a refreshed version of it.
4671
- * The refreshed version may be unsampled.
4672
- * You can check if the session has changed by comparing the session IDs.
4673
- */
4674
- function maybeRefreshSession(
4675
- session,
4676
- {
4677
- timeouts,
4678
- traceInternals,
4679
- }
4680
-
4681
- ,
4682
- sessionOptions,
4683
- ) {
5845
+ /** If the session should be refreshed or not. */
5846
+ function shouldRefreshSession(session, timeouts) {
4684
5847
  // If not expired, all good, just keep the session
4685
5848
  if (!isSessionExpired(session, timeouts)) {
4686
- return session;
4687
- }
4688
-
4689
- const isBuffering = session.sampled === 'buffer';
4690
-
4691
- // If we are buffering & the session may be refreshed, just return it
4692
- if (isBuffering && session.shouldRefresh) {
4693
- return session;
5849
+ return false;
4694
5850
  }
4695
5851
 
4696
- // If we are buffering & the session may not be refreshed (=it was converted to session previously already)
4697
- // We return an unsampled new session
4698
- if (isBuffering) {
4699
- logInfoNextTick('[Replay] Session should not be refreshed', traceInternals);
4700
- return makeSession({ sampled: false });
5852
+ // If we are buffering & haven't ever flushed yet, always continue
5853
+ if (session.sampled === 'buffer' && session.segmentId === 0) {
5854
+ return false;
4701
5855
  }
4702
5856
 
4703
- // Else, we are not buffering, and the session is expired, so we need to create a new one
4704
- logInfoNextTick('[Replay] Session has expired, creating new one...', traceInternals);
4705
-
4706
- const newSession = createSession(sessionOptions, { previousSessionId: session.id });
4707
-
4708
- return newSession;
5857
+ return true;
4709
5858
  }
4710
5859
 
4711
5860
  /**
@@ -4713,27 +5862,33 @@ function maybeRefreshSession(
4713
5862
  * Returns a session that may be unsampled.
4714
5863
  */
4715
5864
  function loadOrCreateSession(
4716
- currentSession,
4717
5865
  {
4718
5866
  timeouts,
4719
5867
  traceInternals,
5868
+ previousSessionId,
4720
5869
  }
4721
5870
 
4722
5871
  ,
4723
5872
  sessionOptions,
4724
5873
  ) {
4725
- // If session exists and is passed, use it instead of always hitting session storage
4726
- const existingSession = currentSession || (sessionOptions.stickySession && fetchSession(traceInternals));
5874
+ const existingSession = sessionOptions.stickySession && fetchSession(traceInternals);
4727
5875
 
4728
5876
  // No session exists yet, just create a new one
4729
5877
  if (!existingSession) {
4730
- logInfoNextTick('[Replay] Created new session', traceInternals);
4731
- return createSession(sessionOptions);
5878
+ logInfoNextTick('[Replay] Creating new session', traceInternals);
5879
+ return createSession(sessionOptions, { previousSessionId });
4732
5880
  }
4733
5881
 
4734
- return maybeRefreshSession(existingSession, { timeouts, traceInternals }, sessionOptions);
5882
+ if (!shouldRefreshSession(existingSession, timeouts)) {
5883
+ return existingSession;
5884
+ }
5885
+
5886
+ logInfoNextTick('[Replay] Session in sessionStorage is expired, creating new one...');
5887
+ return createSession(sessionOptions, { previousSessionId: existingSession.id });
4735
5888
  }
4736
5889
 
5890
+ const ReplayEventTypeCustom = 5;
5891
+
4737
5892
  function isCustomEvent(event) {
4738
5893
  return event.type === EventType.Custom;
4739
5894
  }
@@ -6629,9 +7784,7 @@ function createLargestContentfulPaint(
6629
7784
  data: {
6630
7785
  value, // LCP "duration" in ms
6631
7786
  size,
6632
- // Not sure why this errors, Node should be correct (Argument of type 'Node' is not assignable to parameter of type 'INode')
6633
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
6634
- nodeId: record.mirror.getId(entry.element ),
7787
+ nodeId: record.mirror.getId(entry.element),
6635
7788
  },
6636
7789
  };
6637
7790
  }
@@ -7325,7 +8478,7 @@ class ReplayContainer {
7325
8478
  * Initializes the plugin based on sampling configuration. Should not be
7326
8479
  * called outside of constructor.
7327
8480
  */
7328
- initializeSampling() {
8481
+ initializeSampling(previousSessionId) {
7329
8482
  const { errorSampleRate, sessionSampleRate } = this._options;
7330
8483
 
7331
8484
  // If neither sample rate is > 0, then do nothing - user will need to call one of
@@ -7336,7 +8489,7 @@ class ReplayContainer {
7336
8489
 
7337
8490
  // Otherwise if there is _any_ sample rate set, try to load an existing
7338
8491
  // session, or create a new one.
7339
- this._initializeSessionForSampling();
8492
+ this._initializeSessionForSampling(previousSessionId);
7340
8493
 
7341
8494
  if (!this.session) {
7342
8495
  // This should not happen, something wrong has occurred
@@ -7381,7 +8534,6 @@ class ReplayContainer {
7381
8534
  logInfoNextTick('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
7382
8535
 
7383
8536
  const session = loadOrCreateSession(
7384
- this.session,
7385
8537
  {
7386
8538
  timeouts: this.timeouts,
7387
8539
  traceInternals: this._options._experiments.traceInternals,
@@ -7411,7 +8563,6 @@ class ReplayContainer {
7411
8563
  logInfoNextTick('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
7412
8564
 
7413
8565
  const session = loadOrCreateSession(
7414
- this.session,
7415
8566
  {
7416
8567
  timeouts: this.timeouts,
7417
8568
  traceInternals: this._options._experiments.traceInternals,
@@ -7479,15 +8630,18 @@ class ReplayContainer {
7479
8630
  return;
7480
8631
  }
7481
8632
 
8633
+ // We can't move `_isEnabled` after awaiting a flush, otherwise we can
8634
+ // enter into an infinite loop when `stop()` is called while flushing.
8635
+ this._isEnabled = false;
8636
+
7482
8637
  try {
7483
8638
  logInfo(
7484
- `[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''}`,
8639
+ `[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''} ${new Date().toISOString()} ${
8640
+ this._isEnabled
8641
+ }`,
7485
8642
  this._options._experiments.traceInternals,
7486
8643
  );
7487
8644
 
7488
- // We can't move `_isEnabled` after awaiting a flush, otherwise we can
7489
- // enter into an infinite loop when `stop()` is called while flushing.
7490
- this._isEnabled = false;
7491
8645
  this._removeListeners();
7492
8646
  this.stopRecording();
7493
8647
 
@@ -7581,16 +8735,6 @@ class ReplayContainer {
7581
8735
 
7582
8736
  // Once this session ends, we do not want to refresh it
7583
8737
  if (this.session) {
7584
- this.session.shouldRefresh = false;
7585
-
7586
- // It's possible that the session lifespan is > max session lifespan
7587
- // because we have been buffering beyond max session lifespan (we ignore
7588
- // expiration given that `shouldRefresh` is true). Since we flip
7589
- // `shouldRefresh`, the session could be considered expired due to
7590
- // lifespan, which is not what we want. Update session start date to be
7591
- // the current timestamp, so that session is not considered to be
7592
- // expired. This means that max replay duration can be MAX_SESSION_LIFE +
7593
- // (length of buffer), which we are ok with.
7594
8738
  this._updateUserActivity(activityTime);
7595
8739
  this._updateSessionActivity(activityTime);
7596
8740
  this._maybeSaveSession();
@@ -7752,7 +8896,11 @@ class ReplayContainer {
7752
8896
  }
7753
8897
 
7754
8898
  // Session is expired, trigger a full snapshot (which will create a new session)
7755
- this._triggerFullSnapshot();
8899
+ if (this.isPaused()) {
8900
+ this.resume();
8901
+ } else {
8902
+ this._triggerFullSnapshot();
8903
+ }
7756
8904
 
7757
8905
  return false;
7758
8906
  }
@@ -7795,7 +8943,7 @@ class ReplayContainer {
7795
8943
 
7796
8944
  this.addUpdate(() => {
7797
8945
  void addEvent(this, {
7798
- type: EventType.Custom,
8946
+ type: ReplayEventTypeCustom,
7799
8947
  timestamp: breadcrumb.timestamp || 0,
7800
8948
  data: {
7801
8949
  tag: 'breadcrumb',
@@ -7842,6 +8990,7 @@ class ReplayContainer {
7842
8990
 
7843
8991
  // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
7844
8992
  this._isEnabled = true;
8993
+ this._isPaused = false;
7845
8994
 
7846
8995
  this.startRecording();
7847
8996
  }
@@ -7858,16 +9007,16 @@ class ReplayContainer {
7858
9007
  /**
7859
9008
  * Loads (or refreshes) the current session.
7860
9009
  */
7861
- _initializeSessionForSampling() {
9010
+ _initializeSessionForSampling(previousSessionId) {
7862
9011
  // Whenever there is _any_ error sample rate, we always allow buffering
7863
9012
  // Because we decide on sampling when an error occurs, we need to buffer at all times if sampling for errors
7864
9013
  const allowBuffering = this._options.errorSampleRate > 0;
7865
9014
 
7866
9015
  const session = loadOrCreateSession(
7867
- this.session,
7868
9016
  {
7869
9017
  timeouts: this.timeouts,
7870
9018
  traceInternals: this._options._experiments.traceInternals,
9019
+ previousSessionId,
7871
9020
  },
7872
9021
  {
7873
9022
  stickySession: this._options.stickySession,
@@ -7892,36 +9041,27 @@ class ReplayContainer {
7892
9041
 
7893
9042
  const currentSession = this.session;
7894
9043
 
7895
- const newSession = maybeRefreshSession(
7896
- currentSession,
7897
- {
7898
- timeouts: this.timeouts,
7899
- traceInternals: this._options._experiments.traceInternals,
7900
- },
7901
- {
7902
- stickySession: Boolean(this._options.stickySession),
7903
- sessionSampleRate: this._options.sessionSampleRate,
7904
- allowBuffering: this._options.errorSampleRate > 0,
7905
- },
7906
- );
7907
-
7908
- const isNew = newSession.id !== currentSession.id;
7909
-
7910
- // If session was newly created (i.e. was not loaded from storage), then
7911
- // enable flag to create the root replay
7912
- if (isNew) {
7913
- this.setInitialState();
7914
- this.session = newSession;
7915
- }
7916
-
7917
- if (!this.session.sampled) {
7918
- void this.stop({ reason: 'session not refreshed' });
9044
+ if (shouldRefreshSession(currentSession, this.timeouts)) {
9045
+ void this._refreshSession(currentSession);
7919
9046
  return false;
7920
9047
  }
7921
9048
 
7922
9049
  return true;
7923
9050
  }
7924
9051
 
9052
+ /**
9053
+ * Refresh a session with a new one.
9054
+ * This stops the current session (without forcing a flush, as that would never work since we are expired),
9055
+ * and then does a new sampling based on the refreshed session.
9056
+ */
9057
+ async _refreshSession(session) {
9058
+ if (!this._isEnabled) {
9059
+ return;
9060
+ }
9061
+ await this.stop({ reason: 'refresh session' });
9062
+ this.initializeSampling(session.id);
9063
+ }
9064
+
7925
9065
  /**
7926
9066
  * Adds listeners to record events for the replay
7927
9067
  */
@@ -8178,7 +9318,9 @@ class ReplayContainer {
8178
9318
  * Should never be called directly, only by `flush`
8179
9319
  */
8180
9320
  async _runFlush() {
8181
- if (!this.session || !this.eventBuffer) {
9321
+ const replayId = this.getSessionId();
9322
+
9323
+ if (!this.session || !this.eventBuffer || !replayId) {
8182
9324
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.error('[Replay] No session or eventBuffer found to flush.');
8183
9325
  return;
8184
9326
  }
@@ -8198,13 +9340,15 @@ class ReplayContainer {
8198
9340
  return;
8199
9341
  }
8200
9342
 
9343
+ // if this changed in the meanwhile, e.g. because the session was refreshed or similar, we abort here
9344
+ if (replayId !== this.getSessionId()) {
9345
+ return;
9346
+ }
9347
+
8201
9348
  try {
8202
9349
  // This uses the data from the eventBuffer, so we need to call this before `finish()
8203
9350
  this._updateInitialTimestampFromEventBuffer();
8204
9351
 
8205
- // Note this empties the event buffer regardless of outcome of sending replay
8206
- const recordingData = await this.eventBuffer.finish();
8207
-
8208
9352
  const timestamp = Date.now();
8209
9353
 
8210
9354
  // Check total duration again, to avoid sending outdated stuff
@@ -8214,14 +9358,14 @@ class ReplayContainer {
8214
9358
  throw new Error('Session is too long, not sending replay');
8215
9359
  }
8216
9360
 
8217
- // NOTE: Copy values from instance members, as it's possible they could
8218
- // change before the flush finishes.
8219
- const replayId = this.session.id;
8220
9361
  const eventContext = this._popEventContext();
8221
9362
  // Always increment segmentId regardless of outcome of sending replay
8222
9363
  const segmentId = this.session.segmentId++;
8223
9364
  this._maybeSaveSession();
8224
9365
 
9366
+ // Note this empties the event buffer regardless of outcome of sending replay
9367
+ const recordingData = await this.eventBuffer.finish();
9368
+
8225
9369
  await sendReplay({
8226
9370
  replayId,
8227
9371
  recordingData,
@@ -8428,8 +9572,6 @@ function getPrivacyOptions({
8428
9572
  // We are making the decision to make text and input selectors the same
8429
9573
  maskTextSelector: maskSelector,
8430
9574
  unmaskTextSelector: unmaskSelector,
8431
- maskInputSelector: maskSelector,
8432
- unmaskInputSelector: unmaskSelector,
8433
9575
 
8434
9576
  blockSelector: getOption(
8435
9577
  block,
@@ -8465,6 +9607,39 @@ function isElectronNodeRenderer() {
8465
9607
  return typeof process !== 'undefined' && (process ).type === 'renderer';
8466
9608
  }
8467
9609
 
9610
+ /**
9611
+ * Masks an attribute if necessary, otherwise return attribute value as-is.
9612
+ */
9613
+ function maskAttribute({
9614
+ el,
9615
+ key,
9616
+ maskAttributes,
9617
+ maskAllText,
9618
+ privacyOptions,
9619
+ value,
9620
+ }) {
9621
+ // We only mask attributes if `maskAllText` is true
9622
+ if (!maskAllText) {
9623
+ return value;
9624
+ }
9625
+
9626
+ // unmaskTextSelector takes precendence
9627
+ if (privacyOptions.unmaskTextSelector && el.matches(privacyOptions.unmaskTextSelector)) {
9628
+ return value;
9629
+ }
9630
+
9631
+ if (
9632
+ maskAttributes.includes(key) ||
9633
+ // Need to mask `value` attribute for `<input>` if it's a button-like
9634
+ // type
9635
+ (key === 'value' && el.tagName === 'INPUT' && ['submit', 'button'].includes(el.getAttribute('type') || ''))
9636
+ ) {
9637
+ return value.replace(/[\S]/g, '*');
9638
+ }
9639
+
9640
+ return value;
9641
+ }
9642
+
8468
9643
  const MEDIA_SELECTORS =
8469
9644
  'img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"]';
8470
9645
 
@@ -8523,6 +9698,7 @@ class Replay {
8523
9698
  networkResponseHeaders = [],
8524
9699
 
8525
9700
  mask = [],
9701
+ maskAttributes = ['title', 'placeholder'],
8526
9702
  unmask = [],
8527
9703
  block = [],
8528
9704
  unblock = [],
@@ -8546,25 +9722,36 @@ class Replay {
8546
9722
  } = {}) {
8547
9723
  this.name = Replay.id;
8548
9724
 
9725
+ const privacyOptions = getPrivacyOptions({
9726
+ mask,
9727
+ unmask,
9728
+ block,
9729
+ unblock,
9730
+ ignore,
9731
+ blockClass,
9732
+ blockSelector,
9733
+ maskTextClass,
9734
+ maskTextSelector,
9735
+ ignoreClass,
9736
+ });
9737
+
8549
9738
  this._recordingOptions = {
8550
9739
  maskAllInputs,
8551
9740
  maskAllText,
8552
9741
  maskInputOptions: { ...(maskInputOptions || {}), password: true },
8553
9742
  maskTextFn: maskFn,
8554
9743
  maskInputFn: maskFn,
9744
+ maskAttributeFn: (key, value, el) =>
9745
+ maskAttribute({
9746
+ maskAttributes,
9747
+ maskAllText,
9748
+ privacyOptions,
9749
+ key,
9750
+ value,
9751
+ el,
9752
+ }),
8555
9753
 
8556
- ...getPrivacyOptions({
8557
- mask,
8558
- unmask,
8559
- block,
8560
- unblock,
8561
- ignore,
8562
- blockClass,
8563
- blockSelector,
8564
- maskTextClass,
8565
- maskTextSelector,
8566
- ignoreClass,
8567
- }),
9754
+ ...privacyOptions,
8568
9755
 
8569
9756
  // Our defaults
8570
9757
  slimDOMOptions: 'all',
@@ -8574,6 +9761,16 @@ class Replay {
8574
9761
  // collect fonts, but be aware that `sentry.io` needs to be an allowed
8575
9762
  // origin for playback
8576
9763
  collectFonts: true,
9764
+ errorHandler: (err) => {
9765
+ try {
9766
+ // @ts-ignore Set this so that replay SDK can ignore errors originating from rrweb
9767
+ err.__rrweb__ = true;
9768
+ } catch (e) {
9769
+ // avoid any potential hazards here
9770
+ }
9771
+ // return true to suppress throwing the error inside of rrweb
9772
+ return true;
9773
+ },
8577
9774
  };
8578
9775
 
8579
9776
  this._initialOptions = {