@openreplay/tracker 3.5.3 → 3.5.4

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.
@@ -12,37 +12,60 @@ function isInstance(node, constr) {
12
12
  // @ts-ignore (for EI, Safary)
13
13
  doc.parentWindow ||
14
14
  doc.defaultView; // TODO: smart global typing for Window object
15
- while (context.parent && context.parent !== context) {
15
+ while ((context.parent || context.top) && context.parent !== context) {
16
16
  // @ts-ignore
17
17
  if (node instanceof context[constr.name]) {
18
18
  return true;
19
19
  }
20
20
  // @ts-ignore
21
- context = context.parent;
21
+ context = context.parent || context.top;
22
22
  }
23
23
  // @ts-ignore
24
24
  return node instanceof context[constr.name];
25
25
  }
26
26
  exports.isInstance = isInstance;
27
+ // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient
27
28
  function inDocument(node) {
28
29
  const doc = node.ownerDocument;
29
30
  if (!doc) {
30
- return false;
31
- }
32
- if (doc.contains(node)) {
33
31
  return true;
34
- }
35
- let context =
36
- // @ts-ignore (for EI, Safary)
37
- doc.parentWindow ||
38
- doc.defaultView;
39
- while (context.parent && context.parent !== context) {
40
- if (context.document.contains(node)) {
32
+ } // Document
33
+ let current = node;
34
+ while (current) {
35
+ if (current === doc) {
41
36
  return true;
42
37
  }
43
- // @ts-ignore
44
- context = context.parent;
38
+ else if (isInstance(current, ShadowRoot)) {
39
+ current = current.host;
40
+ }
41
+ else {
42
+ current = current.parentNode;
43
+ }
45
44
  }
46
45
  return false;
47
46
  }
48
47
  exports.inDocument = inDocument;
48
+ // export function inDocument(node: Node): boolean {
49
+ // // @ts-ignore compatability
50
+ // if (node.getRootNode) {
51
+ // let root: Node
52
+ // while ((root = node.getRootNode()) !== node) {
53
+ // ////
54
+ // }
55
+ // }
56
+ // const doc = node.ownerDocument
57
+ // if (!doc) { return false }
58
+ // if (doc.contains(node)) { return true }
59
+ // let context: Window =
60
+ // // @ts-ignore (for EI, Safary)
61
+ // doc.parentWindow ||
62
+ // doc.defaultView;
63
+ // while(context.parent && context.parent !== context) {
64
+ // if (context.document.contains(node)) {
65
+ // return true
66
+ // }
67
+ // // @ts-ignore
68
+ // context = context.parent
69
+ // }
70
+ // return false;
71
+ // }
package/cjs/app/index.js CHANGED
@@ -29,7 +29,7 @@ class App {
29
29
  this.stopCallbacks = [];
30
30
  this.commitCallbacks = [];
31
31
  this.activityState = ActivityState.NotActive;
32
- this.version = '3.5.3'; // TODO: version compatability check inside each plugin.
32
+ this.version = '3.5.4'; // TODO: version compatability check inside each plugin.
33
33
  this.preStartMessages = [];
34
34
  this.projectKey = projectKey;
35
35
  this.options = Object.assign({
@@ -1,7 +1,7 @@
1
1
  import App from "../index.js";
2
2
  export default abstract class Observer {
3
3
  protected readonly app: App;
4
- protected readonly context: Window;
4
+ protected readonly isTopContext: boolean;
5
5
  private readonly observer;
6
6
  private readonly commited;
7
7
  private readonly recents;
@@ -9,8 +9,7 @@ export default abstract class Observer {
9
9
  private readonly indexes;
10
10
  private readonly attributesList;
11
11
  private readonly textSet;
12
- private readonly inUpperContext;
13
- constructor(app: App, context?: Window);
12
+ constructor(app: App, isTopContext?: boolean);
14
13
  private clear;
15
14
  private sendNodeAttribute;
16
15
  private sendNodeData;
@@ -34,16 +34,15 @@ function isObservable(node) {
34
34
  return !isIgnored(node);
35
35
  }
36
36
  class Observer {
37
- constructor(app, context = window) {
37
+ constructor(app, isTopContext = false) {
38
38
  this.app = app;
39
- this.context = context;
39
+ this.isTopContext = isTopContext;
40
40
  this.commited = [];
41
41
  this.recents = [];
42
42
  this.myNodes = [];
43
43
  this.indexes = [];
44
44
  this.attributesList = [];
45
45
  this.textSet = new Set();
46
- this.inUpperContext = context.parent === context; //TODO: get rid of context here
47
46
  this.observer = new MutationObserver(this.app.safe((mutations) => {
48
47
  for (const mutation of mutations) {
49
48
  const target = mutation.target;
@@ -186,7 +185,7 @@ class Observer {
186
185
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
187
186
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
188
187
  // TODO: Clean the logic (though now it workd fine)
189
- if (!(0, context_js_1.isInstance)(node, HTMLHtmlElement) || !this.inUpperContext) {
188
+ if (!(0, context_js_1.isInstance)(node, HTMLHtmlElement) || !this.isTopContext) {
190
189
  if (parent === null) {
191
190
  this.unbindNode(node);
192
191
  return false;
@@ -274,6 +273,8 @@ class Observer {
274
273
  for (let id = 0; id < this.recents.length; id++) {
275
274
  // TODO: make things/logic nice here.
276
275
  // commit required in any case if recents[id] true or false (in case of unbinding) or undefined (in case of attr change).
276
+ // Possible solution: separate new node commit (recents) and new attribute/move node commit
277
+ // Otherwise commitNode is called on each node, which might be a lot
277
278
  if (!this.myNodes[id]) {
278
279
  continue;
279
280
  }
@@ -9,16 +9,17 @@ const utils_js_1 = require("../../utils.js");
9
9
  const attachShadowNativeFn = utils_js_1.IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
10
10
  class TopObserver extends observer_js_1.default {
11
11
  constructor(app, options) {
12
- super(app);
12
+ super(app, true);
13
13
  this.iframeObservers = [];
14
14
  this.shadowRootObservers = [];
15
15
  this.options = Object.assign({
16
- captureIFrames: false
16
+ captureIFrames: true
17
17
  }, options);
18
18
  // IFrames
19
19
  this.app.nodes.attachNodeCallback(node => {
20
20
  if ((0, context_js_1.isInstance)(node, HTMLIFrameElement) &&
21
- (this.options.captureIFrames || node.getAttribute("data-openreplay-capture"))) {
21
+ ((this.options.captureIFrames && !(0, utils_js_1.hasOpenreplayAttribute)(node, "obscured"))
22
+ || (0, utils_js_1.hasOpenreplayAttribute)(node, "capture"))) {
22
23
  this.handleIframe(node);
23
24
  }
24
25
  });
@@ -30,28 +31,28 @@ class TopObserver extends observer_js_1.default {
30
31
  });
31
32
  }
32
33
  handleIframe(iframe) {
33
- let context = null;
34
+ let doc = null;
34
35
  const handle = this.app.safe(() => {
35
36
  const id = this.app.nodes.getID(iframe);
36
37
  if (id === undefined) {
37
38
  return;
38
39
  } //log
39
- if (iframe.contentWindow === context) {
40
+ if (iframe.contentDocument === doc) {
40
41
  return;
41
- } //Does this happen frequently?
42
- context = iframe.contentWindow;
43
- if (!context) {
42
+ } // How frequently can it happen?
43
+ doc = iframe.contentDocument;
44
+ if (!doc || !iframe.contentWindow) {
44
45
  return;
45
46
  }
46
- const observer = new iframe_observer_js_1.default(this.app, context);
47
+ const observer = new iframe_observer_js_1.default(this.app);
47
48
  this.iframeObservers.push(observer);
48
49
  observer.observe(iframe);
49
50
  });
50
- this.app.attachEventListener(iframe, "load", handle);
51
+ iframe.addEventListener("load", handle); // why app.attachEventListener not working?
51
52
  handle();
52
53
  }
53
54
  handleShadowRoot(shRoot) {
54
- const observer = new shadow_root_observer_js_1.default(this.app, this.context);
55
+ const observer = new shadow_root_observer_js_1.default(this.app);
55
56
  this.shadowRootObservers.push(observer);
56
57
  observer.observe(shRoot.host);
57
58
  }
@@ -69,9 +70,9 @@ class TopObserver extends observer_js_1.default {
69
70
  // the change in the re-player behaviour caused by CreateDocument message:
70
71
  // the 0-node ("fRoot") will become #document rather than documentElement as it is now.
71
72
  // Alternatively - observe(#document) then bindNode(documentElement)
72
- this.observeRoot(this.context.document, () => {
73
+ this.observeRoot(window.document, () => {
73
74
  this.app.send(new index_js_1.CreateDocument());
74
- }, this.context.document.documentElement);
75
+ }, window.document.documentElement);
75
76
  }
76
77
  disconnect() {
77
78
  Element.prototype.attachShadow = attachShadowNativeFn;
package/cjs/index.js CHANGED
@@ -127,7 +127,7 @@ class API {
127
127
  // no-cors issue only with text/plain or not-set Content-Type
128
128
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
129
129
  req.send(JSON.stringify({
130
- trackerVersion: '3.5.3',
130
+ trackerVersion: '3.5.4',
131
131
  projectKey: options.projectKey,
132
132
  doNotTrack,
133
133
  // TODO: add precise reason (an exact API missing)
@@ -9,35 +9,58 @@ export function isInstance(node, constr) {
9
9
  // @ts-ignore (for EI, Safary)
10
10
  doc.parentWindow ||
11
11
  doc.defaultView; // TODO: smart global typing for Window object
12
- while (context.parent && context.parent !== context) {
12
+ while ((context.parent || context.top) && context.parent !== context) {
13
13
  // @ts-ignore
14
14
  if (node instanceof context[constr.name]) {
15
15
  return true;
16
16
  }
17
17
  // @ts-ignore
18
- context = context.parent;
18
+ context = context.parent || context.top;
19
19
  }
20
20
  // @ts-ignore
21
21
  return node instanceof context[constr.name];
22
22
  }
23
+ // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient
23
24
  export function inDocument(node) {
24
25
  const doc = node.ownerDocument;
25
26
  if (!doc) {
26
- return false;
27
- }
28
- if (doc.contains(node)) {
29
27
  return true;
30
- }
31
- let context =
32
- // @ts-ignore (for EI, Safary)
33
- doc.parentWindow ||
34
- doc.defaultView;
35
- while (context.parent && context.parent !== context) {
36
- if (context.document.contains(node)) {
28
+ } // Document
29
+ let current = node;
30
+ while (current) {
31
+ if (current === doc) {
37
32
  return true;
38
33
  }
39
- // @ts-ignore
40
- context = context.parent;
34
+ else if (isInstance(current, ShadowRoot)) {
35
+ current = current.host;
36
+ }
37
+ else {
38
+ current = current.parentNode;
39
+ }
41
40
  }
42
41
  return false;
43
42
  }
43
+ // export function inDocument(node: Node): boolean {
44
+ // // @ts-ignore compatability
45
+ // if (node.getRootNode) {
46
+ // let root: Node
47
+ // while ((root = node.getRootNode()) !== node) {
48
+ // ////
49
+ // }
50
+ // }
51
+ // const doc = node.ownerDocument
52
+ // if (!doc) { return false }
53
+ // if (doc.contains(node)) { return true }
54
+ // let context: Window =
55
+ // // @ts-ignore (for EI, Safary)
56
+ // doc.parentWindow ||
57
+ // doc.defaultView;
58
+ // while(context.parent && context.parent !== context) {
59
+ // if (context.document.contains(node)) {
60
+ // return true
61
+ // }
62
+ // // @ts-ignore
63
+ // context = context.parent
64
+ // }
65
+ // return false;
66
+ // }
package/lib/app/index.js CHANGED
@@ -26,7 +26,7 @@ export default class App {
26
26
  this.stopCallbacks = [];
27
27
  this.commitCallbacks = [];
28
28
  this.activityState = ActivityState.NotActive;
29
- this.version = '3.5.3'; // TODO: version compatability check inside each plugin.
29
+ this.version = '3.5.4'; // TODO: version compatability check inside each plugin.
30
30
  this.preStartMessages = [];
31
31
  this.projectKey = projectKey;
32
32
  this.options = Object.assign({
@@ -1,7 +1,7 @@
1
1
  import App from "../index.js";
2
2
  export default abstract class Observer {
3
3
  protected readonly app: App;
4
- protected readonly context: Window;
4
+ protected readonly isTopContext: boolean;
5
5
  private readonly observer;
6
6
  private readonly commited;
7
7
  private readonly recents;
@@ -9,8 +9,7 @@ export default abstract class Observer {
9
9
  private readonly indexes;
10
10
  private readonly attributesList;
11
11
  private readonly textSet;
12
- private readonly inUpperContext;
13
- constructor(app: App, context?: Window);
12
+ constructor(app: App, isTopContext?: boolean);
14
13
  private clear;
15
14
  private sendNodeAttribute;
16
15
  private sendNodeData;
@@ -32,16 +32,15 @@ function isObservable(node) {
32
32
  return !isIgnored(node);
33
33
  }
34
34
  export default class Observer {
35
- constructor(app, context = window) {
35
+ constructor(app, isTopContext = false) {
36
36
  this.app = app;
37
- this.context = context;
37
+ this.isTopContext = isTopContext;
38
38
  this.commited = [];
39
39
  this.recents = [];
40
40
  this.myNodes = [];
41
41
  this.indexes = [];
42
42
  this.attributesList = [];
43
43
  this.textSet = new Set();
44
- this.inUpperContext = context.parent === context; //TODO: get rid of context here
45
44
  this.observer = new MutationObserver(this.app.safe((mutations) => {
46
45
  for (const mutation of mutations) {
47
46
  const target = mutation.target;
@@ -184,7 +183,7 @@ export default class Observer {
184
183
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
185
184
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
186
185
  // TODO: Clean the logic (though now it workd fine)
187
- if (!isInstance(node, HTMLHtmlElement) || !this.inUpperContext) {
186
+ if (!isInstance(node, HTMLHtmlElement) || !this.isTopContext) {
188
187
  if (parent === null) {
189
188
  this.unbindNode(node);
190
189
  return false;
@@ -272,6 +271,8 @@ export default class Observer {
272
271
  for (let id = 0; id < this.recents.length; id++) {
273
272
  // TODO: make things/logic nice here.
274
273
  // commit required in any case if recents[id] true or false (in case of unbinding) or undefined (in case of attr change).
274
+ // Possible solution: separate new node commit (recents) and new attribute/move node commit
275
+ // Otherwise commitNode is called on each node, which might be a lot
275
276
  if (!this.myNodes[id]) {
276
277
  continue;
277
278
  }
@@ -3,20 +3,21 @@ import { isInstance } from "../context.js";
3
3
  import IFrameObserver from "./iframe_observer.js";
4
4
  import ShadowRootObserver from "./shadow_root_observer.js";
5
5
  import { CreateDocument } from "../../messages/index.js";
6
- import { IN_BROWSER } from '../../utils.js';
6
+ import { IN_BROWSER, hasOpenreplayAttribute } from '../../utils.js';
7
7
  const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
8
8
  export default class TopObserver extends Observer {
9
9
  constructor(app, options) {
10
- super(app);
10
+ super(app, true);
11
11
  this.iframeObservers = [];
12
12
  this.shadowRootObservers = [];
13
13
  this.options = Object.assign({
14
- captureIFrames: false
14
+ captureIFrames: true
15
15
  }, options);
16
16
  // IFrames
17
17
  this.app.nodes.attachNodeCallback(node => {
18
18
  if (isInstance(node, HTMLIFrameElement) &&
19
- (this.options.captureIFrames || node.getAttribute("data-openreplay-capture"))) {
19
+ ((this.options.captureIFrames && !hasOpenreplayAttribute(node, "obscured"))
20
+ || hasOpenreplayAttribute(node, "capture"))) {
20
21
  this.handleIframe(node);
21
22
  }
22
23
  });
@@ -28,28 +29,28 @@ export default class TopObserver extends Observer {
28
29
  });
29
30
  }
30
31
  handleIframe(iframe) {
31
- let context = null;
32
+ let doc = null;
32
33
  const handle = this.app.safe(() => {
33
34
  const id = this.app.nodes.getID(iframe);
34
35
  if (id === undefined) {
35
36
  return;
36
37
  } //log
37
- if (iframe.contentWindow === context) {
38
+ if (iframe.contentDocument === doc) {
38
39
  return;
39
- } //Does this happen frequently?
40
- context = iframe.contentWindow;
41
- if (!context) {
40
+ } // How frequently can it happen?
41
+ doc = iframe.contentDocument;
42
+ if (!doc || !iframe.contentWindow) {
42
43
  return;
43
44
  }
44
- const observer = new IFrameObserver(this.app, context);
45
+ const observer = new IFrameObserver(this.app);
45
46
  this.iframeObservers.push(observer);
46
47
  observer.observe(iframe);
47
48
  });
48
- this.app.attachEventListener(iframe, "load", handle);
49
+ iframe.addEventListener("load", handle); // why app.attachEventListener not working?
49
50
  handle();
50
51
  }
51
52
  handleShadowRoot(shRoot) {
52
- const observer = new ShadowRootObserver(this.app, this.context);
53
+ const observer = new ShadowRootObserver(this.app);
53
54
  this.shadowRootObservers.push(observer);
54
55
  observer.observe(shRoot.host);
55
56
  }
@@ -67,9 +68,9 @@ export default class TopObserver extends Observer {
67
68
  // the change in the re-player behaviour caused by CreateDocument message:
68
69
  // the 0-node ("fRoot") will become #document rather than documentElement as it is now.
69
70
  // Alternatively - observe(#document) then bindNode(documentElement)
70
- this.observeRoot(this.context.document, () => {
71
+ this.observeRoot(window.document, () => {
71
72
  this.app.send(new CreateDocument());
72
- }, this.context.document.documentElement);
73
+ }, window.document.documentElement);
73
74
  }
74
75
  disconnect() {
75
76
  Element.prototype.attachShadow = attachShadowNativeFn;
package/lib/index.js CHANGED
@@ -123,7 +123,7 @@ export default class API {
123
123
  // no-cors issue only with text/plain or not-set Content-Type
124
124
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
125
125
  req.send(JSON.stringify({
126
- trackerVersion: '3.5.3',
126
+ trackerVersion: '3.5.4',
127
127
  projectKey: options.projectKey,
128
128
  doNotTrack,
129
129
  // TODO: add precise reason (an exact API missing)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openreplay/tracker",
3
3
  "description": "The OpenReplay tracker main package",
4
- "version": "3.5.3",
4
+ "version": "3.5.4",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"