1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 09:37:12 +02:00

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:
Raymond Hill 2019-09-21 11:30:38 -04:00
parent 6033ebf0d0
commit 23c4c80136
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
9 changed files with 269 additions and 141 deletions

View File

@ -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(),

View File

@ -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;
}; };

View File

@ -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;

View File

@ -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:

View File

@ -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
}); });
}; };

View File

@ -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;
} }

View File

@ -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) ) {

View File

@ -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;
};

View File

@ -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>