diff --git a/platform/mv3/extension/js/action.js b/platform/mv3/extension/js/action.js index 811fe101c..7e194dd08 100644 --- a/platform/mv3/extension/js/action.js +++ b/platform/mv3/extension/js/action.js @@ -19,8 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -import { matchesFromHostnames, strArrayEq } from './utils.js'; import { browser } from './ext.js'; +import { matchesFromHostnames } from './utils.js'; /******************************************************************************/ @@ -91,9 +91,6 @@ export async function registerToolbarIconToggler(context) { if ( toToggle.size === 0 ) { return; } - const registered = context.before.get('toolbar-icon'); - context.before.delete('toolbar-icon'); // Important! - const directive = { id: 'toolbar-icon', js: [ '/js/scripting/toolbar-icon.js' ], @@ -101,10 +98,5 @@ export async function registerToolbarIconToggler(context) { runAt: 'document_start', }; - if ( registered === undefined ) { - context.toAdd.push(directive); - } else if ( strArrayEq(registered.matches, directive.matches) === false ) { - context.toRemove.push('toolbar-icon'); - context.toAdd.push(directive); - } + context.toAdd.push(directive); } diff --git a/platform/mv3/extension/js/filter-manager.js b/platform/mv3/extension/js/filter-manager.js index c7deaab93..b1ec9464d 100644 --- a/platform/mv3/extension/js/filter-manager.js +++ b/platform/mv3/extension/js/filter-manager.js @@ -30,7 +30,6 @@ import { import { intersectHostnameIters, matchesFromHostnames, - strArrayEq, subtractHostnameIters, } from './utils.js'; @@ -189,9 +188,6 @@ export async function registerCustomFilters(context) { } if ( hostnames.length === 0 ) { return; } - const registered = context.before.get('css-user'); - context.before.delete('css-user'); // Important! - const directive = { id: 'css-user', js: [ '/js/scripting/css-user.js' ], @@ -199,12 +195,7 @@ export async function registerCustomFilters(context) { runAt: 'document_start', }; - if ( registered === undefined ) { - context.toAdd.push(directive); - } else if ( strArrayEq(registered.matches, directive.matches) === false ) { - context.toRemove.push('css-user'); - context.toAdd.push(directive); - } + context.toAdd.push(directive); } /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 338c0cdfc..6e5aa13f1 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -71,29 +71,6 @@ const normalizeMatches = matches => { /******************************************************************************/ -// The extensions API does not always return exactly what we fed it, so we -// need to normalize some entries to be sure we properly detect changes when -// comparing registered entries vs. entries to register. - -const normalizeRegisteredContentScripts = registered => { - for ( const entry of registered ) { - const { css = [], js = [] } = entry; - for ( let i = 0; i < css.length; i++ ) { - const path = css[i]; - if ( path.startsWith('/') ) { continue; } - css[i] = `/${path}`; - } - for ( let i = 0; i < js.length; i++ ) { - const path = js[i]; - if ( path.startsWith('/') ) { continue; } - js[i] = `/${path}`; - } - } - return registered; -}; - -/******************************************************************************/ - async function resetCSSCache() { const keys = await sessionKeys(); return Promise.all( @@ -104,7 +81,7 @@ async function resetCSSCache() { /******************************************************************************/ function registerHighGeneric(context, genericDetails) { - const { before, filteringModeDetails, rulesetsDetails } = context; + const { filteringModeDetails, rulesetsDetails } = context; const excludeHostnames = []; const includeHostnames = []; @@ -145,12 +122,8 @@ function registerHighGeneric(context, genericDetails) { ) ); } - if ( matches.length === 0 ) { return; } - const registered = before.get('css-generichigh'); - before.delete('css-generichigh'); // Important! - // https://github.com/w3c/webextensions/issues/414#issuecomment-1623992885 // Once supported, add: // cssOrigin: 'USER', @@ -166,26 +139,13 @@ function registerHighGeneric(context, genericDetails) { } // register - if ( registered === undefined ) { - context.toAdd.push(directive); - return; - } - - // update - if ( - ut.strArrayEq(registered.css, css, false) === false || - ut.strArrayEq(registered.matches, matches) === false || - ut.strArrayEq(registered.excludeMatches, excludeMatches) === false - ) { - context.toRemove.push('css-generichigh'); - context.toAdd.push(directive); - } + context.toAdd.push(directive); } /******************************************************************************/ function registerGeneric(context, genericDetails) { - const { before, filteringModeDetails, rulesetsDetails } = context; + const { filteringModeDetails, rulesetsDetails } = context; const excludedByFilter = []; const includedByFilter = []; @@ -224,8 +184,6 @@ function registerGeneric(context, genericDetails) { ), ]; if ( matches.length === 0 ) { return; } - const registered = before.get('css-generic-some'); - before.delete('css-generic-some'); // Important! const directive = { id: 'css-generic-some', js, @@ -233,15 +191,7 @@ function registerGeneric(context, genericDetails) { matches, runAt: 'document_idle', }; - if ( registered === undefined ) { // register - context.toAdd.push(directive); - } else if ( // update - ut.strArrayEq(registered.js, js, false) === false || - ut.strArrayEq(registered.matches, directive.matches) === false - ) { - context.toRemove.push('css-generic-some'); - context.toAdd.push(directive); - } + context.toAdd.push(directive); return; } @@ -249,8 +199,6 @@ function registerGeneric(context, genericDetails) { ...ut.matchesFromHostnames(excludedByMode), ...ut.matchesFromHostnames(excludedByFilter), ]; - const registeredAll = before.get('css-generic-all'); - before.delete('css-generic-all'); // Important! const directiveAll = { id: 'css-generic-all', js, @@ -261,24 +209,14 @@ function registerGeneric(context, genericDetails) { if ( excludeMatches.length !== 0 ) { directiveAll.excludeMatches = excludeMatches; } + context.toAdd.push(directiveAll); - if ( registeredAll === undefined ) { // register - context.toAdd.push(directiveAll); - } else if ( // update - ut.strArrayEq(registeredAll.js, js, false) === false || - ut.strArrayEq(registeredAll.excludeMatches, directiveAll.excludeMatches) === false - ) { - context.toRemove.push('css-generic-all'); - context.toAdd.push(directiveAll); - } const matches = [ ...ut.matchesFromHostnames( ut.subtractHostnameIters(includedByFilter, excludedByMode) ), ]; if ( matches.length === 0 ) { return; } - const registeredSome = before.get('css-generic-some'); - before.delete('css-generic-some'); // Important! const directiveSome = { id: 'css-generic-some', js, @@ -286,21 +224,13 @@ function registerGeneric(context, genericDetails) { matches, runAt: 'document_idle', }; - if ( registeredSome === undefined ) { // register - context.toAdd.push(directiveSome); - } else if ( // update - ut.strArrayEq(registeredSome.js, js, false) === false || - ut.strArrayEq(registeredSome.matches, directiveSome.matches) === false - ) { - context.toRemove.push('css-generic-some'); - context.toAdd.push(directiveSome); - } + context.toAdd.push(directiveSome); } /******************************************************************************/ async function registerCosmetic(realm, context) { - const { before, filteringModeDetails, rulesetsDetails } = context; + const { filteringModeDetails, rulesetsDetails } = context; { const keys = await localKeys(); @@ -355,9 +285,6 @@ async function registerCosmetic(realm, context) { } } - const registered = before.get(realmid); - before.delete(realmid); // Important! - const directive = { id: realmid, js, @@ -370,26 +297,13 @@ async function registerCosmetic(realm, context) { } // register - if ( registered === undefined ) { - context.toAdd.push(directive); - return; - } - - // update - if ( - ut.strArrayEq(registered.js, js, false) === false || - ut.strArrayEq(registered.matches, matches) === false || - ut.strArrayEq(registered.excludeMatches, excludeMatches) === false - ) { - context.toRemove.push(realmid); - context.toAdd.push(directive); - } + context.toAdd.push(directive); } /******************************************************************************/ function registerScriptlet(context, scriptletDetails) { - const { before, filteringModeDetails, rulesetsDetails } = context; + const { filteringModeDetails, rulesetsDetails } = context; const hasBroadHostPermission = filteringModeDetails.optimal.has('all-urls') || @@ -431,9 +345,6 @@ function registerScriptlet(context, scriptletDetails) { matches.push(...ut.matchesFromHostnames(targetHostnames)); normalizeMatches(matches); - const registered = before.get(id); - before.delete(id); // Important! - const directive = { id, js: [ `/rulesets/scripting/scriptlet/${world.toLowerCase()}/${rulesetId}.js` ], @@ -448,19 +359,7 @@ function registerScriptlet(context, scriptletDetails) { } // register - if ( registered === undefined ) { - context.toAdd.push(directive); - continue; - } - - // update - if ( - ut.strArrayEq(registered.matches, matches) === false || - ut.strArrayEq(registered.excludeMatches, excludeMatches) === false - ) { - context.toRemove.push(id); - context.toAdd.push(directive); - } + context.toAdd.push(directive); } } } @@ -481,26 +380,17 @@ export async function registerInjectables() { rulesetsDetails, scriptletDetails, genericDetails, - registered, ] = await Promise.all([ getFilteringModeDetails(), getEnabledRulesetsDetails(), getScriptletDetails(), getGenericDetails(), - browser.scripting.getRegisteredContentScripts(), ]); - const before = new Map( - normalizeRegisteredContentScripts(registered).map( - entry => [ entry.id, entry ] - ) - ); - const toAdd = [], toRemove = []; + const toAdd = []; const context = { filteringModeDetails, rulesetsDetails, - before, toAdd, - toRemove, }; await Promise.all([ @@ -513,23 +403,17 @@ export async function registerInjectables() { registerToolbarIconToggler(context), ]); - toRemove.push(...Array.from(before.keys())); - - if ( toRemove.length !== 0 ) { - ubolLog(`Unregistered ${toRemove} content (css/js)`); - try { - await browser.scripting.unregisterContentScripts({ ids: toRemove }); - localRemove('$scripting.unregisterContentScripts'); - } catch(reason) { - ubolErr(`unregisterContentScripts/${reason}`); - } + ubolLog(`Unregistered all content (css/js)`); + try { + await browser.scripting.unregisterContentScripts(); + } catch(reason) { + ubolErr(`unregisterContentScripts/${reason}`); } if ( toAdd.length !== 0 ) { ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); try { await browser.scripting.registerContentScripts(toAdd); - localRemove('$scripting.registerContentScripts'); } catch(reason) { ubolErr(`registerContentScripts/${reason}`); } diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index be095c48b..9fe21bae7 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -24,6 +24,46 @@ export const webext = self.browser; /******************************************************************************/ +// Workaround for: +// https://github.com/uBlockOrigin/uBOL-home/issues/515 +// https://bugs.webkit.org/show_bug.cgi?id=300236 +// +// For each realm, we will force-reload registered rulesets once. + +const { windows } = webext; +const NORMAL_REALM = 0b01; +const PRIVATE_REALM = 0b10; +const ALL_REALMS = NORMAL_REALM | PRIVATE_REALM; + +let seenRealms = 0b00; +let seenRealmsReady = webext.storage.session.get('safari.seenRealms').then(bin => { + seenRealms |= bin?.['safari.seenRealms'] ?? 0; +}).catch(( ) => { +}); + +async function forceEnableRulesets(windowId) { + await seenRealmsReady; + if ( seenRealms === ALL_REALMS ) { return; } + if ( windowId === windows.WINDOW_ID_NONE ) { return; } + const details = await windows.get(windowId, { windowTypes: [ 'normal' ] }); + const incognito = details?.incognito; + if ( typeof incognito !== 'boolean' ) { return; } + const currentRealm = incognito ? PRIVATE_REALM : NORMAL_REALM; + if ( (seenRealms & currentRealm) !== 0 ) { return; } + seenRealms |= currentRealm; + webext.storage.session.set({ 'safari.seenRealms': seenRealms }); + const ids = await nativeDNR.getEnabledRulesets(); + if ( ids.length === 0 ) { return; } + nativeDNR.updateEnabledRulesets({ + disableRulesetIds: ids.slice(), + enableRulesetIds: ids.slice(), + }); +} + +windows.onFocusChanged.addListener(forceEnableRulesets); + +/******************************************************************************/ + // https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest/ const nativeDNR = webext.declarativeNetRequest; @@ -142,8 +182,10 @@ export const dnr = { if ( optionsAfter === undefined ) { return; } return nativeDNR.updateDynamicRules(optionsAfter); }, - updateEnabledRulesets(...args) { - return nativeDNR.updateEnabledRulesets(...args); + async updateEnabledRulesets(...args) { + await nativeDNR.updateEnabledRulesets(...args); + seenRealms = 0b00; + await webext.storage.session.remove('safari.seenRealms'); }, async updateSessionRules(optionsBefore) { const optionsAfter = prepareUpdateRules(optionsBefore); @@ -181,44 +223,3 @@ export const dnr = { return nativeDNR.setExtensionActionOptions(...args); }, }; - -/******************************************************************************/ - -// Workaround for: -// https://github.com/uBlockOrigin/uBOL-home/issues/515 -// https://bugs.webkit.org/show_bug.cgi?id=300236 -// -// For each realm, we will force-reload registered rulesets once. - -const { windows } = webext; -const NORMAL_REALM = 0b01; -const PRIVATE_REALM = 0b10; -const ALL_REALMS = NORMAL_REALM | PRIVATE_REALM; -let seenRealms = 0b00; - -async function forceEnableRulesets(windowId) { - await seenRealmsReady; - if ( seenRealms === ALL_REALMS ) { return; } - if ( windowId === windows.WINDOW_ID_NONE ) { return; } - const details = await windows.get(windowId, { windowTypes: [ 'normal' ] }); - const incognito = details?.incognito; - if ( typeof incognito !== 'boolean' ) { return; } - const currentRealm = incognito ? PRIVATE_REALM : NORMAL_REALM; - if ( (seenRealms & currentRealm) !== 0 ) { return; } - seenRealms |= currentRealm; - webext.storage.session.set({ 'safari.seenRealms': seenRealms }); - const ids = await nativeDNR.getEnabledRulesets(); - if ( ids.length === 0 ) { return; } - nativeDNR.updateEnabledRulesets({ - disableRulesetIds: ids.slice(), - enableRulesetIds: ids.slice(), - }); -} - -windows.onFocusChanged.addListener(forceEnableRulesets); - -const seenRealmsReady = webext.storage.session.get('safari.seenRealms').then(bin => { - seenRealms |= bin?.['safari.seenRealms'] ?? 0; -}).catch(( ) => { -}); -