diff --git a/src/js/contentscript.js b/src/js/contentscript.js index aaf996249..38f184c82 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -1275,12 +1275,21 @@ vAPI.DOMFilterer = class { vAPI.domCollapser.start(); - if ( response.noCosmeticFiltering ) { + const { + noSpecificCosmeticFiltering, + noGenericCosmeticFiltering, + scriptlets, + } = response; + + vAPI.noSpecificCosmeticFiltering = noSpecificCosmeticFiltering; + vAPI.noGenericCosmeticFiltering = noGenericCosmeticFiltering; + + if ( noSpecificCosmeticFiltering && noGenericCosmeticFiltering ) { vAPI.domFilterer = null; vAPI.domSurveyor = null; } else { const domFilterer = vAPI.domFilterer = new vAPI.DOMFilterer(); - if ( response.noGenericCosmeticFiltering || cfeDetails.noDOMSurveying ) { + if ( noGenericCosmeticFiltering || cfeDetails.noDOMSurveying ) { vAPI.domSurveyor = null; } domFilterer.exceptions = cfeDetails.exceptionFilters; @@ -1293,9 +1302,9 @@ vAPI.DOMFilterer = class { // Library of resources is located at: // https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt - if ( response.scriptlets ) { - vAPI.injectScriptlet(document, response.scriptlets); - vAPI.injectedScripts = response.scriptlets; + if ( scriptlets ) { + vAPI.injectScriptlet(document, scriptlets); + vAPI.injectedScripts = scriptlets; } if ( vAPI.domSurveyor instanceof Object ) { diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js index 75ec7d36c..12190a430 100644 --- a/src/js/contextmenu.js +++ b/src/js/contextmenu.js @@ -114,6 +114,9 @@ const onEntryClicked = function(details, tab) { if ( details.menuItemId === 'uBlock0-blockElementInFrame' ) { return onBlockElementInFrame(details, tab); } + if ( details.menuItemId === 'uBlock0-blockResource' ) { + return onBlockElement(details, tab); + } if ( details.menuItemId === 'uBlock0-subscribeToList' ) { return onSubscribeToList(details); } @@ -128,23 +131,28 @@ const menuEntries = { blockElement: { id: 'uBlock0-blockElement', title: vAPI.i18n('pickerContextMenuEntry'), - contexts: ['all'], + contexts: [ 'all' ], }, blockElementInFrame: { id: 'uBlock0-blockElementInFrame', title: vAPI.i18n('contextMenuBlockElementInFrame'), - contexts: ['frame'], + contexts: [ 'frame' ], + }, + blockResource: { + id: 'uBlock0-blockResource', + title: vAPI.i18n('pickerContextMenuEntry'), + contexts: [ 'audio', 'frame', 'image', 'video' ], }, subscribeToList: { id: 'uBlock0-subscribeToList', title: vAPI.i18n('contextMenuSubscribeToList'), - contexts: ['link'], + contexts: [ 'link' ], targetUrlPatterns: [ 'abp:*' ], }, temporarilyAllowLargeMediaElements: { id: 'uBlock0-temporarilyAllowLargeMediaElements', title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'), - contexts: ['all'], + contexts: [ 'all' ], } }; @@ -155,25 +163,35 @@ let currentBits = 0; const update = function(tabId = undefined) { let newBits = 0; if ( µBlock.userSettings.contextMenuEnabled && tabId !== undefined ) { - let pageStore = µBlock.pageStoreFromTabId(tabId); + const pageStore = µBlock.pageStoreFromTabId(tabId); if ( pageStore && pageStore.getNetFilteringSwitch() ) { - newBits |= 0x01; + if ( pageStore.shouldApplySpecificCosmeticFilters(0) ) { + newBits |= 0b0001; + } else { + newBits |= 0b0010; + } if ( pageStore.largeMediaCount !== 0 ) { - newBits |= 0x02; + newBits |= 0b0100; } } + newBits |= 0b1000; } if ( newBits === currentBits ) { return; } currentBits = newBits; - let usedEntries = []; - if ( newBits & 0x01 ) { + const usedEntries = []; + if ( newBits & 0b0001 ) { usedEntries.push(menuEntries.blockElement); usedEntries.push(menuEntries.blockElementInFrame); - usedEntries.push(menuEntries.subscribeToList); } - if ( newBits & 0x02 ) { + if ( newBits & 0b0010 ) { + usedEntries.push(menuEntries.blockResource); + } + if ( newBits & 0b0100 ) { usedEntries.push(menuEntries.temporarilyAllowLargeMediaElements); } + if ( newBits & 0b1000 ) { + usedEntries.push(menuEntries.subscribeToList); + } vAPI.contextMenu.setEntries(usedEntries, onEntryClicked); }; diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index bb4340ec1..ce77a89b7 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -966,7 +966,10 @@ FilterContainer.prototype.retrieveSpecificSelectors = function( }; const injectedCSS = []; - if ( options.noCosmeticFiltering !== true ) { + if ( + options.noSpecificCosmeticFiltering !== true || + options.noGenericCosmeticFiltering !== true + ) { const injectedHideFilters = []; const specificSet = this.$specificSet; const proceduralSet = this.$proceduralSet; @@ -1161,7 +1164,7 @@ FilterContainer.prototype.benchmark = async function() { entity: '', }; const options = { - noCosmeticFiltering: false, + noSpecificCosmeticFiltering: false, noGenericCosmeticFiltering: false, }; let count = 0; diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 5e5ca7539..2dd738d2f 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -696,7 +696,7 @@ const viewPort = (( ) => { if ( filteringType === 'static' ) { divcl.add('canLookup'); } else if ( details.realm === 'extended' ) { - divcl.add('canLookup'); + divcl.toggle('canLookup', /^#@?#/.test(filter.raw)); divcl.toggle('isException', filter.raw.startsWith('#@#')); } if ( filter.modifier === true ) { diff --git a/src/js/messaging.js b/src/js/messaging.js index 1dce1fe31..bd311a15d 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -555,73 +555,23 @@ const retrieveContentScriptParameters = async function(sender, request) { } const loggerEnabled = µb.logger.enabled; - const noCosmeticFiltering = pageStore.noCosmeticFiltering === true; + const noSpecificCosmeticFiltering = + pageStore.shouldApplySpecificCosmeticFilters(frameId) === false; + const noGenericCosmeticFiltering = + pageStore.shouldApplyGenericCosmeticFilters(frameId) === false; const response = { collapseBlocked: µb.userSettings.collapseBlocked, - noCosmeticFiltering, - noGenericCosmeticFiltering: noCosmeticFiltering, - noSpecificCosmeticFiltering: noCosmeticFiltering, + noGenericCosmeticFiltering, + noSpecificCosmeticFiltering, }; - // https://github.com/uBlockOrigin/uAssets/issues/5704 - // `generichide` must be evaluated in the frame context. - if ( noCosmeticFiltering === false ) { - const genericHide = - µb.staticNetFilteringEngine.matchStringReverse( - 'generichide', - request.url - ); - response.noGenericCosmeticFiltering = genericHide === 2; - if ( loggerEnabled && genericHide !== 0 ) { - µBlock.filteringContext - .duplicate() - .fromTabId(tabId) - .setURL(request.url) - .setRealm('network') - .setType('generichide') - .setFilter(µb.staticNetFilteringEngine.toLogData()) - .toLogger(); - } - } - request.tabId = tabId; request.frameId = frameId; request.hostname = µb.URI.hostnameFromURI(request.url); request.domain = µb.URI.domainFromHostname(request.hostname); request.entity = µb.URI.entityFromDomain(request.domain); - // https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ - // Add support for `specifichide`. - if ( noCosmeticFiltering === false ) { - const specificHide = - µb.staticNetFilteringEngine.matchStringReverse( - 'specifichide', - request.url - ); - response.noSpecificCosmeticFiltering = specificHide === 2; - if ( loggerEnabled && specificHide !== 0 ) { - µBlock.filteringContext - .duplicate() - .fromTabId(tabId) - .setURL(request.url) - .setRealm('network') - .setType('specifichide') - .setFilter(µb.staticNetFilteringEngine.toLogData()) - .toLogger(); - } - } - - // Cosmetic filtering can be effectively disabled when both specific and - // generic cosmetic filtering are disabled. - if ( - noCosmeticFiltering === false && - response.noGenericCosmeticFiltering && - response.noSpecificCosmeticFiltering - ) { - response.noCosmeticFiltering = true; - } - response.specificCosmeticFilters = µb.cosmeticFilteringEngine.retrieveSpecificSelectors(request, response); @@ -656,14 +606,19 @@ const retrieveContentScriptParameters = async function(sender, request) { // Inject as early as possible to make the cosmetic logger code less // sensitive to the removal of DOM nodes which may match injected // cosmetic filters. - if ( loggerEnabled && response.noCosmeticFiltering !== true ) { - vAPI.tabs.executeScript(tabId, { - allFrames: false, - file: '/js/scriptlets/cosmetic-logger.js', - frameId, - matchAboutBlank: true, - runAt: 'document_start', - }); + if ( loggerEnabled ) { + if ( + noSpecificCosmeticFiltering === false || + noGenericCosmeticFiltering === false + ) { + vAPI.tabs.executeScript(tabId, { + allFrames: false, + file: '/js/scriptlets/cosmetic-logger.js', + frameId, + matchAboutBlank: true, + runAt: 'document_start', + }); + } } return response; diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 57b899bec..f30e88d67 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -192,6 +192,10 @@ const FrameStore = class { this.domain = vAPI.domainFromHostname(this.hostname) || this.hostname; } + // Evaluated on-demand + // - 0b01: specific cosmetic filtering + // - 0b10: generic cosmetic filtering + this._cosmeticFilteringBits = undefined; return this; } @@ -203,6 +207,60 @@ const FrameStore = class { return null; } + getCosmeticFilteringBits(tabId) { + if ( this._cosmeticFilteringBits !== undefined ) { + return this._cosmeticFilteringBits; + } + this._cosmeticFilteringBits = 0b11; + { + const result = µb.staticNetFilteringEngine.matchStringReverse( + 'specifichide', + this.rawURL + ); + if ( result !== 0 && µb.logger.enabled ) { + µBlock.filteringContext + .duplicate() + .fromTabId(tabId) + .setURL(this.rawURL) + .setRealm('network') + .setType('specifichide') + .setFilter(µb.staticNetFilteringEngine.toLogData()) + .toLogger(); + } + if ( result === 2 ) { + this._cosmeticFilteringBits &= ~0b01; + } + } + { + const result = µb.staticNetFilteringEngine.matchStringReverse( + 'generichide', + this.rawURL + ); + if ( result !== 0 && µb.logger.enabled ) { + µBlock.filteringContext + .duplicate() + .fromTabId(tabId) + .setURL(this.rawURL) + .setRealm('network') + .setType('generichide') + .setFilter(µb.staticNetFilteringEngine.toLogData()) + .toLogger(); + } + if ( result === 2 ) { + this._cosmeticFilteringBits &= ~0b10; + } + } + return this._cosmeticFilteringBits; + } + + shouldApplySpecificCosmeticFilters(tabId) { + return (this.getCosmeticFilteringBits(tabId) & 0b01) !== 0; + } + + shouldApplyGenericCosmeticFilters(tabId) { + return (this.getCosmeticFilteringBits(tabId) & 0b10) !== 0; + } + static factory(frameURL, parentId = -1) { const entry = FrameStore.junkyard.pop(); if ( entry === undefined ) { @@ -296,7 +354,7 @@ const PageStore = class { // The context is used to determine whether we report behavior change // to the logger. - init(tabId, context) { + init(tabId) { const tabContext = µb.tabContextManager.mustLookup(tabId); this.tabId = tabId; @@ -327,28 +385,8 @@ const PageStore = class { this.frames = new Map(); this.setFrameURL({ url: tabContext.rawURL }); - // https://github.com/uBlockOrigin/uBlock-issues/issues/314 - const masterSwitch = tabContext.getNetFilteringSwitch(); - - this.noCosmeticFiltering = µb.sessionSwitches.evaluateZ( - 'no-cosmetic-filtering', - tabContext.rootHostname - ) === true; - if ( - masterSwitch && - this.noCosmeticFiltering && - µb.logger.enabled && - context === 'tabCommitted' - ) { - µb.filteringContext - .duplicate() - .fromTabId(tabId) - .setURL(tabContext.rawURL) - .setRealm('cosmetic') - .setType('dom') - .setFilter(µb.sessionSwitches.toLogData()) - .toLogger(); - } + // Evaluated on-demand + this._noCosmeticFiltering = undefined; return this; } @@ -493,15 +531,53 @@ const PageStore = class { .getNetFilteringSwitch(); } - getSpecificCosmeticFilteringSwitch() { - return this.noCosmeticFiltering !== true; - } - toggleNetFilteringSwitch(url, scope, state) { µb.toggleNetFilteringSwitch(url, scope, state); this.netFilteringCache.empty(); } + shouldApplyCosmeticFilters(frameId = 0) { + if ( this._noCosmeticFiltering === undefined ) { + this._noCosmeticFiltering = this.getNetFilteringSwitch() === false; + if ( this._noCosmeticFiltering === false ) { + this._noCosmeticFiltering = µb.sessionSwitches.evaluateZ( + 'no-cosmetic-filtering', + this.tabHostname + ) === true; + if ( this._noCosmeticFiltering && µb.logger.enabled ) { + µb.filteringContext + .duplicate() + .fromTabId(this.tabId) + .setURL(this.rawURL) + .setRealm('cosmetic') + .setType('dom') + .setFilter(µb.sessionSwitches.toLogData()) + .toLogger(); + } + } + } + if ( this._noCosmeticFiltering ) { return false; } + if ( frameId === -1 ) { return true; } + // Cosmetic filtering can be effectively disabled when both specific + // and generic cosmetic filters are disabled. + return this.shouldApplySpecificCosmeticFilters(frameId) || + this.shouldApplyGenericCosmeticFilters(frameId); + } + + shouldApplySpecificCosmeticFilters(frameId) { + if ( this.shouldApplyCosmeticFilters(-1) === false ) { return false; } + const frameStore = this.getFrameStore(frameId); + if ( frameStore === null ) { return false; } + return frameStore.shouldApplySpecificCosmeticFilters(this.tabId); + } + + shouldApplyGenericCosmeticFilters(frameId) { + if ( this.shouldApplyCosmeticFilters(-1) === false ) { return false; } + const frameStore = this.getFrameStore(frameId); + if ( frameStore === null ) { return false; } + return frameStore.shouldApplyGenericCosmeticFilters(this.tabId); + } + // https://github.com/gorhill/uBlock/issues/2105 // Be sure to always include the current page's hostname -- it might not // be present when the page itself is pulled from the browser's diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index 898882050..9ae3971a4 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -378,6 +378,7 @@ const filterTypes = { const cosmeticFilterFromElement = function(elem) { if ( elem === null ) { return 0; } if ( elem.nodeType !== 1 ) { return 0; } + if ( noCosmeticFiltering ) { return 0; } if ( candidateElements.indexOf(elem) === -1 ) { candidateElements.push(elem); @@ -709,6 +710,7 @@ const filterToDOMInterface = (( ) => { // https://github.com/gorhill/uBlock/issues/2515 // Remove trailing pseudo-element when querying. const fromCompiledCosmeticFilter = function(raw) { + if ( noCosmeticFiltering ) { return; } if ( typeof raw !== 'string' ) { return; } let elems, style; try { @@ -798,7 +800,7 @@ const filterToDOMInterface = (( ) => { if ( permanent === false || reCosmeticAnchor.test(lastFilter) === false ) { return apply(); } - if ( vAPI.domFilterer instanceof Object === false ) { return; } + if ( noCosmeticFiltering ) { return; } const cssSelectors = new Set(); const proceduralSelectors = new Set(); for ( const { raw } of lastResultset ) { @@ -886,7 +888,15 @@ const elementFromPoint = (( ) => { const magicAttr = `${vAPI.sessionId}-clickblind`; pickerRoot.setAttribute(magicAttr, ''); let elem = document.elementFromPoint(x, y); - if ( elem === document.body || elem === document.documentElement ) { + if ( + elem === null || /* to skip following tests */ + elem === document.body || + elem === document.documentElement || ( + pickerBootArgs.zap !== true && + noCosmeticFiltering && + resourceURLsFromElement(elem).length === 0 + ) + ) { elem = null; } // https://github.com/uBlockOrigin/uBlock-issues/issues/380 @@ -1013,6 +1023,7 @@ const startPicker = function() { // Try using mouse position if ( pickerBootArgs.mouse && + vAPI.mouseClick instanceof Object && typeof vAPI.mouseClick.x === 'number' && vAPI.mouseClick.x > 0 ) { @@ -1046,9 +1057,15 @@ const startPicker = function() { if ( (src !== url) && (src !== '' || url !== 'about:blank') ) { continue; } - elem.scrollIntoView({ behavior: 'smooth', block: 'start' }); filtersFrom(elem); - return showDialog({ broad: true }); + if ( + netFilterCandidates.length !== 0 || + cosmeticFilterCandidates.length !== 0 + ) { + elem.scrollIntoView({ behavior: 'smooth', block: 'center' }); + showDialog({ broad: true }); + } + return; } // A target was specified, but it wasn't found: abort. @@ -1193,12 +1210,9 @@ const onConnectionMessage = function(msg) { } // The DOM filterer will not be present when cosmetic filtering is disabled. -if ( - pickerBootArgs.zap !== true && - vAPI.domFilterer instanceof Object === false -) { - return; -} +const noCosmeticFiltering = + vAPI.domFilterer instanceof Object === false || + vAPI.noSpecificCosmeticFiltering === true; // https://github.com/gorhill/uBlock/issues/1529 // In addition to inline styles, harden the element picker styles by using