2015-04-05 18:03:14 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
|
2016-03-06 16:51:06 +01:00
|
|
|
uBlock Origin - a browser extension to block requests.
|
2018-07-22 21:33:35 +02:00
|
|
|
Copyright (C) 2015-present Raymond Hill
|
2015-04-05 18:03:14 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2016-06-28 01:09:04 +02:00
|
|
|
'use strict';
|
|
|
|
|
2015-04-05 18:03:14 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-10-21 19:43:46 +02:00
|
|
|
if (
|
|
|
|
typeof vAPI !== 'object' ||
|
|
|
|
vAPI.domFilterer instanceof Object === false ||
|
|
|
|
vAPI.domWatcher instanceof Object === false
|
|
|
|
) {
|
2015-04-05 18:03:14 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
let reHasCSSCombinators = /[ >+~]/,
|
2017-10-21 19:43:46 +02:00
|
|
|
reHasPseudoClass = /:+(?:after|before)$/,
|
|
|
|
sanitizedSelectors = new Map(),
|
2018-07-22 21:33:35 +02:00
|
|
|
simpleDeclarativeSet = new Set(),
|
|
|
|
simpleDeclarativeStr,
|
|
|
|
complexDeclarativeSet = new Set(),
|
|
|
|
complexDeclarativeStr,
|
|
|
|
proceduralDict = new Map(),
|
|
|
|
nodesToProcess = new Set(),
|
|
|
|
shouldProcessDeclarativeComplex = false,
|
2018-07-23 15:54:25 +02:00
|
|
|
shouldProcessProcedural = false,
|
|
|
|
loggedSelectors = new Set();
|
2018-07-22 21:33:35 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let shouldProcess = function() {
|
|
|
|
return nodesToProcess.size !== 0 ||
|
|
|
|
shouldProcessDeclarativeComplex ||
|
|
|
|
shouldProcessProcedural;
|
2017-10-21 19:43:46 +02:00
|
|
|
};
|
2018-07-22 21:33:35 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let processDeclarativeSimple = function(node, out) {
|
|
|
|
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
|
|
|
if ( simpleDeclarativeStr === undefined ) {
|
|
|
|
simpleDeclarativeStr = Array.from(simpleDeclarativeSet).join(',\n');
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2016-12-25 22:56:39 +01:00
|
|
|
if (
|
2018-07-22 21:33:35 +02:00
|
|
|
(node === document || node.matches(simpleDeclarativeStr) === false) &&
|
|
|
|
(node.querySelector(simpleDeclarativeStr) === null)
|
2016-12-25 22:56:39 +01:00
|
|
|
) {
|
2017-10-21 19:43:46 +02:00
|
|
|
return;
|
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
for ( let selector of simpleDeclarativeSet ) {
|
2017-10-21 19:43:46 +02:00
|
|
|
if (
|
2018-07-23 15:54:25 +02:00
|
|
|
(node === document || node.matches(selector) === false) &&
|
|
|
|
(node.querySelector(selector) === null)
|
2017-10-21 19:43:46 +02:00
|
|
|
) {
|
2018-07-23 15:54:25 +02:00
|
|
|
continue;
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-23 15:54:25 +02:00
|
|
|
out.push(sanitizedSelectors.get(selector) || selector);
|
|
|
|
simpleDeclarativeSet.delete(selector);
|
|
|
|
simpleDeclarativeStr = undefined;
|
|
|
|
loggedSelectors.add(selector);
|
|
|
|
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
2015-04-25 13:28:35 +02:00
|
|
|
}
|
2016-07-10 01:21:46 +02:00
|
|
|
};
|
2016-12-25 22:56:39 +01:00
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let processDeclarativeComplex = function(out) {
|
|
|
|
if ( complexDeclarativeSet.size === 0 ) { return; }
|
|
|
|
if ( complexDeclarativeStr === undefined ) {
|
|
|
|
complexDeclarativeStr = Array.from(complexDeclarativeSet).join(',\n');
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( document.querySelector(complexDeclarativeStr) === null ) { return; }
|
|
|
|
for ( let selector of complexDeclarativeSet ) {
|
|
|
|
if ( document.querySelector(selector) === null ) { continue; }
|
|
|
|
out.push(sanitizedSelectors.get(selector) || selector);
|
|
|
|
complexDeclarativeSet.delete(selector);
|
|
|
|
complexDeclarativeStr = undefined;
|
2018-07-23 15:54:25 +02:00
|
|
|
loggedSelectors.add(selector);
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( complexDeclarativeSet.size === 0 ) { return; }
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
};
|
2016-12-25 22:56:39 +01:00
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let processProcedural = function(out) {
|
|
|
|
if ( proceduralDict.size === 0 ) { return; }
|
|
|
|
for ( let entry of proceduralDict ) {
|
|
|
|
if ( entry[1].test() === false ) { continue; }
|
|
|
|
out.push(entry[1].raw);
|
|
|
|
proceduralDict.delete(entry[0]);
|
|
|
|
if ( proceduralDict.size === 0 ) { break; }
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
};
|
2018-07-22 21:33:35 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let processTimer = new vAPI.SafeAnimationFrame(() => {
|
|
|
|
//console.time('dom logger/scanning for matches');
|
|
|
|
processTimer.clear();
|
|
|
|
let toLog = [];
|
|
|
|
if ( nodesToProcess.size !== 0 && simpleDeclarativeSet.size !== 0 ) {
|
2018-07-23 15:54:25 +02:00
|
|
|
if ( nodesToProcess.size !== 1 && nodesToProcess.has(document) ) {
|
|
|
|
nodesToProcess.clear();
|
|
|
|
nodesToProcess.add(document);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
for ( let node of nodesToProcess ) {
|
|
|
|
processDeclarativeSimple(node, toLog);
|
|
|
|
}
|
|
|
|
nodesToProcess.clear();
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( shouldProcessDeclarativeComplex ) {
|
|
|
|
processDeclarativeComplex(toLog);
|
|
|
|
shouldProcessDeclarativeComplex = false;
|
|
|
|
}
|
|
|
|
if ( shouldProcessProcedural ) {
|
|
|
|
processProcedural(toLog);
|
|
|
|
shouldProcessProcedural = false;
|
|
|
|
}
|
|
|
|
if ( toLog.length === 0 ) { return; }
|
|
|
|
vAPI.messaging.send(
|
|
|
|
'scriptlets',
|
|
|
|
{
|
|
|
|
what: 'logCosmeticFilteringData',
|
|
|
|
frameURL: window.location.href,
|
|
|
|
frameHostname: window.location.hostname,
|
|
|
|
matchedSelectors: toLog
|
|
|
|
}
|
|
|
|
);
|
|
|
|
//console.timeEnd('dom logger/scanning for matches');
|
|
|
|
});
|
2016-12-25 22:56:39 +01:00
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let attributeObserver = new MutationObserver(mutations => {
|
|
|
|
if ( simpleDeclarativeSet.size !== 0 ) {
|
|
|
|
for ( let mutation of mutations ) {
|
|
|
|
let node = mutation.target;
|
|
|
|
if ( node.nodeType !== 1 ) { continue; }
|
|
|
|
nodesToProcess.add(node);
|
|
|
|
}
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( complexDeclarativeSet.size !== 0 ) {
|
|
|
|
shouldProcessDeclarativeComplex = true;
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( proceduralDict.size !== 0 ) {
|
|
|
|
shouldProcessProcedural = true;
|
2016-12-30 16:32:17 +01:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( shouldProcess() ) {
|
|
|
|
processTimer.start(100);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2016-12-30 16:32:17 +01:00
|
|
|
});
|
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let handlers = {
|
2017-11-14 21:03:20 +01:00
|
|
|
onFiltersetChanged: function(changes) {
|
2017-10-23 18:21:37 +02:00
|
|
|
//console.time('dom logger/filterset changed');
|
2018-07-22 21:33:35 +02:00
|
|
|
let simpleSizeBefore = simpleDeclarativeSet.size,
|
|
|
|
complexSizeBefore = complexDeclarativeSet.size,
|
2017-11-14 21:03:20 +01:00
|
|
|
logNow = [];
|
2018-07-22 21:33:35 +02:00
|
|
|
for ( let entry of (changes.declarative || []) ) {
|
|
|
|
for ( let selector of entry[0].split(',\n') ) {
|
2018-07-23 15:54:25 +02:00
|
|
|
if ( entry[1] !== 'display:none!important;' ) {
|
2017-11-14 21:03:20 +01:00
|
|
|
logNow.push(selector + ':style(' + entry[1] + ')');
|
2018-07-23 15:54:25 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( reHasPseudoClass.test(selector) ) {
|
|
|
|
let sanitized = selector.replace(reHasPseudoClass, '');
|
|
|
|
sanitizedSelectors.set(sanitized, selector);
|
|
|
|
selector = sanitized;
|
|
|
|
}
|
|
|
|
if ( loggedSelectors.has(selector) ) { continue; }
|
|
|
|
if ( reHasCSSCombinators.test(selector) ) {
|
|
|
|
complexDeclarativeSet.add(selector);
|
|
|
|
complexDeclarativeStr = undefined;
|
|
|
|
} else {
|
|
|
|
simpleDeclarativeSet.add(selector);
|
|
|
|
simpleDeclarativeStr = undefined;
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
}
|
2017-11-14 21:03:20 +01:00
|
|
|
}
|
|
|
|
if ( logNow.length !== 0 ) {
|
|
|
|
vAPI.messaging.send(
|
|
|
|
'scriptlets',
|
|
|
|
{
|
|
|
|
what: 'logCosmeticFilteringData',
|
|
|
|
frameURL: window.location.href,
|
|
|
|
frameHostname: window.location.hostname,
|
|
|
|
matchedSelectors: logNow
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( simpleDeclarativeSet.size !== simpleSizeBefore ) {
|
|
|
|
nodesToProcess.add(document.documentElement);
|
|
|
|
}
|
|
|
|
if ( complexDeclarativeSet.size !== complexSizeBefore ) {
|
|
|
|
shouldProcessDeclarativeComplex = true;
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
Array.isArray(changes.procedural) &&
|
|
|
|
changes.procedural.length !== 0
|
|
|
|
) {
|
|
|
|
for ( let selector of changes.procedural ) {
|
|
|
|
proceduralDict.set(selector.raw, selector);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
shouldProcessProcedural = true;
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( shouldProcess() ) {
|
|
|
|
processTimer.start(1);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2017-10-23 18:21:37 +02:00
|
|
|
//console.timeEnd('dom logger/filterset changed');
|
2017-10-21 19:43:46 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
onDOMCreated: function() {
|
2017-11-14 21:03:20 +01:00
|
|
|
handlers.onFiltersetChanged(vAPI.domFilterer.getAllSelectors());
|
2017-10-21 19:43:46 +02:00
|
|
|
vAPI.domFilterer.addListener(handlers);
|
2018-07-22 21:33:35 +02:00
|
|
|
attributeObserver.observe(document.body, {
|
|
|
|
attributes: true,
|
|
|
|
subtree: true
|
|
|
|
});
|
2017-10-21 19:43:46 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
onDOMChanged: function(addedNodes) {
|
|
|
|
// This is to guard against runaway job queue. I suspect this could
|
|
|
|
// occur on slower devices.
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( simpleDeclarativeSet.size !== 0 ) {
|
|
|
|
for ( let node of addedNodes ) {
|
|
|
|
if ( node.parentNode === null ) { continue; }
|
|
|
|
nodesToProcess.add(node);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( complexDeclarativeSet.size !== 0 ) {
|
|
|
|
shouldProcessDeclarativeComplex = true;
|
|
|
|
}
|
|
|
|
if ( proceduralDict.size !== 0 ) {
|
|
|
|
shouldProcessProcedural = true;
|
|
|
|
}
|
|
|
|
if ( shouldProcess() ) {
|
|
|
|
processTimer.start(100);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2016-12-25 22:56:39 +01:00
|
|
|
}
|
2017-10-21 19:43:46 +02:00
|
|
|
};
|
2015-04-25 13:28:35 +02:00
|
|
|
|
2017-10-21 19:43:46 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
let onMessage = function(msg) {
|
2017-10-21 19:43:46 +02:00
|
|
|
if ( msg.what === 'loggerDisabled' ) {
|
2018-07-22 21:33:35 +02:00
|
|
|
processTimer.clear();
|
|
|
|
attributeObserver.disconnect();
|
2017-10-21 19:43:46 +02:00
|
|
|
vAPI.domFilterer.removeListener(handlers);
|
|
|
|
vAPI.domWatcher.removeListener(handlers);
|
|
|
|
vAPI.messaging.removeChannelListener('domLogger', onMessage);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
vAPI.messaging.addChannelListener('domLogger', onMessage);
|
|
|
|
|
|
|
|
vAPI.domWatcher.addListener(handlers);
|
2015-04-05 18:03:14 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
})();
|
2018-05-03 15:55:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
DO NOT:
|
|
|
|
- Remove the following code
|
|
|
|
- Add code beyond the following code
|
|
|
|
Reason:
|
|
|
|
- https://github.com/gorhill/uBlock/pull/3721
|
|
|
|
- uBO never uses the return value from injected content scripts
|
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
void 0;
|
|
|
|
|