mirror of https://github.com/gorhill/uBlock.git
refactor content script code + add support for new `:has` & `:xpath` filters
Aside extending cosmetic filtering abilities, I expect this will also take care of some long standing issues (I will have to find them and mark them as "resolved" by this commit, as time allow).
This commit is contained in:
parent
72fdce64f0
commit
6c513629bf
|
@ -27,16 +27,10 @@
|
|||
"content_scripts": [
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["js/vapi-client.js", "js/contentscript-start.js"],
|
||||
"js": ["js/vapi-client.js", "js/contentscript.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["js/contentscript-end.js"],
|
||||
"run_at": "document_end",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["js/scriptlets/subscriber.js"],
|
||||
|
|
|
@ -1076,16 +1076,6 @@ vAPI.onLoadAllCompleted = function() {
|
|||
var scriptDone = function() {
|
||||
vAPI.lastError();
|
||||
};
|
||||
var scriptEnd = function(tabId) {
|
||||
if ( vAPI.lastError() ) {
|
||||
return;
|
||||
}
|
||||
vAPI.tabs.injectScript(tabId, {
|
||||
file: 'js/contentscript-end.js',
|
||||
allFrames: true,
|
||||
runAt: 'document_idle'
|
||||
}, scriptDone);
|
||||
};
|
||||
var scriptStart = function(tabId) {
|
||||
vAPI.tabs.injectScript(tabId, {
|
||||
file: 'js/vapi-client.js',
|
||||
|
@ -1093,10 +1083,10 @@ vAPI.onLoadAllCompleted = function() {
|
|||
runAt: 'document_idle'
|
||||
}, function(){ });
|
||||
vAPI.tabs.injectScript(tabId, {
|
||||
file: 'js/contentscript-start.js',
|
||||
file: 'js/contentscript.js',
|
||||
allFrames: true,
|
||||
runAt: 'document_idle'
|
||||
}, function(){ scriptEnd(tabId); });
|
||||
}, scriptDone);
|
||||
};
|
||||
var bindToTabs = function(tabs) {
|
||||
var µb = µBlock;
|
||||
|
|
|
@ -21,14 +21,14 @@
|
|||
|
||||
/* global HTMLDocument, XMLDocument */
|
||||
|
||||
'use strict';
|
||||
|
||||
// For non background pages
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function(self) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
µBlock - a browser extension to block requests.
|
||||
Copyright (C) 2014 The µBlock authors
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-2016 The uBlock Origin authors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -442,7 +442,7 @@ var contentObserver = {
|
|||
|
||||
try {
|
||||
lss(this.contentBaseURI + 'vapi-client.js', sandbox);
|
||||
lss(this.contentBaseURI + 'contentscript-start.js', sandbox);
|
||||
lss(this.contentBaseURI + 'contentscript.js', sandbox);
|
||||
} catch (ex) {
|
||||
//console.exception(ex.msg, ex.stack);
|
||||
return;
|
||||
|
@ -451,7 +451,6 @@ var contentObserver = {
|
|||
let docReady = (e) => {
|
||||
let doc = e.target;
|
||||
doc.removeEventListener(e.type, docReady, true);
|
||||
lss(this.contentBaseURI + 'contentscript-end.js', sandbox);
|
||||
|
||||
if (
|
||||
doc.querySelector('a[href^="abp:"],a[href^="https://subscribe.adblockplus.org/?"]') ||
|
||||
|
|
|
@ -27,16 +27,10 @@
|
|||
"content_scripts": [
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["js/vapi-client.js", "js/contentscript-start.js"],
|
||||
"js": ["js/vapi-client.js", "js/contentscript.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["js/contentscript-end.js"],
|
||||
"run_at": "document_end",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["js/scriptlets/subscriber.js"],
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-2016 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* jshint multistr: true */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Injected into content pages
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/456
|
||||
// Already injected?
|
||||
if ( vAPI.contentscriptStartInjected ) {
|
||||
return;
|
||||
}
|
||||
vAPI.contentscriptStartInjected = true;
|
||||
vAPI.styles = vAPI.styles || [];
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// Domain-based ABP cosmetic filters.
|
||||
// These can be inserted before the DOM is loaded.
|
||||
|
||||
var cosmeticFilters = function(details) {
|
||||
var donthideCosmeticFilters = {};
|
||||
var hideCosmeticFilters = {};
|
||||
var donthide = details.cosmeticDonthide;
|
||||
var hide = details.cosmeticHide;
|
||||
var i;
|
||||
if ( donthide.length !== 0 ) {
|
||||
i = donthide.length;
|
||||
while ( i-- ) {
|
||||
donthideCosmeticFilters[donthide[i]] = true;
|
||||
}
|
||||
}
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/143
|
||||
if ( hide.length !== 0 ) {
|
||||
i = hide.length;
|
||||
var selector;
|
||||
while ( i-- ) {
|
||||
selector = hide[i];
|
||||
if ( donthideCosmeticFilters[selector] ) {
|
||||
hide.splice(i, 1);
|
||||
} else {
|
||||
hideCosmeticFilters[selector] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( hide.length !== 0 ) {
|
||||
// https://github.com/gorhill/uBlock/issues/1015
|
||||
// Boost specificity of our CSS rules.
|
||||
var styleText = ':root ' + hide.join(',\n:root ');
|
||||
var style = document.createElement('style');
|
||||
style.setAttribute('type', 'text/css');
|
||||
// The linefeed before the style block is very important: do not remove!
|
||||
style.appendChild(document.createTextNode(styleText + '\n{display:none !important;}'));
|
||||
//console.debug('µBlock> "%s" cosmetic filters: injecting %d CSS rules:', details.domain, details.hide.length, hideStyleText);
|
||||
var parent = document.head || document.documentElement;
|
||||
if ( parent ) {
|
||||
parent.appendChild(style);
|
||||
vAPI.styles.push(style);
|
||||
}
|
||||
hideElements(styleText);
|
||||
}
|
||||
vAPI.donthideCosmeticFilters = donthideCosmeticFilters;
|
||||
vAPI.hideCosmeticFilters = hideCosmeticFilters;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var netFilters = function(details) {
|
||||
var parent = document.head || document.documentElement;
|
||||
if ( !parent ) {
|
||||
return;
|
||||
}
|
||||
var style = document.createElement('style');
|
||||
var text = details.netHide.join(',\n');
|
||||
var css = details.netCollapse ?
|
||||
'\n{display:none !important;}' :
|
||||
'\n{visibility:hidden !important;}';
|
||||
style.appendChild(document.createTextNode(text + css));
|
||||
parent.appendChild(style);
|
||||
//console.debug('document.querySelectorAll("%s") = %o', text, document.querySelectorAll(text));
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Create script tags and assign data URIs looked up from our library of
|
||||
// redirection resources: Sometimes it is useful to use these resources as
|
||||
// standalone scriptlets. These scriptlets are injected from within the
|
||||
// content scripts because what must be injected, if anything, depends on the
|
||||
// currently active filters, as selected by the user.
|
||||
// Library of redirection resources is located at:
|
||||
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
|
||||
|
||||
var injectScripts = function(scripts) {
|
||||
var parent = document.head || document.documentElement;
|
||||
if ( !parent ) {
|
||||
return;
|
||||
}
|
||||
var scriptTag = document.createElement('script');
|
||||
scriptTag.appendChild(document.createTextNode(scripts));
|
||||
parent.appendChild(scriptTag);
|
||||
vAPI.injectedScripts = scripts;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var filteringHandler = function(details) {
|
||||
var styleTagCount = vAPI.styles.length;
|
||||
|
||||
if ( details ) {
|
||||
if (
|
||||
(vAPI.skipCosmeticFiltering = details.skipCosmeticFiltering) !== true &&
|
||||
(details.cosmeticHide.length !== 0 || details.cosmeticDonthide.length !== 0)
|
||||
) {
|
||||
cosmeticFilters(details);
|
||||
}
|
||||
if ( details.netHide.length !== 0 ) {
|
||||
netFilters(details);
|
||||
}
|
||||
if ( details.scripts ) {
|
||||
injectScripts(details.scripts);
|
||||
}
|
||||
// The port will never be used again at this point, disconnecting allows
|
||||
// the browser to flush this script from memory.
|
||||
}
|
||||
|
||||
// This is just to inform the background process that cosmetic filters were
|
||||
// actually injected.
|
||||
if ( vAPI.styles.length !== styleTagCount ) {
|
||||
vAPI.messaging.send('contentscript', { what: 'cosmeticFiltersActivated' });
|
||||
}
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/587
|
||||
// If no filters were found, maybe the script was injected before uBlock's
|
||||
// process was fully initialized. When this happens, pages won't be
|
||||
// cleaned right after browser launch.
|
||||
vAPI.contentscriptStartInjected = details && details.ready;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var hideElements = function(selectors) {
|
||||
if ( document.body === null ) {
|
||||
return;
|
||||
}
|
||||
var elems = document.querySelectorAll(selectors);
|
||||
var i = elems.length;
|
||||
if ( i === 0 ) {
|
||||
return;
|
||||
}
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/158
|
||||
// Using CSSStyleDeclaration.setProperty is more reliable
|
||||
if ( document.body.shadowRoot === undefined ) {
|
||||
while ( i-- ) {
|
||||
elems[i].style.setProperty('display', 'none', 'important');
|
||||
}
|
||||
return;
|
||||
}
|
||||
// https://github.com/gorhill/uBlock/issues/435
|
||||
// Using shadow content so that we do not have to modify style
|
||||
// attribute.
|
||||
var sessionId = vAPI.sessionId;
|
||||
var elem, shadow;
|
||||
while ( i-- ) {
|
||||
elem = elems[i];
|
||||
shadow = elem.shadowRoot;
|
||||
// https://www.chromestatus.com/features/4668884095336448
|
||||
// "Multiple shadow roots is being deprecated."
|
||||
if ( shadow !== null ) {
|
||||
if ( shadow.className !== sessionId ) {
|
||||
elem.style.setProperty('display', 'none', 'important');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// https://github.com/gorhill/uBlock/pull/555
|
||||
// Not all nodes can be shadowed:
|
||||
// https://github.com/w3c/webcomponents/issues/102
|
||||
// https://github.com/gorhill/uBlock/issues/762
|
||||
// Remove display style that might get in the way of the shadow
|
||||
// node doing its magic.
|
||||
try {
|
||||
shadow = elem.createShadowRoot();
|
||||
shadow.className = sessionId;
|
||||
elem.style.removeProperty('display');
|
||||
} catch (ex) {
|
||||
elem.style.setProperty('display', 'none', 'important');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var url = window.location.href;
|
||||
vAPI.messaging.send(
|
||||
'contentscript',
|
||||
{
|
||||
what: 'retrieveDomainCosmeticSelectors',
|
||||
pageURL: url,
|
||||
locationURL: url
|
||||
},
|
||||
filteringHandler
|
||||
);
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
µBlock - a browser extension to block requests.
|
||||
Copyright (C) 2014 Raymond Hill
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-2016 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,14 +20,14 @@
|
|||
*/
|
||||
|
||||
/* jshint bitwise: false */
|
||||
/* global punycode, µBlock */
|
||||
/* global punycode */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.cosmeticFilteringEngine = (function(){
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
@ -141,40 +141,6 @@ FilterPlainMore.fromSelfie = function(s) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
var FilterBucket = function(a, b) {
|
||||
this.f = null;
|
||||
this.filters = [];
|
||||
if ( a !== undefined ) {
|
||||
this.filters[0] = a;
|
||||
if ( b !== undefined ) {
|
||||
this.filters[1] = b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FilterBucket.prototype.add = function(a) {
|
||||
this.filters.push(a);
|
||||
};
|
||||
|
||||
FilterBucket.prototype.retrieve = function(s, out) {
|
||||
var i = this.filters.length;
|
||||
while ( i-- ) {
|
||||
this.filters[i].retrieve(s, out);
|
||||
}
|
||||
};
|
||||
|
||||
FilterBucket.prototype.fid = '[]';
|
||||
|
||||
FilterBucket.prototype.toSelfie = function() {
|
||||
return this.filters.length.toString();
|
||||
};
|
||||
|
||||
FilterBucket.fromSelfie = function() {
|
||||
return new FilterBucket();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Any selector specific to a hostname
|
||||
// Examples:
|
||||
// search.snapdo.com###ABottomD
|
||||
|
@ -235,6 +201,40 @@ FilterEntity.fromSelfie = function(s) {
|
|||
return new FilterEntity(decode(s.slice(0, pos)), s.slice(pos + 1));
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var FilterBucket = function(a, b) {
|
||||
this.f = null;
|
||||
this.filters = [];
|
||||
if ( a !== undefined ) {
|
||||
this.filters[0] = a;
|
||||
if ( b !== undefined ) {
|
||||
this.filters[1] = b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FilterBucket.prototype.add = function(a) {
|
||||
this.filters.push(a);
|
||||
};
|
||||
|
||||
FilterBucket.prototype.retrieve = function(s, out) {
|
||||
var i = this.filters.length;
|
||||
while ( i-- ) {
|
||||
this.filters[i].retrieve(s, out);
|
||||
}
|
||||
};
|
||||
|
||||
FilterBucket.prototype.fid = '[]';
|
||||
|
||||
FilterBucket.prototype.toSelfie = function() {
|
||||
return this.filters.length.toString();
|
||||
};
|
||||
|
||||
FilterBucket.fromSelfie = function() {
|
||||
return new FilterBucket();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -245,11 +245,13 @@ var FilterParser = function() {
|
|||
this.invalid = false;
|
||||
this.cosmetic = true;
|
||||
this.reScriptTagFilter = /^script:(contains|inject)\((.+?)\)$/;
|
||||
this.reNeedHostname = /^(?:.+?:has|:xpath)\(.+?\)$/;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterParser.prototype.reset = function() {
|
||||
this.raw = '';
|
||||
this.prefix = this.suffix = this.style = '';
|
||||
this.unhide = 0;
|
||||
this.hostnames.length = 0;
|
||||
|
@ -264,6 +266,8 @@ FilterParser.prototype.parse = function(raw) {
|
|||
// important!
|
||||
this.reset();
|
||||
|
||||
this.raw = raw;
|
||||
|
||||
// Find the bounds of the anchor.
|
||||
var lpos = raw.indexOf('#');
|
||||
if ( lpos === -1 ) {
|
||||
|
@ -349,6 +353,16 @@ FilterParser.prototype.parse = function(raw) {
|
|||
this.hostnames = this.prefix.split(/\s*,\s*/);
|
||||
}
|
||||
|
||||
// For some selectors, it is mandatory to have a hostname or entity.
|
||||
if (
|
||||
this.hostnames.length === 0 &&
|
||||
this.unhide === 0 &&
|
||||
this.reNeedHostname.test(this.suffix)
|
||||
) {
|
||||
this.invalid = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Script tag filters: pre-process them so that can be used with minimal
|
||||
// overhead in the content script.
|
||||
// Examples:
|
||||
|
@ -357,10 +371,16 @@ FilterParser.prototype.parse = function(raw) {
|
|||
// focus.de##script:inject(uabinject-defuser.js)
|
||||
|
||||
var matches = this.reScriptTagFilter.exec(this.suffix);
|
||||
if ( matches === null ) {
|
||||
return this;
|
||||
if ( matches !== null ) {
|
||||
return this.parseScriptTagFilter(matches);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterParser.prototype.parseScriptTagFilter = function(matches) {
|
||||
// Currently supported only as non-generic selector. Also, exception
|
||||
// script tag filter makes no sense, ignore.
|
||||
if ( this.hostnames.length === 0 || this.unhide === 1 ) {
|
||||
|
@ -378,7 +398,7 @@ FilterParser.prototype.parse = function(raw) {
|
|||
} else {
|
||||
token = token.slice(1, -1);
|
||||
if ( isBadRegex(token) ) {
|
||||
µb.logger.writeOne('', 'error', 'Cosmetic filtering – bad regular expression: ' + raw + ' (' + isBadRegex.message + ')');
|
||||
µb.logger.writeOne('', 'error', 'Cosmetic filtering – bad regular expression: ' + this.raw + ' (' + isBadRegex.message + ')');
|
||||
this.invalid = true;
|
||||
}
|
||||
}
|
||||
|
@ -676,19 +696,33 @@ FilterContainer.prototype.reset = function() {
|
|||
|
||||
FilterContainer.prototype.isValidSelector = (function() {
|
||||
var div = document.createElement('div');
|
||||
|
||||
var matchesProp = (function() {
|
||||
if ( typeof div.matches === 'function' ) {
|
||||
return 'matches';
|
||||
}
|
||||
if ( typeof div.mozMatchesSelector === 'function' ) {
|
||||
return 'mozMatchesSelector';
|
||||
}
|
||||
if ( typeof div.webkitMatchesSelector === 'function' ) {
|
||||
return 'webkitMatchesSelector';
|
||||
}
|
||||
return '';
|
||||
})();
|
||||
// Not all browsers support `Element.matches`:
|
||||
// http://caniuse.com/#feat=matchesselector
|
||||
if ( typeof div.matches !== 'function' ) {
|
||||
if ( matchesProp === '' ) {
|
||||
return function() {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
var reHasSelector = /^(.+?):has\((.+?)\)$/;
|
||||
var reXpathSelector = /^:xpath\((.+?)\)$/;
|
||||
|
||||
return function(s) {
|
||||
try {
|
||||
// https://github.com/gorhill/uBlock/issues/693
|
||||
div.matches(s + ',\n#foo');
|
||||
div[matchesProp](s + ',\n#foo');
|
||||
// Discard new ABP's `-abp-properties` directive until it is
|
||||
// implemented (if ever).
|
||||
if ( s.indexOf('[-abp-properties=') === -1 ) {
|
||||
|
@ -697,6 +731,24 @@ FilterContainer.prototype.isValidSelector = (function() {
|
|||
} catch (e) {
|
||||
}
|
||||
// We reach this point very rarely.
|
||||
var matches;
|
||||
|
||||
// Future `:has`-based filter? If so, validate both parts of the whole
|
||||
// selector.
|
||||
matches = reHasSelector.exec(s);
|
||||
if ( matches !== null ) {
|
||||
return this.isValidSelector(matches[1]) && this.isValidSelector(matches[2]);
|
||||
}
|
||||
// Custom `:xpath`-based filter?
|
||||
matches = reXpathSelector.exec(s);
|
||||
if ( matches !== null ) {
|
||||
try {
|
||||
return document.createExpression(matches[1], null) instanceof XPathExpression;
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Special `script:` filter?
|
||||
if ( s.startsWith('script') ) {
|
||||
if ( s.startsWith('?', 6) || s.startsWith('+', 6) ) {
|
||||
return true;
|
||||
|
@ -924,7 +976,7 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
|
|||
|
||||
fields = line.split('\v');
|
||||
|
||||
// h ir twitter.com .promoted-tweet
|
||||
// h [\t] ir [\t] twitter.com [\t] .promoted-tweet
|
||||
if ( fields[0] === 'h' ) {
|
||||
// Special filter: script tags. Not a real CSS selector.
|
||||
if ( fields[3].startsWith('script') ) {
|
||||
|
@ -943,8 +995,8 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// lg 105 .largeAd
|
||||
// lg+ 2jx .Mpopup + #Mad > #MadZone
|
||||
// lg [\t] 105 [\t] .largeAd
|
||||
// lg+ [\t] 2jx [\t] .Mpopup + #Mad > #MadZone
|
||||
if ( fields[0] === 'lg' || fields[0] === 'lg+' ) {
|
||||
filter = fields[0] === 'lg' ?
|
||||
filterPlain :
|
||||
|
@ -960,7 +1012,7 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// entity selector
|
||||
// entity [\t] selector
|
||||
if ( fields[0] === 'e' ) {
|
||||
// Special filter: script tags. Not a real CSS selector.
|
||||
if ( fields[2].startsWith('script') ) {
|
||||
|
@ -1392,8 +1444,6 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
|
|||
hideHigh: this.highHighGenericHide,
|
||||
hideHighCount: this.highHighGenericHideCount
|
||||
};
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/497
|
||||
r.donthide = this.genericDonthide;
|
||||
}
|
||||
|
||||
var hideSelectors = r.hide;
|
||||
|
@ -1437,13 +1487,16 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) {
|
|||
// r.ready will tell the content script the cosmetic filtering engine is
|
||||
// up and ready.
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/497
|
||||
// Generic exception filters are to be applied on all pages.
|
||||
|
||||
var r = {
|
||||
ready: this.frozen,
|
||||
domain: domain,
|
||||
entity: pos === -1 ? domain : domain.slice(0, pos - domain.length),
|
||||
skipCosmeticFiltering: this.acceptedCount === 0,
|
||||
cosmeticHide: [],
|
||||
cosmeticDonthide: [],
|
||||
cosmeticDonthide: this.genericDonthide,
|
||||
netHide: [],
|
||||
netCollapse: µb.userSettings.collapseBlocked,
|
||||
scripts: this.retrieveScriptTags(domain, hostname)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015 Raymond Hill
|
||||
Copyright (C) 2015-2016 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,14 +19,14 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, uDom */
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var showdomButton = uDom.nodeFromId('showdom');
|
||||
|
@ -257,21 +257,17 @@ var countFromNode = function(li) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
var selectorFromNode = function(node, nth) {
|
||||
var selectorFromNode = function(node) {
|
||||
var selector = '';
|
||||
var code;
|
||||
if ( nth === undefined ) {
|
||||
nth = 1;
|
||||
}
|
||||
while ( node !== null ) {
|
||||
if ( node.localName === 'li' ) {
|
||||
code = node.querySelector('code:nth-of-type(' + nth + ')');
|
||||
code = node.querySelector('code');
|
||||
if ( code !== null ) {
|
||||
selector = code.textContent + ' > ' + selector;
|
||||
if ( selector.indexOf('#') !== -1 ) {
|
||||
break;
|
||||
}
|
||||
nth = 1;
|
||||
}
|
||||
}
|
||||
node = node.parentElement;
|
||||
|
@ -281,6 +277,21 @@ var selectorFromNode = function(node, nth) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
var selectorFromFilter = function(node) {
|
||||
while ( node !== null ) {
|
||||
if ( node.localName === 'li' ) {
|
||||
var code = node.querySelector('code:nth-of-type(2)');
|
||||
if ( code !== null ) {
|
||||
return code.textContent;
|
||||
}
|
||||
}
|
||||
node = node.parentElement;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var nidFromNode = function(node) {
|
||||
var li = node;
|
||||
while ( li !== null ) {
|
||||
|
@ -482,10 +493,11 @@ var onClick = function(ev) {
|
|||
messaging.sendTo(
|
||||
'loggerUI',
|
||||
{
|
||||
what: 'toggleNodes',
|
||||
what: 'toggleFilter',
|
||||
original: false,
|
||||
target: target.classList.toggle('off'),
|
||||
selector: selectorFromNode(target, 2),
|
||||
selector: selectorFromNode(target),
|
||||
filter: selectorFromFilter(target),
|
||||
nid: ''
|
||||
},
|
||||
inspectedTabId,
|
||||
|
@ -504,7 +516,7 @@ var onClick = function(ev) {
|
|||
what: 'toggleNodes',
|
||||
original: true,
|
||||
target: target.classList.toggle('off') === false,
|
||||
selector: selectorFromNode(target, 1),
|
||||
selector: selectorFromNode(target),
|
||||
nid: nidFromNode(target)
|
||||
},
|
||||
inspectedTabId,
|
||||
|
|
|
@ -19,42 +19,27 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var loggedSelectors = vAPI.loggedSelectors || {};
|
||||
|
||||
var injectedSelectors = [];
|
||||
var reProperties = /\s*\{[^}]+\}\s*/;
|
||||
var i;
|
||||
var styles = vAPI.styles || [];
|
||||
|
||||
i = styles.length;
|
||||
while ( i-- ) {
|
||||
injectedSelectors = injectedSelectors.concat(styles[i].textContent.replace(reProperties, '').split(/\s*,\n\s*/));
|
||||
}
|
||||
|
||||
if ( injectedSelectors.length === 0 ) {
|
||||
if ( typeof vAPI !== 'object' || typeof vAPI.domFilterer !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var matchedSelectors = [];
|
||||
var selector;
|
||||
var loggedSelectors = vAPI.loggedSelectors || {},
|
||||
matchedSelectors = [],
|
||||
selectors, i, selector, entry, nodes, j;
|
||||
|
||||
i = injectedSelectors.length;
|
||||
// CSS-based selectors.
|
||||
selectors = vAPI.domFilterer.simpleSelectors.concat(vAPI.domFilterer.complexSelectors);
|
||||
i = selectors.length;
|
||||
while ( i-- ) {
|
||||
selector = injectedSelectors[i];
|
||||
selector = selectors[i];
|
||||
if ( loggedSelectors.hasOwnProperty(selector) ) {
|
||||
continue;
|
||||
}
|
||||
|
@ -62,27 +47,67 @@ while ( i-- ) {
|
|||
continue;
|
||||
}
|
||||
loggedSelectors[selector] = true;
|
||||
// https://github.com/gorhill/uBlock/issues/1015
|
||||
// Discard `:root ` prefix.
|
||||
matchedSelectors.push(selector.slice(6));
|
||||
matchedSelectors.push(selector);
|
||||
}
|
||||
|
||||
// `:has`-based selectors.
|
||||
selectors = vAPI.domFilterer.simpleHasSelectors.concat(vAPI.domFilterer.complexHasSelectors);
|
||||
i = selectors.length;
|
||||
while ( i-- ) {
|
||||
entry = selectors[i];
|
||||
selector = entry.a + ':has(' + entry.b + ')';
|
||||
if ( loggedSelectors.hasOwnProperty(selector) ) {
|
||||
continue;
|
||||
}
|
||||
nodes = document.querySelectorAll(entry.a);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
if ( nodes[j].querySelector(entry.b) !== null ) {
|
||||
loggedSelectors[selector] = true;
|
||||
matchedSelectors.push(selector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `:xpath`-based selectors.
|
||||
var xpr = null,
|
||||
xpathexpr;
|
||||
selectors = vAPI.domFilterer.xpathSelectors;
|
||||
i = selectors.length;
|
||||
while ( i-- ) {
|
||||
xpathexpr = selectors[i];
|
||||
selector = ':xpath(' + xpathexpr + ')';
|
||||
if ( loggedSelectors.hasOwnProperty(selector) ) {
|
||||
continue;
|
||||
}
|
||||
xpr = document.evaluate(
|
||||
'boolean(' + xpathexpr + ')',
|
||||
document,
|
||||
null,
|
||||
XPathResult.BOOLEAN_TYPE,
|
||||
xpr
|
||||
);
|
||||
if ( xpr.booleanValue ) {
|
||||
loggedSelectors[selector] = true;
|
||||
matchedSelectors.push(selector);
|
||||
}
|
||||
}
|
||||
|
||||
vAPI.loggedSelectors = loggedSelectors;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.messaging.send(
|
||||
'scriptlets',
|
||||
{
|
||||
what: 'logCosmeticFilteringData',
|
||||
frameURL: window.location.href,
|
||||
frameHostname: window.location.hostname,
|
||||
matchedSelectors: matchedSelectors
|
||||
}
|
||||
);
|
||||
if ( matchedSelectors.length ) {
|
||||
vAPI.messaging.send(
|
||||
'scriptlets',
|
||||
{
|
||||
what: 'logCosmeticFilteringData',
|
||||
frameURL: window.location.href,
|
||||
frameHostname: window.location.hostname,
|
||||
matchedSelectors: matchedSelectors
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock - a browser extension to block requests.
|
||||
Copyright (C) 2015 Raymond Hill
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015-2016 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,75 +19,51 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var styles = vAPI.styles;
|
||||
|
||||
if ( Array.isArray(styles) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sessionId = vAPI.sessionId;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Remove all cosmetic filtering-related styles from the DOM
|
||||
|
||||
var selectors = [];
|
||||
var reProperties = /\s*\{[^}]+\}\s*/;
|
||||
var style, i;
|
||||
|
||||
i = styles.length;
|
||||
while ( i-- ) {
|
||||
style = styles[i];
|
||||
selectors.push(style.textContent.replace(reProperties, ''));
|
||||
if ( style.sheet !== null ) {
|
||||
style.sheet.disabled = true;
|
||||
style[vAPI.sessionId] = true;
|
||||
(function() {
|
||||
if ( typeof vAPI !== 'object' || typeof vAPI.domFilterer !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( selectors.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
var styles = vAPI.domFilterer.styleTags;
|
||||
|
||||
var elems = [];
|
||||
try {
|
||||
elems = document.querySelectorAll(selectors.join(','));
|
||||
} catch (e) {
|
||||
}
|
||||
i = elems.length;
|
||||
|
||||
var elem, shadow;
|
||||
while ( i-- ) {
|
||||
elem = elems[i];
|
||||
shadow = elem.shadowRoot;
|
||||
if ( shadow === undefined ) {
|
||||
style = elem.style;
|
||||
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
|
||||
style.removeProperty('display');
|
||||
// Disable all cosmetic filtering-related styles from the DOM
|
||||
var i = styles.length, style;
|
||||
while ( i-- ) {
|
||||
style = styles[i];
|
||||
if ( style.sheet !== null ) {
|
||||
style.sheet.disabled = true;
|
||||
style[vAPI.sessionId] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( shadow !== null && shadow.className === sessionId && shadow.firstElementChild === null ) {
|
||||
shadow.appendChild(document.createElement('content'));
|
||||
|
||||
var elems = [];
|
||||
try {
|
||||
elems = document.querySelectorAll('[' + vAPI.domFilterer.hiddenId + ']');
|
||||
} catch (e) {
|
||||
}
|
||||
i = elems.length;
|
||||
while ( i-- ) {
|
||||
var elem = elems[i];
|
||||
var shadow = elem.shadowRoot;
|
||||
if ( shadow === undefined ) {
|
||||
style = elem.style;
|
||||
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
|
||||
style.removeProperty('display');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
shadow !== null &&
|
||||
shadow.className === vAPI.domFilterer.shadowId &&
|
||||
shadow.firstElementChild === null
|
||||
) {
|
||||
shadow.appendChild(document.createElement('content'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.domFilterer.toggleOff();
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock - a browser extension to block requests.
|
||||
Copyright (C) 2015 Raymond Hill
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015-2016 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,75 +19,51 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var styles = vAPI.styles;
|
||||
|
||||
if ( Array.isArray(styles) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sessionId = vAPI.sessionId;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Insert all cosmetic filtering-related style tags in the DOM
|
||||
|
||||
var selectors = [];
|
||||
var reProperties = /\s*\{[^}]+\}\s*/;
|
||||
var style, i;
|
||||
|
||||
i = styles.length;
|
||||
while ( i-- ) {
|
||||
style = styles[i];
|
||||
selectors.push(style.textContent.replace(reProperties, ''));
|
||||
if ( style.sheet !== null ) {
|
||||
style.sheet.disabled = false;
|
||||
style[vAPI.sessionId] = undefined;
|
||||
(function() {
|
||||
if ( typeof vAPI !== 'object' || typeof vAPI.domFilterer !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( selectors.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
// Insert all cosmetic filtering-related style tags in the DOM
|
||||
|
||||
var elems = [];
|
||||
try {
|
||||
elems = document.querySelectorAll(selectors.join(','));
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
var elem, shadow;
|
||||
i = elems.length;
|
||||
while ( i-- ) {
|
||||
elem = elems[i];
|
||||
shadow = elem.shadowRoot;
|
||||
if ( shadow === undefined ) {
|
||||
style = elems[i].style;
|
||||
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
|
||||
style.setProperty('display', 'none', 'important');
|
||||
var styles = vAPI.domFilterer.styleTags;
|
||||
var i = styles.length, style;
|
||||
while ( i-- ) {
|
||||
style = styles[i];
|
||||
if ( style.sheet !== null ) {
|
||||
style.sheet.disabled = false;
|
||||
style[vAPI.sessionId] = undefined;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( shadow !== null && shadow.className === sessionId && shadow.firstElementChild !== null ) {
|
||||
shadow.removeChild(shadow.firstElementChild);
|
||||
|
||||
var elems = [];
|
||||
try {
|
||||
elems = document.querySelectorAll('[' + vAPI.domFilterer.hiddenId + ']');
|
||||
} catch (e) {
|
||||
}
|
||||
i = elems.length;
|
||||
while ( i-- ) {
|
||||
var elem = elems[i];
|
||||
var shadow = elem.shadowRoot;
|
||||
if ( shadow === undefined ) {
|
||||
style = elems[i].style;
|
||||
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
|
||||
style.setProperty('display', 'none', 'important');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
shadow !== null &&
|
||||
shadow.className === vAPI.domFilterer.shadowId &&
|
||||
shadow.firstElementChild !== null
|
||||
) {
|
||||
shadow.removeChild(shadow.firstElementChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.domFilterer.toggleOn();
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -19,51 +19,29 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Insert all cosmetic filtering-related style tags in the DOM
|
||||
|
||||
var injectedSelectors = [];
|
||||
var filteredElementCount = 0;
|
||||
|
||||
var reProperties = /\s*\{[^}]+\}\s*/;
|
||||
var i;
|
||||
|
||||
var styles = vAPI.styles || [];
|
||||
i = styles.length;
|
||||
while ( i-- ) {
|
||||
injectedSelectors = injectedSelectors.concat(styles[i].textContent.replace(reProperties, '').split(/\s*,\n\s*/));
|
||||
}
|
||||
|
||||
if ( injectedSelectors.length !== 0 ) {
|
||||
filteredElementCount = document.querySelectorAll(injectedSelectors.join(',')).length;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.messaging.send(
|
||||
'scriptlets',
|
||||
{
|
||||
what: 'liveCosmeticFilteringData',
|
||||
pageURL: window.location.href,
|
||||
filteredElementCount: filteredElementCount
|
||||
(function() {
|
||||
if ( typeof vAPI !== 'object' || typeof vAPI.domFilterer !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
/******************************************************************************/
|
||||
var xpr = document.evaluate(
|
||||
'count(//*[@' + vAPI.domFilterer.hiddenId + '])',
|
||||
document,
|
||||
null,
|
||||
XPathResult.NUMBER_TYPE,
|
||||
null
|
||||
);
|
||||
|
||||
vAPI.messaging.send(
|
||||
'scriptlets',
|
||||
{
|
||||
what: 'liveCosmeticFilteringData',
|
||||
pageURL: window.location.href,
|
||||
filteredElementCount: xpr && xpr.numberValue || 0
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -28,13 +28,14 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
if ( typeof vAPI !== 'object' || typeof vAPI.domFilterer !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var sessionId = vAPI.sessionId;
|
||||
var shadowId = vAPI.domFilterer.shadowId;
|
||||
|
||||
if ( document.querySelector('iframe.dom-inspector.' + sessionId) !== null ) {
|
||||
return;
|
||||
|
@ -690,19 +691,20 @@ var cosmeticFilterMapper = (function() {
|
|||
// Not all target nodes have necessarily been force-hidden,
|
||||
// do it now so that the inspector does not unhide these
|
||||
// nodes when disabling style tags.
|
||||
var nodesFromStyleTag = function(styleTag, rootNode) {
|
||||
var filterMap = nodeToCosmeticFilterMap;
|
||||
var styleText = styleTag.textContent;
|
||||
var selectors = styleText.slice(0, styleText.lastIndexOf('\n')).split(/,\n/);
|
||||
var i = selectors.length;
|
||||
var selector, nodes, j, node;
|
||||
var nodesFromStyleTag = function(rootNode) {
|
||||
var filterMap = nodeToCosmeticFilterMap,
|
||||
selectors, selector,
|
||||
nodes, node,
|
||||
entries, entry,
|
||||
i, j;
|
||||
|
||||
// CSS-based selectors: simple one.
|
||||
selectors = vAPI.domFilterer.simpleSelectors;
|
||||
i = selectors.length;
|
||||
while ( i-- ) {
|
||||
// https://github.com/gorhill/uBlock/issues/1015
|
||||
// Discard `:root ` prefix.
|
||||
selector = selectors[i].slice(6);
|
||||
selector = selectors[i];
|
||||
if ( filterMap.has(rootNode) === false && rootNode[matchesFnName](selector) ) {
|
||||
filterMap.set(rootNode, selector);
|
||||
hideNode(node);
|
||||
}
|
||||
nodes = rootNode.querySelectorAll(selector);
|
||||
j = nodes.length;
|
||||
|
@ -710,24 +712,106 @@ var cosmeticFilterMapper = (function() {
|
|||
node = nodes[j];
|
||||
if ( filterMap.has(node) === false ) {
|
||||
filterMap.set(node, selector);
|
||||
hideNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CSS-based selectors: complex one (must query from doc root).
|
||||
selectors = vAPI.domFilterer.complexSelectors;
|
||||
i = selectors.length;
|
||||
while ( i-- ) {
|
||||
selector = selectors[i];
|
||||
nodes = document.querySelectorAll(selector);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
node = nodes[j];
|
||||
if ( filterMap.has(node) === false ) {
|
||||
filterMap.set(node, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `:has()`-based selectors: simple ones.
|
||||
entries = vAPI.domFilterer.simpleHasSelectors;
|
||||
i = entries.length;
|
||||
while ( i-- ) {
|
||||
entry = entries[i];
|
||||
selector = entries.a + ':has(' + entries.b + ')';
|
||||
if (
|
||||
filterMap.has(rootNode) === false &&
|
||||
rootNode[matchesFnName](entry.a) &&
|
||||
rootNode.querySelector(entry.b) !== null
|
||||
) {
|
||||
filterMap.set(rootNode, selector);
|
||||
}
|
||||
nodes = rootNode.querySelectorAll(entries.a);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
node = nodes[j];
|
||||
if (
|
||||
filterMap.has(node) === false &&
|
||||
node.querySelector(entry.b) !== null
|
||||
) {
|
||||
filterMap.set(node, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `:has()`-based selectors: complex ones (must query from doc root).
|
||||
entries = vAPI.domFilterer.complexHasSelectors;
|
||||
i = entries.length;
|
||||
while ( i-- ) {
|
||||
entry = entries[i];
|
||||
selector = entries.a + ':has(' + entries.b + ')';
|
||||
nodes = document.querySelectorAll(entries.a);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
node = nodes[j];
|
||||
if (
|
||||
filterMap.has(node) === false &&
|
||||
node.querySelector(entry.b) !== null
|
||||
) {
|
||||
filterMap.set(node, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `:xpath()`-based selectors.
|
||||
var xpr;
|
||||
entries = vAPI.domFilterer.xpathSelectors;
|
||||
i = entries.length;
|
||||
while ( i-- ) {
|
||||
entry = entries[i];
|
||||
selector = ':xpath(' + entry + ')';
|
||||
xpr = document.evaluate(
|
||||
entry,
|
||||
document,
|
||||
null,
|
||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||
xpr
|
||||
);
|
||||
j = xpr.snapshotLength;
|
||||
while ( j-- ) {
|
||||
node = xpr.snapshotItem(j);
|
||||
if ( filterMap.has(node) === false ) {
|
||||
filterMap.set(node, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var incremental = function(rootNode) {
|
||||
var styleTags = vAPI.styles || [];
|
||||
var styleTags = vAPI.domFilterer.styleTags || [];
|
||||
var styleTag;
|
||||
var i = styleTags.length;
|
||||
while ( i-- ) {
|
||||
styleTag = styleTags[i];
|
||||
nodesFromStyleTag(styleTag, rootNode);
|
||||
if ( styleTag.sheet !== null ) {
|
||||
styleTag.sheet.disabled = true;
|
||||
styleTag[vAPI.sessionId] = true;
|
||||
}
|
||||
}
|
||||
nodesFromStyleTag(rootNode);
|
||||
};
|
||||
|
||||
var reset = function() {
|
||||
|
@ -736,7 +820,7 @@ var cosmeticFilterMapper = (function() {
|
|||
};
|
||||
|
||||
var shutdown = function() {
|
||||
var styleTags = vAPI.styles || [];
|
||||
var styleTags = vAPI.domFilterer.styleTags || [];
|
||||
var styleTag;
|
||||
var i = styleTags.length;
|
||||
while ( i-- ) {
|
||||
|
@ -761,12 +845,51 @@ var elementsFromSelector = function(selector, context) {
|
|||
if ( !context ) {
|
||||
context = document;
|
||||
}
|
||||
var out = [];
|
||||
var out;
|
||||
if ( selector.indexOf(':') !== -1 ) {
|
||||
out = elementsFromSpecialSelector(selector);
|
||||
if ( out !== undefined ) {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
// plain CSS selector
|
||||
try {
|
||||
out = context.querySelectorAll(selector);
|
||||
} catch (ex) {
|
||||
}
|
||||
return out;
|
||||
return out || [];
|
||||
};
|
||||
|
||||
var elementsFromSpecialSelector = function(selector) {
|
||||
var out = [], i;
|
||||
var matches = /^(.+?):has\((.+?)\)$/.exec(selector);
|
||||
if ( matches !== null ) {
|
||||
var nodes = document.querySelector(matches[1]);
|
||||
i = nodes.length;
|
||||
while ( i-- ) {
|
||||
var node = nodes[i];
|
||||
if ( node.querySelector(matches[2]) !== null ) {
|
||||
out.push(node);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
matches = /^:xpath\((.+?)\)$/.exec(selector);
|
||||
if ( matches !== null ) {
|
||||
var xpr = document.evaluate(
|
||||
matches[1],
|
||||
document,
|
||||
null,
|
||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||
null
|
||||
);
|
||||
i = xpr.snapshotLength;
|
||||
while ( i-- ) {
|
||||
out.push(xpr.snapshotItem(i));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -970,7 +1093,7 @@ var showNode = function(node, v1, v2) {
|
|||
} else {
|
||||
node.style.setProperty('display', v1, v2);
|
||||
}
|
||||
} else if ( shadow !== null && shadow.className === sessionId && shadow.firstElementChild === null ) {
|
||||
} else if ( shadow !== null && shadow.className === shadowId && shadow.firstElementChild === null ) {
|
||||
shadow.appendChild(document.createElement('content'));
|
||||
}
|
||||
};
|
||||
|
@ -983,7 +1106,7 @@ var hideNode = function(node) {
|
|||
node.style.setProperty('display', 'none', 'important');
|
||||
return;
|
||||
}
|
||||
if ( shadow !== null && shadow.className === sessionId ) {
|
||||
if ( shadow !== null && shadow.className === shadowId ) {
|
||||
if ( shadow.firstElementChild !== null ) {
|
||||
shadow.removeChild(shadow.firstElementChild);
|
||||
}
|
||||
|
@ -995,7 +1118,7 @@ var hideNode = function(node) {
|
|||
} catch (ex) {
|
||||
return;
|
||||
}
|
||||
shadow.className = sessionId;
|
||||
shadow.className = shadowId;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -1052,6 +1175,12 @@ var onMessage = function(request) {
|
|||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'toggleFilter':
|
||||
highlightedElementLists[0] = selectNodes(request.filter, request.nid);
|
||||
toggleNodes(highlightedElementLists[0], request.original, request.target);
|
||||
highlightElements(true);
|
||||
break;
|
||||
|
||||
case 'toggleNodes':
|
||||
highlightedElementLists[0] = selectNodes(request.selector, request.nid);
|
||||
toggleNodes(highlightedElementLists[0], request.original, request.target);
|
||||
|
|
Loading…
Reference in New Issue