mirror of https://github.com/gorhill/uBlock.git
Add support for `elemhide` (through `specifichide`)
Related documentation: - https://help.eyeo.com/en/adblockplus/how-to-write-filters#element-hiding Related feedback/discussion: - https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ The `elemhide` filter option as per ABP semantic is now supported. Previously uBO would consider `elemhide` to be an alias of `generichide`. The support of `elemhide` is through the convenient conversion of `elemhide` option into existing `generichide` option and new `specifichide` option. The purpose of the new `specifichide` filter option is to disable all specific cosmetic filters, i.e. those who target a specific site. Additionally, for convenience purpose, the filter options `generichide`, `specifichide` and `elemhide` can be aliased using the shorter forms `ghide`, `shide` and `ehide` respectively.
This commit is contained in:
parent
6033ebf0d0
commit
23c4c80136
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -500,81 +500,34 @@ vAPI.messaging.listen({
|
|||
|
||||
const µb = µBlock;
|
||||
|
||||
const onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
default:
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
// Sync
|
||||
let response,
|
||||
tabId, frameId,
|
||||
pageStore = null;
|
||||
|
||||
if ( sender && sender.tab ) {
|
||||
tabId = sender.tab.id;
|
||||
frameId = sender.frameId;
|
||||
pageStore = µb.pageStoreFromTabId(tabId);
|
||||
}
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'cosmeticFiltersInjected':
|
||||
µb.cosmeticFilteringEngine.addToSelectorCache(request);
|
||||
break;
|
||||
|
||||
case 'getCollapsibleBlockedRequests':
|
||||
response = {
|
||||
id: request.id,
|
||||
hash: request.hash,
|
||||
netSelectorCacheCountMax:
|
||||
µb.cosmeticFilteringEngine.netSelectorCacheCountMax
|
||||
};
|
||||
if (
|
||||
µb.userSettings.collapseBlocked &&
|
||||
pageStore &&
|
||||
pageStore.getNetFilteringSwitch()
|
||||
) {
|
||||
pageStore.getBlockedResources(request, response);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'maybeGoodPopup':
|
||||
µb.maybeGoodPopup.tabId = tabId;
|
||||
µb.maybeGoodPopup.url = request.url;
|
||||
break;
|
||||
|
||||
case 'shouldRenderNoscriptTags':
|
||||
if ( pageStore === null ) { break; }
|
||||
const fctxt = µb.filteringContext.fromTabId(tabId);
|
||||
if ( pageStore.filterScripting(fctxt, undefined) ) {
|
||||
vAPI.tabs.executeScript(tabId, {
|
||||
file: '/js/scriptlets/noscript-spoof.js',
|
||||
frameId: frameId,
|
||||
runAt: 'document_end'
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'retrieveContentScriptParameters':
|
||||
if (
|
||||
pageStore === null ||
|
||||
pageStore.getNetFilteringSwitch() === false ||
|
||||
!request.url
|
||||
) {
|
||||
break;
|
||||
}
|
||||
const noCosmeticFiltering = pageStore.noCosmeticFiltering === true;
|
||||
response = {
|
||||
|
||||
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.matchStringGenericHide(request.url);
|
||||
µb.staticNetFilteringEngine.matchStringElementHide(
|
||||
'generic',
|
||||
request.url
|
||||
);
|
||||
response.noGenericCosmeticFiltering = genericHide === 2;
|
||||
if ( genericHide !== 0 && µb.logger.enabled ) {
|
||||
µBlock.filteringContext
|
||||
|
@ -587,29 +540,117 @@ const onMessage = function(request, sender, callback) {
|
|||
.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
|
||||
|
||||
// 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 ) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const senderDetails = µb.getMessageSenderDetails(sender);
|
||||
const pageStore = µb.pageStoreFromTabId(senderDetails.tabId);
|
||||
|
||||
// Sync
|
||||
let response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'cosmeticFiltersInjected':
|
||||
µb.cosmeticFilteringEngine.addToSelectorCache(request);
|
||||
break;
|
||||
|
||||
case 'getCollapsibleBlockedRequests':
|
||||
response = {
|
||||
id: request.id,
|
||||
hash: request.hash,
|
||||
netSelectorCacheCountMax:
|
||||
µb.cosmeticFilteringEngine.netSelectorCacheCountMax,
|
||||
};
|
||||
if (
|
||||
µb.userSettings.collapseBlocked &&
|
||||
pageStore && pageStore.getNetFilteringSwitch()
|
||||
) {
|
||||
pageStore.getBlockedResources(request, response);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'maybeGoodPopup':
|
||||
µb.maybeGoodPopup.tabId = senderDetails.tabId;
|
||||
µb.maybeGoodPopup.url = request.url;
|
||||
break;
|
||||
|
||||
case 'shouldRenderNoscriptTags':
|
||||
if ( pageStore === null ) { break; }
|
||||
const fctxt = µb.filteringContext.fromTabId(senderDetails.tabId);
|
||||
if ( pageStore.filterScripting(fctxt, undefined) ) {
|
||||
vAPI.tabs.executeScript(senderDetails.tabId, {
|
||||
file: '/js/scriptlets/noscript-spoof.js',
|
||||
frameId: senderDetails.frameId,
|
||||
runAt: 'document_end',
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'retrieveContentScriptParameters':
|
||||
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;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) ) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span>
|
||||
<span style="flex-direction: column;">
|
||||
<div style="margin-bottom: 1px;"><span data-filtex="\t(?:css|(?:inline-)?font)\t">css/font</span><span data-filtex="\timage\t">image</span><span data-filtex="\tmedia\t">media</span><span data-filtex="\t(?:inline-)?script(?:ing)?\t">script</span></div>
|
||||
<div><span data-filtex="\t(?:websocket|xhr)\t">xhr</span><span data-filtex="\tframe\t">frame</span><span data-filtex="\t(?:beacon|csp_report|ping|other)\t">other</span><span data-filtex="\t(?:dom|generichide)\t">dom</span></div>
|
||||
<div><span data-filtex="\t(?:websocket|xhr)\t">xhr</span><span data-filtex="\tframe\t">frame</span><span data-filtex="\t(?:beacon|csp_report|ping|other)\t">other</span><span data-filtex="\t(?:dom|g(?:eneric)?hide|s(?:pecific)?hide)\t">dom</span></div>
|
||||
</span>
|
||||
</div>
|
||||
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\t(?:0,)?1\t" data-i18n="loggerRowFiltererBuiltin1p"></span><span data-filtex="\t(?:3(?:,\d)?|0,3)\t" data-i18n="loggerRowFiltererBuiltin3p"></span></div>
|
||||
|
|
Loading…
Reference in New Issue