mirror of https://github.com/gorhill/uBlock.git
Add support to chain `:style()` to procedural operators
Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/382
Additionally, remnant code for pseudo-user stylesheets
has been removed. Related commit:
- 5c68867b92
This commit is contained in:
parent
3a51ca0002
commit
35aefed926
|
@ -56,6 +56,16 @@ window.addEventListener('webextFlavor', function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.randomToken = function() {
|
||||
const n = Math.random();
|
||||
return String.fromCharCode(n * 26 + 97) +
|
||||
Math.floor(
|
||||
(0.25 + n * 0.75) * Number.MAX_SAFE_INTEGER
|
||||
).toString(36).slice(-8);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.app = {
|
||||
name: manifest.name.replace(/ dev\w+ build/, ''),
|
||||
version: (( ) => {
|
||||
|
@ -339,7 +349,10 @@ vAPI.Tabs = class {
|
|||
return tabs.length !== 0 ? tabs[0] : null;
|
||||
}
|
||||
|
||||
async insertCSS() {
|
||||
async insertCSS(tabId, details) {
|
||||
if ( vAPI.supportsUserStylesheets ) {
|
||||
details.cssOrigin = 'user';
|
||||
}
|
||||
try {
|
||||
await webext.tabs.insertCSS(...arguments);
|
||||
}
|
||||
|
@ -357,7 +370,10 @@ vAPI.Tabs = class {
|
|||
return Array.isArray(tabs) ? tabs : [];
|
||||
}
|
||||
|
||||
async removeCSS() {
|
||||
async removeCSS(tabId, details) {
|
||||
if ( vAPI.supportsUserStylesheets ) {
|
||||
details.cssOrigin = 'user';
|
||||
}
|
||||
try {
|
||||
await webext.tabs.removeCSS(...arguments);
|
||||
}
|
||||
|
@ -1003,9 +1019,6 @@ vAPI.messaging = {
|
|||
frameId: sender.frameId,
|
||||
matchAboutBlank: true
|
||||
};
|
||||
if ( vAPI.supportsUserStylesheets ) {
|
||||
details.cssOrigin = 'user';
|
||||
}
|
||||
if ( msg.add ) {
|
||||
details.runAt = 'document_start';
|
||||
}
|
||||
|
|
|
@ -39,9 +39,11 @@ if (
|
|||
/******************************************************************************/
|
||||
|
||||
vAPI.randomToken = function() {
|
||||
const now = Date.now();
|
||||
return String.fromCharCode(now % 26 + 97) +
|
||||
Math.floor((1 + Math.random()) * now).toString(36);
|
||||
const n = Math.random();
|
||||
return String.fromCharCode(n * 26 + 97) +
|
||||
Math.floor(
|
||||
(0.25 + n * 0.75) * Number.MAX_SAFE_INTEGER
|
||||
).toString(36).slice(-8);
|
||||
};
|
||||
|
||||
vAPI.sessionId = vAPI.randomToken();
|
||||
|
|
|
@ -138,8 +138,8 @@ const µBlock = (( ) => { // jshint ignore:line
|
|||
|
||||
// Read-only
|
||||
systemSettings: {
|
||||
compiledMagic: 28, // Increase when compiled format changes
|
||||
selfieMagic: 28, // Increase when selfie format changes
|
||||
compiledMagic: 29, // Increase when compiled format changes
|
||||
selfieMagic: 29, // Increase when selfie format changes
|
||||
},
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501
|
||||
|
|
|
@ -88,16 +88,8 @@
|
|||
|
||||
The domFilterer makes use of platform-dependent user stylesheets[1].
|
||||
|
||||
At time of writing, only modern Firefox provides a custom implementation,
|
||||
which makes for solid, reliable and low overhead cosmetic filtering on
|
||||
Firefox.
|
||||
|
||||
The generic implementation[2] performs as best as can be, but won't ever be
|
||||
as reliable and accurate as real user stylesheets.
|
||||
|
||||
[1] "user stylesheets" refer to local CSS rules which have priority over,
|
||||
and can't be overriden by a web page's own CSS rules.
|
||||
[2] below, see platformUserCSS / platformHideNode / platformUnhideNode
|
||||
|
||||
*/
|
||||
|
||||
|
@ -492,6 +484,11 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
*/
|
||||
|
||||
{
|
||||
vAPI.hideStyle = 'display:none!important;';
|
||||
|
||||
// TODO: Experiment/evaluate loading procedural operator code using an
|
||||
// on demand approach.
|
||||
|
||||
// 'P' stands for 'Procedural'
|
||||
|
||||
const PSelectorHasTextTask = class {
|
||||
|
@ -562,14 +559,6 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
}
|
||||
};
|
||||
|
||||
const PSelectorPassthru = class {
|
||||
constructor() {
|
||||
}
|
||||
transpose(node, output) {
|
||||
output.push(node);
|
||||
}
|
||||
};
|
||||
|
||||
const PSelectorSpathTask = class {
|
||||
constructor(task) {
|
||||
this.spath = task[1];
|
||||
|
@ -701,17 +690,13 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
[ ':min-text-length', PSelectorMinTextLengthTask ],
|
||||
[ ':not', PSelectorIfNotTask ],
|
||||
[ ':nth-ancestor', PSelectorUpwardTask ],
|
||||
[ ':remove', PSelectorPassthru ],
|
||||
[ ':spath', PSelectorSpathTask ],
|
||||
[ ':upward', PSelectorUpwardTask ],
|
||||
[ ':watch-attr', PSelectorWatchAttrs ],
|
||||
[ ':xpath', PSelectorXpathTask ],
|
||||
]);
|
||||
}
|
||||
this.budget = 200; // I arbitrary picked a 1/5 second
|
||||
this.raw = o.raw;
|
||||
this.cost = 0;
|
||||
this.lastAllowanceTime = 0;
|
||||
this.selector = o.selector;
|
||||
this.tasks = [];
|
||||
const tasks = o.tasks;
|
||||
|
@ -722,9 +707,6 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
);
|
||||
}
|
||||
}
|
||||
if ( o.action !== undefined ) {
|
||||
this.action = o.action;
|
||||
}
|
||||
}
|
||||
prime(input) {
|
||||
const root = input || document;
|
||||
|
@ -760,10 +742,20 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
return false;
|
||||
}
|
||||
};
|
||||
PSelector.prototype.action = undefined;
|
||||
PSelector.prototype.hit = false;
|
||||
PSelector.prototype.operatorToTaskMap = undefined;
|
||||
|
||||
const PSelectorRoot = class extends PSelector {
|
||||
constructor(o, styleToken) {
|
||||
super(o);
|
||||
this.budget = 200; // I arbitrary picked a 1/5 second
|
||||
this.raw = o.raw;
|
||||
this.cost = 0;
|
||||
this.lastAllowanceTime = 0;
|
||||
this.styleToken = styleToken;
|
||||
}
|
||||
};
|
||||
PSelectorRoot.prototype.hit = false;
|
||||
|
||||
const DOMProceduralFilterer = class {
|
||||
constructor(domFilterer) {
|
||||
this.domFilterer = domFilterer;
|
||||
|
@ -771,40 +763,48 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
this.domIsWatched = false;
|
||||
this.mustApplySelectors = false;
|
||||
this.selectors = new Map();
|
||||
this.hiddenNodes = new Set();
|
||||
this.masterToken = vAPI.randomToken();
|
||||
this.styleTokenMap = new Map();
|
||||
this.styledNodes = new Set();
|
||||
if ( vAPI.domWatcher instanceof Object ) {
|
||||
vAPI.domWatcher.addListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
addProceduralSelectors(aa) {
|
||||
addProceduralSelectors(selectors) {
|
||||
const addedSelectors = [];
|
||||
let mustCommit = this.domIsWatched;
|
||||
for ( let i = 0, n = aa.length; i < n; i++ ) {
|
||||
const raw = aa[i];
|
||||
for ( const raw of selectors ) {
|
||||
if ( this.selectors.has(raw) ) { continue; }
|
||||
const o = JSON.parse(raw);
|
||||
if ( o.action === 'style' ) {
|
||||
this.domFilterer.addCSSRule(o.selector, o.tasks[0][1]);
|
||||
mustCommit = true;
|
||||
continue;
|
||||
}
|
||||
if ( o.pseudo !== undefined ) {
|
||||
this.domFilterer.addCSSRule(
|
||||
o.selector,
|
||||
'display:none!important;'
|
||||
);
|
||||
this.domFilterer.addCSSRule(o.selector, vAPI.hideStyle);
|
||||
mustCommit = true;
|
||||
continue;
|
||||
}
|
||||
if ( o.tasks !== undefined ) {
|
||||
if ( this.selectors.has(raw) === false ) {
|
||||
const pselector = new PSelector(o);
|
||||
this.selectors.set(raw, pselector);
|
||||
addedSelectors.push(pselector);
|
||||
mustCommit = true;
|
||||
}
|
||||
// CSS selector-based styles.
|
||||
if (
|
||||
o.action !== undefined &&
|
||||
o.action[0] === ':style' &&
|
||||
o.tasks === undefined
|
||||
) {
|
||||
this.domFilterer.addCSSRule(o.selector, o.action[1]);
|
||||
mustCommit = true;
|
||||
continue;
|
||||
}
|
||||
let style, styleToken;
|
||||
if ( o.action === undefined ) {
|
||||
style = vAPI.hideStyle;
|
||||
} else if ( o.action[0] === ':style' ) {
|
||||
style = o.action[1];
|
||||
}
|
||||
if ( style !== undefined ) {
|
||||
styleToken = this.styleTokenFromStyle(style);
|
||||
}
|
||||
const pselector = new PSelectorRoot(o, styleToken);
|
||||
this.selectors.set(raw, pselector);
|
||||
addedSelectors.push(pselector);
|
||||
mustCommit = true;
|
||||
}
|
||||
if ( mustCommit === false ) { return; }
|
||||
this.mustApplySelectors = this.selectors.size !== 0;
|
||||
|
@ -828,8 +828,8 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
// https://github.com/uBlockOrigin/uBlock-issues/issues/341
|
||||
// Be ready to unhide nodes which no longer matches any of
|
||||
// the procedural selectors.
|
||||
const toRemove = this.hiddenNodes;
|
||||
this.hiddenNodes = new Set();
|
||||
const toUnstyle = this.styledNodes;
|
||||
this.styledNodes = new Set();
|
||||
|
||||
let t0 = Date.now();
|
||||
|
||||
|
@ -851,37 +851,54 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
t0 = t1;
|
||||
if ( nodes.length === 0 ) { continue; }
|
||||
pselector.hit = true;
|
||||
if ( pselector.action === 'remove' ) {
|
||||
this.removeNodes(nodes);
|
||||
} else {
|
||||
this.hideNodes(nodes);
|
||||
}
|
||||
this.styleNodes(nodes, pselector.styleToken);
|
||||
}
|
||||
|
||||
for ( const node of toRemove ) {
|
||||
if ( this.hiddenNodes.has(node) ) { continue; }
|
||||
this.domFilterer.unhideNode(node);
|
||||
}
|
||||
this.unstyleNodes(toUnstyle);
|
||||
//console.timeEnd('procedural selectors/dom layout changed');
|
||||
}
|
||||
|
||||
hideNodes(nodes) {
|
||||
styleTokenFromStyle(style) {
|
||||
if ( style === undefined ) { return; }
|
||||
let styleToken = this.styleTokenMap.get(style);
|
||||
if ( styleToken !== undefined ) { return styleToken; }
|
||||
styleToken = vAPI.randomToken();
|
||||
this.styleTokenMap.set(style, styleToken);
|
||||
this.domFilterer.addCSSRule(
|
||||
`[${this.masterToken}][${styleToken}]`,
|
||||
style,
|
||||
{ silent: true }
|
||||
);
|
||||
return styleToken;
|
||||
}
|
||||
|
||||
styleNodes(nodes, styleToken) {
|
||||
if ( styleToken === undefined ) {
|
||||
for ( const node of nodes ) {
|
||||
node.textContent = '';
|
||||
node.remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
for ( const node of nodes ) {
|
||||
if ( node.parentElement === null ) { continue; }
|
||||
this.domFilterer.hideNode(node);
|
||||
this.hiddenNodes.add(node);
|
||||
node.setAttribute(this.masterToken, '');
|
||||
node.setAttribute(styleToken, '');
|
||||
}
|
||||
}
|
||||
|
||||
removeNodes(nodes) {
|
||||
// TODO: Current assumption is one style per hit element. Could be an
|
||||
// issue if an element has multiple styling and one styling is
|
||||
// brough back. Possibly too rare to care about this for now.
|
||||
unstyleNodes(nodes) {
|
||||
for ( const node of nodes ) {
|
||||
node.textContent = '';
|
||||
node.remove();
|
||||
if ( this.styledNodes.has(node) ) { continue; }
|
||||
node.removeAttribute(this.masterToken);
|
||||
}
|
||||
}
|
||||
|
||||
createProceduralFilter(o) {
|
||||
return new PSelector(o);
|
||||
return new PSelectorRoot(o);
|
||||
}
|
||||
|
||||
onDOMCreated() {
|
||||
|
@ -908,14 +925,10 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
this.disabled = false;
|
||||
this.listeners = [];
|
||||
this.filterset = new Set();
|
||||
this.excludedNodeSet = new WeakSet();
|
||||
this.addedCSSRules = new Set();
|
||||
this.exceptedCSSRules = [];
|
||||
this.reOnlySelectors = /\n\{[^\n]+/g;
|
||||
this.exceptions = [];
|
||||
this.proceduralFilterer = null;
|
||||
this.hideNodeAttr = undefined;
|
||||
this.hideNodeStyleSheetInjected = false;
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/167
|
||||
// By the time the DOMContentLoaded is fired, the content script might
|
||||
// have been disconnected from the background page. Unclear why this
|
||||
|
@ -988,33 +1001,6 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
}
|
||||
}
|
||||
|
||||
excludeNode(node) {
|
||||
this.excludedNodeSet.add(node);
|
||||
this.unhideNode(node);
|
||||
}
|
||||
|
||||
unexcludeNode(node) {
|
||||
this.excludedNodeSet.delete(node);
|
||||
}
|
||||
|
||||
hideNode(node) {
|
||||
if ( this.excludedNodeSet.has(node) ) { return; }
|
||||
if ( this.hideNodeAttr === undefined ) { return; }
|
||||
node.setAttribute(this.hideNodeAttr, '');
|
||||
if ( this.hideNodeStyleSheetInjected ) { return; }
|
||||
this.hideNodeStyleSheetInjected = true;
|
||||
this.addCSSRule(
|
||||
`[${this.hideNodeAttr}]`,
|
||||
'display:none!important;',
|
||||
{ silent: true }
|
||||
);
|
||||
}
|
||||
|
||||
unhideNode(node) {
|
||||
if ( this.hideNodeAttr === undefined ) { return; }
|
||||
node.removeAttribute(this.hideNodeAttr);
|
||||
}
|
||||
|
||||
toggle(state, callback) {
|
||||
if ( state === undefined ) { state = this.disabled; }
|
||||
if ( state !== this.disabled ) { return; }
|
||||
|
@ -1031,24 +1017,6 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
userStylesheet.apply(callback);
|
||||
}
|
||||
|
||||
getAllSelectors_(all) {
|
||||
const out = {
|
||||
declarative: [],
|
||||
exceptions: this.exceptedCSSRules,
|
||||
};
|
||||
for ( const entry of this.filterset ) {
|
||||
let selectors = entry.selectors;
|
||||
if ( all !== true && this.hideNodeAttr !== undefined ) {
|
||||
selectors = selectors
|
||||
.replace(`[${this.hideNodeAttr}]`, '')
|
||||
.replace(/^,\n|,\n$/gm, '');
|
||||
if ( selectors === '' ) { continue; }
|
||||
}
|
||||
out.declarative.push([ selectors, entry.declarations ]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Here we will deal with:
|
||||
// - Injecting low priority user styles;
|
||||
// - Notifying listeners about changed filterset.
|
||||
|
@ -1097,7 +1065,7 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
}
|
||||
|
||||
addProceduralSelectors(aa) {
|
||||
if ( aa.length === 0 ) { return; }
|
||||
if ( Array.isArray(aa) === false || aa.length === 0 ) { return; }
|
||||
this.proceduralFiltererInstance().addProceduralSelectors(aa);
|
||||
}
|
||||
|
||||
|
@ -1105,25 +1073,39 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
return this.proceduralFiltererInstance().createProceduralFilter(o);
|
||||
}
|
||||
|
||||
getAllSelectors() {
|
||||
const out = this.getAllSelectors_(false);
|
||||
out.procedural = this.proceduralFilterer instanceof Object
|
||||
? Array.from(this.proceduralFilterer.selectors.values())
|
||||
: [];
|
||||
getAllSelectors(bits = 0) {
|
||||
const out = {
|
||||
declarative: [],
|
||||
exceptions: this.exceptedCSSRules,
|
||||
};
|
||||
const hasProcedural = this.proceduralFilterer instanceof Object;
|
||||
const includePrivateSelectors = (bits & 0b01) !== 0;
|
||||
const masterToken = hasProcedural
|
||||
? `[${this.proceduralFilterer.masterToken}]`
|
||||
: undefined;
|
||||
for ( const entry of this.filterset ) {
|
||||
const selectors = entry.selectors;
|
||||
if (
|
||||
includePrivateSelectors === false &&
|
||||
masterToken !== undefined &&
|
||||
selectors.startsWith(masterToken)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
out.declarative.push([ selectors, entry.declarations ]);
|
||||
}
|
||||
const excludeProcedurals = (bits & 0b10) !== 0;
|
||||
if ( excludeProcedurals !== true ) {
|
||||
out.procedural = hasProcedural
|
||||
? Array.from(this.proceduralFilterer.selectors.values())
|
||||
: [];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
getAllExceptionSelectors() {
|
||||
return this.exceptions.join(',\n');
|
||||
}
|
||||
|
||||
getFilteredElementCount() {
|
||||
const details = this.getAllSelectors_(true);
|
||||
if ( Array.isArray(details.declarative) === false ) { return 0; }
|
||||
const selectors = details.declarative.map(entry => entry[0]);
|
||||
if ( selectors.length === 0 ) { return 0; }
|
||||
return document.querySelectorAll(selectors.join(',\n')).length;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1548,29 +1530,11 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
let mustCommit = false;
|
||||
|
||||
if ( result ) {
|
||||
let selectors = result.simple;
|
||||
if ( Array.isArray(selectors) && selectors.length !== 0 ) {
|
||||
domFilterer.addCSSRule(
|
||||
selectors,
|
||||
'display:none!important;',
|
||||
{ type: 'simple' }
|
||||
);
|
||||
mustCommit = true;
|
||||
}
|
||||
selectors = result.complex;
|
||||
if ( Array.isArray(selectors) && selectors.length !== 0 ) {
|
||||
domFilterer.addCSSRule(
|
||||
selectors,
|
||||
'display:none!important;',
|
||||
{ type: 'complex' }
|
||||
);
|
||||
mustCommit = true;
|
||||
}
|
||||
selectors = result.injected;
|
||||
let selectors = result.injected;
|
||||
if ( typeof selectors === 'string' && selectors.length !== 0 ) {
|
||||
domFilterer.addCSSRule(
|
||||
selectors,
|
||||
'display:none!important;',
|
||||
vAPI.hideStyle,
|
||||
{ injected: true }
|
||||
);
|
||||
mustCommit = true;
|
||||
|
@ -1740,37 +1704,15 @@ vAPI.injectScriptlet = function(doc, text) {
|
|||
vAPI.domSurveyor = null;
|
||||
}
|
||||
domFilterer.exceptions = cfeDetails.exceptionFilters;
|
||||
domFilterer.hideNodeAttr = cfeDetails.hideNodeAttr;
|
||||
domFilterer.hideNodeStyleSheetInjected =
|
||||
cfeDetails.hideNodeStyleSheetInjected === true;
|
||||
domFilterer.addCSSRule(
|
||||
cfeDetails.declarativeFilters,
|
||||
'display:none!important;'
|
||||
);
|
||||
domFilterer.addCSSRule(
|
||||
cfeDetails.highGenericHideSimple,
|
||||
'display:none!important;',
|
||||
{ type: 'simple', lazy: true }
|
||||
);
|
||||
domFilterer.addCSSRule(
|
||||
cfeDetails.highGenericHideComplex,
|
||||
'display:none!important;',
|
||||
{ type: 'complex', lazy: true }
|
||||
);
|
||||
domFilterer.addCSSRule(
|
||||
cfeDetails.injectedHideFilters,
|
||||
'display:none!important;',
|
||||
vAPI.hideStyle,
|
||||
{ injected: true }
|
||||
);
|
||||
domFilterer.addProceduralSelectors(cfeDetails.proceduralFilters);
|
||||
domFilterer.exceptCSSRules(cfeDetails.exceptedFilters);
|
||||
}
|
||||
|
||||
if ( cfeDetails.networkFilters.length !== 0 ) {
|
||||
vAPI.userStylesheet.add(
|
||||
cfeDetails.networkFilters + '\n{display:none!important;}');
|
||||
}
|
||||
|
||||
vAPI.userStylesheet.apply();
|
||||
|
||||
// Library of resources is located at:
|
||||
|
|
|
@ -32,12 +32,6 @@ const cosmeticSurveyingMissCountMax =
|
|||
parseInt(vAPI.localStorage.getItem('cosmeticSurveyingMissCountMax'), 10) ||
|
||||
15;
|
||||
|
||||
let supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet');
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/8dkvqn/116_broken_loading_custom_filters_from_my_filters/
|
||||
window.addEventListener('webextFlavor', function() {
|
||||
supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet');
|
||||
}, { once: true });
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -754,9 +748,9 @@ FilterContainer.prototype.triggerSelectorCachePruner = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
FilterContainer.prototype.addToSelectorCache = function(details) {
|
||||
let hostname = details.hostname;
|
||||
const hostname = details.hostname;
|
||||
if ( typeof hostname !== 'string' || hostname === '' ) { return; }
|
||||
let selectors = details.selectors;
|
||||
const selectors = details.selectors;
|
||||
if ( Array.isArray(selectors) === false ) { return; }
|
||||
let entry = this.selectorCache.get(hostname);
|
||||
if ( entry === undefined ) {
|
||||
|
@ -838,14 +832,6 @@ FilterContainer.prototype.pruneSelectorCacheAsync = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterContainer.prototype.randomAlphaToken = function() {
|
||||
const now = Date.now();
|
||||
return String.fromCharCode(now % 26 + 97) +
|
||||
Math.floor((1 + Math.random()) * now).toString(36);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterContainer.prototype.getSession = function() {
|
||||
return this.sessionFilterDB;
|
||||
};
|
||||
|
@ -912,57 +898,38 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
|
|||
return;
|
||||
}
|
||||
|
||||
const out = {
|
||||
simple: Array.from(simpleSelectors),
|
||||
complex: Array.from(complexSelectors),
|
||||
injected: '',
|
||||
excepted,
|
||||
};
|
||||
const out = { injected: '', excepted, };
|
||||
|
||||
// Important: always clear used registers before leaving.
|
||||
simpleSelectors.clear();
|
||||
complexSelectors.clear();
|
||||
const injected = [];
|
||||
if ( simpleSelectors.size !== 0 ) {
|
||||
injected.push(Array.from(simpleSelectors).join(',\n'));
|
||||
simpleSelectors.clear();
|
||||
}
|
||||
if ( complexSelectors.size !== 0 ) {
|
||||
injected.push(Array.from(complexSelectors).join(',\n'));
|
||||
complexSelectors.clear();
|
||||
}
|
||||
|
||||
// Cache and inject (if user stylesheets supported) looked-up low generic
|
||||
// cosmetic filters.
|
||||
if (
|
||||
(typeof request.hostname === 'string' && request.hostname !== '') &&
|
||||
(out.simple.length !== 0 || out.complex.length !== 0)
|
||||
) {
|
||||
// Cache and inject looked-up low generic cosmetic filters.
|
||||
if ( injected.length === 0 ) { return out; }
|
||||
|
||||
if ( typeof request.hostname === 'string' && request.hostname !== '' ) {
|
||||
this.addToSelectorCache({
|
||||
cost: request.surveyCost || 0,
|
||||
hostname: request.hostname,
|
||||
injectedHideFilters: '',
|
||||
selectors: out.simple.concat(out.complex),
|
||||
type: 'cosmetic'
|
||||
selectors: injected,
|
||||
type: 'cosmetic',
|
||||
});
|
||||
}
|
||||
|
||||
// If user stylesheets are supported in the current process, inject the
|
||||
// cosmetic filters now.
|
||||
if (
|
||||
supportsUserStylesheets &&
|
||||
request.tabId !== undefined &&
|
||||
request.frameId !== undefined
|
||||
) {
|
||||
const injected = [];
|
||||
if ( out.simple.length !== 0 ) {
|
||||
injected.push(out.simple.join(',\n'));
|
||||
out.simple = [];
|
||||
}
|
||||
if ( out.complex.length !== 0 ) {
|
||||
injected.push(out.complex.join(',\n'));
|
||||
out.complex = [];
|
||||
}
|
||||
out.injected = injected.join(',\n');
|
||||
vAPI.tabs.insertCSS(request.tabId, {
|
||||
code: out.injected + '\n{display:none!important;}',
|
||||
cssOrigin: 'user',
|
||||
frameId: request.frameId,
|
||||
matchAboutBlank: true,
|
||||
runAt: 'document_start',
|
||||
});
|
||||
}
|
||||
out.injected = injected.join(',\n');
|
||||
vAPI.tabs.insertCSS(request.tabId, {
|
||||
code: out.injected + '\n{display:none!important;}',
|
||||
frameId: request.frameId,
|
||||
matchAboutBlank: true,
|
||||
runAt: 'document_start',
|
||||
});
|
||||
|
||||
//console.timeEnd('cosmeticFilteringEngine.retrieveGenericSelectors');
|
||||
|
||||
|
@ -989,18 +956,11 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
|||
ready: this.frozen,
|
||||
hostname: hostname,
|
||||
domain: request.domain,
|
||||
declarativeFilters: [],
|
||||
exceptionFilters: [],
|
||||
exceptedFilters: [],
|
||||
hideNodeAttr: this.randomAlphaToken(),
|
||||
hideNodeStyleSheetInjected: false,
|
||||
highGenericHideSimple: '',
|
||||
highGenericHideComplex: '',
|
||||
injectedHideFilters: '',
|
||||
networkFilters: '',
|
||||
noDOMSurveying: this.needDOMSurveyor === false,
|
||||
proceduralFilters: []
|
||||
};
|
||||
const injectedHideFilters = [];
|
||||
|
||||
if ( options.noCosmeticFiltering !== true ) {
|
||||
const specificSet = this.$specificSet;
|
||||
|
@ -1063,7 +1023,7 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
|||
}
|
||||
|
||||
if ( specificSet.size !== 0 ) {
|
||||
out.declarativeFilters = Array.from(specificSet);
|
||||
injectedHideFilters.push(Array.from(specificSet).join(',\n'));
|
||||
}
|
||||
if ( proceduralSet.size !== 0 ) {
|
||||
out.proceduralFilters = Array.from(proceduralSet);
|
||||
|
@ -1078,10 +1038,10 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
|||
// string in memory, which I have observed occurs when the string is
|
||||
// stored directly as a value in a Map.
|
||||
if ( options.noGenericCosmeticFiltering !== true ) {
|
||||
const exceptionHash = out.exceptionFilters.join();
|
||||
for ( const type in this.highlyGeneric ) {
|
||||
const entry = this.highlyGeneric[type];
|
||||
let str = entry.mru.lookup(exceptionHash);
|
||||
const exceptionSetHash = out.exceptionFilters.join();
|
||||
for ( const key in this.highlyGeneric ) {
|
||||
const entry = this.highlyGeneric[key];
|
||||
let str = entry.mru.lookup(exceptionSetHash);
|
||||
if ( str === undefined ) {
|
||||
str = { s: entry.str, excepted: [] };
|
||||
let genericSet = entry.dict;
|
||||
|
@ -1098,13 +1058,14 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
|||
}
|
||||
str.s = Array.from(genericSet).join(',\n');
|
||||
}
|
||||
entry.mru.add(exceptionHash, str);
|
||||
entry.mru.add(exceptionSetHash, str);
|
||||
}
|
||||
out[entry.canonical] = str.s;
|
||||
if ( str.excepted.length !== 0 ) {
|
||||
out.exceptedFilters.push(...str.excepted);
|
||||
}
|
||||
|
||||
if ( str.s.length !== 0 ) {
|
||||
injectedHideFilters.push(str.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1115,55 +1076,27 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
|
|||
dummySet.clear();
|
||||
}
|
||||
|
||||
const details = {
|
||||
code: '',
|
||||
frameId: request.frameId,
|
||||
matchAboutBlank: true,
|
||||
runAt: 'document_start',
|
||||
};
|
||||
|
||||
if ( injectedHideFilters.length !== 0 ) {
|
||||
out.injectedHideFilters = injectedHideFilters.join(',\n');
|
||||
details.code = out.injectedHideFilters + '\n{display:none!important;}';
|
||||
vAPI.tabs.insertCSS(request.tabId, details);
|
||||
}
|
||||
|
||||
// CSS selectors for collapsible blocked elements
|
||||
if ( cacheEntry ) {
|
||||
const networkFilters = [];
|
||||
cacheEntry.retrieve('net', networkFilters);
|
||||
out.networkFilters = networkFilters.join(',\n');
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/3160
|
||||
// If user stylesheets are supported in the current process, inject the
|
||||
// cosmetic filters now.
|
||||
if (
|
||||
supportsUserStylesheets &&
|
||||
request.tabId !== undefined &&
|
||||
request.frameId !== undefined
|
||||
) {
|
||||
const injectedHideFilters = [];
|
||||
if ( out.declarativeFilters.length !== 0 ) {
|
||||
injectedHideFilters.push(out.declarativeFilters.join(',\n'));
|
||||
out.declarativeFilters = [];
|
||||
}
|
||||
if ( out.proceduralFilters.length !== 0 ) {
|
||||
injectedHideFilters.push('[' + out.hideNodeAttr + ']');
|
||||
out.hideNodeStyleSheetInjected = true;
|
||||
}
|
||||
if ( out.highGenericHideSimple.length !== 0 ) {
|
||||
injectedHideFilters.push(out.highGenericHideSimple);
|
||||
out.highGenericHideSimple = '';
|
||||
}
|
||||
if ( out.highGenericHideComplex.length !== 0 ) {
|
||||
injectedHideFilters.push(out.highGenericHideComplex);
|
||||
out.highGenericHideComplex = '';
|
||||
}
|
||||
out.injectedHideFilters = injectedHideFilters.join(',\n');
|
||||
const details = {
|
||||
code: '',
|
||||
cssOrigin: 'user',
|
||||
frameId: request.frameId,
|
||||
matchAboutBlank: true,
|
||||
runAt: 'document_start',
|
||||
};
|
||||
if ( out.injectedHideFilters.length !== 0 ) {
|
||||
details.code = out.injectedHideFilters + '\n{display:none!important;}';
|
||||
if ( networkFilters.length !== 0 ) {
|
||||
details.code = networkFilters.join('\n') + '\n{display:none!important;}';
|
||||
vAPI.tabs.insertCSS(request.tabId, details);
|
||||
}
|
||||
if ( out.networkFilters.length !== 0 ) {
|
||||
details.code = out.networkFilters + '\n{display:none!important;}';
|
||||
vAPI.tabs.insertCSS(request.tabId, details);
|
||||
out.networkFilters = '';
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
|
|
|
@ -56,7 +56,7 @@ if ( epickerId === null ) { return; }
|
|||
let epickerConnectionId;
|
||||
let filterHostname = '';
|
||||
let filterOrigin = '';
|
||||
let filterResultset = [];
|
||||
let resultsetOpt;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -88,11 +88,8 @@ const userFilterFromCandidate = function(filter) {
|
|||
opts.push(`domain=${filterHostname}`);
|
||||
}
|
||||
|
||||
if ( filterResultset.length !== 0 ) {
|
||||
const item = filterResultset[0];
|
||||
if ( item.opts ) {
|
||||
opts.push(item.opts);
|
||||
}
|
||||
if ( resultsetOpt !== undefined ) {
|
||||
opts.push(resultsetOpt);
|
||||
}
|
||||
|
||||
if ( opts.length ) {
|
||||
|
@ -632,10 +629,10 @@ const onPickerMessage = function(msg) {
|
|||
case 'showDialog':
|
||||
showDialog(msg);
|
||||
break;
|
||||
case 'filterResultset': {
|
||||
filterResultset = msg.resultset;
|
||||
$id('resultsetCount').textContent = filterResultset.length;
|
||||
if ( filterResultset.length !== 0 ) {
|
||||
case 'resultsetDetails': {
|
||||
resultsetOpt = msg.opt;
|
||||
$id('resultsetCount').textContent = msg.count;
|
||||
if ( msg.count !== 0 ) {
|
||||
$id('create').removeAttribute('disabled');
|
||||
} else {
|
||||
$id('create').setAttribute('disabled', '');
|
||||
|
|
|
@ -278,7 +278,11 @@ const handlers = {
|
|||
continue;
|
||||
}
|
||||
const details = JSON.parse(selector);
|
||||
if ( details.action === 'style' ) {
|
||||
if (
|
||||
details.action !== undefined &&
|
||||
details.tasks === undefined &&
|
||||
details.action[0] === ':style'
|
||||
) {
|
||||
exceptionDict.set(details.selector, details.raw);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -501,16 +501,15 @@ const cosmeticFilterMapper = (function() {
|
|||
}
|
||||
|
||||
const nodesFromStyleTag = function(rootNode) {
|
||||
var filterMap = roRedNodes,
|
||||
entry, selector, canonical, nodes, node;
|
||||
|
||||
var details = vAPI.domFilterer.getAllSelectors();
|
||||
const filterMap = roRedNodes;
|
||||
const details = vAPI.domFilterer.getAllSelectors();
|
||||
|
||||
// Declarative selectors.
|
||||
for ( entry of (details.declarative || []) ) {
|
||||
for ( selector of entry[0].split(',\n') ) {
|
||||
canonical = selector;
|
||||
if ( entry[1] !== 'display:none!important;' ) {
|
||||
for ( const entry of (details.declarative || []) ) {
|
||||
for ( const selector of entry[0].split(',\n') ) {
|
||||
let canonical = selector;
|
||||
let nodes;
|
||||
if ( entry[1] !== vAPI.hideStyle ) {
|
||||
canonical += ':style(' + entry[1] + ')';
|
||||
}
|
||||
if ( reHasCSSCombinators.test(selector) ) {
|
||||
|
@ -524,7 +523,7 @@ const cosmeticFilterMapper = (function() {
|
|||
}
|
||||
nodes = rootNode.querySelectorAll(selector);
|
||||
}
|
||||
for ( node of nodes ) {
|
||||
for ( const node of nodes ) {
|
||||
if ( filterMap.has(node) === false ) {
|
||||
filterMap.set(node, canonical);
|
||||
}
|
||||
|
@ -533,9 +532,9 @@ const cosmeticFilterMapper = (function() {
|
|||
}
|
||||
|
||||
// Procedural selectors.
|
||||
for ( entry of (details.procedural || []) ) {
|
||||
nodes = entry.exec();
|
||||
for ( node of nodes ) {
|
||||
for ( const entry of (details.procedural || []) ) {
|
||||
const nodes = entry.exec();
|
||||
for ( const node of nodes ) {
|
||||
// Upgrade declarative selector to procedural one
|
||||
filterMap.set(node, entry.raw);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
if ( isNaN(surveyResults.hiddenElementCount) ) {
|
||||
surveyResults.hiddenElementCount = (( ) => {
|
||||
if ( vAPI.domFilterer instanceof Object === false ) { return 0; }
|
||||
const details = vAPI.domFilterer.getAllSelectors_(true);
|
||||
const details = vAPI.domFilterer.getAllSelectors(0b11);
|
||||
if (
|
||||
Array.isArray(details.declarative) === false ||
|
||||
details.declarative.length === 0
|
||||
|
@ -59,7 +59,7 @@
|
|||
return 0;
|
||||
}
|
||||
return document.querySelectorAll(
|
||||
details.declarative.map(entry => entry[0]).join(',')
|
||||
details.declarative.map(entry => entry[0]).join(',')
|
||||
).length;
|
||||
})();
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ const lastNetFilterSession = window.location.host + window.location.pathname;
|
|||
let lastNetFilterHostname = '';
|
||||
let lastNetFilterUnion = '';
|
||||
|
||||
const hideBackgroundStyle = 'background-image:none!important;';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const safeQuerySelectorAll = function(node, selector) {
|
||||
|
@ -645,10 +647,10 @@ const filterToDOMInterface = (( ) => {
|
|||
reFilter.test(elem.currentSrc)
|
||||
) {
|
||||
out.push({
|
||||
type: 'network',
|
||||
elem: elem,
|
||||
elem,
|
||||
src: srcProp,
|
||||
opts: filterTypes[elem.localName],
|
||||
opt: filterTypes[elem.localName],
|
||||
style: vAPI.hideStyle,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -657,10 +659,10 @@ const filterToDOMInterface = (( ) => {
|
|||
for ( const elem of candidateElements ) {
|
||||
if ( reFilter.test(backgroundImageURLFromElement(elem)) ) {
|
||||
out.push({
|
||||
type: 'network',
|
||||
elem: elem,
|
||||
style: 'background-image',
|
||||
opts: 'image',
|
||||
elem,
|
||||
bg: true,
|
||||
opt: 'image',
|
||||
style: hideBackgroundStyle,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -690,7 +692,7 @@ const filterToDOMInterface = (( ) => {
|
|||
const out = [];
|
||||
for ( const elem of elems ) {
|
||||
if ( elem === pickerRoot ) { continue; }
|
||||
out.push({ type: 'cosmetic', elem, raw });
|
||||
out.push({ elem, raw, style: vAPI.hideStyle });
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
@ -702,33 +704,28 @@ const filterToDOMInterface = (( ) => {
|
|||
// Remove trailing pseudo-element when querying.
|
||||
const fromCompiledCosmeticFilter = function(raw) {
|
||||
if ( typeof raw !== 'string' ) { return; }
|
||||
let elems;
|
||||
let elems, style;
|
||||
try {
|
||||
const o = JSON.parse(raw);
|
||||
if ( o.action === 'style' ) {
|
||||
elems = document.querySelectorAll(
|
||||
o.selector.replace(rePseudoElements, '')
|
||||
);
|
||||
lastAction = o.selector + ' {' + o.tasks[0][1] + '}';
|
||||
} else if ( o.tasks ) {
|
||||
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
|
||||
}
|
||||
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
|
||||
style = o.action === undefined || o.action[0] !== ':style'
|
||||
? vAPI.hideStyle
|
||||
: o.action[1];
|
||||
} catch(ex) {
|
||||
return;
|
||||
}
|
||||
if ( !elems ) { return; }
|
||||
const out = [];
|
||||
for ( const elem of elems ) {
|
||||
out.push({ type: 'cosmetic', elem, raw });
|
||||
out.push({ elem, raw, style });
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
vAPI.epickerStyleProxies = vAPI.epickerStyleProxies || new Map();
|
||||
|
||||
let lastFilter;
|
||||
let lastResultset;
|
||||
let lastAction;
|
||||
let appliedStyleTag;
|
||||
let applied = false;
|
||||
let previewing = false;
|
||||
|
||||
const queryAll = function(details) {
|
||||
|
@ -738,11 +735,10 @@ const filterToDOMInterface = (( ) => {
|
|||
unapply();
|
||||
if ( filter === '' || filter === '!' ) {
|
||||
lastFilter = '';
|
||||
lastResultset = [];
|
||||
return lastResultset;
|
||||
lastResultset = undefined;
|
||||
return;
|
||||
}
|
||||
lastFilter = filter;
|
||||
lastAction = undefined;
|
||||
if ( filter.startsWith('##') === false ) {
|
||||
lastResultset = fromNetworkFilter(filter);
|
||||
if ( previewing ) { apply(); }
|
||||
|
@ -759,86 +755,29 @@ const filterToDOMInterface = (( ) => {
|
|||
return lastResultset;
|
||||
};
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/1629
|
||||
// Avoid hiding the element picker's related elements.
|
||||
const applyHide = function() {
|
||||
const htmlElem = document.documentElement;
|
||||
for ( const item of lastResultset ) {
|
||||
const elem = item.elem;
|
||||
if ( elem === pickerRoot ) { continue; }
|
||||
if (
|
||||
(elem !== htmlElem) &&
|
||||
(item.type === 'cosmetic' || item.type === 'network' && item.src !== undefined)
|
||||
) {
|
||||
vAPI.domFilterer.hideNode(elem);
|
||||
item.hidden = true;
|
||||
}
|
||||
if ( item.type === 'network' && item.style === 'background-image' ) {
|
||||
const style = elem.style;
|
||||
item.backgroundImage = style.getPropertyValue('background-image');
|
||||
item.backgroundImagePriority = style.getPropertyPriority('background-image');
|
||||
style.setProperty('background-image', 'none', 'important');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const unapplyHide = function() {
|
||||
if ( lastResultset === undefined ) { return; }
|
||||
for ( const item of lastResultset ) {
|
||||
if ( item.hidden === true ) {
|
||||
vAPI.domFilterer.unhideNode(item.elem);
|
||||
item.hidden = false;
|
||||
}
|
||||
if ( item.hasOwnProperty('backgroundImage') ) {
|
||||
item.elem.style.setProperty(
|
||||
'background-image',
|
||||
item.backgroundImage,
|
||||
item.backgroundImagePriority
|
||||
);
|
||||
delete item.backgroundImage;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const unapplyStyle = function() {
|
||||
if ( !appliedStyleTag || appliedStyleTag.parentNode === null ) {
|
||||
return;
|
||||
}
|
||||
appliedStyleTag.parentNode.removeChild(appliedStyleTag);
|
||||
};
|
||||
|
||||
const applyStyle = function() {
|
||||
if ( !appliedStyleTag ) {
|
||||
appliedStyleTag = document.createElement('style');
|
||||
appliedStyleTag.setAttribute('type', 'text/css');
|
||||
}
|
||||
appliedStyleTag.textContent = lastAction;
|
||||
if ( appliedStyleTag.parentNode === null ) {
|
||||
document.head.appendChild(appliedStyleTag);
|
||||
}
|
||||
};
|
||||
|
||||
const apply = function() {
|
||||
if ( applied ) {
|
||||
unapply();
|
||||
unapply();
|
||||
if ( Array.isArray(lastResultset) === false ) { return; }
|
||||
const rootElem = document.documentElement;
|
||||
for ( const { elem, style } of lastResultset ) {
|
||||
if ( elem === pickerRoot ) { continue; }
|
||||
if ( elem === rootElem && style === vAPI.hideStyle ) { continue; }
|
||||
let styleToken = vAPI.epickerStyleProxies.get(style);
|
||||
if ( styleToken === undefined ) {
|
||||
styleToken = vAPI.randomToken();
|
||||
vAPI.epickerStyleProxies.set(style, styleToken);
|
||||
vAPI.userStylesheet.add(`[${styleToken}]\n{${style}}`, true);
|
||||
}
|
||||
elem.setAttribute(styleToken, '');
|
||||
}
|
||||
if ( lastResultset === undefined ) { return; }
|
||||
if ( typeof lastAction === 'string' ) {
|
||||
applyStyle();
|
||||
} else {
|
||||
applyHide();
|
||||
}
|
||||
applied = true;
|
||||
};
|
||||
|
||||
const unapply = function() {
|
||||
if ( !applied ) { return; }
|
||||
if ( typeof lastAction === 'string' ) {
|
||||
unapplyStyle();
|
||||
} else {
|
||||
unapplyHide();
|
||||
for ( const styleToken of vAPI.epickerStyleProxies.values() ) {
|
||||
for ( const elem of document.querySelectorAll(`[${styleToken}]`) ) {
|
||||
elem.removeAttribute(styleToken);
|
||||
}
|
||||
}
|
||||
applied = false;
|
||||
};
|
||||
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/c62irc/
|
||||
|
@ -849,24 +788,24 @@ const filterToDOMInterface = (( ) => {
|
|||
if ( previewing === false ) {
|
||||
return unapply();
|
||||
}
|
||||
if ( lastResultset === undefined ) { return; }
|
||||
apply();
|
||||
if ( permanent === false ) { return; }
|
||||
if ( Array.isArray(lastResultset) === false ) { return; }
|
||||
if ( permanent === false || lastFilter.startsWith('##') === false ) {
|
||||
return apply();
|
||||
}
|
||||
if ( vAPI.domFilterer instanceof Object === false ) { return; }
|
||||
const cssSelectors = new Set();
|
||||
const proceduralSelectors = new Set();
|
||||
for ( const item of lastResultset ) {
|
||||
if ( item.type !== 'cosmetic' ) { continue; }
|
||||
if ( item.raw.startsWith('{') ) {
|
||||
proceduralSelectors.add(item.raw);
|
||||
for ( const { raw } of lastResultset ) {
|
||||
if ( raw.startsWith('{') ) {
|
||||
proceduralSelectors.add(raw);
|
||||
} else {
|
||||
cssSelectors.add(item.raw);
|
||||
cssSelectors.add(raw);
|
||||
}
|
||||
}
|
||||
if ( cssSelectors.size !== 0 ) {
|
||||
vAPI.domFilterer.addCSSRule(
|
||||
Array.from(cssSelectors),
|
||||
'display:none!important;'
|
||||
vAPI.hideStyle
|
||||
);
|
||||
}
|
||||
if ( proceduralSelectors.size !== 0 ) {
|
||||
|
@ -876,11 +815,7 @@ const filterToDOMInterface = (( ) => {
|
|||
}
|
||||
};
|
||||
|
||||
return {
|
||||
get previewing() { return previewing; },
|
||||
preview,
|
||||
queryAll,
|
||||
};
|
||||
return { preview, queryAll };
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -1091,11 +1026,6 @@ const quitPicker = function() {
|
|||
|
||||
if ( pickerRoot === null ) { return; }
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/2060
|
||||
if ( vAPI.domFilterer instanceof Object ) {
|
||||
vAPI.domFilterer.unexcludeNode(pickerRoot);
|
||||
}
|
||||
|
||||
pickerRoot.remove();
|
||||
pickerRoot = null;
|
||||
|
||||
|
@ -1118,16 +1048,13 @@ const onDialogMessage = function(msg) {
|
|||
quitPicker();
|
||||
break;
|
||||
case 'dialogSetFilter': {
|
||||
const resultset = filterToDOMInterface.queryAll(msg);
|
||||
const resultset = filterToDOMInterface.queryAll(msg) || [];
|
||||
highlightElements(resultset.map(a => a.elem), true);
|
||||
if ( msg.filter === '!' ) { break; }
|
||||
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||
what: 'filterResultset',
|
||||
resultset: resultset.map(a => {
|
||||
const o = Object.assign({}, a);
|
||||
o.elem = undefined;
|
||||
return o;
|
||||
}),
|
||||
what: 'resultsetDetails',
|
||||
count: resultset.length,
|
||||
opt: resultset.length !== 0 ? resultset[0].opt : undefined,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -1250,7 +1177,7 @@ const pickerCSSStyle = [
|
|||
].join(' !important;');
|
||||
|
||||
const pickerCSS = `
|
||||
:root [${vAPI.sessionId}] {
|
||||
:root > [${vAPI.sessionId}] {
|
||||
${pickerCSSStyle}
|
||||
}
|
||||
:root [${vAPI.sessionId}-clickblind] {
|
||||
|
@ -1265,11 +1192,6 @@ pickerRoot = document.createElement('iframe');
|
|||
pickerRoot.setAttribute(vAPI.sessionId, '');
|
||||
document.documentElement.append(pickerRoot);
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/2060
|
||||
if ( vAPI.domFilterer instanceof Object ) {
|
||||
vAPI.domFilterer.excludeNode(pickerRoot);
|
||||
}
|
||||
|
||||
vAPI.shutdown.add(quitPicker);
|
||||
|
||||
vAPI.MessagingConnection.addListener(onConnectionMessage);
|
||||
|
|
|
@ -1177,7 +1177,7 @@ Parser.prototype.SelectorCompiler = class {
|
|||
]);
|
||||
this.reSimpleSelector = /^[#.][A-Za-z_][\w-]*$/;
|
||||
this.div = document.createElement('div');
|
||||
this.rePseudoClass = /:(?::?after|:?before|:-?[a-z][a-z-]*[a-z])$/;
|
||||
this.rePseudoElement = /:(?::?after|:?before|:-?[a-z][a-z-]*[a-z])$/;
|
||||
this.reProceduralOperator = new RegExp([
|
||||
'^(?:',
|
||||
Array.from(parser.proceduralOperatorTokens.keys()).join('|'),
|
||||
|
@ -1296,7 +1296,7 @@ Parser.prototype.SelectorCompiler = class {
|
|||
// is fixed.
|
||||
cssSelectorType(s) {
|
||||
if ( this.reSimpleSelector.test(s) ) { return 1; }
|
||||
const pos = this.cssPseudoSelector(s);
|
||||
const pos = this.cssPseudoElement(s);
|
||||
if ( pos !== -1 ) {
|
||||
return this.cssSelectorType(s.slice(0, pos)) === 1 ? 3 : 0;
|
||||
}
|
||||
|
@ -1308,9 +1308,9 @@ Parser.prototype.SelectorCompiler = class {
|
|||
return 1;
|
||||
}
|
||||
|
||||
cssPseudoSelector(s) {
|
||||
cssPseudoElement(s) {
|
||||
if ( s.lastIndexOf(':') === -1 ) { return -1; }
|
||||
const match = this.rePseudoClass.exec(s);
|
||||
const match = this.rePseudoElement.exec(s);
|
||||
return match !== null ? match.index : -1;
|
||||
}
|
||||
|
||||
|
@ -1450,13 +1450,10 @@ Parser.prototype.SelectorCompiler = class {
|
|||
// The normalized string version is what is reported in the logger,
|
||||
// by design.
|
||||
decompileProcedural(compiled) {
|
||||
const tasks = compiled.tasks;
|
||||
if ( Array.isArray(tasks) === false ) {
|
||||
return compiled.selector;
|
||||
}
|
||||
const tasks = compiled.tasks || [];
|
||||
const raw = [ compiled.selector ];
|
||||
let value;
|
||||
for ( const task of tasks ) {
|
||||
let value;
|
||||
switch ( task[0] ) {
|
||||
case ':has':
|
||||
case ':if':
|
||||
|
@ -1494,8 +1491,6 @@ Parser.prototype.SelectorCompiler = class {
|
|||
raw.push(task[1]);
|
||||
break;
|
||||
case ':min-text-length':
|
||||
case ':remove':
|
||||
case ':style':
|
||||
case ':upward':
|
||||
case ':watch-attr':
|
||||
case ':xpath':
|
||||
|
@ -1503,6 +1498,10 @@ Parser.prototype.SelectorCompiler = class {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if ( Array.isArray(compiled.action) ) {
|
||||
const [ op, arg ] = compiled.action;
|
||||
raw.push(`${op}(${arg})`);
|
||||
}
|
||||
return raw.join('');
|
||||
}
|
||||
|
||||
|
@ -1578,10 +1577,12 @@ Parser.prototype.SelectorCompiler = class {
|
|||
tasks.push([ ':spath', spath ]);
|
||||
}
|
||||
if ( action !== undefined ) { return; }
|
||||
tasks.push([ operator, args ]);
|
||||
const task = [ operator, args ];
|
||||
if ( this.actionOperators.has(operator) ) {
|
||||
if ( root === false ) { return; }
|
||||
action = operator.slice(1);
|
||||
action = task;
|
||||
} else {
|
||||
tasks.push(task);
|
||||
}
|
||||
opPrefixBeg = i;
|
||||
if ( i === n ) { break; }
|
||||
|
@ -1589,7 +1590,7 @@ Parser.prototype.SelectorCompiler = class {
|
|||
|
||||
// No task found: then we have a CSS selector.
|
||||
// At least one task found: nothing should be left to parse.
|
||||
if ( tasks.length === 0 ) {
|
||||
if ( tasks.length === 0 && action === undefined ) {
|
||||
prefix = raw;
|
||||
} else if ( opPrefixBeg < n ) {
|
||||
if ( action !== undefined ) { return; }
|
||||
|
@ -1626,21 +1627,17 @@ Parser.prototype.SelectorCompiler = class {
|
|||
}
|
||||
|
||||
// Expose action to take in root descriptor.
|
||||
//
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/961
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/382
|
||||
// For the time being, `style` action can't be used in a
|
||||
// procedural selector.
|
||||
if ( action !== undefined ) {
|
||||
if ( tasks.length > 1 && action === 'style' ) { return; }
|
||||
out.action = action;
|
||||
}
|
||||
|
||||
// Pseudo-selectors are valid only when used in a root task list.
|
||||
// Pseudo elements are valid only when used in a root task list AND
|
||||
// only when there are no procedural operators: pseudo elements can't
|
||||
// be querySelectorAll-ed.
|
||||
if ( prefix !== '' ) {
|
||||
const pos = this.cssPseudoSelector(prefix);
|
||||
const pos = this.cssPseudoElement(prefix);
|
||||
if ( pos !== -1 ) {
|
||||
if ( root === false ) { return; }
|
||||
if ( root === false || tasks.length !== 0 ) { return; }
|
||||
out.pseudo = pos;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue