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 = {
|
const hiddenSettingsDefault = {
|
||||||
allowGenericProceduralFilters: false,
|
allowGenericProceduralFilters: false,
|
||||||
|
@ -84,7 +84,7 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
requestLogMaxEntries: 1000,
|
requestLogMaxEntries: 1000,
|
||||||
showIconBadge: true,
|
showIconBadge: true,
|
||||||
tooltipsDisabled: false,
|
tooltipsDisabled: false,
|
||||||
webrtcIPAddressHidden: false
|
webrtcIPAddressHidden: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
hiddenSettingsDefault: hiddenSettingsDefault,
|
hiddenSettingsDefault: hiddenSettingsDefault,
|
||||||
|
@ -133,22 +133,22 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
|
|
||||||
localSettings: {
|
localSettings: {
|
||||||
blockedRequestCount: 0,
|
blockedRequestCount: 0,
|
||||||
allowedRequestCount: 0
|
allowedRequestCount: 0,
|
||||||
},
|
},
|
||||||
localSettingsLastModified: 0,
|
localSettingsLastModified: 0,
|
||||||
localSettingsLastSaved: 0,
|
localSettingsLastSaved: 0,
|
||||||
|
|
||||||
// Read-only
|
// Read-only
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
compiledMagic: 18, // Increase when compiled format changes
|
compiledMagic: 19, // Increase when compiled format changes
|
||||||
selfieMagic: 18 // Increase when selfie format changes
|
selfieMagic: 19, // Increase when selfie format changes
|
||||||
},
|
},
|
||||||
|
|
||||||
restoreBackupSettings: {
|
restoreBackupSettings: {
|
||||||
lastRestoreFile: '',
|
lastRestoreFile: '',
|
||||||
lastRestoreTime: 0,
|
lastRestoreTime: 0,
|
||||||
lastBackupFile: '',
|
lastBackupFile: '',
|
||||||
lastBackupTime: 0
|
lastBackupTime: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
commandShortcuts: new Map(),
|
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
|
// is to prevent repeated allocation/deallocation overheads -- the
|
||||||
// constructors/destructors of javascript Set/Map is assumed to be costlier
|
// constructors/destructors of javascript Set/Map is assumed to be costlier
|
||||||
// than just calling clear() on these.
|
// than just calling clear() on these.
|
||||||
this.setRegister0 = new Set();
|
this.simpleSet$ = new Set();
|
||||||
this.setRegister1 = new Set();
|
this.complexSet$ = new Set();
|
||||||
this.setRegister2 = new Set();
|
this.specificSet$ = new Set();
|
||||||
this.mapRegister0 = new Map();
|
this.exceptionSet$ = new Set();
|
||||||
|
this.proceduralSet$ = new Set();
|
||||||
|
this.dummySet$ = new Set();
|
||||||
|
|
||||||
this.reset();
|
this.reset();
|
||||||
};
|
};
|
||||||
|
@ -830,11 +832,11 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
|
||||||
|
|
||||||
//console.time('cosmeticFilteringEngine.retrieveGenericSelectors');
|
//console.time('cosmeticFilteringEngine.retrieveGenericSelectors');
|
||||||
|
|
||||||
const simpleSelectors = this.setRegister0;
|
const simpleSelectors = this.simpleSet$;
|
||||||
const complexSelectors = this.setRegister1;
|
const complexSelectors = this.complexSet$;
|
||||||
|
|
||||||
const cacheEntry = this.selectorCache.get(request.hostname);
|
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 ) {
|
for ( const type in this.lowlyGeneric ) {
|
||||||
const entry = this.lowlyGeneric[type];
|
const entry = this.lowlyGeneric[type];
|
||||||
|
@ -891,6 +893,10 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
|
||||||
excepted,
|
excepted,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Important: always clear used registers before leaving.
|
||||||
|
simpleSelectors.clear();
|
||||||
|
complexSelectors.clear();
|
||||||
|
|
||||||
// Cache and inject (if user stylesheets supported) looked-up low generic
|
// Cache and inject (if user stylesheets supported) looked-up low generic
|
||||||
// cosmetic filters.
|
// cosmetic filters.
|
||||||
if (
|
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');
|
//console.timeEnd('cosmeticFilteringEngine.retrieveGenericSelectors');
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -946,8 +948,6 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
||||||
request,
|
request,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
//console.time('cosmeticFilteringEngine.retrieveSpecificSelectors');
|
|
||||||
|
|
||||||
const hostname = request.hostname;
|
const hostname = request.hostname;
|
||||||
const cacheEntry = this.selectorCache.get(hostname);
|
const cacheEntry = this.selectorCache.get(hostname);
|
||||||
|
|
||||||
|
@ -976,7 +976,11 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( options.noCosmeticFiltering !== true ) {
|
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.
|
// Cached cosmetic filters: these are always declarative.
|
||||||
if ( cacheEntry !== undefined ) {
|
if ( cacheEntry !== undefined ) {
|
||||||
cacheEntry.retrieve('cosmetic', specificSet);
|
cacheEntry.retrieve('cosmetic', specificSet);
|
||||||
|
@ -986,17 +990,30 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exceptionSet = this.setRegister0;
|
// Retrieve filters with a non-empty hostname
|
||||||
const proceduralSet = this.setRegister2;
|
|
||||||
|
|
||||||
this.specificFilters.retrieve(
|
this.specificFilters.retrieve(
|
||||||
hostname,
|
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 !== '' ) {
|
if ( request.entity !== '' ) {
|
||||||
this.specificFilters.retrieve(
|
this.specificFilters.retrieve(
|
||||||
`${hostname.slice(0, -request.domain.length)}${request.entity}`,
|
`${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.
|
// Important: always clear used registers before leaving.
|
||||||
this.setRegister0.clear();
|
specificSet.clear();
|
||||||
this.setRegister1.clear();
|
proceduralSet.clear();
|
||||||
this.setRegister2.clear();
|
exceptionSet.clear();
|
||||||
|
dummySet.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS selectors for collapsible blocked elements
|
// CSS selectors for collapsible blocked elements
|
||||||
|
@ -1115,8 +1133,6 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.timeEnd('cosmeticFilteringEngine.retrieveSpecificSelectors');
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -500,6 +500,98 @@ vAPI.messaging.listen({
|
||||||
|
|
||||||
const µb = µBlock;
|
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) {
|
const onMessage = function(request, sender, callback) {
|
||||||
// Async
|
// Async
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
|
@ -507,16 +599,11 @@ const onMessage = function(request, sender, callback) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync
|
const senderDetails = µb.getMessageSenderDetails(sender);
|
||||||
let response,
|
const pageStore = µb.pageStoreFromTabId(senderDetails.tabId);
|
||||||
tabId, frameId,
|
|
||||||
pageStore = null;
|
|
||||||
|
|
||||||
if ( sender && sender.tab ) {
|
// Sync
|
||||||
tabId = sender.tab.id;
|
let response;
|
||||||
frameId = sender.frameId;
|
|
||||||
pageStore = µb.pageStoreFromTabId(tabId);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'cosmeticFiltersInjected':
|
case 'cosmeticFiltersInjected':
|
||||||
|
@ -528,88 +615,42 @@ const onMessage = function(request, sender, callback) {
|
||||||
id: request.id,
|
id: request.id,
|
||||||
hash: request.hash,
|
hash: request.hash,
|
||||||
netSelectorCacheCountMax:
|
netSelectorCacheCountMax:
|
||||||
µb.cosmeticFilteringEngine.netSelectorCacheCountMax
|
µb.cosmeticFilteringEngine.netSelectorCacheCountMax,
|
||||||
};
|
};
|
||||||
if (
|
if (
|
||||||
µb.userSettings.collapseBlocked &&
|
µb.userSettings.collapseBlocked &&
|
||||||
pageStore &&
|
pageStore && pageStore.getNetFilteringSwitch()
|
||||||
pageStore.getNetFilteringSwitch()
|
|
||||||
) {
|
) {
|
||||||
pageStore.getBlockedResources(request, response);
|
pageStore.getBlockedResources(request, response);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'maybeGoodPopup':
|
case 'maybeGoodPopup':
|
||||||
µb.maybeGoodPopup.tabId = tabId;
|
µb.maybeGoodPopup.tabId = senderDetails.tabId;
|
||||||
µb.maybeGoodPopup.url = request.url;
|
µb.maybeGoodPopup.url = request.url;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'shouldRenderNoscriptTags':
|
case 'shouldRenderNoscriptTags':
|
||||||
if ( pageStore === null ) { break; }
|
if ( pageStore === null ) { break; }
|
||||||
const fctxt = µb.filteringContext.fromTabId(tabId);
|
const fctxt = µb.filteringContext.fromTabId(senderDetails.tabId);
|
||||||
if ( pageStore.filterScripting(fctxt, undefined) ) {
|
if ( pageStore.filterScripting(fctxt, undefined) ) {
|
||||||
vAPI.tabs.executeScript(tabId, {
|
vAPI.tabs.executeScript(senderDetails.tabId, {
|
||||||
file: '/js/scriptlets/noscript-spoof.js',
|
file: '/js/scriptlets/noscript-spoof.js',
|
||||||
frameId: frameId,
|
frameId: senderDetails.frameId,
|
||||||
runAt: 'document_end'
|
runAt: 'document_end',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'retrieveContentScriptParameters':
|
case 'retrieveContentScriptParameters':
|
||||||
if (
|
response = retrieveContentScriptParameters(senderDetails, request);
|
||||||
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);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'retrieveGenericCosmeticSelectors':
|
case 'retrieveGenericCosmeticSelectors':
|
||||||
request.tabId = tabId;
|
request.tabId = senderDetails.tabId;
|
||||||
request.frameId = frameId;
|
request.frameId = senderDetails.frameId;
|
||||||
response = {
|
response = {
|
||||||
result: µb.cosmeticFilteringEngine.retrieveGenericSelectors(request)
|
result: µb.cosmeticFilteringEngine.retrieveGenericSelectors(request),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -176,17 +176,18 @@ const fromCosmeticFilter = function(details) {
|
||||||
let end = content.indexOf('\n', pos);
|
let end = content.indexOf('\n', pos);
|
||||||
if ( end === -1 ) { end = content.length; }
|
if ( end === -1 ) { end = content.length; }
|
||||||
pos = end;
|
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
|
// https://github.com/gorhill/uBlock/issues/2763
|
||||||
if ( fargs[0] >= 0 && fargs[0] <= 5 && details.ignoreGeneric ) {
|
if ( filterType >= 0 && filterType <= 5 && details.ignoreGeneric ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not confuse cosmetic filters with HTML ones.
|
// 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
|
// Lowly generic cosmetic filters
|
||||||
case 0: // simple id-based
|
case 0: // simple id-based
|
||||||
if (
|
if (
|
||||||
|
@ -232,9 +233,17 @@ const fromCosmeticFilter = function(details) {
|
||||||
) {
|
) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( hostnameMatches(fargs[1]) ) {
|
if ( hostnameMatches(fargs[1]) === false ) { break; }
|
||||||
found = fargs[1] + prefix + selector;
|
// 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;
|
break;
|
||||||
// Scriptlet injection
|
// Scriptlet injection
|
||||||
case 32:
|
case 32:
|
||||||
|
|
|
@ -170,8 +170,16 @@ const fromCosmeticFilter = async function(details, callback) {
|
||||||
id: id,
|
id: id,
|
||||||
domain: µBlock.URI.domainFromHostname(hostname),
|
domain: µBlock.URI.domainFromHostname(hostname),
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
ignoreGeneric: µBlock.staticNetFilteringEngine
|
ignoreGeneric:
|
||||||
.matchStringGenericHide(details.url) === 2,
|
µBlock.staticNetFilteringEngine.matchStringElementHide(
|
||||||
|
'generic',
|
||||||
|
details.url
|
||||||
|
) === 2,
|
||||||
|
ignoreSpecific:
|
||||||
|
µBlock.staticNetFilteringEngine.matchStringElementHide(
|
||||||
|
'specific',
|
||||||
|
details.url
|
||||||
|
) === 2,
|
||||||
rawFilter: details.rawFilter
|
rawFilter: details.rawFilter
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
optional.
|
optional.
|
||||||
|
|
||||||
The static extended filtering engine also offers parsing capabilities which
|
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
|
cosmetic and html filtering can ask the extended filtering engine to
|
||||||
compile/validate selectors.
|
compile/validate selectors.
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@
|
||||||
[ ':nth-ancestor', compileNthAncestorSelector ],
|
[ ':nth-ancestor', compileNthAncestorSelector ],
|
||||||
[ ':spath', compileSpathExpression ],
|
[ ':spath', compileSpathExpression ],
|
||||||
[ ':watch-attr', compileAttrList ],
|
[ ':watch-attr', compileAttrList ],
|
||||||
[ ':xpath', compileXpathExpression ]
|
[ ':xpath', compileXpathExpression ],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387
|
// https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387
|
||||||
|
@ -517,7 +517,11 @@
|
||||||
this.timer = undefined;
|
this.timer = undefined;
|
||||||
this.strToIdMap = new Map();
|
this.strToIdMap = new Map();
|
||||||
this.hostnameToSlotIdMap = 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.strSlots = [];
|
||||||
this.size = 0;
|
this.size = 0;
|
||||||
if ( selfie !== undefined ) {
|
if ( selfie !== undefined ) {
|
||||||
|
@ -543,24 +547,27 @@
|
||||||
this.hostnameSlots.push(strId);
|
this.hostnameSlots.push(strId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bucket = this.hostnameSlots[iHn];
|
if ( iHn < 0 ) {
|
||||||
if ( Array.isArray(bucket) ) {
|
this.hostnameSlotsEx[-iHn].push(strId);
|
||||||
bucket.push(strId);
|
return;
|
||||||
} else {
|
|
||||||
this.hostnameSlots[iHn] = [ bucket, strId ];
|
|
||||||
}
|
}
|
||||||
|
const strIdEx = -this.hostnameSlotsEx.length;
|
||||||
|
this.hostnameToSlotIdMap.set(hn, strIdEx);
|
||||||
|
this.hostnameSlotsEx.push([ this.hostnameSlots[iHn], strId ]);
|
||||||
|
this.hostnameSlots[iHn] = strIdEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.hostnameToSlotIdMap.clear();
|
this.hostnameToSlotIdMap.clear();
|
||||||
this.hostnameSlots.length = 0;
|
this.hostnameSlots.length = 0;
|
||||||
|
this.hostnameSlotsEx.length = 1; // IMPORTANT: 1, not 0
|
||||||
this.strSlots.length = 0;
|
this.strSlots.length = 0;
|
||||||
this.strToIdMap.clear();
|
this.strToIdMap.clear();
|
||||||
this.size = 0;
|
this.size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
collectGarbage(async = false) {
|
collectGarbage(later = false) {
|
||||||
if ( async === false ) {
|
if ( later === false ) {
|
||||||
if ( this.timer !== undefined ) {
|
if ( this.timer !== undefined ) {
|
||||||
self.cancelIdleCallback(this.timer);
|
self.cancelIdleCallback(this.timer);
|
||||||
this.timer = undefined;
|
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
|
const mask = out.length - 1; // out.length must be power of two
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const filterId = this.hostnameToSlotIdMap.get(hostname);
|
const filterId = this.hostnameToSlotIdMap.get(hostname);
|
||||||
if ( filterId !== undefined ) {
|
if ( filterId !== undefined ) {
|
||||||
const bucket = this.hostnameSlots[filterId];
|
if ( filterId < 0 ) {
|
||||||
if ( Array.isArray(bucket) ) {
|
const bucket = this.hostnameSlotsEx[-filterId];
|
||||||
for ( const id of bucket ) {
|
for ( const strId of bucket ) {
|
||||||
out[id & mask].add(this.strSlots[id >>> this.nBits]);
|
out[strId & mask].add(
|
||||||
|
this.strSlots[strId >>> this.nBits]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} 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; }
|
if ( hostname === '' ) { break; }
|
||||||
const pos = hostname.indexOf('.');
|
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 {
|
return {
|
||||||
hostnameToSlotIdMap: Array.from(this.hostnameToSlotIdMap),
|
hostnameToSlotIdMap: Array.from(this.hostnameToSlotIdMap),
|
||||||
hostnameSlots: this.hostnameSlots,
|
hostnameSlots: this.hostnameSlots,
|
||||||
|
hostnameSlotsEx: this.hostnameSlotsEx,
|
||||||
strSlots: this.strSlots,
|
strSlots: this.strSlots,
|
||||||
size: this.size
|
size: this.size
|
||||||
};
|
};
|
||||||
|
@ -610,6 +634,7 @@
|
||||||
fromSelfie(selfie) {
|
fromSelfie(selfie) {
|
||||||
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
|
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
|
||||||
this.hostnameSlots = selfie.hostnameSlots;
|
this.hostnameSlots = selfie.hostnameSlots;
|
||||||
|
this.hostnameSlotsEx = selfie.hostnameSlotsEx;
|
||||||
this.strSlots = selfie.strSlots;
|
this.strSlots = selfie.strSlots;
|
||||||
this.size = selfie.size;
|
this.size = selfie.size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,12 +68,13 @@ const typeNameToTypeValue = {
|
||||||
'popunder': 12 << 4,
|
'popunder': 12 << 4,
|
||||||
'main_frame': 13 << 4, // start of 1st-party-only behavorial filtering
|
'main_frame': 13 << 4, // start of 1st-party-only behavorial filtering
|
||||||
'generichide': 14 << 4,
|
'generichide': 14 << 4,
|
||||||
'inline-font': 15 << 4,
|
'specifichide': 15 << 4,
|
||||||
'inline-script': 16 << 4,
|
'inline-font': 16 << 4,
|
||||||
'data': 17 << 4, // special: a generic data holder
|
'inline-script': 17 << 4,
|
||||||
'redirect': 18 << 4,
|
'data': 18 << 4, // special: a generic data holder
|
||||||
'webrtc': 19 << 4,
|
'redirect': 19 << 4,
|
||||||
'unsupported': 20 << 4
|
'webrtc': 20 << 4,
|
||||||
|
'unsupported': 21 << 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
const otherTypeBitValue = typeNameToTypeValue.other;
|
const otherTypeBitValue = typeNameToTypeValue.other;
|
||||||
|
@ -110,12 +111,13 @@ const typeValueToTypeName = {
|
||||||
12: 'popunder',
|
12: 'popunder',
|
||||||
13: 'document',
|
13: 'document',
|
||||||
14: 'generichide',
|
14: 'generichide',
|
||||||
15: 'inline-font',
|
15: 'specifichide',
|
||||||
16: 'inline-script',
|
16: 'inline-font',
|
||||||
17: 'data',
|
17: 'inline-script',
|
||||||
18: 'redirect',
|
18: 'data',
|
||||||
19: 'webrtc',
|
19: 'redirect',
|
||||||
20: 'unsupported'
|
20: 'webrtc',
|
||||||
|
21: 'unsupported'
|
||||||
};
|
};
|
||||||
|
|
||||||
const BlockImportant = BlockAction | Important;
|
const BlockImportant = BlockAction | Important;
|
||||||
|
@ -1848,11 +1850,11 @@ FilterParser.prototype.toNormalizedType = {
|
||||||
'data': 'data',
|
'data': 'data',
|
||||||
'doc': 'main_frame',
|
'doc': 'main_frame',
|
||||||
'document': 'main_frame',
|
'document': 'main_frame',
|
||||||
'elemhide': 'generichide',
|
|
||||||
'font': 'font',
|
'font': 'font',
|
||||||
'frame': 'sub_frame',
|
'frame': 'sub_frame',
|
||||||
'genericblock': 'unsupported',
|
'genericblock': 'unsupported',
|
||||||
'generichide': 'generichide',
|
'generichide': 'generichide',
|
||||||
|
'ghide': 'generichide',
|
||||||
'image': 'image',
|
'image': 'image',
|
||||||
'inline-font': 'inline-font',
|
'inline-font': 'inline-font',
|
||||||
'inline-script': 'inline-script',
|
'inline-script': 'inline-script',
|
||||||
|
@ -1864,6 +1866,8 @@ FilterParser.prototype.toNormalizedType = {
|
||||||
'popunder': 'popunder',
|
'popunder': 'popunder',
|
||||||
'popup': 'popup',
|
'popup': 'popup',
|
||||||
'script': 'script',
|
'script': 'script',
|
||||||
|
'specifichide': 'specifichide',
|
||||||
|
'shide': 'specifichide',
|
||||||
'stylesheet': 'stylesheet',
|
'stylesheet': 'stylesheet',
|
||||||
'subdocument': 'sub_frame',
|
'subdocument': 'sub_frame',
|
||||||
'xhr': 'xmlhttprequest',
|
'xhr': 'xmlhttprequest',
|
||||||
|
@ -2017,7 +2021,8 @@ FilterParser.prototype.parseOptions = function(s) {
|
||||||
this.dataStr = '';
|
this.dataStr = '';
|
||||||
continue;
|
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 ( opt === 'empty' || opt === 'mp4' ) {
|
||||||
if ( this.redirect !== 0 ) {
|
if ( this.redirect !== 0 ) {
|
||||||
this.unsupported = true;
|
this.unsupported = true;
|
||||||
|
@ -2031,6 +2036,13 @@ FilterParser.prototype.parseOptions = function(s) {
|
||||||
this.badFilter = true;
|
this.badFilter = true;
|
||||||
continue;
|
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.
|
// Unrecognized filter option: ignore whole filter.
|
||||||
this.unsupported = true;
|
this.unsupported = true;
|
||||||
break;
|
break;
|
||||||
|
@ -3055,17 +3067,19 @@ FilterContainer.prototype.realmMatchString = function(
|
||||||
// filter if and only if there was a hit on an exception filter.
|
// filter if and only if there was a hit on an exception filter.
|
||||||
// https://github.com/gorhill/uBlock/issues/2103
|
// https://github.com/gorhill/uBlock/issues/2103
|
||||||
// User may want to override `generichide` exception filters.
|
// 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) {
|
FilterContainer.prototype.matchStringElementHide = function(type, url) {
|
||||||
const typeBits = typeNameToTypeValue['generichide'] | 0x80000000;
|
const typeBits = typeNameToTypeValue[`${type}hide`] | 0x80000000;
|
||||||
|
|
||||||
// Prime tokenizer: we get a normalized URL in return.
|
// Prime tokenizer: we get a normalized URL in return.
|
||||||
urlRegister = this.urlTokenizer.setURL(requestURL);
|
urlRegister = this.urlTokenizer.setURL(url);
|
||||||
this.filterRegister = null;
|
this.filterRegister = null;
|
||||||
|
|
||||||
// These registers will be used by various filters
|
// These registers will be used by various filters
|
||||||
pageHostnameRegister = requestHostnameRegister =
|
pageHostnameRegister = requestHostnameRegister =
|
||||||
µb.URI.hostnameFromURI(requestURL);
|
µb.URI.hostnameFromURI(url);
|
||||||
|
|
||||||
// Exception filters
|
// Exception filters
|
||||||
if ( this.realmMatchString(AllowAction, typeBits, FirstParty) ) {
|
if ( this.realmMatchString(AllowAction, typeBits, FirstParty) ) {
|
||||||
|
|
|
@ -707,3 +707,18 @@
|
||||||
window.dispatchEvent(new CustomEvent(name));
|
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>
|
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span>
|
||||||
<span style="flex-direction: column;">
|
<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 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>
|
</span>
|
||||||
</div>
|
</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>
|
<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