diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 6b8a815c3..4d9c644c5 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -55,8 +55,6 @@ import { import { broadcastMessage, - gotoURL, - hasBroadHostPermissions, hostnameFromMatch, hostnamesFromMatches, } from './utils.js'; @@ -101,6 +99,11 @@ import { ubolLog, } from './debug.js'; +import { + gotoURL, + hasBroadHostPermissions, +} from './ext-utils.js'; + import { dnr } from './ext-compat.js'; import { toggleToolbarIcon } from './action.js'; @@ -662,12 +665,14 @@ async function startSession() { } // Permissions may have been removed while the extension was disabled - await syncWithBrowserPermissions(); + const permissionsUpdated = await syncWithBrowserPermissions(); // Unsure whether the browser remembers correctly registered css/scripts // after we quit the browser. For now uBOL will check unconditionally at // launch time whether content css/scripts are properly registered. - registerInjectables(); + if ( isNewVersion || permissionsUpdated ) { + registerInjectables(); + } // Cosmetic filtering-related content scripts cache fitlering data in // session storage. diff --git a/platform/mv3/extension/js/ext-compat.js b/platform/mv3/extension/js/ext-compat.js index e5f66f042..5391c8962 100644 --- a/platform/mv3/extension/js/ext-compat.js +++ b/platform/mv3/extension/js/ext-compat.js @@ -19,21 +19,13 @@ Home: https://github.com/gorhill/uBlock */ +import { deepEquals } from './utils.js'; + export const webext = self.browser || self.chrome; export const dnr = webext.declarativeNetRequest || {}; /******************************************************************************/ -const ruleCompare = (a, b) => a.id - b.id; - -const isSameRules = (a, b) => { - a.sort(ruleCompare); - b.sort(ruleCompare); - return JSON.stringify(a) === JSON.stringify(b); -}; - -/******************************************************************************/ - export function normalizeDNRRules(rules, ruleIds) { if ( Array.isArray(rules) === false ) { return rules; } return Array.isArray(ruleIds) @@ -85,19 +77,23 @@ dnr.setAllowAllRules = async function(id, allowed, notAllowed, reverse, priority } addSessionRules.push(rule1); } - if ( isSameRules(addDynamicRules, beforeDynamicRules) ) { return false; } - return Promise.all([ - dnr.updateDynamicRules({ - addRules: addDynamicRules, - removeRuleIds: beforeDynamicRules.map(r => r.id), - }), - dnr.updateSessionRules({ - addRules: addSessionRules, - removeRuleIds: beforeSessionRules.map(r => r.id), - }), - ]).then(( ) => - true - ).catch(( ) => - false - ); + const promises = []; + const modified = deepEquals(addDynamicRules, beforeDynamicRules) === false; + if ( modified ) { + promises.push( + dnr.updateDynamicRules({ + addRules: addDynamicRules, + removeRuleIds: beforeDynamicRules.map(r => r.id), + }) + ); + } + if ( deepEquals(addSessionRules, beforeSessionRules) === false ) { + promises.push( + dnr.updateSessionRules({ + addRules: addSessionRules, + removeRuleIds: beforeSessionRules.map(r => r.id), + }) + ); + } + return Promise.all(promises).then(( ) => modified).catch(( ) => false); }; diff --git a/platform/mv3/extension/js/ext-utils.js b/platform/mv3/extension/js/ext-utils.js new file mode 100644 index 000000000..4ac7142e4 --- /dev/null +++ b/platform/mv3/extension/js/ext-utils.js @@ -0,0 +1,68 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { + browser, + runtime, +} from './ext.js'; + +/******************************************************************************/ + +// https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/manifest.json/host_permissions#requested_permissions_and_user_prompts +// "Users can grant or revoke host permissions on an ad hoc basis. Therefore, +// most browsers treat host_permissions as optional." + +export async function hasBroadHostPermissions() { + return browser.permissions.getAll().then(permissions => + permissions.origins.includes('') || + permissions.origins.includes('*://*/*') + ).catch(( ) => false); +} + +/******************************************************************************/ + +export async function gotoURL(url, type) { + const pageURL = new URL(url, runtime.getURL('/')); + const tabs = await browser.tabs.query({ + url: pageURL.href, + windowType: type !== 'popup' ? 'normal' : 'popup' + }); + + if ( Array.isArray(tabs) && tabs.length !== 0 ) { + const { windowId, id } = tabs[0]; + return Promise.all([ + browser.windows.update(windowId, { focused: true }), + browser.tabs.update(id, { active: true }), + ]); + } + + if ( type === 'popup' ) { + return browser.windows.create({ + type: 'popup', + url: pageURL.href, + }); + } + + return browser.tabs.create({ + active: true, + url: pageURL.href, + }); +} diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index f30649c42..8a7831a0d 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -21,7 +21,6 @@ import { broadcastMessage, - hasBroadHostPermissions, hostnamesFromMatches, isDescendantHostnameOfIter, toBroaderHostname, @@ -40,6 +39,8 @@ import { import { adminReadEx } from './admin.js'; import { filteringModesToDNR } from './ruleset-manager.js'; +import { hasBroadHostPermissions } from './ext-utils.js'; + /******************************************************************************/ diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 3758d7d25..8d59d250b 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -36,7 +36,7 @@ import { ubolErr, ubolLog } from './debug.js'; import { dnr } from './ext-compat.js'; import { fetchJSON } from './fetch.js'; import { getAdminRulesets } from './admin.js'; -import { hasBroadHostPermissions } from './utils.js'; +import { hasBroadHostPermissions } from './ext-utils.js'; import { rulesFromText } from './dnr-parser.js'; /******************************************************************************/ diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index e9ebee601..2fdd02eea 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -19,11 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -import { - browser, - runtime, -} from './ext.js'; - /******************************************************************************/ function parsedURLromOrigin(origin) { @@ -135,53 +130,40 @@ export const hostnamesFromMatches = origins => { /******************************************************************************/ -const broadcastMessage = message => { - const bc = new self.BroadcastChannel('uBOL'); - bc.postMessage(message); +export const deepEquals = (a, b) => { + switch ( typeof a ) { + case 'undefined': + case 'boolean': + case 'number': + case 'string': + return a === b; + } + // case 'object': + if ( typeof b !== 'object' ) { return false; } + if ( a === null || b === null ) { return a === b; } + if ( Array.isArray(a) || Array.isArray(b) ) { + if ( Array.isArray(a) === false || Array.isArray(b) === false ) { return false; } + if ( a.length !== b.length ) { return false; } + for ( let i = 0; i < a.length; i++ ) { + if ( deepEquals(a[i], b[i]) === false ) { return false; } + } + return true; + } + const akeys = Object.keys(a); + const bkeys = Object.keys(b); + if ( akeys.length !== bkeys.length ) { return false; } + for ( const k of akeys ) { + if ( deepEquals(a[k], b[k]) === false ) { return false; } + } + return true; }; /******************************************************************************/ -// https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/manifest.json/host_permissions#requested_permissions_and_user_prompts -// "Users can grant or revoke host permissions on an ad hoc basis. Therefore, -// most browsers treat host_permissions as optional." - -async function hasBroadHostPermissions() { - return browser.permissions.getAll().then(permissions => - permissions.origins.includes('') || - permissions.origins.includes('*://*/*') - ).catch(( ) => false); -} - -/******************************************************************************/ - -async function gotoURL(url, type) { - const pageURL = new URL(url, runtime.getURL('/')); - const tabs = await browser.tabs.query({ - url: pageURL.href, - windowType: type !== 'popup' ? 'normal' : 'popup' - }); - - if ( Array.isArray(tabs) && tabs.length !== 0 ) { - const { windowId, id } = tabs[0]; - return Promise.all([ - browser.windows.update(windowId, { focused: true }), - browser.tabs.update(id, { active: true }), - ]); - } - - if ( type === 'popup' ) { - return browser.windows.create({ - type: 'popup', - url: pageURL.href, - }); - } - - return browser.tabs.create({ - active: true, - url: pageURL.href, - }); -} +const broadcastMessage = message => { + const bc = new self.BroadcastChannel('uBOL'); + bc.postMessage(message); +}; /******************************************************************************/ @@ -220,7 +202,5 @@ export { isDescendantHostnameOfIter, intersectHostnameIters, subtractHostnameIters, - hasBroadHostPermissions, - gotoURL, strArrayEq, }; diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index 9fe21bae7..47ce82c8a 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -19,6 +19,7 @@ Home: https://github.com/gorhill/uBlock */ +import { deepEquals } from './utils.js'; export const webext = self.browser; @@ -114,14 +115,6 @@ const prepareUpdateRules = optionsBefore => { return optionsAfter; }; -const ruleCompare = (a, b) => a.id - b.id; - -const isSameRules = (a, b) => { - a.sort(ruleCompare); - b.sort(ruleCompare); - return JSON.stringify(a) === JSON.stringify(b); -}; - /******************************************************************************/ export function normalizeDNRRules(rules, ruleIds) { @@ -209,7 +202,7 @@ export const dnr = { } addRules.push(rule0); } - if ( isSameRules(addRules, beforeRules) ) { return false; } + if ( deepEquals(addRules, beforeRules) ) { return false; } return this.updateDynamicRules({ addRules, removeRuleIds: beforeRules.map(r => r.id),