diff --git a/src/js/background.js b/src/js/background.js index dfe5a716c..5c955d880 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -33,7 +33,7 @@ if ( vAPI.webextFlavor === undefined ) { /******************************************************************************/ -const µBlock = (function() { // jshint ignore:line +const µBlock = (( ) => { // jshint ignore:line const hiddenSettingsDefault = { allowGenericProceduralFilters: false, @@ -84,7 +84,7 @@ const µBlock = (function() { // jshint ignore:line requestLogMaxEntries: 1000, showIconBadge: true, tooltipsDisabled: false, - webrtcIPAddressHidden: false + webrtcIPAddressHidden: false, }, hiddenSettingsDefault: hiddenSettingsDefault, @@ -133,22 +133,22 @@ const µBlock = (function() { // jshint ignore:line localSettings: { blockedRequestCount: 0, - allowedRequestCount: 0 + allowedRequestCount: 0, }, localSettingsLastModified: 0, localSettingsLastSaved: 0, // Read-only systemSettings: { - compiledMagic: 18, // Increase when compiled format changes - selfieMagic: 18 // Increase when selfie format changes + compiledMagic: 19, // Increase when compiled format changes + selfieMagic: 19, // Increase when selfie format changes }, restoreBackupSettings: { lastRestoreFile: '', lastRestoreTime: 0, lastBackupFile: '', - lastBackupTime: 0 + lastBackupTime: 0, }, commandShortcuts: new Map(), diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 20f8f1f3d..6506daf20 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -23,7 +23,7 @@ /******************************************************************************/ -µBlock.cosmeticFilteringEngine = (function(){ +µBlock.cosmeticFilteringEngine = (( ) => { /******************************************************************************/ @@ -238,10 +238,12 @@ const FilterContainer = function() { // is to prevent repeated allocation/deallocation overheads -- the // constructors/destructors of javascript Set/Map is assumed to be costlier // than just calling clear() on these. - this.setRegister0 = new Set(); - this.setRegister1 = new Set(); - this.setRegister2 = new Set(); - this.mapRegister0 = new Map(); + this.simpleSet$ = new Set(); + this.complexSet$ = new Set(); + this.specificSet$ = new Set(); + this.exceptionSet$ = new Set(); + this.proceduralSet$ = new Set(); + this.dummySet$ = new Set(); this.reset(); }; @@ -830,11 +832,11 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) { //console.time('cosmeticFilteringEngine.retrieveGenericSelectors'); - const simpleSelectors = this.setRegister0; - const complexSelectors = this.setRegister1; + const simpleSelectors = this.simpleSet$; + const complexSelectors = this.complexSet$; const cacheEntry = this.selectorCache.get(request.hostname); - const previousHits = cacheEntry && cacheEntry.cosmetic || this.setRegister2; + const previousHits = cacheEntry && cacheEntry.cosmetic || this.dummySet$; for ( const type in this.lowlyGeneric ) { const entry = this.lowlyGeneric[type]; @@ -891,6 +893,10 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) { excepted, }; + // Important: always clear used registers before leaving. + simpleSelectors.clear(); + complexSelectors.clear(); + // Cache and inject (if user stylesheets supported) looked-up low generic // cosmetic filters. if ( @@ -931,10 +937,6 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) { }); } - // Important: always clear used registers before leaving. - this.setRegister0.clear(); - this.setRegister1.clear(); - //console.timeEnd('cosmeticFilteringEngine.retrieveGenericSelectors'); return out; @@ -946,8 +948,6 @@ FilterContainer.prototype.retrieveSpecificSelectors = function( request, options ) { - //console.time('cosmeticFilteringEngine.retrieveSpecificSelectors'); - const hostname = request.hostname; const cacheEntry = this.selectorCache.get(hostname); @@ -976,7 +976,11 @@ FilterContainer.prototype.retrieveSpecificSelectors = function( }; if ( options.noCosmeticFiltering !== true ) { - const specificSet = this.setRegister1; + const specificSet = this.specificSet$; + const proceduralSet = this.proceduralSet$; + const exceptionSet = this.exceptionSet$; + const dummySet = this.dummySet$; + // Cached cosmetic filters: these are always declarative. if ( cacheEntry !== undefined ) { cacheEntry.retrieve('cosmetic', specificSet); @@ -986,17 +990,30 @@ FilterContainer.prototype.retrieveSpecificSelectors = function( } } - const exceptionSet = this.setRegister0; - const proceduralSet = this.setRegister2; - + // Retrieve filters with a non-empty hostname this.specificFilters.retrieve( hostname, - [ specificSet, exceptionSet, proceduralSet, exceptionSet ] + options.noSpecificCosmeticFiltering !== true + ? [ specificSet, exceptionSet, proceduralSet, exceptionSet ] + : [ dummySet, exceptionSet, dummySet, exceptionSet ], + 1 ); + // Retrieve filters with an empty hostname + this.specificFilters.retrieve( + hostname, + options.noGenericCosmeticFiltering !== true + ? [ specificSet, exceptionSet, proceduralSet, exceptionSet ] + : [ dummySet, exceptionSet, dummySet, exceptionSet ], + 2 + ); + // Retrieve filters with a non-empty entity if ( request.entity !== '' ) { this.specificFilters.retrieve( `${hostname.slice(0, -request.domain.length)}${request.entity}`, - [ specificSet, exceptionSet, proceduralSet, exceptionSet ] + options.noSpecificCosmeticFiltering !== true + ? [ specificSet, exceptionSet, proceduralSet, exceptionSet ] + : [ dummySet, exceptionSet, dummySet, exceptionSet ], + 1 ); } @@ -1060,9 +1077,10 @@ FilterContainer.prototype.retrieveSpecificSelectors = function( } // Important: always clear used registers before leaving. - this.setRegister0.clear(); - this.setRegister1.clear(); - this.setRegister2.clear(); + specificSet.clear(); + proceduralSet.clear(); + exceptionSet.clear(); + dummySet.clear(); } // CSS selectors for collapsible blocked elements @@ -1115,8 +1133,6 @@ FilterContainer.prototype.retrieveSpecificSelectors = function( } } - //console.timeEnd('cosmeticFilteringEngine.retrieveSpecificSelectors'); - return out; }; diff --git a/src/js/messaging.js b/src/js/messaging.js index 15e553b92..a3b786a84 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -500,6 +500,98 @@ vAPI.messaging.listen({ const µb = µBlock; +const retrieveContentScriptParameters = function(senderDetails, request) { + const { url, tabId, frameId } = senderDetails; + if ( url === undefined || tabId === undefined || frameId === undefined ) { + return; + } + if ( request.url !== url ) { return; } + const pageStore = µb.pageStoreFromTabId(tabId); + if ( pageStore === null || pageStore.getNetFilteringSwitch() === false ) { + return; + } + + const noCosmeticFiltering = pageStore.noCosmeticFiltering === true; + + const response = { + collapseBlocked: µb.userSettings.collapseBlocked, + noCosmeticFiltering, + noGenericCosmeticFiltering: noCosmeticFiltering, + noSpecificCosmeticFiltering: noCosmeticFiltering, + }; + + // https://github.com/uBlockOrigin/uAssets/issues/5704 + // `generichide` must be evaluated in the frame context. + if ( noCosmeticFiltering === false ) { + const genericHide = + µb.staticNetFilteringEngine.matchStringElementHide( + 'generic', + request.url + ); + response.noGenericCosmeticFiltering = genericHide === 2; + if ( genericHide !== 0 && µb.logger.enabled ) { + µ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.matchStringElementHide( + 'specific', + request.url + ); + response.noSpecificCosmeticFiltering = specificHide === 2; + if ( specificHide !== 0 && µb.logger.enabled ) { + µ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); + + if ( µb.canInjectScriptletsNow === false ) { + response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); + } + + if ( µb.logger.enabled && response.noCosmeticFiltering !== true ) { + µb.logCosmeticFilters(tabId, frameId); + } + + return response; +}; + const onMessage = function(request, sender, callback) { // Async switch ( request.what ) { @@ -507,16 +599,11 @@ const onMessage = function(request, sender, callback) { break; } - // Sync - let response, - tabId, frameId, - pageStore = null; + const senderDetails = µb.getMessageSenderDetails(sender); + const pageStore = µb.pageStoreFromTabId(senderDetails.tabId); - if ( sender && sender.tab ) { - tabId = sender.tab.id; - frameId = sender.frameId; - pageStore = µb.pageStoreFromTabId(tabId); - } + // Sync + let response; switch ( request.what ) { case 'cosmeticFiltersInjected': @@ -528,88 +615,42 @@ const onMessage = function(request, sender, callback) { id: request.id, hash: request.hash, netSelectorCacheCountMax: - µb.cosmeticFilteringEngine.netSelectorCacheCountMax + µb.cosmeticFilteringEngine.netSelectorCacheCountMax, }; if ( µb.userSettings.collapseBlocked && - pageStore && - pageStore.getNetFilteringSwitch() + pageStore && pageStore.getNetFilteringSwitch() ) { pageStore.getBlockedResources(request, response); } break; case 'maybeGoodPopup': - µb.maybeGoodPopup.tabId = tabId; + µb.maybeGoodPopup.tabId = senderDetails.tabId; µb.maybeGoodPopup.url = request.url; break; case 'shouldRenderNoscriptTags': if ( pageStore === null ) { break; } - const fctxt = µb.filteringContext.fromTabId(tabId); + const fctxt = µb.filteringContext.fromTabId(senderDetails.tabId); if ( pageStore.filterScripting(fctxt, undefined) ) { - vAPI.tabs.executeScript(tabId, { + vAPI.tabs.executeScript(senderDetails.tabId, { file: '/js/scriptlets/noscript-spoof.js', - frameId: frameId, - runAt: 'document_end' + frameId: senderDetails.frameId, + runAt: 'document_end', }); } break; case 'retrieveContentScriptParameters': - if ( - pageStore === null || - pageStore.getNetFilteringSwitch() === false || - !request.url - ) { - break; - } - const noCosmeticFiltering = pageStore.noCosmeticFiltering === true; - response = { - collapseBlocked: µb.userSettings.collapseBlocked, - noCosmeticFiltering, - noGenericCosmeticFiltering: noCosmeticFiltering, - }; - // https://github.com/uBlockOrigin/uAssets/issues/5704 - // `generichide` must be evaluated in the frame context. - if ( noCosmeticFiltering === false ) { - const genericHide = - µb.staticNetFilteringEngine.matchStringGenericHide(request.url); - response.noGenericCosmeticFiltering = genericHide === 2; - if ( genericHide !== 0 && µb.logger.enabled ) { - µ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); - response.specificCosmeticFilters = - µb.cosmeticFilteringEngine.retrieveSpecificSelectors( - request, - response - ); - if ( µb.canInjectScriptletsNow === false ) { - response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); - } - if ( µb.logger.enabled && response.noCosmeticFiltering !== true ) { - µb.logCosmeticFilters(tabId, frameId); - } + response = retrieveContentScriptParameters(senderDetails, request); break; case 'retrieveGenericCosmeticSelectors': - request.tabId = tabId; - request.frameId = frameId; + request.tabId = senderDetails.tabId; + request.frameId = senderDetails.frameId; response = { - result: µb.cosmeticFilteringEngine.retrieveGenericSelectors(request) + result: µb.cosmeticFilteringEngine.retrieveGenericSelectors(request), }; break; diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index 529388631..0983e578d 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -176,17 +176,18 @@ const fromCosmeticFilter = function(details) { let end = content.indexOf('\n', pos); if ( end === -1 ) { end = content.length; } pos = end; - let fargs = JSON.parse(content.slice(beg, end)); + const fargs = JSON.parse(content.slice(beg, end)); + const filterType = fargs[0]; // https://github.com/gorhill/uBlock/issues/2763 - if ( fargs[0] >= 0 && fargs[0] <= 5 && details.ignoreGeneric ) { + if ( filterType >= 0 && filterType <= 5 && details.ignoreGeneric ) { continue; } // Do not confuse cosmetic filters with HTML ones. - if ( (fargs[0] === 64) !== isHtmlFilter ) { continue; } + if ( (filterType === 64) !== isHtmlFilter ) { continue; } - switch ( fargs[0] ) { + switch ( filterType ) { // Lowly generic cosmetic filters case 0: // simple id-based if ( @@ -232,9 +233,17 @@ const fromCosmeticFilter = function(details) { ) { break; } - if ( hostnameMatches(fargs[1]) ) { - found = fargs[1] + prefix + selector; + if ( hostnameMatches(fargs[1]) === false ) { break; } + // https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ + // Ignore match if specific cosmetic filters are disabled + if ( + filterType === 8 && + exception === false && + details.ignoreSpecific + ) { + break; } + found = fargs[1] + prefix + selector; break; // Scriptlet injection case 32: diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index 94947650d..fe8cb048b 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -170,8 +170,16 @@ const fromCosmeticFilter = async function(details, callback) { id: id, domain: µBlock.URI.domainFromHostname(hostname), hostname: hostname, - ignoreGeneric: µBlock.staticNetFilteringEngine - .matchStringGenericHide(details.url) === 2, + ignoreGeneric: + µBlock.staticNetFilteringEngine.matchStringElementHide( + 'generic', + details.url + ) === 2, + ignoreSpecific: + µBlock.staticNetFilteringEngine.matchStringElementHide( + 'specific', + details.url + ) === 2, rawFilter: details.rawFilter }); }; diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index 6e4a6d1dc..52fa4ad9b 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -44,7 +44,7 @@ optional. The static extended filtering engine also offers parsing capabilities which - are available to all other specialized fitlering engines. For example, + are available to all other specialized filtering engines. For example, cosmetic and html filtering can ask the extended filtering engine to compile/validate selectors. @@ -303,7 +303,7 @@ [ ':nth-ancestor', compileNthAncestorSelector ], [ ':spath', compileSpathExpression ], [ ':watch-attr', compileAttrList ], - [ ':xpath', compileXpathExpression ] + [ ':xpath', compileXpathExpression ], ]); // https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387 @@ -517,7 +517,11 @@ this.timer = undefined; this.strToIdMap = new Map(); this.hostnameToSlotIdMap = new Map(); - this.hostnameSlots = []; + // Avoid heterogeneous arrays. Thus: + this.hostnameSlots = []; // array of integers + // IMPORTANT: initialize with an empty array because -0 is NOT < 0. + this.hostnameSlotsEx = [ [] ]; // Array of arrays of integers + // Array of strings (selectors and pseudo-selectors) this.strSlots = []; this.size = 0; if ( selfie !== undefined ) { @@ -543,24 +547,27 @@ this.hostnameSlots.push(strId); return; } - const bucket = this.hostnameSlots[iHn]; - if ( Array.isArray(bucket) ) { - bucket.push(strId); - } else { - this.hostnameSlots[iHn] = [ bucket, strId ]; + if ( iHn < 0 ) { + this.hostnameSlotsEx[-iHn].push(strId); + return; } + const strIdEx = -this.hostnameSlotsEx.length; + this.hostnameToSlotIdMap.set(hn, strIdEx); + this.hostnameSlotsEx.push([ this.hostnameSlots[iHn], strId ]); + this.hostnameSlots[iHn] = strIdEx; } clear() { this.hostnameToSlotIdMap.clear(); this.hostnameSlots.length = 0; + this.hostnameSlotsEx.length = 1; // IMPORTANT: 1, not 0 this.strSlots.length = 0; this.strToIdMap.clear(); this.size = 0; } - collectGarbage(async = false) { - if ( async === false ) { + collectGarbage(later = false) { + if ( later === false ) { if ( this.timer !== undefined ) { self.cancelIdleCallback(this.timer); this.timer = undefined; @@ -578,23 +585,39 @@ ); } - retrieve(hostname, out) { + // modifiers = 1: return only specific items + // modifiers = 2: return only generic items + // + retrieve(hostname, out, modifiers = 0) { + if ( modifiers === 2 ) { + hostname = ''; + } const mask = out.length - 1; // out.length must be power of two for (;;) { const filterId = this.hostnameToSlotIdMap.get(hostname); if ( filterId !== undefined ) { - const bucket = this.hostnameSlots[filterId]; - if ( Array.isArray(bucket) ) { - for ( const id of bucket ) { - out[id & mask].add(this.strSlots[id >>> this.nBits]); + if ( filterId < 0 ) { + const bucket = this.hostnameSlotsEx[-filterId]; + for ( const strId of bucket ) { + out[strId & mask].add( + this.strSlots[strId >>> this.nBits] + ); } } else { - out[bucket & mask].add(this.strSlots[bucket >>> this.nBits]); + const strId = this.hostnameSlots[filterId]; + out[strId & mask].add( + this.strSlots[strId >>> this.nBits] + ); } } if ( hostname === '' ) { break; } const pos = hostname.indexOf('.'); - hostname = pos !== -1 ? hostname.slice(pos + 1) : ''; + if ( pos === -1 ) { + if ( modifiers === 1 ) { break; } + hostname = ''; + } else { + hostname = hostname.slice(pos + 1); + } } } @@ -602,6 +625,7 @@ return { hostnameToSlotIdMap: Array.from(this.hostnameToSlotIdMap), hostnameSlots: this.hostnameSlots, + hostnameSlotsEx: this.hostnameSlotsEx, strSlots: this.strSlots, size: this.size }; @@ -610,6 +634,7 @@ fromSelfie(selfie) { this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap); this.hostnameSlots = selfie.hostnameSlots; + this.hostnameSlotsEx = selfie.hostnameSlotsEx; this.strSlots = selfie.strSlots; this.size = selfie.size; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 04e2fa3bb..c51c92c0b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -68,12 +68,13 @@ const typeNameToTypeValue = { 'popunder': 12 << 4, 'main_frame': 13 << 4, // start of 1st-party-only behavorial filtering 'generichide': 14 << 4, - 'inline-font': 15 << 4, - 'inline-script': 16 << 4, - 'data': 17 << 4, // special: a generic data holder - 'redirect': 18 << 4, - 'webrtc': 19 << 4, - 'unsupported': 20 << 4 + 'specifichide': 15 << 4, + 'inline-font': 16 << 4, + 'inline-script': 17 << 4, + 'data': 18 << 4, // special: a generic data holder + 'redirect': 19 << 4, + 'webrtc': 20 << 4, + 'unsupported': 21 << 4, }; const otherTypeBitValue = typeNameToTypeValue.other; @@ -110,12 +111,13 @@ const typeValueToTypeName = { 12: 'popunder', 13: 'document', 14: 'generichide', - 15: 'inline-font', - 16: 'inline-script', - 17: 'data', - 18: 'redirect', - 19: 'webrtc', - 20: 'unsupported' + 15: 'specifichide', + 16: 'inline-font', + 17: 'inline-script', + 18: 'data', + 19: 'redirect', + 20: 'webrtc', + 21: 'unsupported' }; const BlockImportant = BlockAction | Important; @@ -1848,11 +1850,11 @@ FilterParser.prototype.toNormalizedType = { 'data': 'data', 'doc': 'main_frame', 'document': 'main_frame', - 'elemhide': 'generichide', 'font': 'font', 'frame': 'sub_frame', 'genericblock': 'unsupported', 'generichide': 'generichide', + 'ghide': 'generichide', 'image': 'image', 'inline-font': 'inline-font', 'inline-script': 'inline-script', @@ -1864,6 +1866,8 @@ FilterParser.prototype.toNormalizedType = { 'popunder': 'popunder', 'popup': 'popup', 'script': 'script', + 'specifichide': 'specifichide', + 'shide': 'specifichide', 'stylesheet': 'stylesheet', 'subdocument': 'sub_frame', 'xhr': 'xmlhttprequest', @@ -2017,7 +2021,8 @@ FilterParser.prototype.parseOptions = function(s) { this.dataStr = ''; continue; } - // Used by Adguard, purpose is unclear -- just ignore for now. + // Used by Adguard: + // https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters?aid=16593#empty-modifier if ( opt === 'empty' || opt === 'mp4' ) { if ( this.redirect !== 0 ) { this.unsupported = true; @@ -2031,6 +2036,13 @@ FilterParser.prototype.parseOptions = function(s) { this.badFilter = true; continue; } + // https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ + // Add support for `elemhide`. Rarely used but it happens. + if ( opt === 'elemhide' || opt === 'ehide' ) { + this.parseTypeOption('specifichide', not); + this.parseTypeOption('generichide', not); + continue; + } // Unrecognized filter option: ignore whole filter. this.unsupported = true; break; @@ -3055,17 +3067,19 @@ FilterContainer.prototype.realmMatchString = function( // filter if and only if there was a hit on an exception filter. // https://github.com/gorhill/uBlock/issues/2103 // User may want to override `generichide` exception filters. +// https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ +// Add support for `specifichide`. -FilterContainer.prototype.matchStringGenericHide = function(requestURL) { - const typeBits = typeNameToTypeValue['generichide'] | 0x80000000; +FilterContainer.prototype.matchStringElementHide = function(type, url) { + const typeBits = typeNameToTypeValue[`${type}hide`] | 0x80000000; // Prime tokenizer: we get a normalized URL in return. - urlRegister = this.urlTokenizer.setURL(requestURL); + urlRegister = this.urlTokenizer.setURL(url); this.filterRegister = null; // These registers will be used by various filters pageHostnameRegister = requestHostnameRegister = - µb.URI.hostnameFromURI(requestURL); + µb.URI.hostnameFromURI(url); // Exception filters if ( this.realmMatchString(AllowAction, typeBits, FirstParty) ) { diff --git a/src/js/utils.js b/src/js/utils.js index a43cfbf0d..ed493033a 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -707,3 +707,18 @@ window.dispatchEvent(new CustomEvent(name)); } }; + +/******************************************************************************/ + +µBlock.getMessageSenderDetails = function(sender) { + const r = {}; + if ( sender instanceof Object ) { + r.url = sender.url; + r.frameId = sender.frameId; + const tab = sender.tab; + if ( tab instanceof Object ) { + r.tabId = tab.id; + } + } + return r; +}; diff --git a/src/logger-ui.html b/src/logger-ui.html index 2202579f6..da31228bb 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -61,7 +61,7 @@
css/fontimagemediascript
-
xhrframeotherdom
+
xhrframeotherdom