@openreplay/tracker 3.5.17-beta.0 → 3.6.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.
- package/.eslintignore +1 -1
- package/.prettierignore +1 -0
- package/cjs/app/guards.d.ts +2 -1
- package/cjs/app/guards.js +5 -1
- package/cjs/app/index.d.ts +12 -10
- package/cjs/app/index.js +38 -44
- package/cjs/app/messages.d.ts +52 -0
- package/cjs/app/messages.gen.d.ts +57 -0
- package/cjs/app/messages.gen.js +494 -0
- package/cjs/app/messages.js +235 -0
- package/cjs/app/nodes.js +2 -0
- package/cjs/app/observer/iframe_observer.js +2 -2
- package/cjs/app/observer/observer.d.ts +1 -2
- package/cjs/app/observer/observer.js +40 -39
- package/cjs/app/observer/shadow_root_observer.js +2 -2
- package/cjs/app/observer/top_observer.d.ts +9 -0
- package/cjs/app/observer/top_observer.js +40 -12
- package/cjs/app/session.d.ts +17 -1
- package/cjs/app/session.js +51 -3
- package/cjs/common/{webworker.d.ts → interaction.d.ts} +3 -3
- package/cjs/common/{types.js → interaction.js} +0 -0
- package/cjs/common/messages.gen.d.ts +382 -0
- package/cjs/common/messages.gen.js +62 -0
- package/cjs/index.d.ts +3 -3
- package/cjs/index.js +15 -10
- package/cjs/modules/adoptedStyleSheets.d.ts +2 -0
- package/cjs/modules/adoptedStyleSheets.js +127 -0
- package/cjs/modules/connection.js +2 -2
- package/cjs/modules/console.js +6 -20
- package/cjs/modules/cssrules.js +16 -12
- package/cjs/modules/exception.d.ts +2 -2
- package/cjs/modules/exception.js +18 -14
- package/cjs/modules/img.js +31 -21
- package/cjs/modules/input.js +6 -6
- package/cjs/modules/mouse.js +42 -33
- package/cjs/modules/performance.js +2 -2
- package/cjs/modules/scroll.js +16 -7
- package/cjs/modules/timing.js +4 -4
- package/cjs/modules/viewport.js +4 -4
- package/lib/app/guards.d.ts +2 -1
- package/lib/app/guards.js +3 -0
- package/lib/app/index.d.ts +12 -10
- package/lib/app/index.js +39 -45
- package/lib/app/messages.d.ts +52 -0
- package/lib/app/messages.gen.d.ts +57 -0
- package/lib/app/messages.gen.js +435 -0
- package/lib/app/messages.js +182 -0
- package/lib/app/nodes.js +2 -0
- package/lib/app/observer/iframe_observer.js +1 -1
- package/lib/app/observer/observer.d.ts +1 -2
- package/lib/app/observer/observer.js +40 -39
- package/lib/app/observer/shadow_root_observer.js +1 -1
- package/lib/app/observer/top_observer.d.ts +9 -0
- package/lib/app/observer/top_observer.js +40 -12
- package/lib/app/session.d.ts +17 -1
- package/lib/app/session.js +51 -3
- package/lib/common/{webworker.d.ts → interaction.d.ts} +3 -3
- package/lib/common/{types.js → interaction.js} +0 -0
- package/lib/common/messages.gen.d.ts +382 -0
- package/lib/common/messages.gen.js +59 -0
- package/lib/common/tsconfig.tsbuildinfo +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +15 -10
- package/lib/modules/adoptedStyleSheets.d.ts +2 -0
- package/lib/modules/adoptedStyleSheets.js +124 -0
- package/lib/modules/connection.js +2 -2
- package/lib/modules/console.js +6 -20
- package/lib/modules/cssrules.js +16 -12
- package/lib/modules/exception.d.ts +2 -2
- package/lib/modules/exception.js +18 -14
- package/lib/modules/img.js +31 -21
- package/lib/modules/input.js +6 -6
- package/lib/modules/mouse.js +43 -34
- package/lib/modules/performance.js +2 -2
- package/lib/modules/scroll.js +17 -8
- package/lib/modules/timing.js +4 -4
- package/lib/modules/viewport.js +4 -4
- package/package.json +1 -1
- package/cjs/common/messages.d.ts +0 -444
- package/cjs/common/messages.js +0 -794
- package/cjs/common/types.d.ts +0 -9
- package/cjs/common/webworker.js +0 -2
- package/cjs/modules/longtasks.d.ts +0 -2
- package/cjs/modules/longtasks.js +0 -34
- package/lib/common/messages.d.ts +0 -444
- package/lib/common/messages.js +0 -790
- package/lib/common/types.d.ts +0 -9
- package/lib/common/webworker.js +0 -1
- package/lib/modules/longtasks.d.ts +0 -2
- package/lib/modules/longtasks.js +0 -31
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { TechnicalInfo, AdoptedSSReplaceURLBased, AdoptedSSInsertRuleURLBased, AdoptedSSDeleteRule, AdoptedSSAddOwner, AdoptedSSRemoveOwner, } from '../app/messages.gen.js';
|
|
2
|
+
import { isRootNode } from '../app/guards.js';
|
|
3
|
+
function hasAdoptedSS(node) {
|
|
4
|
+
return (isRootNode(node) &&
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
typeof node.adoptedStyleSheets !== 'undefined');
|
|
7
|
+
}
|
|
8
|
+
export default function (app) {
|
|
9
|
+
if (app === null) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (!hasAdoptedSS(document)) {
|
|
13
|
+
app.attachStartCallback(() => {
|
|
14
|
+
// MBTODO: pre-start sendQueue app
|
|
15
|
+
app.send(TechnicalInfo('no_adopted_stylesheets', ''));
|
|
16
|
+
});
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let nextID = 0xf;
|
|
20
|
+
const styleSheetIDMap = new Map();
|
|
21
|
+
const adoptedStyleSheetsOwnings = new Map();
|
|
22
|
+
const updateAdoptedStyleSheets = (root) => {
|
|
23
|
+
let nodeID = app.nodes.getID(root);
|
|
24
|
+
if (root === document) {
|
|
25
|
+
nodeID = 0; // main document doesn't have nodeID. ID count starts from the documentElement
|
|
26
|
+
}
|
|
27
|
+
if (!nodeID) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let pastOwning = adoptedStyleSheetsOwnings.get(nodeID);
|
|
31
|
+
if (!pastOwning) {
|
|
32
|
+
pastOwning = [];
|
|
33
|
+
}
|
|
34
|
+
const nowOwning = [];
|
|
35
|
+
const styleSheets = root.adoptedStyleSheets;
|
|
36
|
+
for (const s of styleSheets) {
|
|
37
|
+
let sheetID = styleSheetIDMap.get(s);
|
|
38
|
+
const init = !sheetID;
|
|
39
|
+
if (!sheetID) {
|
|
40
|
+
sheetID = ++nextID;
|
|
41
|
+
}
|
|
42
|
+
nowOwning.push(sheetID);
|
|
43
|
+
if (!pastOwning.includes(sheetID)) {
|
|
44
|
+
app.send(AdoptedSSAddOwner(sheetID, nodeID));
|
|
45
|
+
}
|
|
46
|
+
if (init) {
|
|
47
|
+
const rules = s.cssRules;
|
|
48
|
+
for (let i = 0; i < rules.length; i++) {
|
|
49
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, rules[i].cssText, i, app.getBaseHref()));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const sheetID of pastOwning) {
|
|
54
|
+
if (!nowOwning.includes(sheetID)) {
|
|
55
|
+
app.send(AdoptedSSRemoveOwner(sheetID, nodeID));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
adoptedStyleSheetsOwnings.set(nodeID, nowOwning);
|
|
59
|
+
};
|
|
60
|
+
function patchAdoptedStyleSheets(prototype) {
|
|
61
|
+
const nativeAdoptedStyleSheetsDescriptor = Object.getOwnPropertyDescriptor(prototype, 'adoptedStyleSheets');
|
|
62
|
+
if (nativeAdoptedStyleSheetsDescriptor) {
|
|
63
|
+
Object.defineProperty(prototype, 'adoptedStyleSheets', Object.assign(Object.assign({}, nativeAdoptedStyleSheetsDescriptor), { set: function (value) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
const retVal = nativeAdoptedStyleSheetsDescriptor.set.call(this, value);
|
|
66
|
+
updateAdoptedStyleSheets(this);
|
|
67
|
+
return retVal;
|
|
68
|
+
} }));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const patchContext = (context) => {
|
|
72
|
+
patchAdoptedStyleSheets(context.Document.prototype);
|
|
73
|
+
patchAdoptedStyleSheets(context.ShadowRoot.prototype);
|
|
74
|
+
//@ts-ignore TODO: configure ts (use necessary lib)
|
|
75
|
+
const { insertRule, deleteRule, replace, replaceSync } = context.CSSStyleSheet.prototype;
|
|
76
|
+
//@ts-ignore
|
|
77
|
+
context.CSSStyleSheet.prototype.replace = function (text) {
|
|
78
|
+
return replace.call(this, text).then((sheet) => {
|
|
79
|
+
const sheetID = styleSheetIDMap.get(this);
|
|
80
|
+
if (sheetID) {
|
|
81
|
+
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
|
|
82
|
+
}
|
|
83
|
+
return sheet;
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
//@ts-ignore
|
|
87
|
+
context.CSSStyleSheet.prototype.replaceSync = function (text) {
|
|
88
|
+
const sheetID = styleSheetIDMap.get(this);
|
|
89
|
+
if (sheetID) {
|
|
90
|
+
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
|
|
91
|
+
}
|
|
92
|
+
return replaceSync.call(this, text);
|
|
93
|
+
};
|
|
94
|
+
context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
|
|
95
|
+
const sheetID = styleSheetIDMap.get(this);
|
|
96
|
+
if (sheetID) {
|
|
97
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, rule, index, app.getBaseHref()));
|
|
98
|
+
}
|
|
99
|
+
return insertRule.call(this, rule, index);
|
|
100
|
+
};
|
|
101
|
+
context.CSSStyleSheet.prototype.deleteRule = function (index) {
|
|
102
|
+
const sheetID = styleSheetIDMap.get(this);
|
|
103
|
+
if (sheetID) {
|
|
104
|
+
app.send(AdoptedSSDeleteRule(sheetID, index));
|
|
105
|
+
}
|
|
106
|
+
return deleteRule.call(this, index);
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
patchContext(window);
|
|
110
|
+
app.observer.attachContextCallback(patchContext);
|
|
111
|
+
app.attachStopCallback(() => {
|
|
112
|
+
styleSheetIDMap.clear();
|
|
113
|
+
adoptedStyleSheetsOwnings.clear();
|
|
114
|
+
});
|
|
115
|
+
// So far main Document is not triggered with nodeCallbacks
|
|
116
|
+
app.attachStartCallback(() => {
|
|
117
|
+
updateAdoptedStyleSheets(document);
|
|
118
|
+
});
|
|
119
|
+
app.nodes.attachNodeCallback((node) => {
|
|
120
|
+
if (hasAdoptedSS(node)) {
|
|
121
|
+
updateAdoptedStyleSheets(node);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConnectionInformation } from '../
|
|
1
|
+
import { ConnectionInformation } from '../app/messages.gen.js';
|
|
2
2
|
export default function (app) {
|
|
3
3
|
const connection = navigator.connection ||
|
|
4
4
|
navigator.mozConnection ||
|
|
@@ -6,7 +6,7 @@ export default function (app) {
|
|
|
6
6
|
if (connection === undefined) {
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
|
-
const sendConnectionInformation = () => app.send(
|
|
9
|
+
const sendConnectionInformation = () => app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.type || 'unknown'));
|
|
10
10
|
sendConnectionInformation();
|
|
11
11
|
connection.addEventListener('change', sendConnectionInformation);
|
|
12
12
|
}
|
package/lib/modules/console.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { hasTag } from '../app/guards.js';
|
|
2
1
|
import { IN_BROWSER } from '../utils.js';
|
|
3
|
-
import { ConsoleLog } from '../
|
|
2
|
+
import { ConsoleLog } from '../app/messages.gen.js';
|
|
4
3
|
const printError = IN_BROWSER && 'InstallTrigger' in window // detect Firefox
|
|
5
4
|
? (e) => e.message + '\n' + e.stack
|
|
6
5
|
: (e) => e.stack || e.message;
|
|
@@ -90,7 +89,7 @@ export default function (app, opts) {
|
|
|
90
89
|
if (!Array.isArray(options.consoleMethods) || options.consoleMethods.length === 0) {
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
|
-
const sendConsoleLog = app.safe((level, args) => app.send(
|
|
92
|
+
const sendConsoleLog = app.safe((level, args) => app.send(ConsoleLog(level, printf(args))));
|
|
94
93
|
let n;
|
|
95
94
|
const reset = () => {
|
|
96
95
|
n = 0;
|
|
@@ -99,7 +98,7 @@ export default function (app, opts) {
|
|
|
99
98
|
app.ticker.attach(reset, 33, false);
|
|
100
99
|
const patchConsole = (console) => options.consoleMethods.forEach((method) => {
|
|
101
100
|
if (consoleMethods.indexOf(method) === -1) {
|
|
102
|
-
|
|
101
|
+
app.debug.error(`OpenReplay: unsupported console method "${method}"`);
|
|
103
102
|
return;
|
|
104
103
|
}
|
|
105
104
|
const fn = console[method];
|
|
@@ -111,20 +110,7 @@ export default function (app, opts) {
|
|
|
111
110
|
sendConsoleLog(method, args);
|
|
112
111
|
};
|
|
113
112
|
});
|
|
114
|
-
patchConsole(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// TODO: newContextCallback
|
|
118
|
-
let context = node.contentWindow;
|
|
119
|
-
if (context) {
|
|
120
|
-
patchConsole(context.console);
|
|
121
|
-
}
|
|
122
|
-
app.attachEventListener(node, 'load', () => {
|
|
123
|
-
if (node.contentWindow !== context) {
|
|
124
|
-
context = node.contentWindow;
|
|
125
|
-
patchConsole(context.console);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}));
|
|
113
|
+
const patchContext = app.safe((context) => patchConsole(context.console));
|
|
114
|
+
patchContext(window);
|
|
115
|
+
app.observer.attachContextCallback(patchContext);
|
|
130
116
|
}
|
package/lib/modules/cssrules.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from '../
|
|
1
|
+
import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from '../app/messages.gen.js';
|
|
2
2
|
import { hasTag } from '../app/guards.js';
|
|
3
3
|
export default function (app) {
|
|
4
4
|
if (app === null) {
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
7
7
|
if (!window.CSSStyleSheet) {
|
|
8
|
-
app.send(
|
|
8
|
+
app.send(TechnicalInfo('no_stylesheet_prototype_in_window', ''));
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
const processOperation = app.safe((stylesheet, index, rule) => {
|
|
12
12
|
const sendMessage = typeof rule === 'string'
|
|
13
|
-
? (nodeID) => app.send(
|
|
14
|
-
: (nodeID) => app.send(
|
|
13
|
+
? (nodeID) => app.send(CSSInsertRuleURLBased(nodeID, rule, index, app.getBaseHref()))
|
|
14
|
+
: (nodeID) => app.send(CSSDeleteRule(nodeID, index));
|
|
15
15
|
// TODO: Extend messages to maintain nested rules (CSSGroupingRule prototype, as well as CSSKeyframesRule)
|
|
16
16
|
if (stylesheet.ownerNode == null) {
|
|
17
17
|
throw new Error('Owner Node not found');
|
|
@@ -21,15 +21,19 @@ export default function (app) {
|
|
|
21
21
|
sendMessage(nodeID);
|
|
22
22
|
} // else error?
|
|
23
23
|
});
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
const patchContext = (context) => {
|
|
25
|
+
const { insertRule, deleteRule } = context.CSSStyleSheet.prototype;
|
|
26
|
+
context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
|
|
27
|
+
processOperation(this, index, rule);
|
|
28
|
+
return insertRule.call(this, rule, index);
|
|
29
|
+
};
|
|
30
|
+
context.CSSStyleSheet.prototype.deleteRule = function (index) {
|
|
31
|
+
processOperation(this, index);
|
|
32
|
+
return deleteRule.call(this, index);
|
|
33
|
+
};
|
|
32
34
|
};
|
|
35
|
+
patchContext(window);
|
|
36
|
+
app.observer.attachContextCallback(patchContext);
|
|
33
37
|
app.nodes.attachNodeCallback((node) => {
|
|
34
38
|
if (!hasTag(node, 'STYLE') || !node.sheet) {
|
|
35
39
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type App from '../app/index.js';
|
|
2
|
-
import type Message from '../
|
|
2
|
+
import type Message from '../app/messages.gen.js';
|
|
3
3
|
export interface Options {
|
|
4
4
|
captureExceptions: boolean;
|
|
5
5
|
}
|
|
@@ -11,6 +11,6 @@ interface StackFrame {
|
|
|
11
11
|
source?: string;
|
|
12
12
|
}
|
|
13
13
|
export declare function getExceptionMessage(error: Error, fallbackStack: Array<StackFrame>): Message;
|
|
14
|
-
export declare function getExceptionMessageFromEvent(e: ErrorEvent | PromiseRejectionEvent): Message | null;
|
|
14
|
+
export declare function getExceptionMessageFromEvent(e: ErrorEvent | PromiseRejectionEvent, context?: typeof globalThis): Message | null;
|
|
15
15
|
export default function (app: App, opts: Partial<Options>): void;
|
|
16
16
|
export {};
|
package/lib/modules/exception.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSException } from '../
|
|
1
|
+
import { JSException } from '../app/messages.gen.js';
|
|
2
2
|
import ErrorStackParser from 'error-stack-parser';
|
|
3
3
|
function getDefaultStack(e) {
|
|
4
4
|
return [
|
|
@@ -17,9 +17,9 @@ export function getExceptionMessage(error, fallbackStack) {
|
|
|
17
17
|
stack = ErrorStackParser.parse(error);
|
|
18
18
|
}
|
|
19
19
|
catch (e) { }
|
|
20
|
-
return
|
|
20
|
+
return JSException(error.name, error.message, JSON.stringify(stack));
|
|
21
21
|
}
|
|
22
|
-
export function getExceptionMessageFromEvent(e) {
|
|
22
|
+
export function getExceptionMessageFromEvent(e, context = window) {
|
|
23
23
|
if (e instanceof ErrorEvent) {
|
|
24
24
|
if (e.error instanceof Error) {
|
|
25
25
|
return getExceptionMessage(e.error, getDefaultStack(e));
|
|
@@ -30,10 +30,10 @@ export function getExceptionMessageFromEvent(e) {
|
|
|
30
30
|
name = 'Error';
|
|
31
31
|
message = e.message;
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return JSException(name, message, JSON.stringify(getDefaultStack(e)));
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
else if ('PromiseRejectionEvent' in
|
|
36
|
+
else if ('PromiseRejectionEvent' in context && e instanceof context.PromiseRejectionEvent) {
|
|
37
37
|
if (e.reason instanceof Error) {
|
|
38
38
|
return getExceptionMessage(e.reason, []);
|
|
39
39
|
}
|
|
@@ -45,7 +45,7 @@ export function getExceptionMessageFromEvent(e) {
|
|
|
45
45
|
catch (_) {
|
|
46
46
|
message = String(e.reason);
|
|
47
47
|
}
|
|
48
|
-
return
|
|
48
|
+
return JSException('Unhandled Promise Rejection', message, '[]');
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
return null;
|
|
@@ -55,13 +55,17 @@ export default function (app, opts) {
|
|
|
55
55
|
captureExceptions: true,
|
|
56
56
|
}, opts);
|
|
57
57
|
if (options.captureExceptions) {
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
function patchContext(context) {
|
|
59
|
+
const handler = (e) => {
|
|
60
|
+
const msg = getExceptionMessageFromEvent(e, context);
|
|
61
|
+
if (msg != null) {
|
|
62
|
+
app.send(msg);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
app.attachEventListener(context, 'unhandledrejection', handler);
|
|
66
|
+
app.attachEventListener(context, 'error', handler);
|
|
67
|
+
}
|
|
68
|
+
app.observer.attachContextCallback(patchContext);
|
|
69
|
+
patchContext(window);
|
|
66
70
|
}
|
|
67
71
|
}
|
package/lib/modules/img.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { timestamp, isURL } from '../utils.js';
|
|
2
|
-
import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute
|
|
2
|
+
import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from '../app/messages.gen.js';
|
|
3
3
|
import { hasTag } from '../app/guards.js';
|
|
4
4
|
function resolveURL(url, location = document.location) {
|
|
5
5
|
url = url.trim();
|
|
@@ -19,16 +19,31 @@ function resolveURL(url, location = document.location) {
|
|
|
19
19
|
const PLACEHOLDER_SRC = 'https://static.openreplay.com/tracker/placeholder.jpeg';
|
|
20
20
|
export default function (app) {
|
|
21
21
|
function sendPlaceholder(id, node) {
|
|
22
|
-
app.send(
|
|
22
|
+
app.send(SetNodeAttribute(id, 'src', PLACEHOLDER_SRC));
|
|
23
23
|
const { width, height } = node.getBoundingClientRect();
|
|
24
24
|
if (!node.hasAttribute('width')) {
|
|
25
|
-
app.send(
|
|
25
|
+
app.send(SetNodeAttribute(id, 'width', String(width)));
|
|
26
26
|
}
|
|
27
27
|
if (!node.hasAttribute('height')) {
|
|
28
|
-
app.send(
|
|
28
|
+
app.send(SetNodeAttribute(id, 'height', String(height)));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const sendSrcset = function (id, img) {
|
|
32
|
+
const { srcset } = img;
|
|
33
|
+
if (!srcset) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const resolvedSrcset = srcset
|
|
37
|
+
.split(',')
|
|
38
|
+
.map((str) => resolveURL(str))
|
|
39
|
+
.join(',');
|
|
40
|
+
app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset));
|
|
41
|
+
};
|
|
42
|
+
const sendSrc = function (id, img) {
|
|
43
|
+
const src = img.src;
|
|
44
|
+
app.send(SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
|
|
45
|
+
};
|
|
46
|
+
const sendImgAttrs = app.safe(function () {
|
|
32
47
|
const id = app.nodes.getID(this);
|
|
33
48
|
if (id === undefined) {
|
|
34
49
|
return;
|
|
@@ -40,21 +55,15 @@ export default function (app) {
|
|
|
40
55
|
const resolvedSrc = resolveURL(src || ''); // Src type is null sometimes. - is it true?
|
|
41
56
|
if (naturalWidth === 0 && naturalHeight === 0) {
|
|
42
57
|
if (isURL(resolvedSrc)) {
|
|
43
|
-
app.send(
|
|
58
|
+
app.send(ResourceTiming(timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
|
|
44
59
|
}
|
|
45
60
|
}
|
|
46
61
|
else if (resolvedSrc.length >= 1e5 || app.sanitizer.isMasked(id)) {
|
|
47
62
|
sendPlaceholder(id, this);
|
|
48
63
|
}
|
|
49
64
|
else {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const resolvedSrcset = srcset
|
|
53
|
-
.split(',')
|
|
54
|
-
.map((str) => resolveURL(str))
|
|
55
|
-
.join(',');
|
|
56
|
-
app.send(new SetNodeAttribute(id, 'srcset', resolvedSrcset));
|
|
57
|
-
}
|
|
65
|
+
sendSrc(id, this);
|
|
66
|
+
sendSrcset(id, this);
|
|
58
67
|
}
|
|
59
68
|
});
|
|
60
69
|
const observer = new MutationObserver((mutations) => {
|
|
@@ -66,23 +75,24 @@ export default function (app) {
|
|
|
66
75
|
return;
|
|
67
76
|
}
|
|
68
77
|
if (mutation.attributeName === 'src') {
|
|
69
|
-
|
|
70
|
-
app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
|
|
78
|
+
sendSrc(id, target);
|
|
71
79
|
}
|
|
72
80
|
if (mutation.attributeName === 'srcset') {
|
|
73
|
-
|
|
74
|
-
app.send(new SetNodeAttribute(id, 'srcset', srcset));
|
|
81
|
+
sendSrcset(id, target);
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
}
|
|
78
85
|
});
|
|
86
|
+
app.attachStopCallback(() => {
|
|
87
|
+
observer.disconnect();
|
|
88
|
+
});
|
|
79
89
|
app.nodes.attachNodeCallback((node) => {
|
|
80
90
|
if (!hasTag(node, 'IMG')) {
|
|
81
91
|
return;
|
|
82
92
|
}
|
|
83
|
-
app.nodes.attachElementListener('error', node,
|
|
84
|
-
app.nodes.attachElementListener('load', node,
|
|
85
|
-
|
|
93
|
+
app.nodes.attachElementListener('error', node, sendImgAttrs.bind(node));
|
|
94
|
+
app.nodes.attachElementListener('load', node, sendImgAttrs.bind(node));
|
|
95
|
+
sendImgAttrs.call(node);
|
|
86
96
|
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
|
|
87
97
|
});
|
|
88
98
|
}
|
package/lib/modules/input.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute } from '../utils.js';
|
|
2
2
|
import { hasTag } from '../app/guards.js';
|
|
3
|
-
import { SetInputTarget, SetInputValue, SetInputChecked } from '../
|
|
3
|
+
import { SetInputTarget, SetInputValue, SetInputChecked } from '../app/messages.gen.js';
|
|
4
4
|
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
|
|
5
5
|
function isTextEditable(node) {
|
|
6
6
|
if (hasTag(node, 'TEXTAREA')) {
|
|
@@ -40,7 +40,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
40
40
|
}
|
|
41
41
|
const id = node.id;
|
|
42
42
|
if (id) {
|
|
43
|
-
const labels =
|
|
43
|
+
const labels = node.ownerDocument.querySelectorAll('label[for="' + id + '"]');
|
|
44
44
|
if (labels !== null && labels.length === 1) {
|
|
45
45
|
return labels[0];
|
|
46
46
|
}
|
|
@@ -70,7 +70,7 @@ export default function (app, opts) {
|
|
|
70
70
|
function sendInputTarget(id, node) {
|
|
71
71
|
const label = getInputLabel(node);
|
|
72
72
|
if (label !== '') {
|
|
73
|
-
app.send(
|
|
73
|
+
app.send(SetInputTarget(id, label));
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
function sendInputValue(id, node) {
|
|
@@ -97,7 +97,7 @@ export default function (app, opts) {
|
|
|
97
97
|
value = '';
|
|
98
98
|
break;
|
|
99
99
|
}
|
|
100
|
-
app.send(
|
|
100
|
+
app.send(SetInputValue(id, value, mask));
|
|
101
101
|
}
|
|
102
102
|
const inputValues = new Map();
|
|
103
103
|
const checkableValues = new Map();
|
|
@@ -135,7 +135,7 @@ export default function (app, opts) {
|
|
|
135
135
|
}
|
|
136
136
|
if (checked !== node.checked) {
|
|
137
137
|
checkableValues.set(id, node.checked);
|
|
138
|
-
app.send(
|
|
138
|
+
app.send(SetInputChecked(id, node.checked));
|
|
139
139
|
}
|
|
140
140
|
});
|
|
141
141
|
});
|
|
@@ -159,7 +159,7 @@ export default function (app, opts) {
|
|
|
159
159
|
}
|
|
160
160
|
if (isCheckable(node)) {
|
|
161
161
|
checkableValues.set(id, node.checked);
|
|
162
|
-
app.send(
|
|
162
|
+
app.send(SetInputChecked(id, node.checked));
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
}));
|
package/lib/modules/mouse.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { hasTag, isSVGElement } from '../app/guards.js';
|
|
1
|
+
import { hasTag, isSVGElement, isDocument } from '../app/guards.js';
|
|
2
2
|
import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils.js';
|
|
3
|
-
import { MouseMove, MouseClick } from '../
|
|
3
|
+
import { MouseMove, MouseClick } from '../app/messages.gen.js';
|
|
4
4
|
import { getInputLabel } from './input.js';
|
|
5
|
-
function _getSelector(target) {
|
|
5
|
+
function _getSelector(target, document) {
|
|
6
6
|
let el = target;
|
|
7
7
|
let selector = null;
|
|
8
8
|
do {
|
|
@@ -32,16 +32,16 @@ function isClickable(element) {
|
|
|
32
32
|
element.onclick != null ||
|
|
33
33
|
element.getAttribute('role') === 'button');
|
|
34
34
|
//|| element.className.includes("btn")
|
|
35
|
-
// MBTODO:
|
|
35
|
+
// MBTODO: intersept addEventListener
|
|
36
36
|
}
|
|
37
|
-
//TODO: fix (typescript
|
|
38
|
-
function getTarget(target) {
|
|
37
|
+
//TODO: fix (typescript is not sure about target variable after assignation of svg)
|
|
38
|
+
function getTarget(target, document) {
|
|
39
39
|
if (target instanceof Element) {
|
|
40
|
-
return _getTarget(target);
|
|
40
|
+
return _getTarget(target, document);
|
|
41
41
|
}
|
|
42
42
|
return null;
|
|
43
43
|
}
|
|
44
|
-
function _getTarget(target) {
|
|
44
|
+
function _getTarget(target, document) {
|
|
45
45
|
let element = target;
|
|
46
46
|
while (element !== null && element !== document.documentElement) {
|
|
47
47
|
if (hasOpenreplayAttribute(element, 'masked')) {
|
|
@@ -104,37 +104,46 @@ export default function (app) {
|
|
|
104
104
|
});
|
|
105
105
|
const sendMouseMove = () => {
|
|
106
106
|
if (mousePositionChanged) {
|
|
107
|
-
app.send(
|
|
107
|
+
app.send(MouseMove(mousePositionX, mousePositionY));
|
|
108
108
|
mousePositionChanged = false;
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
app.attachEventListener(document.documentElement, 'mouseover', (e) => {
|
|
116
|
-
const target = getTarget(e.target);
|
|
117
|
-
if (target !== mouseTarget) {
|
|
118
|
-
mouseTarget = target;
|
|
119
|
-
mouseTargetTime = performance.now();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
app.attachEventListener(document, 'mousemove', (e) => {
|
|
123
|
-
mousePositionX = e.clientX;
|
|
124
|
-
mousePositionY = e.clientY;
|
|
125
|
-
mousePositionChanged = true;
|
|
126
|
-
}, false);
|
|
127
|
-
app.attachEventListener(document, 'click', (e) => {
|
|
128
|
-
const target = getTarget(e.target);
|
|
129
|
-
if ((!e.clientX && !e.clientY) || target === null) {
|
|
130
|
-
return;
|
|
111
|
+
const patchDocument = (document) => {
|
|
112
|
+
const selectorMap = {};
|
|
113
|
+
function getSelector(id, target) {
|
|
114
|
+
return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
|
|
131
115
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
116
|
+
app.attachEventListener(document.documentElement, 'mouseover', (e) => {
|
|
117
|
+
const target = getTarget(e.target, document);
|
|
118
|
+
if (target !== mouseTarget) {
|
|
119
|
+
mouseTarget = target;
|
|
120
|
+
mouseTargetTime = performance.now();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
app.attachEventListener(document, 'mousemove', (e) => {
|
|
124
|
+
const { top, left } = app.observer.getDocumentOffset(document);
|
|
125
|
+
mousePositionX = e.clientX + left;
|
|
126
|
+
mousePositionY = e.clientY + top;
|
|
127
|
+
mousePositionChanged = true;
|
|
128
|
+
}, false);
|
|
129
|
+
app.attachEventListener(document, 'click', (e) => {
|
|
130
|
+
const target = getTarget(e.target, document);
|
|
131
|
+
if ((!e.clientX && !e.clientY) || target === null) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const id = app.nodes.getID(target);
|
|
135
|
+
if (id !== undefined) {
|
|
136
|
+
sendMouseMove();
|
|
137
|
+
app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), getSelector(id, target)), true);
|
|
138
|
+
}
|
|
139
|
+
mouseTarget = null;
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
app.nodes.attachNodeCallback((node) => {
|
|
143
|
+
if (isDocument(node)) {
|
|
144
|
+
patchDocument(node);
|
|
136
145
|
}
|
|
137
|
-
mouseTarget = null;
|
|
138
146
|
});
|
|
147
|
+
patchDocument(document);
|
|
139
148
|
app.ticker.attach(sendMouseMove, 10);
|
|
140
149
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IN_BROWSER } from '../utils.js';
|
|
2
|
-
import { PerformanceTrack } from '../
|
|
2
|
+
import { PerformanceTrack } from '../app/messages.gen.js';
|
|
3
3
|
const perf = IN_BROWSER && 'performance' in window && 'memory' in performance // works in Chrome only
|
|
4
4
|
? performance
|
|
5
5
|
: { memory: {} };
|
|
@@ -31,7 +31,7 @@ export default function (app, opts) {
|
|
|
31
31
|
if (frames === undefined || ticks === undefined) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
app.send(
|
|
34
|
+
app.send(PerformanceTrack(frames, ticks, perf.memory.totalJSHeapSize || 0, perf.memory.usedJSHeapSize || 0));
|
|
35
35
|
ticks = frames = document.hidden ? -1 : 0;
|
|
36
36
|
};
|
|
37
37
|
app.attachStartCallback(() => {
|
package/lib/modules/scroll.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { SetViewportScroll, SetNodeScroll } from '../
|
|
2
|
-
import { isElementNode } from '../app/guards.js';
|
|
1
|
+
import { SetViewportScroll, SetNodeScroll } from '../app/messages.gen.js';
|
|
2
|
+
import { isElementNode, isRootNode } from '../app/guards.js';
|
|
3
3
|
export default function (app) {
|
|
4
4
|
let documentScroll = false;
|
|
5
5
|
const nodeScroll = new Map();
|
|
6
|
-
|
|
6
|
+
function setNodeScroll(target) {
|
|
7
|
+
if (target instanceof Element) {
|
|
8
|
+
nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const sendSetViewportScroll = app.safe(() => app.send(SetViewportScroll(window.pageXOffset ||
|
|
7
12
|
(document.documentElement && document.documentElement.scrollLeft) ||
|
|
8
13
|
(document.body && document.body.scrollLeft) ||
|
|
9
14
|
0, window.pageYOffset ||
|
|
@@ -13,7 +18,7 @@ export default function (app) {
|
|
|
13
18
|
const sendSetNodeScroll = app.safe((s, node) => {
|
|
14
19
|
const id = app.nodes.getID(node);
|
|
15
20
|
if (id !== undefined) {
|
|
16
|
-
app.send(
|
|
21
|
+
app.send(SetNodeScroll(id, s[0], s[1]));
|
|
17
22
|
}
|
|
18
23
|
});
|
|
19
24
|
app.attachStartCallback(sendSetViewportScroll);
|
|
@@ -25,16 +30,20 @@ export default function (app) {
|
|
|
25
30
|
if (isStart && isElementNode(node) && node.scrollLeft + node.scrollTop > 0) {
|
|
26
31
|
nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
|
|
27
32
|
}
|
|
33
|
+
else if (isRootNode(node)) {
|
|
34
|
+
// scroll is not-composed event (https://javascript.info/shadow-dom-events)
|
|
35
|
+
app.attachEventListener(node, 'scroll', (e) => {
|
|
36
|
+
setNodeScroll(e.target);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
28
39
|
});
|
|
29
|
-
app.attachEventListener(
|
|
40
|
+
app.attachEventListener(document, 'scroll', (e) => {
|
|
30
41
|
const target = e.target;
|
|
31
42
|
if (target === document) {
|
|
32
43
|
documentScroll = true;
|
|
33
44
|
return;
|
|
34
45
|
}
|
|
35
|
-
|
|
36
|
-
nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
|
|
37
|
-
}
|
|
46
|
+
setNodeScroll(target);
|
|
38
47
|
});
|
|
39
48
|
app.ticker.attach(() => {
|
|
40
49
|
if (documentScroll) {
|