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
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-07-23 17:42:04 +02:00
|
|
|
(( ) => {
|
2019-09-19 14:31:38 +02:00
|
|
|
// >>>>>>>> start of private namespace
|
2015-04-05 18:03:14 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-10-21 19:43:46 +02:00
|
|
|
if (
|
|
|
|
typeof vAPI !== 'object' ||
|
|
|
|
vAPI.domWatcher instanceof Object === false
|
|
|
|
) {
|
2015-04-05 18:03:14 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const reHasCSSCombinators = /[ >+~]/;
|
|
|
|
const simpleDeclarativeSet = new Set();
|
|
|
|
let simpleDeclarativeStr;
|
|
|
|
const complexDeclarativeSet = new Set();
|
|
|
|
let complexDeclarativeStr;
|
2019-07-23 17:42:04 +02:00
|
|
|
const declarativeStyleDict = new Map();
|
|
|
|
let declarativeStyleStr;
|
2019-05-16 19:44:49 +02:00
|
|
|
const proceduralDict = new Map();
|
2019-05-28 13:21:16 +02:00
|
|
|
const exceptionDict = new Map();
|
2019-05-16 19:44:49 +02:00
|
|
|
let exceptionStr;
|
2019-10-01 00:21:24 +02:00
|
|
|
const proceduralExceptionDict = new Map();
|
2019-05-16 19:44:49 +02:00
|
|
|
const nodesToProcess = new Set();
|
|
|
|
const loggedSelectors = new Set();
|
2018-07-22 21:33:35 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-08-11 22:32:49 +02:00
|
|
|
const rePseudoElements = /:(?::?after|:?before|:[a-z-]+)$/;
|
2019-07-30 18:27:09 +02:00
|
|
|
|
|
|
|
const safeMatchSelector = function(selector, context) {
|
|
|
|
const safeSelector = rePseudoElements.test(selector)
|
|
|
|
? selector.replace(rePseudoElements, '')
|
|
|
|
: selector;
|
|
|
|
return context.matches(safeSelector);
|
|
|
|
};
|
|
|
|
|
|
|
|
const safeQuerySelector = function(selector, context = document) {
|
|
|
|
const safeSelector = rePseudoElements.test(selector)
|
|
|
|
? selector.replace(rePseudoElements, '')
|
|
|
|
: selector;
|
|
|
|
return context.querySelector(safeSelector);
|
|
|
|
};
|
|
|
|
|
|
|
|
const safeGroupSelectors = function(selectors) {
|
|
|
|
const arr = Array.isArray(selectors)
|
|
|
|
? selectors
|
|
|
|
: Array.from(selectors);
|
|
|
|
return arr.map(s => {
|
|
|
|
return rePseudoElements.test(s)
|
|
|
|
? s.replace(rePseudoElements, '')
|
|
|
|
: s;
|
|
|
|
}).join(',\n');
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const processDeclarativeSimple = function(node, out) {
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
|
|
|
if ( simpleDeclarativeStr === undefined ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
simpleDeclarativeStr = safeGroupSelectors(simpleDeclarativeSet);
|
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;
|
|
|
|
}
|
2019-05-16 19:44:49 +02:00
|
|
|
for ( const selector of simpleDeclarativeSet ) {
|
2017-10-21 19:43:46 +02:00
|
|
|
if (
|
2019-07-30 18:27:09 +02:00
|
|
|
(node === document || safeMatchSelector(selector, node) === false) &&
|
|
|
|
(safeQuerySelector(selector, node) === 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
|
|
|
}
|
2019-07-30 18:27:09 +02:00
|
|
|
out.push(`##${selector}`);
|
2018-07-23 15:54:25 +02:00
|
|
|
simpleDeclarativeSet.delete(selector);
|
|
|
|
simpleDeclarativeStr = undefined;
|
|
|
|
loggedSelectors.add(selector);
|
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
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const processDeclarativeComplex = function(out) {
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( complexDeclarativeSet.size === 0 ) { return; }
|
|
|
|
if ( complexDeclarativeStr === undefined ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
complexDeclarativeStr = safeGroupSelectors(complexDeclarativeSet);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( document.querySelector(complexDeclarativeStr) === null ) { return; }
|
2019-05-16 19:44:49 +02:00
|
|
|
for ( const selector of complexDeclarativeSet ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
if ( safeQuerySelector(selector) === null ) { continue; }
|
|
|
|
out.push(`##${selector}`);
|
2018-07-22 21:33:35 +02:00
|
|
|
complexDeclarativeSet.delete(selector);
|
|
|
|
complexDeclarativeStr = undefined;
|
2018-07-23 15:54:25 +02:00
|
|
|
loggedSelectors.add(selector);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
};
|
2016-12-25 22:56:39 +01:00
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-07-23 17:42:04 +02:00
|
|
|
const processDeclarativeStyle = function(out) {
|
|
|
|
if ( declarativeStyleDict.size === 0 ) { return; }
|
|
|
|
if ( declarativeStyleStr === undefined ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
declarativeStyleStr = safeGroupSelectors(declarativeStyleDict.keys());
|
2019-07-23 17:42:04 +02:00
|
|
|
}
|
|
|
|
if ( document.querySelector(declarativeStyleStr) === null ) { return; }
|
2019-09-22 13:51:20 +02:00
|
|
|
for ( const selector of declarativeStyleDict.keys() ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
if ( safeQuerySelector(selector) === null ) { continue; }
|
2019-09-22 13:51:20 +02:00
|
|
|
for ( const style of declarativeStyleDict.get(selector) ) {
|
|
|
|
const raw = `##${selector}:style(${style})`;
|
|
|
|
out.push(raw);
|
|
|
|
loggedSelectors.add(raw);
|
|
|
|
}
|
2019-07-23 17:42:04 +02:00
|
|
|
declarativeStyleDict.delete(selector);
|
|
|
|
declarativeStyleStr = undefined;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const processProcedural = function(out) {
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( proceduralDict.size === 0 ) { return; }
|
2020-03-07 20:25:06 +01:00
|
|
|
for ( const [ raw, pselector ] of proceduralDict ) {
|
|
|
|
if ( pselector.hit === false ) { continue; }
|
|
|
|
out.push(`##${raw}`);
|
|
|
|
proceduralDict.delete(raw);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
};
|
2018-07-22 21:33:35 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const processExceptions = function(out) {
|
2019-05-28 13:21:16 +02:00
|
|
|
if ( exceptionDict.size === 0 ) { return; }
|
2019-05-16 19:44:49 +02:00
|
|
|
if ( exceptionStr === undefined ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
exceptionStr = safeGroupSelectors(exceptionDict.keys());
|
2019-05-16 19:44:49 +02:00
|
|
|
}
|
|
|
|
if ( document.querySelector(exceptionStr) === null ) { return; }
|
2019-05-28 13:21:16 +02:00
|
|
|
for ( const [ selector, raw ] of exceptionDict ) {
|
2019-07-30 18:27:09 +02:00
|
|
|
if ( safeQuerySelector(selector) === null ) { continue; }
|
2019-05-28 13:21:16 +02:00
|
|
|
out.push(`#@#${raw}`);
|
|
|
|
exceptionDict.delete(selector);
|
2019-05-16 19:44:49 +02:00
|
|
|
exceptionStr = undefined;
|
2019-05-28 13:21:16 +02:00
|
|
|
loggedSelectors.add(raw);
|
2019-05-16 19:44:49 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-10-01 00:21:24 +02:00
|
|
|
const processProceduralExceptions = function(out) {
|
|
|
|
if ( proceduralExceptionDict.size === 0 ) { return; }
|
|
|
|
for ( const exception of proceduralExceptionDict.values() ) {
|
|
|
|
if ( exception.test() === false ) { continue; }
|
|
|
|
out.push(`#@#${exception.raw}`);
|
|
|
|
proceduralExceptionDict.delete(exception.raw);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-07-23 17:42:04 +02:00
|
|
|
const processTimer = new vAPI.SafeAnimationFrame(( ) => {
|
2018-07-22 21:33:35 +02:00
|
|
|
//console.time('dom logger/scanning for matches');
|
|
|
|
processTimer.clear();
|
2019-07-23 17:42:04 +02:00
|
|
|
if ( nodesToProcess.size === 0 ) { return; }
|
|
|
|
|
|
|
|
if ( nodesToProcess.size !== 1 && nodesToProcess.has(document) ) {
|
|
|
|
nodesToProcess.clear();
|
|
|
|
nodesToProcess.add(document);
|
|
|
|
}
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const toLog = [];
|
2019-07-23 17:42:04 +02:00
|
|
|
if ( simpleDeclarativeSet.size !== 0 ) {
|
2019-05-16 19:44:49 +02:00
|
|
|
for ( const node of nodesToProcess ) {
|
2018-07-22 21:33:35 +02:00
|
|
|
processDeclarativeSimple(node, toLog);
|
|
|
|
}
|
2019-05-16 19:44:49 +02:00
|
|
|
}
|
2019-07-23 17:42:04 +02:00
|
|
|
|
|
|
|
processDeclarativeComplex(toLog);
|
|
|
|
processDeclarativeStyle(toLog);
|
|
|
|
processProcedural(toLog);
|
|
|
|
processExceptions(toLog);
|
2019-10-01 00:21:24 +02:00
|
|
|
processProceduralExceptions(toLog);
|
2019-07-23 17:42:04 +02:00
|
|
|
|
|
|
|
nodesToProcess.clear();
|
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
if ( toLog.length === 0 ) { return; }
|
2019-07-23 17:42:04 +02:00
|
|
|
|
2020-07-29 13:38:49 +02:00
|
|
|
const location = vAPI.effectiveSelf.location;
|
2020-07-25 00:50:12 +02:00
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
vAPI.messaging.send('scriptlets', {
|
|
|
|
what: 'logCosmeticFilteringData',
|
2020-07-25 00:50:12 +02:00
|
|
|
frameURL: location.href,
|
|
|
|
frameHostname: location.hostname,
|
2019-09-17 21:15:01 +02:00
|
|
|
matchedSelectors: toLog,
|
|
|
|
});
|
2018-07-22 21:33:35 +02:00
|
|
|
//console.timeEnd('dom logger/scanning for matches');
|
|
|
|
});
|
2016-12-25 22:56:39 +01:00
|
|
|
|
2018-07-22 21:33:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const attributeObserver = new MutationObserver(mutations => {
|
2019-07-23 17:42:04 +02:00
|
|
|
if ( nodesToProcess.has(document) ) { return; }
|
|
|
|
for ( const mutation of mutations ) {
|
|
|
|
const node = mutation.target;
|
|
|
|
if ( node.nodeType !== 1 ) { continue; }
|
|
|
|
nodesToProcess.add(node);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2019-07-23 17:42:04 +02:00
|
|
|
if ( nodesToProcess.size !== 0 ) {
|
2018-07-22 21:33:35 +02:00
|
|
|
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
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-16 19:44:49 +02:00
|
|
|
const 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');
|
2019-05-16 19:44:49 +02:00
|
|
|
for ( const entry of (changes.declarative || []) ) {
|
2018-07-22 21:33:35 +02:00
|
|
|
for ( let selector of entry[0].split(',\n') ) {
|
2018-07-23 15:54:25 +02:00
|
|
|
if ( entry[1] !== 'display:none!important;' ) {
|
2019-07-23 17:42:04 +02:00
|
|
|
declarativeStyleStr = undefined;
|
2019-09-22 13:51:20 +02:00
|
|
|
const styles = declarativeStyleDict.get(selector);
|
|
|
|
if ( styles === undefined ) {
|
|
|
|
declarativeStyleDict.set(selector, [ entry[1] ]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
styles.push(entry[1]);
|
2018-07-23 15:54:25 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|
2018-07-22 21:33:35 +02:00
|
|
|
if (
|
|
|
|
Array.isArray(changes.procedural) &&
|
|
|
|
changes.procedural.length !== 0
|
|
|
|
) {
|
2019-05-16 19:44:49 +02:00
|
|
|
for ( const selector of changes.procedural ) {
|
2018-07-22 21:33:35 +02:00
|
|
|
proceduralDict.set(selector.raw, selector);
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-16 19:44:49 +02:00
|
|
|
if ( Array.isArray(changes.exceptions) ) {
|
|
|
|
for ( const selector of changes.exceptions ) {
|
|
|
|
if ( loggedSelectors.has(selector) ) { continue; }
|
2019-09-12 19:05:41 +02:00
|
|
|
if ( selector.charCodeAt(0) !== 0x7B /* '{' */ ) {
|
2019-05-28 13:21:16 +02:00
|
|
|
exceptionDict.set(selector, selector);
|
2019-09-12 19:05:41 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const details = JSON.parse(selector);
|
2020-09-07 14:28:01 +02:00
|
|
|
if (
|
|
|
|
details.action !== undefined &&
|
|
|
|
details.tasks === undefined &&
|
|
|
|
details.action[0] === ':style'
|
|
|
|
) {
|
2020-03-26 14:19:02 +01:00
|
|
|
exceptionDict.set(details.selector, details.raw);
|
2019-09-12 19:05:41 +02:00
|
|
|
continue;
|
2019-05-28 13:21:16 +02:00
|
|
|
}
|
2019-10-01 00:21:24 +02:00
|
|
|
proceduralExceptionDict.set(
|
|
|
|
details.raw,
|
|
|
|
vAPI.domFilterer.createProceduralFilter(details)
|
|
|
|
);
|
2019-05-16 19:44:49 +02:00
|
|
|
}
|
|
|
|
exceptionStr = undefined;
|
2017-10-21 19:43:46 +02:00
|
|
|
}
|
2019-07-23 17:42:04 +02:00
|
|
|
nodesToProcess.clear();
|
|
|
|
nodesToProcess.add(document);
|
|
|
|
processTimer.start(1);
|
2017-10-23 18:21:37 +02:00
|
|
|
//console.timeEnd('dom logger/filterset changed');
|
2017-10-21 19:43:46 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
onDOMCreated: function() {
|
2020-07-27 19:30:57 +02:00
|
|
|
if ( vAPI.domFilterer instanceof Object === false ) {
|
|
|
|
return shutdown();
|
|
|
|
}
|
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) {
|
2019-07-23 17:42:04 +02:00
|
|
|
if ( nodesToProcess.has(document) ) { return; }
|
|
|
|
for ( const node of addedNodes ) {
|
|
|
|
if ( node.parentNode === null ) { continue; }
|
|
|
|
nodesToProcess.add(node);
|
2019-05-16 19:44:49 +02:00
|
|
|
}
|
2019-07-23 17:42:04 +02:00
|
|
|
if ( nodesToProcess.size !== 0 ) {
|
2018-07-22 21:33:35 +02:00
|
|
|
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
|
|
|
/******************************************************************************/
|
|
|
|
|
2020-07-27 19:30:57 +02:00
|
|
|
const shutdown = function() {
|
|
|
|
processTimer.clear();
|
|
|
|
attributeObserver.disconnect();
|
|
|
|
if ( typeof vAPI !== 'object' ) { return; }
|
|
|
|
if ( vAPI.domFilterer instanceof Object ) {
|
|
|
|
vAPI.domFilterer.removeListener(handlers);
|
|
|
|
}
|
|
|
|
if ( vAPI.domWatcher instanceof Object ) {
|
|
|
|
vAPI.domWatcher.removeListener(handlers);
|
|
|
|
}
|
|
|
|
if ( vAPI.broadcastListener instanceof Object ) {
|
|
|
|
vAPI.broadcastListener.remove(broadcastListener);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
const broadcastListener = msg => {
|
|
|
|
if ( msg.what === 'loggerDisabled' ) {
|
|
|
|
shutdown();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-09-26 21:57:55 +02:00
|
|
|
vAPI.messaging.extend().then(extended => {
|
2020-07-27 19:30:57 +02:00
|
|
|
if ( extended !== true ) {
|
|
|
|
return shutdown();
|
|
|
|
}
|
2019-09-19 14:31:38 +02:00
|
|
|
vAPI.broadcastListener.add(broadcastListener);
|
2019-09-26 21:57:55 +02:00
|
|
|
});
|
2017-10-21 19:43:46 +02:00
|
|
|
|
|
|
|
vAPI.domWatcher.addListener(handlers);
|
2015-04-05 18:03:14 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-09-19 14:31:38 +02:00
|
|
|
// <<<<<<<< end of private namespace
|
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;
|
|
|
|
|