@openreplay/tracker 16.2.1 → 16.2.2-beta.19

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.
package/dist/lib/index.js CHANGED
@@ -4042,6 +4042,7 @@ function ConstructedStyleSheets (app) {
4042
4042
  return replace.call(this, text).then((sheet) => {
4043
4043
  const sheetID = styleSheetIDMap.get(this);
4044
4044
  if (sheetID) {
4045
+ console.log('replace');
4045
4046
  app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
4046
4047
  }
4047
4048
  return sheet;
@@ -4051,6 +4052,7 @@ function ConstructedStyleSheets (app) {
4051
4052
  context.CSSStyleSheet.prototype.replaceSync = function (text) {
4052
4053
  const sheetID = styleSheetIDMap.get(this);
4053
4054
  if (sheetID) {
4055
+ console.log('replaceSync');
4054
4056
  app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
4055
4057
  }
4056
4058
  return replaceSync.call(this, text);
@@ -5202,7 +5204,7 @@ class App {
5202
5204
  this.stopCallbacks = [];
5203
5205
  this.commitCallbacks = [];
5204
5206
  this.activityState = ActivityState.NotActive;
5205
- this.version = '16.2.1'; // TODO: version compatability check inside each plugin.
5207
+ this.version = '16.2.2-beta.19'; // TODO: version compatability check inside each plugin.
5206
5208
  this.socketMode = false;
5207
5209
  this.compressionThreshold = 24 * 1000;
5208
5210
  this.bc = null;
@@ -8037,60 +8039,107 @@ function Viewport (app) {
8037
8039
  app.ticker.attach(sendSetViewportSize, 5, false);
8038
8040
  }
8039
8041
 
8040
- function CSSRules (app) {
8041
- if (app === null) {
8042
+ function CSSRules (app, opts) {
8043
+ if (app === null)
8042
8044
  return;
8043
- }
8044
8045
  if (!window.CSSStyleSheet) {
8045
8046
  app.send(TechnicalInfo('no_stylesheet_prototype_in_window', ''));
8046
8047
  return;
8047
8048
  }
8049
+ // Track CSS rule snapshots by sheetID:index
8050
+ const ruleSnapshots = new Map();
8051
+ let checkInterval = null;
8052
+ const checkIntervalMs = opts.checkCssInterval || 200; // Check every 200ms
8053
+ // Check all rules for changes
8054
+ function checkRuleChanges() {
8055
+ for (let i = 0; i < document.styleSheets.length; i++) {
8056
+ try {
8057
+ const sheet = document.styleSheets[i];
8058
+ const sheetID = styleSheetIDMap.get(sheet);
8059
+ if (!sheetID)
8060
+ continue;
8061
+ // Check each rule in the sheet
8062
+ for (let j = 0; j < sheet.cssRules.length; j++) {
8063
+ try {
8064
+ const rule = sheet.cssRules[j];
8065
+ const key = `${sheetID}:${j}`;
8066
+ const newText = rule.cssText;
8067
+ const oldText = ruleSnapshots.get(key);
8068
+ if (oldText !== newText) {
8069
+ // Rule is new or changed
8070
+ if (oldText !== undefined) {
8071
+ // Rule changed - send update
8072
+ app.send(AdoptedSSDeleteRule(sheetID, j));
8073
+ app.send(AdoptedSSInsertRuleURLBased(sheetID, newText, j, app.getBaseHref()));
8074
+ }
8075
+ else {
8076
+ // Rule added - send insert
8077
+ app.send(AdoptedSSInsertRuleURLBased(sheetID, newText, j, app.getBaseHref()));
8078
+ }
8079
+ ruleSnapshots.set(key, newText);
8080
+ }
8081
+ }
8082
+ catch (e) {
8083
+ /* Skip inaccessible rules */
8084
+ }
8085
+ }
8086
+ // Check for deleted rules
8087
+ const keysToCheck = Array.from(ruleSnapshots.keys()).filter((key) => key.startsWith(`${sheetID}:`));
8088
+ for (const key of keysToCheck) {
8089
+ const index = parseInt(key.split(':')[1], 10);
8090
+ if (index >= sheet.cssRules.length) {
8091
+ ruleSnapshots.delete(key);
8092
+ }
8093
+ }
8094
+ }
8095
+ catch (e) {
8096
+ /* Skip inaccessible sheets */
8097
+ }
8098
+ }
8099
+ }
8100
+ // Standard API hooks
8048
8101
  const sendInsertDeleteRule = app.safe((sheet, index, rule) => {
8049
8102
  const sheetID = styleSheetIDMap.get(sheet);
8050
- if (!sheetID) {
8051
- // OK-case. Sheet haven't been registered yet. Rules will be sent on registration.
8103
+ if (!sheetID)
8052
8104
  return;
8053
- }
8054
8105
  if (typeof rule === 'string') {
8055
8106
  app.send(AdoptedSSInsertRuleURLBased(sheetID, rule, index, app.getBaseHref()));
8107
+ ruleSnapshots.set(`${sheetID}:${index}`, rule);
8056
8108
  }
8057
8109
  else {
8058
8110
  app.send(AdoptedSSDeleteRule(sheetID, index));
8111
+ ruleSnapshots.delete(`${sheetID}:${index}`);
8059
8112
  }
8060
8113
  });
8061
- // TODO: proper rule insertion/removal (how?)
8062
8114
  const sendReplaceGroupingRule = app.safe((rule) => {
8063
8115
  let topmostRule = rule;
8064
- while (topmostRule.parentRule) {
8116
+ while (topmostRule.parentRule)
8065
8117
  topmostRule = topmostRule.parentRule;
8066
- }
8067
8118
  const sheet = topmostRule.parentStyleSheet;
8068
- if (!sheet) {
8069
- app.debug.warn('No parent StyleSheet found for', topmostRule, rule);
8119
+ if (!sheet)
8070
8120
  return;
8071
- }
8072
8121
  const sheetID = styleSheetIDMap.get(sheet);
8073
- if (!sheetID) {
8074
- app.debug.warn('No sheedID found for', sheet, styleSheetIDMap);
8122
+ if (!sheetID)
8075
8123
  return;
8076
- }
8077
8124
  const cssText = topmostRule.cssText;
8078
- const ruleList = sheet.cssRules;
8079
- const idx = Array.from(ruleList).indexOf(topmostRule);
8125
+ const idx = Array.from(sheet.cssRules).indexOf(topmostRule);
8080
8126
  if (idx >= 0) {
8081
8127
  app.send(AdoptedSSInsertRuleURLBased(sheetID, cssText, idx, app.getBaseHref()));
8082
- app.send(AdoptedSSDeleteRule(sheetID, idx + 1)); // Remove previous clone
8083
- }
8084
- else {
8085
- app.debug.warn('Rule index not found in', sheet, topmostRule);
8128
+ app.send(AdoptedSSDeleteRule(sheetID, idx + 1));
8129
+ ruleSnapshots.set(`${sheetID}:${idx}`, cssText);
8086
8130
  }
8087
8131
  });
8132
+ // Patch prototype methods
8088
8133
  const patchContext = app.safe((context) => {
8134
+ if (context.__css_tracking_patched__)
8135
+ return;
8136
+ context.__css_tracking_patched__ = true;
8089
8137
  const { insertRule, deleteRule } = context.CSSStyleSheet.prototype;
8090
8138
  const { insertRule: groupInsertRule, deleteRule: groupDeleteRule } = context.CSSGroupingRule.prototype;
8091
8139
  context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
8092
- sendInsertDeleteRule(this, index, rule);
8093
- return insertRule.call(this, rule, index);
8140
+ const result = insertRule.call(this, rule, index);
8141
+ sendInsertDeleteRule(this, result, rule);
8142
+ return result;
8094
8143
  };
8095
8144
  context.CSSStyleSheet.prototype.deleteRule = function (index) {
8096
8145
  sendInsertDeleteRule(this, index);
@@ -8101,33 +8150,50 @@ function CSSRules (app) {
8101
8150
  sendReplaceGroupingRule(this);
8102
8151
  return result;
8103
8152
  };
8104
- context.CSSGroupingRule.prototype.deleteRule = function (index = 0) {
8153
+ context.CSSGroupingRule.prototype.deleteRule = function (index) {
8105
8154
  const result = groupDeleteRule.call(this, index);
8106
8155
  sendReplaceGroupingRule(this);
8107
8156
  return result;
8108
8157
  };
8109
8158
  });
8159
+ // Apply patches
8110
8160
  patchContext(window);
8111
8161
  app.observer.attachContextCallback(patchContext);
8162
+ // Track style nodes
8112
8163
  app.nodes.attachNodeCallback((node) => {
8113
- if (!hasTag(node, 'style') || !node.sheet) {
8164
+ if (!hasTag(node, 'style') || !node.sheet)
8165
+ return;
8166
+ if (node.textContent !== null && node.textContent.trim().length > 0)
8114
8167
  return;
8115
- }
8116
- if (node.textContent !== null && node.textContent.trim().length > 0) {
8117
- return; // Non-virtual styles captured by the observer as a text
8118
- }
8119
8168
  const nodeID = app.nodes.getID(node);
8120
- if (!nodeID) {
8169
+ if (!nodeID)
8121
8170
  return;
8122
- }
8123
8171
  const sheet = node.sheet;
8124
8172
  const sheetID = nextID();
8125
8173
  styleSheetIDMap.set(sheet, sheetID);
8126
8174
  app.send(AdoptedSSAddOwner(sheetID, nodeID));
8127
- const rules = sheet.cssRules;
8128
- for (let i = 0; i < rules.length; i++) {
8129
- sendInsertDeleteRule(sheet, i, rules[i].cssText);
8175
+ for (let i = 0; i < sheet.cssRules.length; i++) {
8176
+ try {
8177
+ sendInsertDeleteRule(sheet, i, sheet.cssRules[i].cssText);
8178
+ }
8179
+ catch (e) {
8180
+ // Skip inaccessible rules
8181
+ }
8182
+ }
8183
+ });
8184
+ // Start checking and setup cleanup
8185
+ function startChecking() {
8186
+ if (checkInterval)
8187
+ return;
8188
+ checkInterval = window.setInterval(checkRuleChanges, checkIntervalMs);
8189
+ }
8190
+ setTimeout(startChecking, 50);
8191
+ app.attachStopCallback(() => {
8192
+ if (checkInterval) {
8193
+ clearInterval(checkInterval);
8194
+ checkInterval = null;
8130
8195
  }
8196
+ ruleSnapshots.clear();
8131
8197
  });
8132
8198
  }
8133
8199
 
@@ -9692,7 +9758,7 @@ class API {
9692
9758
  this.signalStartIssue = (reason, missingApi) => {
9693
9759
  const doNotTrack = this.checkDoNotTrack();
9694
9760
  console.log("Tracker couldn't start due to:", JSON.stringify({
9695
- trackerVersion: '16.2.1',
9761
+ trackerVersion: '16.2.2-beta.19',
9696
9762
  projectKey: this.options.projectKey,
9697
9763
  doNotTrack,
9698
9764
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
@@ -9791,7 +9857,7 @@ class API {
9791
9857
  Mouse(app, options.mouse);
9792
9858
  // inside iframe, we ignore viewport scroll
9793
9859
  Scroll(app, this.crossdomainMode);
9794
- CSSRules(app);
9860
+ CSSRules(app, options);
9795
9861
  ConstructedStyleSheets(app);
9796
9862
  Console(app, options);
9797
9863
  Exception(app, options);