Skip to content

Commit d7df476

Browse files
Share one safeSelf() cache between the scriptlets in a frame.
Context: Each scriptlet has its own copy of safeSelf(). It saves the functions that it finds when it first runs, and those were often already the wrappers that earlier scriptlets had installed, not the browser's own functions. So each hook called all earlier hooks again. With six hooks on JSON.stringify (as on youtube.com), one JSON.stringify call from the page became 2^6=64 native calls. The extension gives each scriptlet its own copy of scriptletGlobals, so the first scriptlet in a frame (and world) now keeps the cache on globalThis, and the later scriptlets use it. The property is read-only, and its key has the uBO version, so other versions do not share it. This works for all injection paths (registered scripts and executeScript), without changes in the extension. refs ghostery/ghostery-extension#3672
1 parent 44cbeb1 commit d7df476

2 files changed

Lines changed: 734 additions & 1 deletion

File tree

‎build.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ for (const scriptlet of scriptlets) {
1818
}
1919
}
2020

21+
// uBO runs all scriptlets of a page in one scope, so they share one safeSelf() cache. Our scriptlets are separate functions, so they share it on globalThis.
22+
// Else each hook captures the earlier hooks as natives, and calls grow as 2^n. The uBO version is in the key, so other versions do not share it.
23+
const safeSelf = index.get('safe-self.fn').fn;
24+
const shareSafeSelf = `try {
25+
const key = Symbol.for('safeSelf.${tagName}');
26+
safeSelf.safe = globalThis[key];
27+
if ( safeSelf.safe === undefined ) {
28+
Object.defineProperty(globalThis, key, { value: safeSelf() });
29+
}
30+
} catch {
31+
}
32+
`;
33+
2134
console.log(`
2235
/*******************************************************************************
2336
@@ -67,7 +80,7 @@ requiresTrust: ${scriptlet.requiresTrust || false},
6780
func: function (scriptletGlobals = {}, ...args) {
6881
${deps.map((dep) => dep.toString()).join('\n')}
6982
${scriptlet.fn.toString()};
70-
${scriptlet.fn.name}(...args);
83+
${deps.includes(safeSelf) ? shareSafeSelf : ''}${scriptlet.fn.name}(...args);
7184
},
7285
};
7386
`;

0 commit comments

Comments
 (0)