Skip to content

Commit 9af8ef4

Browse files
committed
Refactor scriptlet injection code
- Eliminate scriptlet-related race conditions in Firefox - Reduce scriptlet injection delay at browser launch in Chromium - Eliminate scriptlet-related spurious CSP violations in Firefox - Related issue: uBlockOrigin/uBlock-issues#4061 - Fix issues related to cached ancestor-based scriptlets (the `>>##`-related scriptlets in "uBO Lite Test Page")
1 parent 01092d9 commit 9af8ef4

28 files changed

Lines changed: 809 additions & 901 deletions
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*******************************************************************************
2+
3+
uBlock Origin - a comprehensive, efficient content blocker
4+
Copyright (C) 2026-present Raymond Hill
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see {http://www.gnu.org/licenses/}.
18+
19+
Home: https://github.com/gorhill/uBlock
20+
*/
21+
22+
/* global browser */
23+
24+
/******************************************************************************/
25+
26+
function uBO_isolatedCode(mainCode) {
27+
if ( self.vapiScripting ) { return 0; }
28+
self.vapiScripting = true;
29+
30+
(function isolatedCode(){})();
31+
32+
if ( mainCode ) {
33+
let script;
34+
try {
35+
const doc = document;
36+
script = doc.createElement('script');
37+
script.appendChild(doc.createTextNode(mainCode));
38+
(doc.head || doc.documentElement).appendChild(script);
39+
} catch {
40+
}
41+
if ( script ) {
42+
script.remove();
43+
script.textContent = '';
44+
}
45+
}
46+
47+
return 0;
48+
}
49+
50+
function uBO_assembleIsolatedCode(details) {
51+
const { isolatedCode = '', mainCode = '' } = details;
52+
if ( Boolean(isolatedCode) === false ) {
53+
if ( Boolean(mainCode) === false ) { return; }
54+
}
55+
let code = [ '(function uBO_isolatedWorldWrapper() {' ];
56+
if ( details.earlyBailoutCode ) {
57+
code.push(details.earlyBailoutCode);
58+
}
59+
code.push(`(${uBO_isolatedCode.toString()})(`, JSON.stringify(mainCode), `);`);
60+
code.push('})();');
61+
code = code.join('\n');
62+
const match = /\(function isolatedCode\(\)\{\}\)\(\);/.exec(code);
63+
return code.slice(0, match.index) +
64+
isolatedCode +
65+
code.slice(match.index + match[0].length);
66+
}
67+
68+
/******************************************************************************/
69+
70+
let contentScriptCode;
71+
72+
let onceFn = async ( ) => {
73+
if ( Boolean(contentScriptCode) === false ) { return; }
74+
const tabs = await vAPI.tabs.query({ url: '' });
75+
for ( const tab of tabs ) {
76+
if ( tab.discarded === true ) { continue; }
77+
if ( tab.status === 'unloaded' ) { continue; }
78+
browser.tabs.executeScript(tab.id, {
79+
allFrames: true,
80+
matchAboutBlank: true,
81+
runAt: 'document_start',
82+
code: contentScriptCode,
83+
}).catch(( ) => {
84+
});
85+
}
86+
};
87+
88+
export function registerContentScripts(details) {
89+
contentScriptCode = uBO_assembleIsolatedCode(details);
90+
if ( onceFn === undefined ) { return; }
91+
onceFn();
92+
onceFn = undefined;
93+
}
94+
95+
export function unregisterContentScripts() {
96+
contentScriptCode = undefined;
97+
}
98+
99+
/******************************************************************************/
100+
101+
browser.webNavigation.onCommitted.addListener(details => {
102+
if ( /^https?:|^about:/.test(details.url) === false ) { return; }
103+
if ( Boolean(contentScriptCode) === false ) { return; }
104+
browser.tabs.executeScript(details.tabId, {
105+
frameId: details.frameId,
106+
matchAboutBlank: true,
107+
runAt: 'document_start',
108+
code: contentScriptCode,
109+
}).catch(( ) => {
110+
});
111+
});

‎platform/firefox/vapi-scripting.js‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*******************************************************************************
2+
3+
uBlock Origin - a comprehensive, efficient content blocker
4+
Copyright (C) 2026-present Raymond Hill
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see {http://www.gnu.org/licenses/}.
18+
19+
Home: https://github.com/gorhill/uBlock
20+
*/
21+
22+
/* global browser */
23+
24+
/******************************************************************************/
25+
26+
function uBO_assembleCode(world, contentScriptCode, details) {
27+
if ( Boolean(contentScriptCode) === false ) { return; }
28+
const code = [ `(function uBO_${world}WorldWrapper() {` ];
29+
if ( details.earlyBailoutCode ) {
30+
code.push(details.earlyBailoutCode);
31+
}
32+
code.push(contentScriptCode);
33+
code.push('})();');
34+
return code.join('\n');
35+
}
36+
37+
/******************************************************************************/
38+
39+
export function registerContentScripts(details) {
40+
unregisterContentScripts();
41+
const isolatedCode = uBO_assembleCode('isolated', details.isolatedCode, details);
42+
if ( isolatedCode ) {
43+
registerContentScripts.isolatedHandle = browser.contentScripts.register({
44+
allFrames: true,
45+
js: [ { code: isolatedCode } ],
46+
matchAboutBlank: true,
47+
matches: [ 'http://*/*', 'https://*/*' ],
48+
runAt: 'document_start',
49+
world: 'ISOLATED',
50+
}).catch(( ) => {
51+
registerContentScripts.isolatedHandle = undefined;
52+
});
53+
}
54+
const mainCode = uBO_assembleCode('main', details.mainCode, details);
55+
if ( mainCode ) {
56+
registerContentScripts.mainHandle = browser.contentScripts.register({
57+
allFrames: true,
58+
js: [ { code: mainCode } ],
59+
matchAboutBlank: true,
60+
matches: [ 'http://*/*', 'https://*/*' ],
61+
runAt: 'document_start',
62+
world: 'MAIN',
63+
}).catch(( ) => {
64+
registerContentScripts.mainHandle = undefined;
65+
});
66+
}
67+
}
68+
69+
export function unregisterContentScripts() {
70+
if ( registerContentScripts.isolatedHandle ) {
71+
registerContentScripts.isolatedHandle.then(handle => {
72+
if ( Boolean(handle) === false ) { return; }
73+
handle.unregister();
74+
});
75+
registerContentScripts.isolatedHandle = undefined;
76+
}
77+
if ( registerContentScripts.mainHandle ) {
78+
registerContentScripts.mainHandle.then(handle => {
79+
if ( Boolean(handle) === false ) { return; }
80+
handle.unregister();
81+
});
82+
registerContentScripts.mainHandle = undefined;
83+
}
84+
}

‎src/js/background.js‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
Home: https://github.com/gorhill/uBlock
2020
*/
2121

22+
/* global browser */
23+
2224
/******************************************************************************/
2325

2426
import {
@@ -28,6 +30,7 @@ import {
2830
} from './uri-utils.js';
2931

3032
import { FilteringContext } from './filtering-context.js';
33+
import { TrustedSiteManager } from './trusted-sites.js';
3134
import logger from './logger.js';
3235
import { ubologSet } from './console.js';
3336

@@ -62,8 +65,8 @@ const hiddenSettingsDefault = {
6265
cnameReplayFullURL: false,
6366
consoleLogLevel: 'unset',
6467
debugAssetsJson: false,
65-
debugScriptlets: false,
66-
debugScriptletInjector: false,
68+
debugMainScriptlets: false,
69+
debugIsolatedScriptlets: false,
6770
differentialUpdate: true,
6871
disableWebAssembly: false,
6972
dnsCacheTTL: 600,
@@ -165,10 +168,9 @@ const µBlock = { // jshint ignore:line
165168
canFilterResponseData: typeof browser.webRequest.filterResponseData === 'function',
166169

167170
// https://github.com/chrisaljoudi/uBlock/issues/180
168-
// Whitelist directives need to be loaded once the PSL is available
169-
netWhitelist: new Map(),
170-
netWhitelistModifyTime: 0,
171-
netWhitelistDefault: [
171+
// Trusted-site directives need to be loaded once the PSL is available
172+
trustedSites: new TrustedSiteManager(),
173+
defaultTrustedSiteDirectives: [
172174
'chrome-extension-scheme',
173175
'moz-extension-scheme',
174176
],

‎src/js/commands.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const relaxBlockingMode = (( ) => {
3939

4040
const normalURL = µb.normalizeTabURL(tab.id, tab.url);
4141

42-
if ( µb.getNetFilteringSwitch(normalURL) === false ) { return; }
42+
if ( µb.isTrustedSite(normalURL) ) { return; }
4343

4444
const hn = hostnameFromURI(normalURL);
4545
const curProfileBits = µb.blockingModeFromHostname(hn);

‎src/js/contentscript.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1320,7 +1320,7 @@ vAPI.DOMFilterer = class {
13201320
vAPI.messaging.send('contentscript', {
13211321
what: 'retrieveContentScriptParameters',
13221322
url: vAPI.effectiveSelf.location.href,
1323-
needScriptlets: self.uBO_scriptletsInjected === undefined,
1323+
realURL: document.location.href,
13241324
}).then(response => {
13251325
onResponseReady(response);
13261326
});

‎src/js/contextmenu.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ const update = function(tabId = undefined) {
203203
let newBits = 0;
204204
if ( µb.userSettings.contextMenuEnabled ) {
205205
const pageStore = tabId && µb.pageStoreFromTabId(tabId) || null;
206-
if ( pageStore?.getNetFilteringSwitch() ) {
206+
if ( pageStore?.isNotTrusted() ) {
207207
if ( µb.userFiltersAreEnabled() ) {
208208
if ( pageStore.shouldApplySpecificCosmeticFilters(0) ) {
209209
newBits |= BLOCK_ELEMENT_BIT;

‎src/js/dynamic-net-filtering.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,20 @@ class DynamicHostRuleFiltering {
458458
return false;
459459
}
460460

461+
export1stPartyRules() {
462+
const out = [];
463+
const bitoffset = typeBitOffsets['*'];
464+
for ( const [ srcdes, bits ] of this.rules ) {
465+
const match = /^(\S+) (\S+)/.exec(srcdes);
466+
if ( match === null ) { continue; }
467+
if ( match[1] !== match[2] && match[2] !== '*' ) { continue; }
468+
const value = (bits >>> bitoffset) & 0b11;
469+
if ( value === 0 ) { continue; }
470+
out.push([ match[1], value === 2 ]);
471+
}
472+
return out;
473+
}
474+
461475
toSelfie() {
462476
return {
463477
magicId: this.magicId,

platform/mv3/extension/js/offscreen/make-scriptlets.js renamed to src/js/make-scriptlets.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,11 @@ export function commit(rulesetId, template) {
216216
);
217217
content = safeReplace(content,
218218
'self.$scriptletArglistRefs$',
219-
`/* ${hostnames.length} */ ${JSON.stringify(hostnames.map(a => a[1]).join(';'))}`
219+
`/* ${hostnames.length} */ ${JSON.stringify(hostnames.map(a => a[1]))}`
220220
);
221221
content = safeReplace(content,
222222
'self.$scriptletArglists$',
223-
`/* ${arglists.size} */ ${JSON.stringify(Array.from(arglists.keys()).join(';'))}`
223+
`/* ${arglists.size} */ ${JSON.stringify(Array.from(arglists.keys()))}`
224224
);
225225
content = safeReplace(content,
226226
'self.$scriptletArgs$',

platform/mv3/extension/js/offscreen/make-utils.js renamed to src/js/make-utils.js

File renamed without changes.

0 commit comments

Comments
 (0)