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