@openreplay/tracker 16.1.4 → 16.2.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/dist/cjs/entry.js +504 -332
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +504 -332
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/index.d.ts +2 -1
- package/dist/cjs/main/app/observer/cssInliner.d.ts +1 -0
- package/dist/cjs/main/app/observer/observer.d.ts +17 -3
- package/dist/cjs/main/app/observer/top_observer.d.ts +14 -0
- package/dist/lib/entry.js +504 -332
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +504 -332
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/index.d.ts +2 -1
- package/dist/lib/main/app/observer/cssInliner.d.ts +1 -0
- package/dist/lib/main/app/observer/observer.d.ts +17 -3
- package/dist/lib/main/app/observer/top_observer.d.ts +14 -0
- package/dist/types/main/app/index.d.ts +2 -1
- package/dist/types/main/app/observer/cssInliner.d.ts +1 -0
- package/dist/types/main/app/observer/observer.d.ts +17 -3
- package/dist/types/main/app/observer/top_observer.d.ts +14 -0
- package/package.json +1 -1
package/dist/lib/entry.js
CHANGED
|
@@ -3698,6 +3698,381 @@ class Nodes {
|
|
|
3698
3698
|
}
|
|
3699
3699
|
}
|
|
3700
3700
|
|
|
3701
|
+
let fakeIdHolder = 1000000 * 99;
|
|
3702
|
+
function inlineRemoteCss(node, id, baseHref, getNextID, insertRule, addOwner, forceFetch, sendPlain, onPlain) {
|
|
3703
|
+
const sheetId = sendPlain ? null : getNextID();
|
|
3704
|
+
if (!sendPlain) {
|
|
3705
|
+
addOwner(sheetId, id);
|
|
3706
|
+
}
|
|
3707
|
+
const sheet = node.sheet;
|
|
3708
|
+
if (sheet && !forceFetch) {
|
|
3709
|
+
try {
|
|
3710
|
+
const cssText = stringifyStylesheet(sheet);
|
|
3711
|
+
if (cssText) {
|
|
3712
|
+
processCssText(cssText);
|
|
3713
|
+
return;
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
catch (e) {
|
|
3717
|
+
// console.warn("Could not stringify sheet, falling back to fetch:", e);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
// Fall back to fetching if we couldn't get or stringify the sheet
|
|
3721
|
+
if (node.href) {
|
|
3722
|
+
fetch(node.href)
|
|
3723
|
+
.then(response => {
|
|
3724
|
+
if (!response.ok) {
|
|
3725
|
+
throw new Error(`response status ${response.status}`);
|
|
3726
|
+
}
|
|
3727
|
+
return response.text();
|
|
3728
|
+
})
|
|
3729
|
+
.then(cssText => {
|
|
3730
|
+
if (sendPlain && onPlain) {
|
|
3731
|
+
onPlain(cssText, fakeIdHolder++);
|
|
3732
|
+
}
|
|
3733
|
+
else {
|
|
3734
|
+
processCssText(cssText);
|
|
3735
|
+
}
|
|
3736
|
+
})
|
|
3737
|
+
.catch(error => {
|
|
3738
|
+
console.error(`OpenReplay: Failed to fetch CSS from ${node.href}:`, error);
|
|
3739
|
+
});
|
|
3740
|
+
}
|
|
3741
|
+
function processCssText(cssText) {
|
|
3742
|
+
// Remove comments
|
|
3743
|
+
cssText = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
3744
|
+
// Parse and process the CSS text to extract rules
|
|
3745
|
+
const ruleTexts = parseCSS(cssText);
|
|
3746
|
+
for (let i = 0; i < ruleTexts.length; i++) {
|
|
3747
|
+
insertRule(sheetId, ruleTexts[i], i, baseHref);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
function parseCSS(cssText) {
|
|
3751
|
+
const rules = [];
|
|
3752
|
+
let inComment = false;
|
|
3753
|
+
let inString = false;
|
|
3754
|
+
let stringChar = '';
|
|
3755
|
+
let braceLevel = 0;
|
|
3756
|
+
let currentRule = '';
|
|
3757
|
+
for (let i = 0; i < cssText.length; i++) {
|
|
3758
|
+
const char = cssText[i];
|
|
3759
|
+
const nextChar = cssText[i + 1] || '';
|
|
3760
|
+
// comments
|
|
3761
|
+
if (!inString && char === '/' && nextChar === '*') {
|
|
3762
|
+
inComment = true;
|
|
3763
|
+
i++; // Skip the next character
|
|
3764
|
+
continue;
|
|
3765
|
+
}
|
|
3766
|
+
if (inComment) {
|
|
3767
|
+
if (char === '*' && nextChar === '/') {
|
|
3768
|
+
inComment = false;
|
|
3769
|
+
i++; // Skip the next character
|
|
3770
|
+
}
|
|
3771
|
+
continue;
|
|
3772
|
+
}
|
|
3773
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
3774
|
+
inString = true;
|
|
3775
|
+
stringChar = char;
|
|
3776
|
+
currentRule += char;
|
|
3777
|
+
continue;
|
|
3778
|
+
}
|
|
3779
|
+
if (inString) {
|
|
3780
|
+
currentRule += char;
|
|
3781
|
+
if (char === stringChar && cssText[i - 1] !== '\\') {
|
|
3782
|
+
inString = false;
|
|
3783
|
+
}
|
|
3784
|
+
continue;
|
|
3785
|
+
}
|
|
3786
|
+
currentRule += char;
|
|
3787
|
+
if (char === '{') {
|
|
3788
|
+
braceLevel++;
|
|
3789
|
+
}
|
|
3790
|
+
else if (char === '}') {
|
|
3791
|
+
braceLevel--;
|
|
3792
|
+
if (braceLevel === 0) {
|
|
3793
|
+
// End of a top-level rule
|
|
3794
|
+
rules.push(currentRule.trim());
|
|
3795
|
+
currentRule = '';
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
// Handle any remaining text (should be rare)
|
|
3800
|
+
if (currentRule.trim()) {
|
|
3801
|
+
rules.push(currentRule.trim());
|
|
3802
|
+
}
|
|
3803
|
+
return rules;
|
|
3804
|
+
}
|
|
3805
|
+
function stringifyStylesheet(s) {
|
|
3806
|
+
try {
|
|
3807
|
+
const rules = s.rules || s.cssRules;
|
|
3808
|
+
if (!rules) {
|
|
3809
|
+
return null;
|
|
3810
|
+
}
|
|
3811
|
+
let sheetHref = s.href;
|
|
3812
|
+
if (!sheetHref && s.ownerNode && s.ownerNode.ownerDocument) {
|
|
3813
|
+
// an inline <style> element
|
|
3814
|
+
sheetHref = s.ownerNode.ownerDocument.location.href;
|
|
3815
|
+
}
|
|
3816
|
+
const stringifiedRules = Array.from(rules, (rule) => stringifyRule(rule, sheetHref)).join('');
|
|
3817
|
+
return fixBrowserCompatibilityIssuesInCSS(stringifiedRules);
|
|
3818
|
+
}
|
|
3819
|
+
catch (error) {
|
|
3820
|
+
return null;
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
function stringifyRule(rule, sheetHref) {
|
|
3824
|
+
if (isCSSImportRule(rule)) {
|
|
3825
|
+
let importStringified;
|
|
3826
|
+
try {
|
|
3827
|
+
importStringified =
|
|
3828
|
+
// for same-origin stylesheets,
|
|
3829
|
+
// we can access the imported stylesheet rules directly
|
|
3830
|
+
stringifyStylesheet(rule.styleSheet) ||
|
|
3831
|
+
// work around browser issues with the raw string `@import url(...)` statement
|
|
3832
|
+
escapeImportStatement(rule);
|
|
3833
|
+
}
|
|
3834
|
+
catch (error) {
|
|
3835
|
+
importStringified = rule.cssText;
|
|
3836
|
+
}
|
|
3837
|
+
if (rule.styleSheet.href) {
|
|
3838
|
+
// url()s within the imported stylesheet are relative to _that_ sheet's href
|
|
3839
|
+
return absolutifyURLs(importStringified, rule.styleSheet.href);
|
|
3840
|
+
}
|
|
3841
|
+
return importStringified;
|
|
3842
|
+
}
|
|
3843
|
+
else {
|
|
3844
|
+
let ruleStringified = rule.cssText;
|
|
3845
|
+
if (isCSSStyleRule(rule) && rule.selectorText.includes(':')) {
|
|
3846
|
+
// Safari does not escape selectors with : properly
|
|
3847
|
+
ruleStringified = fixSafariColons(ruleStringified);
|
|
3848
|
+
}
|
|
3849
|
+
if (sheetHref) {
|
|
3850
|
+
return absolutifyURLs(ruleStringified, sheetHref);
|
|
3851
|
+
}
|
|
3852
|
+
return ruleStringified;
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
function fixBrowserCompatibilityIssuesInCSS(cssText) {
|
|
3856
|
+
// Fix for Chrome's handling of webkit-background-clip
|
|
3857
|
+
if (cssText.includes(' background-clip: text;') &&
|
|
3858
|
+
!cssText.includes(' -webkit-background-clip: text;')) {
|
|
3859
|
+
cssText = cssText.replace(/\sbackground-clip:\s*text;/g, ' -webkit-background-clip: text; background-clip: text;');
|
|
3860
|
+
}
|
|
3861
|
+
return cssText;
|
|
3862
|
+
}
|
|
3863
|
+
function escapeImportStatement(rule) {
|
|
3864
|
+
const { cssText } = rule;
|
|
3865
|
+
if (cssText.split('"').length < 3)
|
|
3866
|
+
return cssText;
|
|
3867
|
+
const statement = ['@import', `url(${JSON.stringify(rule.href)})`];
|
|
3868
|
+
if (rule.layerName === '') {
|
|
3869
|
+
statement.push(`layer`);
|
|
3870
|
+
}
|
|
3871
|
+
else if (rule.layerName) {
|
|
3872
|
+
statement.push(`layer(${rule.layerName})`);
|
|
3873
|
+
}
|
|
3874
|
+
if (rule.supportsText) {
|
|
3875
|
+
statement.push(`supports(${rule.supportsText})`);
|
|
3876
|
+
}
|
|
3877
|
+
if (rule.media.length) {
|
|
3878
|
+
statement.push(rule.media.mediaText);
|
|
3879
|
+
}
|
|
3880
|
+
return statement.join(' ') + ';';
|
|
3881
|
+
}
|
|
3882
|
+
function fixSafariColons(cssStringified) {
|
|
3883
|
+
const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
|
|
3884
|
+
return cssStringified.replace(regex, '$1\\$2');
|
|
3885
|
+
}
|
|
3886
|
+
function isCSSImportRule(rule) {
|
|
3887
|
+
return 'styleSheet' in rule;
|
|
3888
|
+
}
|
|
3889
|
+
function isCSSStyleRule(rule) {
|
|
3890
|
+
return 'selectorText' in rule;
|
|
3891
|
+
}
|
|
3892
|
+
function absolutifyURLs(cssText, href) {
|
|
3893
|
+
if (!cssText)
|
|
3894
|
+
return '';
|
|
3895
|
+
const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm;
|
|
3896
|
+
const URL_PROTOCOL_MATCH = /^(?:[a-z+]+:)?\/\//i;
|
|
3897
|
+
const URL_WWW_MATCH = /^www\..*/i;
|
|
3898
|
+
const DATA_URI = /^(data:)([^,]*),(.*)/i;
|
|
3899
|
+
return cssText.replace(URL_IN_CSS_REF, (origin, quote1, path1, quote2, path2, path3) => {
|
|
3900
|
+
const filePath = path1 || path2 || path3;
|
|
3901
|
+
const maybeQuote = quote1 || quote2 || '';
|
|
3902
|
+
if (!filePath) {
|
|
3903
|
+
return origin;
|
|
3904
|
+
}
|
|
3905
|
+
if (URL_PROTOCOL_MATCH.test(filePath) || URL_WWW_MATCH.test(filePath)) {
|
|
3906
|
+
return `url(${maybeQuote}${filePath}${maybeQuote})`;
|
|
3907
|
+
}
|
|
3908
|
+
if (DATA_URI.test(filePath)) {
|
|
3909
|
+
return `url(${maybeQuote}${filePath}${maybeQuote})`;
|
|
3910
|
+
}
|
|
3911
|
+
if (filePath[0] === '/') {
|
|
3912
|
+
return `url(${maybeQuote}${extractOrigin(href) + filePath}${maybeQuote})`;
|
|
3913
|
+
}
|
|
3914
|
+
const stack = href.split('/');
|
|
3915
|
+
const parts = filePath.split('/');
|
|
3916
|
+
stack.pop();
|
|
3917
|
+
for (const part of parts) {
|
|
3918
|
+
if (part === '.') {
|
|
3919
|
+
continue;
|
|
3920
|
+
}
|
|
3921
|
+
else if (part === '..') {
|
|
3922
|
+
stack.pop();
|
|
3923
|
+
}
|
|
3924
|
+
else {
|
|
3925
|
+
stack.push(part);
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
return `url(${maybeQuote}${stack.join('/')}${maybeQuote})`;
|
|
3929
|
+
});
|
|
3930
|
+
}
|
|
3931
|
+
function extractOrigin(url) {
|
|
3932
|
+
let origin = '';
|
|
3933
|
+
if (url.indexOf('//') > -1) {
|
|
3934
|
+
origin = url.split('/').slice(0, 3).join('/');
|
|
3935
|
+
}
|
|
3936
|
+
else {
|
|
3937
|
+
origin = url.split('/')[0];
|
|
3938
|
+
}
|
|
3939
|
+
origin = origin.split('?')[0];
|
|
3940
|
+
return origin;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
function hasAdoptedSS(node) {
|
|
3945
|
+
return (isRootNode(node) &&
|
|
3946
|
+
// @ts-ignore
|
|
3947
|
+
!!node.adoptedStyleSheets);
|
|
3948
|
+
}
|
|
3949
|
+
// TODO: encapsulate to be init-ed on-start and join with cssrules.ts under one folder
|
|
3950
|
+
let _id = 0xf;
|
|
3951
|
+
function nextID() {
|
|
3952
|
+
return _id++;
|
|
3953
|
+
}
|
|
3954
|
+
const styleSheetIDMap = new Map();
|
|
3955
|
+
function ConstructedStyleSheets (app) {
|
|
3956
|
+
if (app === null) {
|
|
3957
|
+
return;
|
|
3958
|
+
}
|
|
3959
|
+
if (!hasAdoptedSS(document)) {
|
|
3960
|
+
return;
|
|
3961
|
+
}
|
|
3962
|
+
const styleSheetIDMap = new Map();
|
|
3963
|
+
const adoptedStyleSheetsOwnings = new Map();
|
|
3964
|
+
const sendAdoptedStyleSheetsUpdate = (root) => setTimeout(() => {
|
|
3965
|
+
let nodeID = app.nodes.getID(root);
|
|
3966
|
+
if (root === document) {
|
|
3967
|
+
nodeID = 0; // main document doesn't have nodeID. ID count starts from the documentElement
|
|
3968
|
+
}
|
|
3969
|
+
if (nodeID === undefined) {
|
|
3970
|
+
return;
|
|
3971
|
+
}
|
|
3972
|
+
let pastOwning = adoptedStyleSheetsOwnings.get(nodeID);
|
|
3973
|
+
if (!pastOwning) {
|
|
3974
|
+
pastOwning = [];
|
|
3975
|
+
}
|
|
3976
|
+
const nowOwning = [];
|
|
3977
|
+
const styleSheets = root.adoptedStyleSheets;
|
|
3978
|
+
if (styleSheets && Symbol.iterator in styleSheets) {
|
|
3979
|
+
for (const s of styleSheets) {
|
|
3980
|
+
let sheetID = styleSheetIDMap.get(s);
|
|
3981
|
+
const init = !sheetID;
|
|
3982
|
+
if (!sheetID) {
|
|
3983
|
+
sheetID = nextID();
|
|
3984
|
+
styleSheetIDMap.set(s, sheetID);
|
|
3985
|
+
}
|
|
3986
|
+
if (!pastOwning.includes(sheetID)) {
|
|
3987
|
+
app.send(AdoptedSSAddOwner(sheetID, nodeID));
|
|
3988
|
+
}
|
|
3989
|
+
if (init) {
|
|
3990
|
+
const rules = s.cssRules;
|
|
3991
|
+
for (let i = 0; i < rules.length; i++) {
|
|
3992
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, rules[i].cssText, i, app.getBaseHref()));
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
nowOwning.push(sheetID);
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
if (Symbol.iterator in pastOwning) {
|
|
3999
|
+
for (const sheetID of pastOwning) {
|
|
4000
|
+
if (!nowOwning.includes(sheetID)) {
|
|
4001
|
+
app.send(AdoptedSSRemoveOwner(sheetID, nodeID));
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
adoptedStyleSheetsOwnings.set(nodeID, nowOwning);
|
|
4006
|
+
}, 20); // Mysterious bug:
|
|
4007
|
+
/* On the page https://explore.fast.design/components/fast-accordion
|
|
4008
|
+
the only rule inside the only adoptedStyleSheet of the iframe-s document
|
|
4009
|
+
gets changed during first milliseconds after the load.
|
|
4010
|
+
However, none of the documented methods (replace, insertRule) is triggered.
|
|
4011
|
+
The rule is not substituted (remains the same object), however the text gets changed.
|
|
4012
|
+
*/
|
|
4013
|
+
function patchAdoptedStyleSheets(prototype) {
|
|
4014
|
+
const nativeAdoptedStyleSheetsDescriptor = Object.getOwnPropertyDescriptor(prototype, 'adoptedStyleSheets');
|
|
4015
|
+
if (nativeAdoptedStyleSheetsDescriptor) {
|
|
4016
|
+
Object.defineProperty(prototype, 'adoptedStyleSheets', {
|
|
4017
|
+
...nativeAdoptedStyleSheetsDescriptor,
|
|
4018
|
+
set: function (value) {
|
|
4019
|
+
// @ts-ignore
|
|
4020
|
+
const retVal = nativeAdoptedStyleSheetsDescriptor.set.call(this, value);
|
|
4021
|
+
sendAdoptedStyleSheetsUpdate(this);
|
|
4022
|
+
return retVal;
|
|
4023
|
+
},
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
const patchContext = (context) => {
|
|
4028
|
+
// @ts-ignore
|
|
4029
|
+
if (context.__openreplay_adpss_patched__) {
|
|
4030
|
+
return;
|
|
4031
|
+
}
|
|
4032
|
+
else {
|
|
4033
|
+
// @ts-ignore
|
|
4034
|
+
context.__openreplay_adpss_patched__ = true;
|
|
4035
|
+
}
|
|
4036
|
+
patchAdoptedStyleSheets(context.Document.prototype);
|
|
4037
|
+
patchAdoptedStyleSheets(context.ShadowRoot.prototype);
|
|
4038
|
+
//@ts-ignore TODO: upgrade ts to 4.8+
|
|
4039
|
+
const { replace, replaceSync } = context.CSSStyleSheet.prototype;
|
|
4040
|
+
//@ts-ignore
|
|
4041
|
+
context.CSSStyleSheet.prototype.replace = function (text) {
|
|
4042
|
+
return replace.call(this, text).then((sheet) => {
|
|
4043
|
+
const sheetID = styleSheetIDMap.get(this);
|
|
4044
|
+
if (sheetID) {
|
|
4045
|
+
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
|
|
4046
|
+
}
|
|
4047
|
+
return sheet;
|
|
4048
|
+
});
|
|
4049
|
+
};
|
|
4050
|
+
//@ts-ignore
|
|
4051
|
+
context.CSSStyleSheet.prototype.replaceSync = function (text) {
|
|
4052
|
+
const sheetID = styleSheetIDMap.get(this);
|
|
4053
|
+
if (sheetID) {
|
|
4054
|
+
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
|
|
4055
|
+
}
|
|
4056
|
+
return replaceSync.call(this, text);
|
|
4057
|
+
};
|
|
4058
|
+
};
|
|
4059
|
+
patchContext(window);
|
|
4060
|
+
app.observer.attachContextCallback(app.safe(patchContext));
|
|
4061
|
+
app.attachStopCallback(() => {
|
|
4062
|
+
styleSheetIDMap.clear();
|
|
4063
|
+
adoptedStyleSheetsOwnings.clear();
|
|
4064
|
+
});
|
|
4065
|
+
// So far main Document is not triggered with nodeCallbacks
|
|
4066
|
+
app.attachStartCallback(() => {
|
|
4067
|
+
sendAdoptedStyleSheetsUpdate(document);
|
|
4068
|
+
});
|
|
4069
|
+
app.nodes.attachNodeCallback((node) => {
|
|
4070
|
+
if (hasAdoptedSS(node)) {
|
|
4071
|
+
sendAdoptedStyleSheetsUpdate(node);
|
|
4072
|
+
}
|
|
4073
|
+
});
|
|
4074
|
+
}
|
|
4075
|
+
|
|
3701
4076
|
const iconCache = {};
|
|
3702
4077
|
const svgUrlCache = {};
|
|
3703
4078
|
async function parseUseEl(useElement, mode, domParser) {
|
|
@@ -3716,7 +4091,7 @@ async function parseUseEl(useElement, mode, domParser) {
|
|
|
3716
4091
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="${symbol.getAttribute('viewBox') || '0 0 24 24'}">
|
|
3717
4092
|
${symbol.innerHTML}
|
|
3718
4093
|
</svg>
|
|
3719
|
-
|
|
4094
|
+
`;
|
|
3720
4095
|
iconCache[symbolId] = inlineSvg;
|
|
3721
4096
|
return inlineSvg;
|
|
3722
4097
|
}
|
|
@@ -3776,7 +4151,7 @@ async function parseUseEl(useElement, mode, domParser) {
|
|
|
3776
4151
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="${symbol.getAttribute('viewBox') || '0 0 24 24'}">
|
|
3777
4152
|
${symbol.innerHTML}
|
|
3778
4153
|
</svg>
|
|
3779
|
-
|
|
4154
|
+
`;
|
|
3780
4155
|
iconCache[symbolId] = inlineSvg;
|
|
3781
4156
|
return inlineSvg;
|
|
3782
4157
|
}
|
|
@@ -3823,7 +4198,7 @@ var RecentsType;
|
|
|
3823
4198
|
RecentsType[RecentsType["Changed"] = 2] = "Changed";
|
|
3824
4199
|
})(RecentsType || (RecentsType = {}));
|
|
3825
4200
|
class Observer {
|
|
3826
|
-
constructor(app, isTopContext = false, options = {
|
|
4201
|
+
constructor(app, isTopContext = false, options = {}) {
|
|
3827
4202
|
this.app = app;
|
|
3828
4203
|
this.isTopContext = isTopContext;
|
|
3829
4204
|
this.commited = [];
|
|
@@ -3832,8 +4207,17 @@ class Observer {
|
|
|
3832
4207
|
this.attributesMap = new Map();
|
|
3833
4208
|
this.textSet = new Set();
|
|
3834
4209
|
this.disableSprites = false;
|
|
4210
|
+
/**
|
|
4211
|
+
* this option means that, instead of using link element with href to load css,
|
|
4212
|
+
* we will try to parse the css text instead and send it as css rules set
|
|
4213
|
+
* can (and will) affect performance
|
|
4214
|
+
* */
|
|
4215
|
+
this.inlineRemoteCss = false;
|
|
4216
|
+
this.inlinerOptions = undefined;
|
|
3835
4217
|
this.domParser = new DOMParser();
|
|
3836
|
-
this.disableSprites = options.disableSprites;
|
|
4218
|
+
this.disableSprites = Boolean(options.disableSprites);
|
|
4219
|
+
this.inlineRemoteCss = Boolean(options.inlineRemoteCss);
|
|
4220
|
+
this.inlinerOptions = options.inlinerOptions;
|
|
3837
4221
|
this.observer = createMutationObserver(this.app.safe((mutations) => {
|
|
3838
4222
|
for (const mutation of mutations) {
|
|
3839
4223
|
// mutations order is sequential
|
|
@@ -3907,10 +4291,12 @@ class Observer {
|
|
|
3907
4291
|
false);
|
|
3908
4292
|
let removed = 0;
|
|
3909
4293
|
const totalBeforeRemove = this.app.nodes.getNodeCount();
|
|
4294
|
+
const contentDocument = iframe.contentDocument;
|
|
4295
|
+
const nodesUnregister = this.app.nodes.unregisterNode.bind(this.app.nodes);
|
|
3910
4296
|
while (walker.nextNode()) {
|
|
3911
|
-
if (!
|
|
4297
|
+
if (!contentDocument.contains(walker.currentNode)) {
|
|
3912
4298
|
removed += 1;
|
|
3913
|
-
|
|
4299
|
+
nodesUnregister(walker.currentNode);
|
|
3914
4300
|
}
|
|
3915
4301
|
}
|
|
3916
4302
|
const removedPercent = Math.floor((removed / totalBeforeRemove) * 100);
|
|
@@ -3922,7 +4308,7 @@ class Observer {
|
|
|
3922
4308
|
}
|
|
3923
4309
|
sendNodeAttribute(id, node, name, value) {
|
|
3924
4310
|
if (isSVGElement(node)) {
|
|
3925
|
-
if (name.
|
|
4311
|
+
if (name.startsWith('xlink:')) {
|
|
3926
4312
|
name = name.substring(6);
|
|
3927
4313
|
}
|
|
3928
4314
|
if (value === null) {
|
|
@@ -3971,6 +4357,23 @@ class Observer {
|
|
|
3971
4357
|
return;
|
|
3972
4358
|
}
|
|
3973
4359
|
if (name === 'style' || (name === 'href' && hasTag(node, 'link'))) {
|
|
4360
|
+
if ('rel' in node && node.rel === 'stylesheet' && this.inlineRemoteCss) {
|
|
4361
|
+
setTimeout(() => {
|
|
4362
|
+
inlineRemoteCss(
|
|
4363
|
+
// @ts-ignore
|
|
4364
|
+
node, id, this.app.getBaseHref(), nextID, (id, cssText, index, baseHref) => {
|
|
4365
|
+
this.app.send(AdoptedSSInsertRuleURLBased(id, cssText, index, baseHref));
|
|
4366
|
+
}, (sheetId, ownerId) => {
|
|
4367
|
+
this.app.send(AdoptedSSAddOwner(sheetId, ownerId));
|
|
4368
|
+
}, this.inlinerOptions?.forceFetch, this.inlinerOptions?.forcePlain, (cssText, fakeTextId) => {
|
|
4369
|
+
this.app.send(CreateTextNode(fakeTextId, id, 0));
|
|
4370
|
+
setTimeout(() => {
|
|
4371
|
+
this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
|
|
4372
|
+
}, 10);
|
|
4373
|
+
});
|
|
4374
|
+
}, 0);
|
|
4375
|
+
return;
|
|
4376
|
+
}
|
|
3974
4377
|
this.app.send(SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
|
|
3975
4378
|
return;
|
|
3976
4379
|
}
|
|
@@ -4050,7 +4453,8 @@ class Observer {
|
|
|
4050
4453
|
if (isRootNode(node)) {
|
|
4051
4454
|
return true;
|
|
4052
4455
|
}
|
|
4053
|
-
|
|
4456
|
+
// @ts-ignore SALESFORCE
|
|
4457
|
+
const parent = node.assignedSlot ? node.assignedSlot : node.parentNode;
|
|
4054
4458
|
let parentID;
|
|
4055
4459
|
// Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
|
|
4056
4460
|
// TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
|
|
@@ -4107,7 +4511,12 @@ class Observer {
|
|
|
4107
4511
|
el.style.width = `${width}px`;
|
|
4108
4512
|
el.style.height = `${height}px`;
|
|
4109
4513
|
}
|
|
4110
|
-
|
|
4514
|
+
if ('rel' in el && el.rel === 'stylesheet' && this.inlineRemoteCss) {
|
|
4515
|
+
this.app.send(CreateElementNode(id, parentID, index, 'STYLE', false));
|
|
4516
|
+
}
|
|
4517
|
+
else {
|
|
4518
|
+
this.app.send(CreateElementNode(id, parentID, index, el.tagName, isSVGElement(node)));
|
|
4519
|
+
}
|
|
4111
4520
|
}
|
|
4112
4521
|
for (let i = 0; i < el.attributes.length; i++) {
|
|
4113
4522
|
const attr = el.attributes[i];
|
|
@@ -4155,12 +4564,12 @@ class Observer {
|
|
|
4155
4564
|
}
|
|
4156
4565
|
commitNodes(isStart = false) {
|
|
4157
4566
|
let node;
|
|
4158
|
-
this.recents.
|
|
4567
|
+
for (const [id, type] of this.recents.entries()) {
|
|
4159
4568
|
this.commitNode(id);
|
|
4160
4569
|
if (type === RecentsType.New && (node = this.app.nodes.getNode(id))) {
|
|
4161
4570
|
this.app.nodes.callNodeCallbacks(node, isStart);
|
|
4162
4571
|
}
|
|
4163
|
-
}
|
|
4572
|
+
}
|
|
4164
4573
|
this.clear();
|
|
4165
4574
|
}
|
|
4166
4575
|
// ISSSUE (nodeToBinde should be the same as node in all cases. Look at the comment about 0-node at the beginning of the file.)
|
|
@@ -4282,12 +4691,20 @@ class IFrameOffsets {
|
|
|
4282
4691
|
}
|
|
4283
4692
|
}
|
|
4284
4693
|
|
|
4694
|
+
var InlineCssMode;
|
|
4695
|
+
(function (InlineCssMode) {
|
|
4696
|
+
InlineCssMode[InlineCssMode["None"] = 0] = "None";
|
|
4697
|
+
InlineCssMode[InlineCssMode["RemoteOnly"] = 1] = "RemoteOnly";
|
|
4698
|
+
InlineCssMode[InlineCssMode["RemoteWithForceFetch"] = 2] = "RemoteWithForceFetch";
|
|
4699
|
+
InlineCssMode[InlineCssMode["All"] = 3] = "All";
|
|
4700
|
+
})(InlineCssMode || (InlineCssMode = {}));
|
|
4285
4701
|
const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
|
|
4286
4702
|
class TopObserver extends Observer {
|
|
4287
4703
|
constructor(params) {
|
|
4288
4704
|
const opts = Object.assign({
|
|
4289
4705
|
captureIFrames: true,
|
|
4290
4706
|
disableSprites: false,
|
|
4707
|
+
inlineCss: 0,
|
|
4291
4708
|
}, params.options);
|
|
4292
4709
|
super(params.app, true, opts);
|
|
4293
4710
|
this.iframeOffsets = new IFrameOffsets();
|
|
@@ -4338,7 +4755,7 @@ class TopObserver extends Observer {
|
|
|
4338
4755
|
this.app.debug.info('doc already observed for', id);
|
|
4339
4756
|
return;
|
|
4340
4757
|
}
|
|
4341
|
-
const observer = new IFrameObserver(this.app);
|
|
4758
|
+
const observer = new IFrameObserver(this.app, false, {});
|
|
4342
4759
|
this.iframeObservers.set(iframe, observer);
|
|
4343
4760
|
this.docObservers.set(currentDoc, observer);
|
|
4344
4761
|
this.iframeObserversArr.push(observer);
|
|
@@ -4709,6 +5126,43 @@ function getTimezone() {
|
|
|
4709
5126
|
return `UTC${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
4710
5127
|
}
|
|
4711
5128
|
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
5129
|
+
function getInlineOptions(mode) {
|
|
5130
|
+
switch (mode) {
|
|
5131
|
+
case InlineCssMode.RemoteOnly:
|
|
5132
|
+
return {
|
|
5133
|
+
inlineRemoteCss: true,
|
|
5134
|
+
inlinerOptions: {
|
|
5135
|
+
forceFetch: false,
|
|
5136
|
+
forcePlain: false,
|
|
5137
|
+
},
|
|
5138
|
+
};
|
|
5139
|
+
case InlineCssMode.RemoteWithForceFetch:
|
|
5140
|
+
return {
|
|
5141
|
+
inlineRemoteCss: true,
|
|
5142
|
+
inlinerOptions: {
|
|
5143
|
+
forceFetch: true,
|
|
5144
|
+
forcePlain: false,
|
|
5145
|
+
},
|
|
5146
|
+
};
|
|
5147
|
+
case InlineCssMode.All:
|
|
5148
|
+
return {
|
|
5149
|
+
inlineRemoteCss: true,
|
|
5150
|
+
inlinerOptions: {
|
|
5151
|
+
forceFetch: true,
|
|
5152
|
+
forcePlain: true,
|
|
5153
|
+
},
|
|
5154
|
+
};
|
|
5155
|
+
case InlineCssMode.None:
|
|
5156
|
+
default:
|
|
5157
|
+
return {
|
|
5158
|
+
inlineRemoteCss: false,
|
|
5159
|
+
inlinerOptions: {
|
|
5160
|
+
forceFetch: false,
|
|
5161
|
+
forcePlain: false,
|
|
5162
|
+
},
|
|
5163
|
+
};
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
4712
5166
|
const proto = {
|
|
4713
5167
|
// ask if there are any tabs alive
|
|
4714
5168
|
ask: 'never-gonna-give-you-up',
|
|
@@ -4740,7 +5194,7 @@ class App {
|
|
|
4740
5194
|
this.stopCallbacks = [];
|
|
4741
5195
|
this.commitCallbacks = [];
|
|
4742
5196
|
this.activityState = ActivityState.NotActive;
|
|
4743
|
-
this.version = '16.
|
|
5197
|
+
this.version = '16.2.0'; // TODO: version compatability check inside each plugin.
|
|
4744
5198
|
this.socketMode = false;
|
|
4745
5199
|
this.compressionThreshold = 24 * 1000;
|
|
4746
5200
|
this.bc = null;
|
|
@@ -4973,6 +5427,30 @@ class App {
|
|
|
4973
5427
|
}
|
|
4974
5428
|
};
|
|
4975
5429
|
this.startTimeout = null;
|
|
5430
|
+
this.send = (message, urgent = false) => {
|
|
5431
|
+
if (this.activityState === ActivityState.NotActive) {
|
|
5432
|
+
return;
|
|
5433
|
+
}
|
|
5434
|
+
// ====================================================
|
|
5435
|
+
if (this.activityState === ActivityState.ColdStart) {
|
|
5436
|
+
this.bufferedMessages1.push(message);
|
|
5437
|
+
if (!this.singleBuffer) {
|
|
5438
|
+
this.bufferedMessages2.push(message);
|
|
5439
|
+
}
|
|
5440
|
+
this.conditionsManager?.processMessage(message);
|
|
5441
|
+
}
|
|
5442
|
+
else {
|
|
5443
|
+
this.messages.push(message);
|
|
5444
|
+
}
|
|
5445
|
+
// TODO: commit on start if there were `urgent` sends;
|
|
5446
|
+
// Clarify where urgent can be used for;
|
|
5447
|
+
// Clarify workflow for each type of message in case it was sent before start
|
|
5448
|
+
// (like Fetch before start; maybe add an option "preCapture: boolean" or sth alike)
|
|
5449
|
+
// Careful: `this.delay` is equal to zero before start so all Timestamp-s will have to be updated on start
|
|
5450
|
+
if (this.activityState === ActivityState.Active && urgent) {
|
|
5451
|
+
this.commit();
|
|
5452
|
+
}
|
|
5453
|
+
};
|
|
4976
5454
|
this.coldStartCommitN = 0;
|
|
4977
5455
|
this.delay = 0;
|
|
4978
5456
|
this.attachStartCallback = (cb, useSafe = false) => {
|
|
@@ -5039,6 +5517,7 @@ class App {
|
|
|
5039
5517
|
this.onUxtCb = [];
|
|
5040
5518
|
this.contextId = Math.random().toString(36).slice(2);
|
|
5041
5519
|
this.projectKey = projectKey;
|
|
5520
|
+
this.inlineCss = getInlineOptions(options.inlineCss ?? 0);
|
|
5042
5521
|
if (Object.keys(options).findIndex((k) => ['fixedCanvasScaling', 'disableCanvas'].includes(k)) !==
|
|
5043
5522
|
-1) {
|
|
5044
5523
|
console.warn('Openreplay: canvas options are moving to separate key "canvas" in next update. Please update your configuration.');
|
|
@@ -5068,15 +5547,14 @@ class App {
|
|
|
5068
5547
|
__save_canvas_locally: false,
|
|
5069
5548
|
localStorage: null,
|
|
5070
5549
|
sessionStorage: null,
|
|
5071
|
-
disableStringDict: false,
|
|
5072
5550
|
forceSingleTab: false,
|
|
5073
5551
|
assistSocketHost: '',
|
|
5074
5552
|
fixedCanvasScaling: false,
|
|
5075
5553
|
disableCanvas: false,
|
|
5076
5554
|
captureIFrames: true,
|
|
5077
|
-
|
|
5078
|
-
obscureTextEmails: true,
|
|
5555
|
+
obscureTextEmails: false,
|
|
5079
5556
|
obscureTextNumbers: false,
|
|
5557
|
+
disableStringDict: false,
|
|
5080
5558
|
crossdomain: {
|
|
5081
5559
|
parentDomain: '*',
|
|
5082
5560
|
},
|
|
@@ -5087,6 +5565,8 @@ class App {
|
|
|
5087
5565
|
useAnimationFrame: false,
|
|
5088
5566
|
},
|
|
5089
5567
|
forceNgOff: false,
|
|
5568
|
+
inlineCss: 0,
|
|
5569
|
+
disableSprites: false,
|
|
5090
5570
|
};
|
|
5091
5571
|
this.options = simpleMerge(defaultOptions, options);
|
|
5092
5572
|
if (!this.insideIframe &&
|
|
@@ -5108,7 +5588,7 @@ class App {
|
|
|
5108
5588
|
forceNgOff: Boolean(options.forceNgOff),
|
|
5109
5589
|
maintainer: this.options.nodes?.maintainer,
|
|
5110
5590
|
});
|
|
5111
|
-
this.observer = new TopObserver({ app: this, options });
|
|
5591
|
+
this.observer = new TopObserver({ app: this, options: { ...options, ...this.inlineCss } });
|
|
5112
5592
|
this.ticker = new Ticker(this);
|
|
5113
5593
|
this.ticker.attach(() => this.commit());
|
|
5114
5594
|
this.debug = new Logger(this.options.__debug__);
|
|
@@ -5325,30 +5805,6 @@ class App {
|
|
|
5325
5805
|
}
|
|
5326
5806
|
this.debug.error('OpenReplay error: ', context, e);
|
|
5327
5807
|
}
|
|
5328
|
-
send(message, urgent = false) {
|
|
5329
|
-
if (this.activityState === ActivityState.NotActive) {
|
|
5330
|
-
return;
|
|
5331
|
-
}
|
|
5332
|
-
// ====================================================
|
|
5333
|
-
if (this.activityState === ActivityState.ColdStart) {
|
|
5334
|
-
this.bufferedMessages1.push(message);
|
|
5335
|
-
if (!this.singleBuffer) {
|
|
5336
|
-
this.bufferedMessages2.push(message);
|
|
5337
|
-
}
|
|
5338
|
-
this.conditionsManager?.processMessage(message);
|
|
5339
|
-
}
|
|
5340
|
-
else {
|
|
5341
|
-
this.messages.push(message);
|
|
5342
|
-
}
|
|
5343
|
-
// TODO: commit on start if there were `urgent` sends;
|
|
5344
|
-
// Clarify where urgent can be used for;
|
|
5345
|
-
// Clarify workflow for each type of message in case it was sent before start
|
|
5346
|
-
// (like Fetch before start; maybe add an option "preCapture: boolean" or sth alike)
|
|
5347
|
-
// Careful: `this.delay` is equal to zero before start so all Timestamp-s will have to be updated on start
|
|
5348
|
-
if (this.activityState === ActivityState.Active && urgent) {
|
|
5349
|
-
this.commit();
|
|
5350
|
-
}
|
|
5351
|
-
}
|
|
5352
5808
|
/**
|
|
5353
5809
|
* Normal workflow: add timestamp and tab data to batch, then commit it
|
|
5354
5810
|
* every ~30ms
|
|
@@ -7589,138 +8045,6 @@ function Viewport (app) {
|
|
|
7589
8045
|
app.ticker.attach(sendSetViewportSize, 5, false);
|
|
7590
8046
|
}
|
|
7591
8047
|
|
|
7592
|
-
function hasAdoptedSS(node) {
|
|
7593
|
-
return (isRootNode(node) &&
|
|
7594
|
-
// @ts-ignore
|
|
7595
|
-
!!node.adoptedStyleSheets);
|
|
7596
|
-
}
|
|
7597
|
-
// TODO: encapsulate to be init-ed on-start and join with cssrules.ts under one folder
|
|
7598
|
-
let _id = 0xf;
|
|
7599
|
-
function nextID() {
|
|
7600
|
-
return _id++;
|
|
7601
|
-
}
|
|
7602
|
-
const styleSheetIDMap = new Map();
|
|
7603
|
-
function ConstructedStyleSheets (app) {
|
|
7604
|
-
if (app === null) {
|
|
7605
|
-
return;
|
|
7606
|
-
}
|
|
7607
|
-
if (!hasAdoptedSS(document)) {
|
|
7608
|
-
return;
|
|
7609
|
-
}
|
|
7610
|
-
const styleSheetIDMap = new Map();
|
|
7611
|
-
const adoptedStyleSheetsOwnings = new Map();
|
|
7612
|
-
const sendAdoptedStyleSheetsUpdate = (root) => setTimeout(() => {
|
|
7613
|
-
let nodeID = app.nodes.getID(root);
|
|
7614
|
-
if (root === document) {
|
|
7615
|
-
nodeID = 0; // main document doesn't have nodeID. ID count starts from the documentElement
|
|
7616
|
-
}
|
|
7617
|
-
if (nodeID === undefined) {
|
|
7618
|
-
return;
|
|
7619
|
-
}
|
|
7620
|
-
let pastOwning = adoptedStyleSheetsOwnings.get(nodeID);
|
|
7621
|
-
if (!pastOwning) {
|
|
7622
|
-
pastOwning = [];
|
|
7623
|
-
}
|
|
7624
|
-
const nowOwning = [];
|
|
7625
|
-
const styleSheets = root.adoptedStyleSheets;
|
|
7626
|
-
if (styleSheets && Symbol.iterator in styleSheets) {
|
|
7627
|
-
for (const s of styleSheets) {
|
|
7628
|
-
let sheetID = styleSheetIDMap.get(s);
|
|
7629
|
-
const init = !sheetID;
|
|
7630
|
-
if (!sheetID) {
|
|
7631
|
-
sheetID = nextID();
|
|
7632
|
-
styleSheetIDMap.set(s, sheetID);
|
|
7633
|
-
}
|
|
7634
|
-
if (!pastOwning.includes(sheetID)) {
|
|
7635
|
-
app.send(AdoptedSSAddOwner(sheetID, nodeID));
|
|
7636
|
-
}
|
|
7637
|
-
if (init) {
|
|
7638
|
-
const rules = s.cssRules;
|
|
7639
|
-
for (let i = 0; i < rules.length; i++) {
|
|
7640
|
-
app.send(AdoptedSSInsertRuleURLBased(sheetID, rules[i].cssText, i, app.getBaseHref()));
|
|
7641
|
-
}
|
|
7642
|
-
}
|
|
7643
|
-
nowOwning.push(sheetID);
|
|
7644
|
-
}
|
|
7645
|
-
}
|
|
7646
|
-
if (Symbol.iterator in pastOwning) {
|
|
7647
|
-
for (const sheetID of pastOwning) {
|
|
7648
|
-
if (!nowOwning.includes(sheetID)) {
|
|
7649
|
-
app.send(AdoptedSSRemoveOwner(sheetID, nodeID));
|
|
7650
|
-
}
|
|
7651
|
-
}
|
|
7652
|
-
}
|
|
7653
|
-
adoptedStyleSheetsOwnings.set(nodeID, nowOwning);
|
|
7654
|
-
}, 20); // Mysterious bug:
|
|
7655
|
-
/* On the page https://explore.fast.design/components/fast-accordion
|
|
7656
|
-
the only rule inside the only adoptedStyleSheet of the iframe-s document
|
|
7657
|
-
gets changed during first milliseconds after the load.
|
|
7658
|
-
However, none of the documented methods (replace, insertRule) is triggered.
|
|
7659
|
-
The rule is not substituted (remains the same object), however the text gets changed.
|
|
7660
|
-
*/
|
|
7661
|
-
function patchAdoptedStyleSheets(prototype) {
|
|
7662
|
-
const nativeAdoptedStyleSheetsDescriptor = Object.getOwnPropertyDescriptor(prototype, 'adoptedStyleSheets');
|
|
7663
|
-
if (nativeAdoptedStyleSheetsDescriptor) {
|
|
7664
|
-
Object.defineProperty(prototype, 'adoptedStyleSheets', {
|
|
7665
|
-
...nativeAdoptedStyleSheetsDescriptor,
|
|
7666
|
-
set: function (value) {
|
|
7667
|
-
// @ts-ignore
|
|
7668
|
-
const retVal = nativeAdoptedStyleSheetsDescriptor.set.call(this, value);
|
|
7669
|
-
sendAdoptedStyleSheetsUpdate(this);
|
|
7670
|
-
return retVal;
|
|
7671
|
-
},
|
|
7672
|
-
});
|
|
7673
|
-
}
|
|
7674
|
-
}
|
|
7675
|
-
const patchContext = (context) => {
|
|
7676
|
-
// @ts-ignore
|
|
7677
|
-
if (context.__openreplay_adpss_patched__) {
|
|
7678
|
-
return;
|
|
7679
|
-
}
|
|
7680
|
-
else {
|
|
7681
|
-
// @ts-ignore
|
|
7682
|
-
context.__openreplay_adpss_patched__ = true;
|
|
7683
|
-
}
|
|
7684
|
-
patchAdoptedStyleSheets(context.Document.prototype);
|
|
7685
|
-
patchAdoptedStyleSheets(context.ShadowRoot.prototype);
|
|
7686
|
-
//@ts-ignore TODO: upgrade ts to 4.8+
|
|
7687
|
-
const { replace, replaceSync } = context.CSSStyleSheet.prototype;
|
|
7688
|
-
//@ts-ignore
|
|
7689
|
-
context.CSSStyleSheet.prototype.replace = function (text) {
|
|
7690
|
-
return replace.call(this, text).then((sheet) => {
|
|
7691
|
-
const sheetID = styleSheetIDMap.get(this);
|
|
7692
|
-
if (sheetID) {
|
|
7693
|
-
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
|
|
7694
|
-
}
|
|
7695
|
-
return sheet;
|
|
7696
|
-
});
|
|
7697
|
-
};
|
|
7698
|
-
//@ts-ignore
|
|
7699
|
-
context.CSSStyleSheet.prototype.replaceSync = function (text) {
|
|
7700
|
-
const sheetID = styleSheetIDMap.get(this);
|
|
7701
|
-
if (sheetID) {
|
|
7702
|
-
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()));
|
|
7703
|
-
}
|
|
7704
|
-
return replaceSync.call(this, text);
|
|
7705
|
-
};
|
|
7706
|
-
};
|
|
7707
|
-
patchContext(window);
|
|
7708
|
-
app.observer.attachContextCallback(app.safe(patchContext));
|
|
7709
|
-
app.attachStopCallback(() => {
|
|
7710
|
-
styleSheetIDMap.clear();
|
|
7711
|
-
adoptedStyleSheetsOwnings.clear();
|
|
7712
|
-
});
|
|
7713
|
-
// So far main Document is not triggered with nodeCallbacks
|
|
7714
|
-
app.attachStartCallback(() => {
|
|
7715
|
-
sendAdoptedStyleSheetsUpdate(document);
|
|
7716
|
-
});
|
|
7717
|
-
app.nodes.attachNodeCallback((node) => {
|
|
7718
|
-
if (hasAdoptedSS(node)) {
|
|
7719
|
-
sendAdoptedStyleSheetsUpdate(node);
|
|
7720
|
-
}
|
|
7721
|
-
});
|
|
7722
|
-
}
|
|
7723
|
-
|
|
7724
8048
|
function CSSRules (app) {
|
|
7725
8049
|
if (app === null) {
|
|
7726
8050
|
return;
|
|
@@ -8027,152 +8351,6 @@ function isObject(thing) {
|
|
|
8027
8351
|
return thing !== null && typeof thing === 'object';
|
|
8028
8352
|
}
|
|
8029
8353
|
|
|
8030
|
-
const sensitiveParams = new Set([
|
|
8031
|
-
"password",
|
|
8032
|
-
"pass",
|
|
8033
|
-
"pwd",
|
|
8034
|
-
"mdp",
|
|
8035
|
-
"token",
|
|
8036
|
-
"bearer",
|
|
8037
|
-
"jwt",
|
|
8038
|
-
"api_key",
|
|
8039
|
-
"api-key",
|
|
8040
|
-
"apiKey",
|
|
8041
|
-
"secret",
|
|
8042
|
-
"ssn",
|
|
8043
|
-
"zip",
|
|
8044
|
-
"zipcode",
|
|
8045
|
-
"x-api-key",
|
|
8046
|
-
"www-authenticate",
|
|
8047
|
-
"x-csrf-token",
|
|
8048
|
-
"x-requested-with",
|
|
8049
|
-
"x-forwarded-for",
|
|
8050
|
-
"x-real-ip",
|
|
8051
|
-
"cookie",
|
|
8052
|
-
"authorization",
|
|
8053
|
-
"auth",
|
|
8054
|
-
"proxy-authorization",
|
|
8055
|
-
"set-cookie",
|
|
8056
|
-
"account_key",
|
|
8057
|
-
]);
|
|
8058
|
-
function numDigits(x) {
|
|
8059
|
-
return (Math.log10((x ^ (x >> 31)) - (x >> 31)) | 0) + 1;
|
|
8060
|
-
}
|
|
8061
|
-
function obscure(value) {
|
|
8062
|
-
if (typeof value === "number") {
|
|
8063
|
-
const digits = numDigits(value);
|
|
8064
|
-
return "9".repeat(digits);
|
|
8065
|
-
}
|
|
8066
|
-
if (typeof value === "string") {
|
|
8067
|
-
return value.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
8068
|
-
}
|
|
8069
|
-
return value;
|
|
8070
|
-
}
|
|
8071
|
-
function filterHeaders(headers) {
|
|
8072
|
-
const filteredHeaders = {};
|
|
8073
|
-
if (Array.isArray(headers)) {
|
|
8074
|
-
headers.forEach(({ name, value }) => {
|
|
8075
|
-
if (sensitiveParams.has(name.toLowerCase())) {
|
|
8076
|
-
filteredHeaders[name] = obscure(value);
|
|
8077
|
-
}
|
|
8078
|
-
else {
|
|
8079
|
-
filteredHeaders[name] = value;
|
|
8080
|
-
}
|
|
8081
|
-
});
|
|
8082
|
-
}
|
|
8083
|
-
else {
|
|
8084
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
8085
|
-
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8086
|
-
filteredHeaders[key] = obscure(value);
|
|
8087
|
-
}
|
|
8088
|
-
else {
|
|
8089
|
-
filteredHeaders[key] = value;
|
|
8090
|
-
}
|
|
8091
|
-
}
|
|
8092
|
-
}
|
|
8093
|
-
return filteredHeaders;
|
|
8094
|
-
}
|
|
8095
|
-
function filterBody(body) {
|
|
8096
|
-
if (!body) {
|
|
8097
|
-
return body;
|
|
8098
|
-
}
|
|
8099
|
-
let parsedBody;
|
|
8100
|
-
let isJSON = false;
|
|
8101
|
-
try {
|
|
8102
|
-
parsedBody = JSON.parse(body);
|
|
8103
|
-
isJSON = true;
|
|
8104
|
-
}
|
|
8105
|
-
catch (e) {
|
|
8106
|
-
// not json
|
|
8107
|
-
}
|
|
8108
|
-
if (isJSON) {
|
|
8109
|
-
obscureSensitiveData(parsedBody);
|
|
8110
|
-
return JSON.stringify(parsedBody);
|
|
8111
|
-
}
|
|
8112
|
-
else {
|
|
8113
|
-
const isUrlSearch = typeof body === "string" && body.includes("?") && body.includes("=");
|
|
8114
|
-
if (isUrlSearch) {
|
|
8115
|
-
try {
|
|
8116
|
-
const params = new URLSearchParams(body);
|
|
8117
|
-
for (const key of params.keys()) {
|
|
8118
|
-
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8119
|
-
const value = obscure(params.get(key));
|
|
8120
|
-
params.set(key, value);
|
|
8121
|
-
}
|
|
8122
|
-
}
|
|
8123
|
-
return params.toString();
|
|
8124
|
-
}
|
|
8125
|
-
catch (e) {
|
|
8126
|
-
// not url query ?
|
|
8127
|
-
return body;
|
|
8128
|
-
}
|
|
8129
|
-
}
|
|
8130
|
-
else {
|
|
8131
|
-
// not json or url query
|
|
8132
|
-
return body;
|
|
8133
|
-
}
|
|
8134
|
-
}
|
|
8135
|
-
}
|
|
8136
|
-
function sanitizeObject(obj) {
|
|
8137
|
-
obscureSensitiveData(obj);
|
|
8138
|
-
return obj;
|
|
8139
|
-
}
|
|
8140
|
-
function obscureSensitiveData(obj) {
|
|
8141
|
-
if (Array.isArray(obj)) {
|
|
8142
|
-
obj.forEach(obscureSensitiveData);
|
|
8143
|
-
}
|
|
8144
|
-
else if (obj && typeof obj === "object") {
|
|
8145
|
-
for (const key in obj) {
|
|
8146
|
-
if (Object.hasOwn(obj, key)) {
|
|
8147
|
-
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8148
|
-
obj[key] = obscure(obj[key]);
|
|
8149
|
-
}
|
|
8150
|
-
else if (obj[key] !== null && typeof obj[key] === "object") {
|
|
8151
|
-
obscureSensitiveData(obj[key]);
|
|
8152
|
-
}
|
|
8153
|
-
}
|
|
8154
|
-
}
|
|
8155
|
-
}
|
|
8156
|
-
}
|
|
8157
|
-
function tryFilterUrl(url) {
|
|
8158
|
-
if (!url)
|
|
8159
|
-
return "";
|
|
8160
|
-
try {
|
|
8161
|
-
const urlObj = new URL(url);
|
|
8162
|
-
if (urlObj.searchParams) {
|
|
8163
|
-
for (const key of urlObj.searchParams.keys()) {
|
|
8164
|
-
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8165
|
-
urlObj.searchParams.set(key, "******");
|
|
8166
|
-
}
|
|
8167
|
-
}
|
|
8168
|
-
}
|
|
8169
|
-
return urlObj.toString();
|
|
8170
|
-
}
|
|
8171
|
-
catch (e) {
|
|
8172
|
-
return url;
|
|
8173
|
-
}
|
|
8174
|
-
}
|
|
8175
|
-
|
|
8176
8354
|
/**
|
|
8177
8355
|
* I know we're not using most of the information from this class
|
|
8178
8356
|
* but it can be useful in the future if we will decide to display more stuff in our ui
|
|
@@ -8204,18 +8382,13 @@ class NetworkMessage {
|
|
|
8204
8382
|
}
|
|
8205
8383
|
getMessage() {
|
|
8206
8384
|
const { reqHs, resHs } = this.writeHeaders();
|
|
8207
|
-
const reqBody = this.method === 'GET'
|
|
8208
|
-
? JSON.stringify(sanitizeObject(this.getData)) : filterBody(this.requestData);
|
|
8209
8385
|
const request = {
|
|
8210
|
-
headers:
|
|
8211
|
-
body:
|
|
8212
|
-
};
|
|
8213
|
-
const response = {
|
|
8214
|
-
headers: filterHeaders(resHs),
|
|
8215
|
-
body: filterBody(this.response)
|
|
8386
|
+
headers: reqHs,
|
|
8387
|
+
body: this.method === 'GET' ? JSON.stringify(this.getData) : this.requestData,
|
|
8216
8388
|
};
|
|
8389
|
+
const response = { headers: resHs, body: this.response };
|
|
8217
8390
|
const messageInfo = this.sanitize({
|
|
8218
|
-
url:
|
|
8391
|
+
url: this.url,
|
|
8219
8392
|
method: this.method,
|
|
8220
8393
|
status: this.status,
|
|
8221
8394
|
request,
|
|
@@ -8531,10 +8704,9 @@ class ResponseProxyHandler {
|
|
|
8531
8704
|
if (typeof this.resp.body.getReader !== 'function') {
|
|
8532
8705
|
return;
|
|
8533
8706
|
}
|
|
8534
|
-
const
|
|
8535
|
-
const _getReader = clonedResp.body.getReader;
|
|
8707
|
+
const _getReader = this.resp.body.getReader;
|
|
8536
8708
|
// @ts-ignore
|
|
8537
|
-
|
|
8709
|
+
this.resp.body.getReader = () => {
|
|
8538
8710
|
const reader = _getReader.apply(this.resp.body);
|
|
8539
8711
|
// when readyState is already 4,
|
|
8540
8712
|
// it's not a chunked stream, or it had already been read.
|
|
@@ -9376,7 +9548,7 @@ class API {
|
|
|
9376
9548
|
this.signalStartIssue = (reason, missingApi) => {
|
|
9377
9549
|
const doNotTrack = this.checkDoNotTrack();
|
|
9378
9550
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
9379
|
-
trackerVersion: '16.
|
|
9551
|
+
trackerVersion: '16.2.0',
|
|
9380
9552
|
projectKey: this.options.projectKey,
|
|
9381
9553
|
doNotTrack,
|
|
9382
9554
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|