mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2026-01-04 01:31:36 +03:00
Big Change Update
This commit is contained in:
365
uBlock0.chromium/js/scriptlets/cosmetic-logger.js
Normal file
365
uBlock0.chromium/js/scriptlets/cosmetic-logger.js
Normal file
@@ -0,0 +1,365 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
/* globals browser */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
// >>>>>>>> start of private namespace
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
if ( vAPI.domWatcher instanceof Object === false ) { return; }
|
||||
|
||||
const reHasCSSCombinators = /[ >+~]/;
|
||||
const simpleDeclarativeSet = new Set();
|
||||
let simpleDeclarativeStr;
|
||||
const complexDeclarativeSet = new Set();
|
||||
let complexDeclarativeStr;
|
||||
const proceduralDict = new Map();
|
||||
const exceptionDict = new Map();
|
||||
let exceptionStr;
|
||||
const proceduralExceptionDict = new Map();
|
||||
const nodesToProcess = new Set();
|
||||
const loggedSelectors = new Set();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const rePseudoElements = /:(?::?after|:?before|:[a-z-]+)$/;
|
||||
|
||||
function hasSelector(selector, context = document) {
|
||||
try {
|
||||
return context.querySelector(selector) !== null;
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function safeMatchSelector(selector, context) {
|
||||
const safeSelector = rePseudoElements.test(selector)
|
||||
? selector.replace(rePseudoElements, '')
|
||||
: selector;
|
||||
try {
|
||||
return context.matches(safeSelector);
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function safeQuerySelector(selector, context = document) {
|
||||
const safeSelector = rePseudoElements.test(selector)
|
||||
? selector.replace(rePseudoElements, '')
|
||||
: selector;
|
||||
try {
|
||||
return context.querySelector(safeSelector);
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function safeGroupSelectors(selectors) {
|
||||
const arr = Array.isArray(selectors)
|
||||
? selectors
|
||||
: Array.from(selectors);
|
||||
return arr.map(s => {
|
||||
return rePseudoElements.test(s)
|
||||
? s.replace(rePseudoElements, '')
|
||||
: s;
|
||||
}).join(',\n');
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function processDeclarativeSimple(node, out) {
|
||||
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
||||
if ( simpleDeclarativeStr === undefined ) {
|
||||
simpleDeclarativeStr = safeGroupSelectors(simpleDeclarativeSet);
|
||||
}
|
||||
if (
|
||||
(node === document || node.matches(simpleDeclarativeStr) === false) &&
|
||||
(hasSelector(simpleDeclarativeStr, node) === false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
for ( const selector of simpleDeclarativeSet ) {
|
||||
if (
|
||||
(node === document || safeMatchSelector(selector, node) === false) &&
|
||||
(safeQuerySelector(selector, node) === null)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
out.push(`##${selector}`);
|
||||
simpleDeclarativeSet.delete(selector);
|
||||
simpleDeclarativeStr = undefined;
|
||||
loggedSelectors.add(selector);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function processDeclarativeComplex(out) {
|
||||
if ( complexDeclarativeSet.size === 0 ) { return; }
|
||||
if ( complexDeclarativeStr === undefined ) {
|
||||
complexDeclarativeStr = safeGroupSelectors(complexDeclarativeSet);
|
||||
}
|
||||
if ( hasSelector(complexDeclarativeStr) === false ) { return; }
|
||||
for ( const selector of complexDeclarativeSet ) {
|
||||
if ( safeQuerySelector(selector) === null ) { continue; }
|
||||
out.push(`##${selector}`);
|
||||
complexDeclarativeSet.delete(selector);
|
||||
complexDeclarativeStr = undefined;
|
||||
loggedSelectors.add(selector);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function processProcedural(out) {
|
||||
if ( proceduralDict.size === 0 ) { return; }
|
||||
for ( const [ raw, pselector ] of proceduralDict ) {
|
||||
if ( pselector.converted ) {
|
||||
if ( safeQuerySelector(pselector.selector) === null ) { continue; }
|
||||
} else if ( pselector.hit === false && pselector.exec().length === 0 ) {
|
||||
continue;
|
||||
}
|
||||
out.push(`##${raw}`);
|
||||
proceduralDict.delete(raw);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function processExceptions(out) {
|
||||
if ( exceptionDict.size === 0 ) { return; }
|
||||
if ( exceptionStr === undefined ) {
|
||||
exceptionStr = safeGroupSelectors(exceptionDict.keys());
|
||||
}
|
||||
if ( hasSelector(exceptionStr) === false ) { return; }
|
||||
for ( const [ selector, raw ] of exceptionDict ) {
|
||||
if ( safeQuerySelector(selector) === null ) { continue; }
|
||||
out.push(`#@#${raw}`);
|
||||
exceptionDict.delete(selector);
|
||||
exceptionStr = undefined;
|
||||
loggedSelectors.add(raw);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function processProceduralExceptions(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);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const processTimer = new vAPI.SafeAnimationFrame(( ) => {
|
||||
//console.time('dom logger/scanning for matches');
|
||||
processTimer.clear();
|
||||
if ( nodesToProcess.size === 0 ) { return; }
|
||||
|
||||
if ( nodesToProcess.size !== 1 && nodesToProcess.has(document) ) {
|
||||
nodesToProcess.clear();
|
||||
nodesToProcess.add(document);
|
||||
}
|
||||
|
||||
const toLog = [];
|
||||
if ( simpleDeclarativeSet.size !== 0 ) {
|
||||
for ( const node of nodesToProcess ) {
|
||||
processDeclarativeSimple(node, toLog);
|
||||
}
|
||||
}
|
||||
|
||||
processDeclarativeComplex(toLog);
|
||||
processProcedural(toLog);
|
||||
processExceptions(toLog);
|
||||
processProceduralExceptions(toLog);
|
||||
|
||||
nodesToProcess.clear();
|
||||
|
||||
if ( toLog.length === 0 ) { return; }
|
||||
|
||||
const location = vAPI.effectiveSelf.location;
|
||||
|
||||
vAPI.messaging.send('scriptlets', {
|
||||
what: 'logCosmeticFilteringData',
|
||||
frameURL: location.href,
|
||||
frameHostname: location.hostname,
|
||||
matchedSelectors: toLog,
|
||||
});
|
||||
//console.timeEnd('dom logger/scanning for matches');
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const attributeObserver = new MutationObserver(mutations => {
|
||||
if ( nodesToProcess.has(document) ) { return; }
|
||||
for ( const mutation of mutations ) {
|
||||
const node = mutation.target;
|
||||
if ( node.nodeType !== 1 ) { continue; }
|
||||
nodesToProcess.add(node);
|
||||
}
|
||||
if ( nodesToProcess.size !== 0 ) {
|
||||
processTimer.start(100);
|
||||
}
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const handlers = {
|
||||
onFiltersetChanged: function(changes) {
|
||||
//console.time('dom logger/filterset changed');
|
||||
for ( const block of (changes.declarative || []) ) {
|
||||
for ( const selector of block.split(',\n') ) {
|
||||
if ( loggedSelectors.has(selector) ) { continue; }
|
||||
if ( reHasCSSCombinators.test(selector) ) {
|
||||
complexDeclarativeSet.add(selector);
|
||||
complexDeclarativeStr = undefined;
|
||||
} else {
|
||||
simpleDeclarativeSet.add(selector);
|
||||
simpleDeclarativeStr = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
Array.isArray(changes.procedural) &&
|
||||
changes.procedural.length !== 0
|
||||
) {
|
||||
for ( const selector of changes.procedural ) {
|
||||
proceduralDict.set(selector.raw, selector);
|
||||
}
|
||||
}
|
||||
if ( Array.isArray(changes.exceptions) ) {
|
||||
for ( const selector of changes.exceptions ) {
|
||||
if ( loggedSelectors.has(selector) ) { continue; }
|
||||
if ( selector.charCodeAt(0) !== 0x7B /* '{' */ ) {
|
||||
exceptionDict.set(selector, selector);
|
||||
continue;
|
||||
}
|
||||
const details = JSON.parse(selector);
|
||||
if (
|
||||
details.action !== undefined &&
|
||||
details.tasks === undefined &&
|
||||
details.action[0] === 'style'
|
||||
) {
|
||||
exceptionDict.set(details.selector, details.raw);
|
||||
continue;
|
||||
}
|
||||
proceduralExceptionDict.set(
|
||||
details.raw,
|
||||
vAPI.domFilterer.createProceduralFilter(details)
|
||||
);
|
||||
}
|
||||
exceptionStr = undefined;
|
||||
}
|
||||
nodesToProcess.clear();
|
||||
nodesToProcess.add(document);
|
||||
processTimer.start(1);
|
||||
//console.timeEnd('dom logger/filterset changed');
|
||||
},
|
||||
|
||||
onDOMCreated: function() {
|
||||
if ( vAPI.domFilterer instanceof Object === false ) {
|
||||
return shutdown();
|
||||
}
|
||||
handlers.onFiltersetChanged(vAPI.domFilterer.getAllSelectors());
|
||||
vAPI.domFilterer.addListener(handlers);
|
||||
attributeObserver.observe(document.body, {
|
||||
attributes: true,
|
||||
subtree: true
|
||||
});
|
||||
},
|
||||
|
||||
onDOMChanged: function(addedNodes) {
|
||||
if ( nodesToProcess.has(document) ) { return; }
|
||||
for ( const node of addedNodes ) {
|
||||
if ( node.parentNode === null ) { continue; }
|
||||
nodesToProcess.add(node);
|
||||
}
|
||||
if ( nodesToProcess.size !== 0 ) {
|
||||
processTimer.start(100);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vAPI.domWatcher.addListener(handlers);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const broadcastHandler = msg => {
|
||||
if ( msg.what === 'loggerDisabled' ) {
|
||||
shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
browser.runtime.onMessage.addListener(broadcastHandler);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function shutdown() {
|
||||
browser.runtime.onMessage.removeListener(broadcastHandler);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// <<<<<<<< end of private namespace
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
|
||||
48
uBlock0.chromium/js/scriptlets/cosmetic-off.js
Normal file
48
uBlock0.chromium/js/scriptlets/cosmetic-off.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-2018 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI === 'object' && vAPI.domFilterer ) {
|
||||
vAPI.domFilterer.toggle(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
48
uBlock0.chromium/js/scriptlets/cosmetic-on.js
Normal file
48
uBlock0.chromium/js/scriptlets/cosmetic-on.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-2018 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI === 'object' && vAPI.domFilterer ) {
|
||||
vAPI.domFilterer.toggle(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
142
uBlock0.chromium/js/scriptlets/cosmetic-report.js
Normal file
142
uBlock0.chromium/js/scriptlets/cosmetic-report.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
// >>>>>>>> start of private namespace
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
if ( typeof vAPI.domFilterer !== 'object' ) { return; }
|
||||
if ( vAPI.domFilterer === null ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const rePseudoElements = /:(?::?after|:?before|:[a-z-]+)$/;
|
||||
|
||||
const hasSelector = selector => {
|
||||
try {
|
||||
return document.querySelector(selector) !== null;
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const safeQuerySelector = selector => {
|
||||
const safeSelector = rePseudoElements.test(selector)
|
||||
? selector.replace(rePseudoElements, '')
|
||||
: selector;
|
||||
try {
|
||||
return document.querySelector(safeSelector);
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const safeGroupSelectors = selectors => {
|
||||
const arr = Array.isArray(selectors)
|
||||
? selectors
|
||||
: Array.from(selectors);
|
||||
return arr.map(s => {
|
||||
return rePseudoElements.test(s)
|
||||
? s.replace(rePseudoElements, '')
|
||||
: s;
|
||||
}).join(',\n');
|
||||
};
|
||||
|
||||
const allSelectors = vAPI.domFilterer.getAllSelectors();
|
||||
const matchedSelectors = [];
|
||||
|
||||
if ( Array.isArray(allSelectors.declarative) ) {
|
||||
const declarativeSet = new Set();
|
||||
for ( const block of allSelectors.declarative ) {
|
||||
for ( const selector of block.split(',\n') ) {
|
||||
declarativeSet.add(selector);
|
||||
}
|
||||
}
|
||||
if ( hasSelector(safeGroupSelectors(declarativeSet)) ) {
|
||||
for ( const selector of declarativeSet ) {
|
||||
if ( safeQuerySelector(selector) === null ) { continue; }
|
||||
matchedSelectors.push(`##${selector}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(allSelectors.procedural) &&
|
||||
allSelectors.procedural.length !== 0
|
||||
) {
|
||||
for ( const pselector of allSelectors.procedural ) {
|
||||
if ( pselector.hit === false && pselector.exec().length === 0 ) { continue; }
|
||||
matchedSelectors.push(`##${pselector.raw}`);
|
||||
}
|
||||
}
|
||||
|
||||
if ( Array.isArray(allSelectors.exceptions) ) {
|
||||
const exceptionDict = new Map();
|
||||
for ( const selector of allSelectors.exceptions ) {
|
||||
if ( selector.charCodeAt(0) !== 0x7B /* '{' */ ) {
|
||||
exceptionDict.set(selector, selector);
|
||||
continue;
|
||||
}
|
||||
const details = JSON.parse(selector);
|
||||
if (
|
||||
details.action !== undefined &&
|
||||
details.tasks === undefined &&
|
||||
details.action[0] === 'style'
|
||||
) {
|
||||
exceptionDict.set(details.selector, details.raw);
|
||||
continue;
|
||||
}
|
||||
const pselector = vAPI.domFilterer.createProceduralFilter(details);
|
||||
if ( pselector.test() === false ) { continue; }
|
||||
matchedSelectors.push(`#@#${pselector.raw}`);
|
||||
}
|
||||
if (
|
||||
exceptionDict.size !== 0 &&
|
||||
hasSelector(safeGroupSelectors(exceptionDict.keys()))
|
||||
) {
|
||||
for ( const [ selector, raw ] of exceptionDict ) {
|
||||
if ( safeQuerySelector(selector) === null ) { continue; }
|
||||
matchedSelectors.push(`#@#${raw}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( typeof self.uBO_scriptletsInjected === 'string' ) {
|
||||
matchedSelectors.push(...self.uBO_scriptletsInjected.split('\n'));
|
||||
}
|
||||
|
||||
if ( matchedSelectors.length === 0 ) { return; }
|
||||
|
||||
return matchedSelectors;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// <<<<<<<< end of private namespace
|
||||
})();
|
||||
|
||||
920
uBlock0.chromium/js/scriptlets/dom-inspector.js
Normal file
920
uBlock0.chromium/js/scriptlets/dom-inspector.js
Normal file
@@ -0,0 +1,920 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
(async ( ) => {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
if ( vAPI === null ) { return; }
|
||||
if ( vAPI.domFilterer instanceof Object === false ) { return; }
|
||||
|
||||
if ( vAPI.inspectorFrame ) { return; }
|
||||
vAPI.inspectorFrame = true;
|
||||
|
||||
const inspectorUniqueId = vAPI.randomToken();
|
||||
|
||||
const nodeToIdMap = new WeakMap(); // No need to iterate
|
||||
|
||||
let blueNodes = [];
|
||||
const roRedNodes = new Map(); // node => current cosmetic filter
|
||||
const rwRedNodes = new Set(); // node => new cosmetic filter (toggle node)
|
||||
const rwGreenNodes = new Set(); // node => new exception cosmetic filter (toggle filter)
|
||||
//const roGreenNodes = new Map(); // node => current exception cosmetic filter (can't toggle)
|
||||
|
||||
const reHasCSSCombinators = /[ >+~]/;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const domLayout = (( ) => {
|
||||
const skipTagNames = new Set([
|
||||
'br', 'head', 'link', 'meta', 'script', 'style', 'title'
|
||||
]);
|
||||
const resourceAttrNames = new Map([
|
||||
[ 'a', 'href' ],
|
||||
[ 'iframe', 'src' ],
|
||||
[ 'img', 'src' ],
|
||||
[ 'object', 'data' ]
|
||||
]);
|
||||
|
||||
let idGenerator = 1;
|
||||
|
||||
// This will be used to uniquely identify nodes across process.
|
||||
|
||||
const newNodeId = node => {
|
||||
const nid = `n${(idGenerator++).toString(36)}`;
|
||||
nodeToIdMap.set(node, nid);
|
||||
return nid;
|
||||
};
|
||||
|
||||
const selectorFromNode = node => {
|
||||
const tag = node.localName;
|
||||
let selector = CSS.escape(tag);
|
||||
// Id
|
||||
if ( typeof node.id === 'string' ) {
|
||||
let str = node.id.trim();
|
||||
if ( str !== '' ) {
|
||||
selector += `#${CSS.escape(str)}`;
|
||||
}
|
||||
}
|
||||
// Class
|
||||
const cl = node.classList;
|
||||
if ( cl ) {
|
||||
for ( let i = 0; i < cl.length; i++ ) {
|
||||
selector += `.${CSS.escape(cl[i])}`;
|
||||
}
|
||||
}
|
||||
// Tag-specific attributes
|
||||
const attr = resourceAttrNames.get(tag);
|
||||
if ( attr !== undefined ) {
|
||||
let str = node.getAttribute(attr) || '';
|
||||
str = str.trim();
|
||||
const pos = str.startsWith('data:') ? 5 : str.search(/[#?]/);
|
||||
let sw = '';
|
||||
if ( pos !== -1 ) {
|
||||
str = str.slice(0, pos);
|
||||
sw = '^';
|
||||
}
|
||||
if ( str !== '' ) {
|
||||
selector += `[${attr}${sw}="${CSS.escape(str, true)}"]`;
|
||||
}
|
||||
}
|
||||
return selector;
|
||||
};
|
||||
|
||||
function DomRoot() {
|
||||
this.nid = newNodeId(document.body);
|
||||
this.lvl = 0;
|
||||
this.sel = 'body';
|
||||
this.cnt = 0;
|
||||
this.filter = roRedNodes.get(document.body);
|
||||
}
|
||||
|
||||
function DomNode(node, level) {
|
||||
this.nid = newNodeId(node);
|
||||
this.lvl = level;
|
||||
this.sel = selectorFromNode(node);
|
||||
this.cnt = 0;
|
||||
this.filter = roRedNodes.get(node);
|
||||
}
|
||||
|
||||
const domNodeFactory = (level, node) => {
|
||||
const localName = node.localName;
|
||||
if ( skipTagNames.has(localName) ) { return null; }
|
||||
// skip uBlock's own nodes
|
||||
if ( node === inspectorFrame ) { return null; }
|
||||
if ( level === 0 && localName === 'body' ) {
|
||||
return new DomRoot();
|
||||
}
|
||||
return new DomNode(node, level);
|
||||
};
|
||||
|
||||
// Collect layout data
|
||||
|
||||
const getLayoutData = ( ) => {
|
||||
const layout = [];
|
||||
const stack = [];
|
||||
let lvl = 0;
|
||||
let node = document.documentElement;
|
||||
if ( node === null ) { return layout; }
|
||||
|
||||
for (;;) {
|
||||
const domNode = domNodeFactory(lvl, node);
|
||||
if ( domNode !== null ) {
|
||||
layout.push(domNode);
|
||||
}
|
||||
// children
|
||||
if ( domNode !== null && node.firstElementChild !== null ) {
|
||||
stack.push(node);
|
||||
lvl += 1;
|
||||
node = node.firstElementChild;
|
||||
continue;
|
||||
}
|
||||
// sibling
|
||||
if ( node instanceof Element ) {
|
||||
if ( node.nextElementSibling === null ) {
|
||||
do {
|
||||
node = stack.pop();
|
||||
if ( !node ) { break; }
|
||||
lvl -= 1;
|
||||
} while ( node.nextElementSibling === null );
|
||||
if ( !node ) { break; }
|
||||
}
|
||||
node = node.nextElementSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
};
|
||||
|
||||
// Descendant count for each node.
|
||||
|
||||
const patchLayoutData = layout => {
|
||||
const stack = [];
|
||||
let ptr;
|
||||
let lvl = 0;
|
||||
let i = layout.length;
|
||||
|
||||
while ( i-- ) {
|
||||
const domNode = layout[i];
|
||||
if ( domNode.lvl === lvl ) {
|
||||
stack[ptr] += 1;
|
||||
continue;
|
||||
}
|
||||
if ( domNode.lvl > lvl ) {
|
||||
while ( lvl < domNode.lvl ) {
|
||||
stack.push(0);
|
||||
lvl += 1;
|
||||
}
|
||||
ptr = lvl - 1;
|
||||
stack[ptr] += 1;
|
||||
continue;
|
||||
}
|
||||
// domNode.lvl < lvl
|
||||
const cnt = stack.pop();
|
||||
domNode.cnt = cnt;
|
||||
lvl -= 1;
|
||||
ptr = lvl - 1;
|
||||
stack[ptr] += cnt + 1;
|
||||
}
|
||||
return layout;
|
||||
};
|
||||
|
||||
// Track and report mutations of the DOM
|
||||
|
||||
let mutationObserver = null;
|
||||
let mutationTimer;
|
||||
let addedNodelists = [];
|
||||
let removedNodelist = [];
|
||||
|
||||
const previousElementSiblingId = node => {
|
||||
let sibling = node;
|
||||
for (;;) {
|
||||
sibling = sibling.previousElementSibling;
|
||||
if ( sibling === null ) { return null; }
|
||||
if ( skipTagNames.has(sibling.localName) ) { continue; }
|
||||
return nodeToIdMap.get(sibling);
|
||||
}
|
||||
};
|
||||
|
||||
const journalFromBranch = (root, newNodes, newNodeToIdMap) => {
|
||||
let node = root.firstElementChild;
|
||||
while ( node !== null ) {
|
||||
const domNode = domNodeFactory(undefined, node);
|
||||
if ( domNode !== null ) {
|
||||
newNodeToIdMap.set(domNode.nid, domNode);
|
||||
newNodes.push(node);
|
||||
}
|
||||
// down
|
||||
if ( node.firstElementChild !== null ) {
|
||||
node = node.firstElementChild;
|
||||
continue;
|
||||
}
|
||||
// right
|
||||
if ( node.nextElementSibling !== null ) {
|
||||
node = node.nextElementSibling;
|
||||
continue;
|
||||
}
|
||||
// up then right
|
||||
for (;;) {
|
||||
if ( node.parentElement === root ) { return; }
|
||||
node = node.parentElement;
|
||||
if ( node.nextElementSibling !== null ) {
|
||||
node = node.nextElementSibling;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const journalFromMutations = ( ) => {
|
||||
mutationTimer = undefined;
|
||||
|
||||
// This is used to temporarily hold all added nodes, before resolving
|
||||
// their node id and relative position.
|
||||
const newNodes = [];
|
||||
const journalEntries = [];
|
||||
const newNodeToIdMap = new Map();
|
||||
|
||||
for ( const nodelist of addedNodelists ) {
|
||||
for ( const node of nodelist ) {
|
||||
if ( node.nodeType !== 1 ) { continue; }
|
||||
if ( node.parentElement === null ) { continue; }
|
||||
cosmeticFilterMapper.incremental(node);
|
||||
const domNode = domNodeFactory(undefined, node);
|
||||
if ( domNode !== null ) {
|
||||
newNodeToIdMap.set(domNode.nid, domNode);
|
||||
newNodes.push(node);
|
||||
}
|
||||
journalFromBranch(node, newNodes, newNodeToIdMap);
|
||||
}
|
||||
}
|
||||
addedNodelists = [];
|
||||
for ( const nodelist of removedNodelist ) {
|
||||
for ( const node of nodelist ) {
|
||||
if ( node.nodeType !== 1 ) { continue; }
|
||||
const nid = nodeToIdMap.get(node);
|
||||
if ( nid === undefined ) { continue; }
|
||||
journalEntries.push({ what: -1, nid });
|
||||
}
|
||||
}
|
||||
removedNodelist = [];
|
||||
for ( const node of newNodes ) {
|
||||
journalEntries.push({
|
||||
what: 1,
|
||||
nid: nodeToIdMap.get(node),
|
||||
u: nodeToIdMap.get(node.parentElement),
|
||||
l: previousElementSiblingId(node)
|
||||
});
|
||||
}
|
||||
|
||||
if ( journalEntries.length === 0 ) { return; }
|
||||
|
||||
contentInspectorChannel.toLogger({
|
||||
what: 'domLayoutIncremental',
|
||||
url: window.location.href,
|
||||
hostname: window.location.hostname,
|
||||
journal: journalEntries,
|
||||
nodes: Array.from(newNodeToIdMap)
|
||||
});
|
||||
};
|
||||
|
||||
const onMutationObserved = mutationRecords => {
|
||||
for ( const record of mutationRecords ) {
|
||||
if ( record.addedNodes.length !== 0 ) {
|
||||
addedNodelists.push(record.addedNodes);
|
||||
}
|
||||
if ( record.removedNodes.length !== 0 ) {
|
||||
removedNodelist.push(record.removedNodes);
|
||||
}
|
||||
}
|
||||
if ( mutationTimer === undefined ) {
|
||||
mutationTimer = vAPI.setTimeout(journalFromMutations, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// API
|
||||
|
||||
const getLayout = ( ) => {
|
||||
cosmeticFilterMapper.reset();
|
||||
mutationObserver = new MutationObserver(onMutationObserved);
|
||||
mutationObserver.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
return {
|
||||
what: 'domLayoutFull',
|
||||
url: window.location.href,
|
||||
hostname: window.location.hostname,
|
||||
layout: patchLayoutData(getLayoutData())
|
||||
};
|
||||
};
|
||||
|
||||
const reset = ( ) => {
|
||||
shutdown();
|
||||
};
|
||||
|
||||
const shutdown = ( ) => {
|
||||
if ( mutationTimer !== undefined ) {
|
||||
clearTimeout(mutationTimer);
|
||||
mutationTimer = undefined;
|
||||
}
|
||||
if ( mutationObserver !== null ) {
|
||||
mutationObserver.disconnect();
|
||||
mutationObserver = null;
|
||||
}
|
||||
addedNodelists = [];
|
||||
removedNodelist = [];
|
||||
};
|
||||
|
||||
return {
|
||||
get: getLayout,
|
||||
reset,
|
||||
shutdown,
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
const cosmeticFilterMapper = (( ) => {
|
||||
const nodesFromStyleTag = rootNode => {
|
||||
const filterMap = roRedNodes;
|
||||
const details = vAPI.domFilterer.getAllSelectors();
|
||||
|
||||
// Declarative selectors.
|
||||
for ( const block of (details.declarative || []) ) {
|
||||
for ( const selector of block.split(',\n') ) {
|
||||
let nodes;
|
||||
if ( reHasCSSCombinators.test(selector) ) {
|
||||
nodes = document.querySelectorAll(selector);
|
||||
} else {
|
||||
if (
|
||||
filterMap.has(rootNode) === false &&
|
||||
rootNode.matches(selector)
|
||||
) {
|
||||
filterMap.set(rootNode, selector);
|
||||
}
|
||||
nodes = rootNode.querySelectorAll(selector);
|
||||
}
|
||||
for ( const node of nodes ) {
|
||||
if ( filterMap.has(node) ) { continue; }
|
||||
filterMap.set(node, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procedural selectors.
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const incremental = rootNode => {
|
||||
nodesFromStyleTag(rootNode);
|
||||
};
|
||||
|
||||
const reset = ( ) => {
|
||||
roRedNodes.clear();
|
||||
if ( document.documentElement !== null ) {
|
||||
incremental(document.documentElement);
|
||||
}
|
||||
};
|
||||
|
||||
const shutdown = ( ) => {
|
||||
vAPI.domFilterer.toggle(true);
|
||||
};
|
||||
|
||||
return {
|
||||
incremental,
|
||||
reset,
|
||||
shutdown,
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const elementsFromSelector = function(selector, context) {
|
||||
if ( !context ) {
|
||||
context = document;
|
||||
}
|
||||
if ( selector.indexOf(':') !== -1 ) {
|
||||
const out = elementsFromSpecialSelector(selector);
|
||||
if ( out !== undefined ) { return out; }
|
||||
}
|
||||
// plain CSS selector
|
||||
try {
|
||||
return context.querySelectorAll(selector);
|
||||
} catch (ex) {
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const elementsFromSpecialSelector = function(selector) {
|
||||
const out = [];
|
||||
let matches = /^(.+?):has\((.+?)\)$/.exec(selector);
|
||||
if ( matches !== null ) {
|
||||
let nodes;
|
||||
try {
|
||||
nodes = document.querySelectorAll(matches[1]);
|
||||
} catch(ex) {
|
||||
nodes = [];
|
||||
}
|
||||
for ( const node of nodes ) {
|
||||
if ( node.querySelector(matches[2]) === null ) { continue; }
|
||||
out.push(node);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
matches = /^:xpath\((.+?)\)$/.exec(selector);
|
||||
if ( matches === null ) { return; }
|
||||
const xpr = document.evaluate(
|
||||
matches[1],
|
||||
document,
|
||||
null,
|
||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||
null
|
||||
);
|
||||
let i = xpr.snapshotLength;
|
||||
while ( i-- ) {
|
||||
out.push(xpr.snapshotItem(i));
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const highlightElements = ( ) => {
|
||||
const paths = [];
|
||||
|
||||
const path = [];
|
||||
for ( const elem of rwRedNodes.keys() ) {
|
||||
if ( elem === inspectorFrame ) { continue; }
|
||||
if ( rwGreenNodes.has(elem) ) { continue; }
|
||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||
const rect = elem.getBoundingClientRect();
|
||||
const xl = rect.left;
|
||||
const w = rect.width;
|
||||
const yt = rect.top;
|
||||
const h = rect.height;
|
||||
const ws = w.toFixed(1);
|
||||
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||
'h' + ws +
|
||||
'v' + h.toFixed(1) +
|
||||
'h-' + ws +
|
||||
'z';
|
||||
path.push(poly);
|
||||
}
|
||||
paths.push(path.join('') || 'M0 0');
|
||||
|
||||
path.length = 0;
|
||||
for ( const elem of rwGreenNodes ) {
|
||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||
const rect = elem.getBoundingClientRect();
|
||||
const xl = rect.left;
|
||||
const w = rect.width;
|
||||
const yt = rect.top;
|
||||
const h = rect.height;
|
||||
const ws = w.toFixed(1);
|
||||
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||
'h' + ws +
|
||||
'v' + h.toFixed(1) +
|
||||
'h-' + ws +
|
||||
'z';
|
||||
path.push(poly);
|
||||
}
|
||||
paths.push(path.join('') || 'M0 0');
|
||||
|
||||
path.length = 0;
|
||||
for ( const elem of roRedNodes.keys() ) {
|
||||
if ( elem === inspectorFrame ) { continue; }
|
||||
if ( rwGreenNodes.has(elem) ) { continue; }
|
||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||
const rect = elem.getBoundingClientRect();
|
||||
const xl = rect.left;
|
||||
const w = rect.width;
|
||||
const yt = rect.top;
|
||||
const h = rect.height;
|
||||
const ws = w.toFixed(1);
|
||||
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||
'h' + ws +
|
||||
'v' + h.toFixed(1) +
|
||||
'h-' + ws +
|
||||
'z';
|
||||
path.push(poly);
|
||||
}
|
||||
paths.push(path.join('') || 'M0 0');
|
||||
|
||||
path.length = 0;
|
||||
for ( const elem of blueNodes ) {
|
||||
if ( elem === inspectorFrame ) { continue; }
|
||||
if ( typeof elem.getBoundingClientRect !== 'function' ) { continue; }
|
||||
const rect = elem.getBoundingClientRect();
|
||||
const xl = rect.left;
|
||||
const w = rect.width;
|
||||
const yt = rect.top;
|
||||
const h = rect.height;
|
||||
const ws = w.toFixed(1);
|
||||
const poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||
'h' + ws +
|
||||
'v' + h.toFixed(1) +
|
||||
'h-' + ws +
|
||||
'z';
|
||||
path.push(poly);
|
||||
}
|
||||
paths.push(path.join('') || 'M0 0');
|
||||
|
||||
contentInspectorChannel.toFrame({
|
||||
what: 'svgPaths',
|
||||
paths,
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const onScrolled = (( ) => {
|
||||
let timer;
|
||||
return ( ) => {
|
||||
if ( timer ) { return; }
|
||||
timer = window.requestAnimationFrame(( ) => {
|
||||
timer = undefined;
|
||||
highlightElements();
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
const onMouseOver = ( ) => {
|
||||
if ( blueNodes.length === 0 ) { return; }
|
||||
blueNodes = [];
|
||||
highlightElements();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const selectNodes = (selector, nid) => {
|
||||
const nodes = elementsFromSelector(selector);
|
||||
if ( nid === '' ) { return nodes; }
|
||||
for ( const node of nodes ) {
|
||||
if ( nodeToIdMap.get(node) === nid ) {
|
||||
return [ node ];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const nodesFromFilter = selector => {
|
||||
const out = [];
|
||||
for ( const entry of roRedNodes ) {
|
||||
if ( entry[1] === selector ) {
|
||||
out.push(entry[0]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const toggleExceptions = (nodes, targetState) => {
|
||||
for ( const node of nodes ) {
|
||||
if ( targetState ) {
|
||||
rwGreenNodes.add(node);
|
||||
} else {
|
||||
rwGreenNodes.delete(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toggleFilter = (nodes, targetState) => {
|
||||
for ( const node of nodes ) {
|
||||
if ( targetState ) {
|
||||
rwRedNodes.delete(node);
|
||||
} else {
|
||||
rwRedNodes.add(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetToggledNodes = ( ) => {
|
||||
rwGreenNodes.clear();
|
||||
rwRedNodes.clear();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const startInspector = ( ) => {
|
||||
const onReady = ( ) => {
|
||||
window.addEventListener('scroll', onScrolled, {
|
||||
capture: true,
|
||||
passive: true,
|
||||
});
|
||||
window.addEventListener('mouseover', onMouseOver, {
|
||||
capture: true,
|
||||
passive: true,
|
||||
});
|
||||
contentInspectorChannel.toLogger(domLayout.get());
|
||||
vAPI.domFilterer.toggle(false, highlightElements);
|
||||
};
|
||||
if ( document.readyState === 'loading' ) {
|
||||
document.addEventListener('DOMContentLoaded', onReady, { once: true });
|
||||
} else {
|
||||
onReady();
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const shutdownInspector = ( ) => {
|
||||
cosmeticFilterMapper.shutdown();
|
||||
domLayout.shutdown();
|
||||
window.removeEventListener('scroll', onScrolled, {
|
||||
capture: true,
|
||||
passive: true,
|
||||
});
|
||||
window.removeEventListener('mouseover', onMouseOver, {
|
||||
capture: true,
|
||||
passive: true,
|
||||
});
|
||||
contentInspectorChannel.shutdown();
|
||||
if ( inspectorFrame ) {
|
||||
inspectorFrame.remove();
|
||||
inspectorFrame = null;
|
||||
}
|
||||
vAPI.userStylesheet.remove(inspectorCSS);
|
||||
vAPI.userStylesheet.apply();
|
||||
vAPI.inspectorFrame = false;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
const onMessage = request => {
|
||||
switch ( request.what ) {
|
||||
case 'startInspector':
|
||||
startInspector();
|
||||
break;
|
||||
|
||||
case 'quitInspector':
|
||||
shutdownInspector();
|
||||
break;
|
||||
|
||||
case 'commitFilters':
|
||||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'domLayout':
|
||||
domLayout.get();
|
||||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'highlightMode':
|
||||
break;
|
||||
|
||||
case 'highlightOne':
|
||||
blueNodes = selectNodes(request.selector, request.nid);
|
||||
if ( blueNodes.length !== 0 ) {
|
||||
blueNodes[0].scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
inline: 'nearest',
|
||||
});
|
||||
}
|
||||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'resetToggledNodes':
|
||||
resetToggledNodes();
|
||||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'showCommitted':
|
||||
blueNodes = [];
|
||||
// TODO: show only the new filters and exceptions.
|
||||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'showInteractive':
|
||||
blueNodes = [];
|
||||
highlightElements();
|
||||
break;
|
||||
|
||||
case 'toggleFilter': {
|
||||
const nodes = selectNodes(request.selector, request.nid);
|
||||
if ( nodes.length !== 0 ) {
|
||||
nodes[0].scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
inline: 'nearest',
|
||||
});
|
||||
}
|
||||
toggleExceptions(nodesFromFilter(request.filter), request.target);
|
||||
highlightElements();
|
||||
break;
|
||||
}
|
||||
case 'toggleNodes': {
|
||||
const nodes = selectNodes(request.selector, request.nid);
|
||||
if ( nodes.length !== 0 ) {
|
||||
nodes[0].scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
inline: 'nearest',
|
||||
});
|
||||
}
|
||||
toggleFilter(nodes, request.target);
|
||||
highlightElements();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Establish two-way communication with logger/inspector window and
|
||||
* inspector frame
|
||||
*
|
||||
* */
|
||||
|
||||
const contentInspectorChannel = (( ) => {
|
||||
let toLoggerPort;
|
||||
let toFramePort;
|
||||
|
||||
const toLogger = msg => {
|
||||
if ( toLoggerPort === undefined ) { return; }
|
||||
try {
|
||||
toLoggerPort.postMessage(msg);
|
||||
} catch(_) {
|
||||
shutdownInspector();
|
||||
}
|
||||
};
|
||||
|
||||
const onLoggerMessage = msg => {
|
||||
onMessage(msg);
|
||||
};
|
||||
|
||||
const onLoggerDisconnect = ( ) => {
|
||||
shutdownInspector();
|
||||
};
|
||||
|
||||
const onLoggerConnect = port => {
|
||||
browser.runtime.onConnect.removeListener(onLoggerConnect);
|
||||
toLoggerPort = port;
|
||||
port.onMessage.addListener(onLoggerMessage);
|
||||
port.onDisconnect.addListener(onLoggerDisconnect);
|
||||
};
|
||||
|
||||
const toFrame = msg => {
|
||||
if ( toFramePort === undefined ) { return; }
|
||||
toFramePort.postMessage(msg);
|
||||
};
|
||||
|
||||
const shutdown = ( ) => {
|
||||
if ( toFramePort !== undefined ) {
|
||||
toFrame({ what: 'quitInspector' });
|
||||
toFramePort.onmessage = null;
|
||||
toFramePort.close();
|
||||
toFramePort = undefined;
|
||||
}
|
||||
if ( toLoggerPort !== undefined ) {
|
||||
toLoggerPort.onMessage.removeListener(onLoggerMessage);
|
||||
toLoggerPort.onDisconnect.removeListener(onLoggerDisconnect);
|
||||
toLoggerPort.disconnect();
|
||||
toLoggerPort = undefined;
|
||||
}
|
||||
browser.runtime.onConnect.removeListener(onLoggerConnect);
|
||||
};
|
||||
|
||||
const start = async ( ) => {
|
||||
browser.runtime.onConnect.addListener(onLoggerConnect);
|
||||
const inspectorArgs = await vAPI.messaging.send('domInspectorContent', {
|
||||
what: 'getInspectorArgs',
|
||||
});
|
||||
if ( typeof inspectorArgs !== 'object' ) { return; }
|
||||
if ( inspectorArgs === null ) { return; }
|
||||
return new Promise(resolve => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute(inspectorUniqueId, '');
|
||||
document.documentElement.append(iframe);
|
||||
iframe.addEventListener('load', ( ) => {
|
||||
iframe.setAttribute(`${inspectorUniqueId}-loaded`, '');
|
||||
const channel = new MessageChannel();
|
||||
toFramePort = channel.port1;
|
||||
toFramePort.onmessage = ev => {
|
||||
const msg = ev.data || {};
|
||||
if ( msg.what !== 'startInspector' ) { return; }
|
||||
};
|
||||
iframe.contentWindow.postMessage(
|
||||
{ what: 'startInspector' },
|
||||
inspectorArgs.inspectorURL,
|
||||
[ channel.port2 ]
|
||||
);
|
||||
resolve(iframe);
|
||||
}, { once: true });
|
||||
iframe.contentWindow.location = inspectorArgs.inspectorURL;
|
||||
});
|
||||
};
|
||||
|
||||
return { start, toLogger, toFrame, shutdown };
|
||||
})();
|
||||
|
||||
|
||||
// Install DOM inspector widget
|
||||
const inspectorCSSStyle = [
|
||||
'background: transparent',
|
||||
'border: 0',
|
||||
'border-radius: 0',
|
||||
'box-shadow: none',
|
||||
'color-scheme: light dark',
|
||||
'display: block',
|
||||
'filter: none',
|
||||
'height: 100%',
|
||||
'left: 0',
|
||||
'margin: 0',
|
||||
'max-height: none',
|
||||
'max-width: none',
|
||||
'min-height: unset',
|
||||
'min-width: unset',
|
||||
'opacity: 1',
|
||||
'outline: 0',
|
||||
'padding: 0',
|
||||
'pointer-events: none',
|
||||
'position: fixed',
|
||||
'top: 0',
|
||||
'transform: none',
|
||||
'visibility: hidden',
|
||||
'width: 100%',
|
||||
'z-index: 2147483647',
|
||||
''
|
||||
].join(' !important;\n');
|
||||
|
||||
const inspectorCSS = `
|
||||
:root > [${inspectorUniqueId}] {
|
||||
${inspectorCSSStyle}
|
||||
}
|
||||
:root > [${inspectorUniqueId}-loaded] {
|
||||
visibility: visible !important;
|
||||
}
|
||||
`;
|
||||
|
||||
vAPI.userStylesheet.add(inspectorCSS);
|
||||
vAPI.userStylesheet.apply();
|
||||
|
||||
let inspectorFrame = await contentInspectorChannel.start();
|
||||
if ( inspectorFrame instanceof HTMLIFrameElement === false ) {
|
||||
return shutdownInspector();
|
||||
}
|
||||
|
||||
startInspector();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
72
uBlock0.chromium/js/scriptlets/dom-survey-elements.js
Normal file
72
uBlock0.chromium/js/scriptlets/dom-survey-elements.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/756
|
||||
// Keep in mind CPU usage with large DOM and/or filterset.
|
||||
|
||||
(( ) => {
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
|
||||
const t0 = Date.now();
|
||||
|
||||
if ( vAPI.domSurveyElements instanceof Object === false ) {
|
||||
vAPI.domSurveyElements = {
|
||||
busy: false,
|
||||
hiddenElementCount: Number.NaN,
|
||||
surveyTime: t0,
|
||||
};
|
||||
}
|
||||
const surveyResults = vAPI.domSurveyElements;
|
||||
|
||||
if ( surveyResults.busy ) { return; }
|
||||
surveyResults.busy = true;
|
||||
|
||||
if ( surveyResults.surveyTime < vAPI.domMutationTime ) {
|
||||
surveyResults.hiddenElementCount = Number.NaN;
|
||||
}
|
||||
surveyResults.surveyTime = t0;
|
||||
|
||||
if ( isNaN(surveyResults.hiddenElementCount) ) {
|
||||
surveyResults.hiddenElementCount = (( ) => {
|
||||
if ( vAPI.domFilterer instanceof Object === false ) { return 0; }
|
||||
const details = vAPI.domFilterer.getAllSelectors(0b11);
|
||||
if (
|
||||
Array.isArray(details.declarative) === false ||
|
||||
details.declarative.length === 0
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
return document.querySelectorAll(
|
||||
details.declarative.join(',\n')
|
||||
).length;
|
||||
})();
|
||||
}
|
||||
|
||||
surveyResults.busy = false;
|
||||
|
||||
// IMPORTANT: This is returned to the injector, so this MUST be
|
||||
// the last statement.
|
||||
return surveyResults.hiddenElementCount;
|
||||
})();
|
||||
126
uBlock0.chromium/js/scriptlets/dom-survey-scripts.js
Normal file
126
uBlock0.chromium/js/scriptlets/dom-survey-scripts.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Scriptlets to count the number of script tags in a document.
|
||||
|
||||
(( ) => {
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
|
||||
const t0 = Date.now();
|
||||
|
||||
if ( vAPI.domSurveyScripts instanceof Object === false ) {
|
||||
vAPI.domSurveyScripts = {
|
||||
busy: false,
|
||||
scriptCount: -1,
|
||||
surveyTime: t0,
|
||||
};
|
||||
}
|
||||
const surveyResults = vAPI.domSurveyScripts;
|
||||
|
||||
if ( surveyResults.busy ) { return; }
|
||||
surveyResults.busy = true;
|
||||
|
||||
if ( surveyResults.surveyTime < vAPI.domMutationTime ) {
|
||||
surveyResults.scriptCount = -1;
|
||||
}
|
||||
surveyResults.surveyTime = t0;
|
||||
|
||||
if ( surveyResults.scriptCount === -1 ) {
|
||||
const reInlineScript = /^(data:|blob:|$)/;
|
||||
let inlineScriptCount = 0;
|
||||
let scriptCount = 0;
|
||||
for ( const script of document.scripts ) {
|
||||
if ( reInlineScript.test(script.src) ) {
|
||||
inlineScriptCount = 1;
|
||||
continue;
|
||||
}
|
||||
scriptCount += 1;
|
||||
if ( scriptCount === 99 ) { break; }
|
||||
}
|
||||
scriptCount += inlineScriptCount;
|
||||
if ( scriptCount !== 0 ) {
|
||||
surveyResults.scriptCount = scriptCount;
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/756
|
||||
// Keep trying to find inline script-like instances but only if we
|
||||
// have the time-budget to do so.
|
||||
if ( surveyResults.scriptCount === -1 ) {
|
||||
if ( document.querySelector('a[href^="javascript:"]') !== null ) {
|
||||
surveyResults.scriptCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/1756
|
||||
// Mind that there might be no body element.
|
||||
if ( surveyResults.scriptCount === -1 && document.body !== null ) {
|
||||
surveyResults.scriptCount = 0;
|
||||
const onHandlers = new Set([
|
||||
'onabort', 'onblur', 'oncancel', 'oncanplay',
|
||||
'oncanplaythrough', 'onchange', 'onclick', 'onclose',
|
||||
'oncontextmenu', 'oncuechange', 'ondblclick', 'ondrag',
|
||||
'ondragend', 'ondragenter', 'ondragexit', 'ondragleave',
|
||||
'ondragover', 'ondragstart', 'ondrop', 'ondurationchange',
|
||||
'onemptied', 'onended', 'onerror', 'onfocus',
|
||||
'oninput', 'oninvalid', 'onkeydown', 'onkeypress',
|
||||
'onkeyup', 'onload', 'onloadeddata', 'onloadedmetadata',
|
||||
'onloadstart', 'onmousedown', 'onmouseenter', 'onmouseleave',
|
||||
'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup',
|
||||
'onwheel', 'onpause', 'onplay', 'onplaying',
|
||||
'onprogress', 'onratechange', 'onreset', 'onresize',
|
||||
'onscroll', 'onseeked', 'onseeking', 'onselect',
|
||||
'onshow', 'onstalled', 'onsubmit', 'onsuspend',
|
||||
'ontimeupdate', 'ontoggle', 'onvolumechange', 'onwaiting',
|
||||
'onafterprint', 'onbeforeprint', 'onbeforeunload', 'onhashchange',
|
||||
'onlanguagechange', 'onmessage', 'onoffline', 'ononline',
|
||||
'onpagehide', 'onpageshow', 'onrejectionhandled', 'onpopstate',
|
||||
'onstorage', 'onunhandledrejection', 'onunload',
|
||||
'oncopy', 'oncut', 'onpaste'
|
||||
]);
|
||||
const nodeIter = document.createNodeIterator(
|
||||
document.body,
|
||||
NodeFilter.SHOW_ELEMENT
|
||||
);
|
||||
for (;;) {
|
||||
const node = nodeIter.nextNode();
|
||||
if ( node === null ) { break; }
|
||||
if ( node.hasAttributes() === false ) { continue; }
|
||||
for ( const attr of node.getAttributeNames() ) {
|
||||
if ( onHandlers.has(attr) === false ) { continue; }
|
||||
surveyResults.scriptCount = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
surveyResults.busy = false;
|
||||
|
||||
// IMPORTANT: This is returned to the injector, so this MUST be
|
||||
// the last statement.
|
||||
if ( surveyResults.scriptCount !== -1 ) {
|
||||
return surveyResults.scriptCount;
|
||||
}
|
||||
})();
|
||||
1359
uBlock0.chromium/js/scriptlets/epicker.js
Normal file
1359
uBlock0.chromium/js/scriptlets/epicker.js
Normal file
File diff suppressed because it is too large
Load Diff
67
uBlock0.chromium/js/scriptlets/load-3p-css.js
Normal file
67
uBlock0.chromium/js/scriptlets/load-3p-css.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2020-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
|
||||
if ( vAPI.dynamicReloadToken === undefined ) {
|
||||
vAPI.dynamicReloadToken = vAPI.randomToken();
|
||||
}
|
||||
|
||||
for ( const sheet of Array.from(document.styleSheets) ) {
|
||||
let loaded = false;
|
||||
try {
|
||||
loaded = sheet.rules.length !== 0;
|
||||
} catch(ex) {
|
||||
}
|
||||
if ( loaded ) { continue; }
|
||||
const link = sheet.ownerNode || null;
|
||||
if ( link === null || link.localName !== 'link' ) { continue; }
|
||||
if ( link.hasAttribute(vAPI.dynamicReloadToken) ) { continue; }
|
||||
const clone = link.cloneNode(true);
|
||||
clone.setAttribute(vAPI.dynamicReloadToken, '');
|
||||
link.replaceWith(clone);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
62
uBlock0.chromium/js/scriptlets/load-large-media-all.js
Normal file
62
uBlock0.chromium/js/scriptlets/load-large-media-all.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-2018 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (
|
||||
typeof vAPI !== 'object' ||
|
||||
vAPI.loadAllLargeMedia instanceof Function === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
vAPI.loadAllLargeMedia();
|
||||
vAPI.loadAllLargeMedia = undefined;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
312
uBlock0.chromium/js/scriptlets/load-large-media-interactive.js
Normal file
312
uBlock0.chromium/js/scriptlets/load-large-media-interactive.js
Normal file
@@ -0,0 +1,312 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
(( ) => {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' || vAPI.loadAllLargeMedia instanceof Function ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const largeMediaElementAttribute = 'data-' + vAPI.sessionId;
|
||||
const largeMediaElementSelector =
|
||||
':root audio[' + largeMediaElementAttribute + '],\n' +
|
||||
':root img[' + largeMediaElementAttribute + '],\n' +
|
||||
':root picture[' + largeMediaElementAttribute + '],\n' +
|
||||
':root video[' + largeMediaElementAttribute + ']';
|
||||
|
||||
const isMediaElement = elem =>
|
||||
(/^(?:audio|img|picture|video)$/.test(elem.localName));
|
||||
|
||||
const isPlayableMediaElement = elem =>
|
||||
(/^(?:audio|video)$/.test(elem.localName));
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const mediaNotLoaded = function(elem) {
|
||||
switch ( elem.localName ) {
|
||||
case 'audio':
|
||||
case 'video':
|
||||
return elem.readyState === 0 || elem.error !== null;
|
||||
case 'img': {
|
||||
if ( elem.naturalWidth !== 0 || elem.naturalHeight !== 0 ) {
|
||||
break;
|
||||
}
|
||||
const style = window.getComputedStyle(elem);
|
||||
// For some reason, style can be null with Pale Moon.
|
||||
return style !== null ?
|
||||
style.getPropertyValue('display') !== 'none' :
|
||||
elem.offsetHeight !== 0 && elem.offsetWidth !== 0;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// For all media resources which have failed to load, trigger a reload.
|
||||
|
||||
// <audio> and <video> elements.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
|
||||
|
||||
const surveyMissingMediaElements = function() {
|
||||
let largeMediaElementCount = 0;
|
||||
for ( const elem of document.querySelectorAll('audio,img,video') ) {
|
||||
if ( mediaNotLoaded(elem) === false ) { continue; }
|
||||
elem.setAttribute(largeMediaElementAttribute, '');
|
||||
largeMediaElementCount += 1;
|
||||
switch ( elem.localName ) {
|
||||
case 'img': {
|
||||
const picture = elem.closest('picture');
|
||||
if ( picture !== null ) {
|
||||
picture.setAttribute(largeMediaElementAttribute, '');
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return largeMediaElementCount;
|
||||
};
|
||||
|
||||
if ( surveyMissingMediaElements() ) {
|
||||
// Insert CSS to highlight blocked media elements.
|
||||
if ( vAPI.largeMediaElementStyleSheet === undefined ) {
|
||||
vAPI.largeMediaElementStyleSheet = [
|
||||
largeMediaElementSelector + ' {',
|
||||
'border: 2px dotted red !important;',
|
||||
'box-sizing: border-box !important;',
|
||||
'cursor: zoom-in !important;',
|
||||
'display: inline-block;',
|
||||
'filter: none !important;',
|
||||
'font-size: 1rem !important;',
|
||||
'min-height: 1em !important;',
|
||||
'min-width: 1em !important;',
|
||||
'opacity: 1 !important;',
|
||||
'outline: none !important;',
|
||||
'transform: none !important;',
|
||||
'visibility: visible !important;',
|
||||
'z-index: 2147483647',
|
||||
'}',
|
||||
].join('\n');
|
||||
vAPI.userStylesheet.add(vAPI.largeMediaElementStyleSheet);
|
||||
vAPI.userStylesheet.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const loadMedia = async function(elem) {
|
||||
const src = elem.getAttribute('src') || '';
|
||||
if ( src === '' ) { return; }
|
||||
elem.removeAttribute('src');
|
||||
await vAPI.messaging.send('scriptlets', {
|
||||
what: 'temporarilyAllowLargeMediaElement',
|
||||
});
|
||||
elem.setAttribute('src', src);
|
||||
elem.load();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const loadImage = async function(elem) {
|
||||
const src = elem.getAttribute('src') || '';
|
||||
const srcset = src === '' && elem.getAttribute('srcset') || '';
|
||||
if ( src === '' && srcset === '' ) { return; }
|
||||
if ( src !== '' ) {
|
||||
elem.removeAttribute('src');
|
||||
}
|
||||
if ( srcset !== '' ) {
|
||||
elem.removeAttribute('srcset');
|
||||
}
|
||||
await vAPI.messaging.send('scriptlets', {
|
||||
what: 'temporarilyAllowLargeMediaElement',
|
||||
});
|
||||
if ( src !== '' ) {
|
||||
elem.setAttribute('src', src);
|
||||
} else if ( srcset !== '' ) {
|
||||
elem.setAttribute('srcset', srcset);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const loadMany = function(elems) {
|
||||
for ( const elem of elems ) {
|
||||
switch ( elem.localName ) {
|
||||
case 'audio':
|
||||
case 'video':
|
||||
loadMedia(elem);
|
||||
break;
|
||||
case 'img':
|
||||
loadImage(elem);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const onMouseClick = function(ev) {
|
||||
if ( ev.button !== 0 || ev.isTrusted === false ) { return; }
|
||||
|
||||
const toLoad = [];
|
||||
const elems = document.elementsFromPoint instanceof Function
|
||||
? document.elementsFromPoint(ev.clientX, ev.clientY)
|
||||
: [ ev.target ];
|
||||
for ( const elem of elems ) {
|
||||
if ( elem.matches(largeMediaElementSelector) === false ) { continue; }
|
||||
elem.removeAttribute(largeMediaElementAttribute);
|
||||
if ( mediaNotLoaded(elem) ) {
|
||||
toLoad.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if ( toLoad.length === 0 ) { return; }
|
||||
|
||||
loadMany(toLoad);
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
};
|
||||
|
||||
document.addEventListener('click', onMouseClick, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const onLoadedData = function(ev) {
|
||||
const media = ev.target;
|
||||
if ( media.localName !== 'audio' && media.localName !== 'video' ) {
|
||||
return;
|
||||
}
|
||||
const src = media.src;
|
||||
if ( typeof src === 'string' && src.startsWith('blob:') === false ) {
|
||||
return;
|
||||
}
|
||||
media.autoplay = false;
|
||||
media.pause();
|
||||
};
|
||||
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/mxgpmc/
|
||||
// Support cases where the media source is not yet set.
|
||||
for ( const media of document.querySelectorAll('audio,video') ) {
|
||||
const src = media.src;
|
||||
if (
|
||||
(typeof src === 'string') &&
|
||||
(src === '' || src.startsWith('blob:'))
|
||||
) {
|
||||
media.autoplay = false;
|
||||
media.pause();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('loadeddata', onLoadedData);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const onLoad = function(ev) {
|
||||
const elem = ev.target;
|
||||
if ( isMediaElement(elem) === false ) { return; }
|
||||
elem.removeAttribute(largeMediaElementAttribute);
|
||||
};
|
||||
|
||||
document.addEventListener('load', onLoad, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const onLoadError = function(ev) {
|
||||
const elem = ev.target;
|
||||
if ( isMediaElement(elem) === false ) { return; }
|
||||
if ( mediaNotLoaded(elem) ) {
|
||||
elem.setAttribute(largeMediaElementAttribute, '');
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('error', onLoadError, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const autoPausedMedia = new WeakMap();
|
||||
|
||||
for ( const elem of document.querySelectorAll('audio,video') ) {
|
||||
elem.setAttribute('autoplay', 'false');
|
||||
}
|
||||
|
||||
const preventAutoplay = function(ev) {
|
||||
const elem = ev.target;
|
||||
if ( isPlayableMediaElement(elem) === false ) { return; }
|
||||
const currentSrc = elem.getAttribute('src') || '';
|
||||
const pausedSrc = autoPausedMedia.get(elem);
|
||||
if ( pausedSrc === currentSrc ) { return; }
|
||||
autoPausedMedia.set(elem, currentSrc);
|
||||
elem.setAttribute('autoplay', 'false');
|
||||
elem.pause();
|
||||
};
|
||||
|
||||
document.addEventListener('timeupdate', preventAutoplay, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.loadAllLargeMedia = function() {
|
||||
document.removeEventListener('click', onMouseClick, true);
|
||||
document.removeEventListener('loadeddata', onLoadedData, true);
|
||||
document.removeEventListener('load', onLoad, true);
|
||||
document.removeEventListener('error', onLoadError, true);
|
||||
|
||||
const toLoad = [];
|
||||
for ( const elem of document.querySelectorAll(largeMediaElementSelector) ) {
|
||||
elem.removeAttribute(largeMediaElementAttribute);
|
||||
if ( mediaNotLoaded(elem) ) {
|
||||
toLoad.push(elem);
|
||||
}
|
||||
}
|
||||
loadMany(toLoad);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
89
uBlock0.chromium/js/scriptlets/noscript-spoof.js
Normal file
89
uBlock0.chromium/js/scriptlets/noscript-spoof.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2014-present 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
|
||||
*/
|
||||
|
||||
// Code below has been imported from uMatrix and modified to fit uBO:
|
||||
// https://github.com/gorhill/uMatrix/blob/3f8794dd899a05e066c24066c6c0a2515d5c60d2/src/js/contentscript.js#L464-L531
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uMatrix/issues/232
|
||||
// Force `display` property, Firefox is still affected by the issue.
|
||||
|
||||
(( ) => {
|
||||
const noscripts = document.querySelectorAll('noscript');
|
||||
if ( noscripts.length === 0 ) { return; }
|
||||
|
||||
const reMetaContent = /^\s*(\d+)\s*;\s*url=(?:"([^"]+)"|'([^']+)'|(.+))/i;
|
||||
const reSafeURL = /^https?:\/\//;
|
||||
let redirectTimer;
|
||||
|
||||
const autoRefresh = function(root) {
|
||||
const meta = root.querySelector('meta[http-equiv="refresh"][content]');
|
||||
if ( meta === null ) { return; }
|
||||
const match = reMetaContent.exec(meta.getAttribute('content'));
|
||||
if ( match === null ) { return; }
|
||||
const refreshURL = (match[2] || match[3] || match[4] || '').trim();
|
||||
let url;
|
||||
try {
|
||||
url = new URL(refreshURL, document.baseURI);
|
||||
} catch(ex) {
|
||||
return;
|
||||
}
|
||||
if ( reSafeURL.test(url.href) === false ) { return; }
|
||||
redirectTimer = setTimeout(( ) => {
|
||||
location.assign(url.href);
|
||||
},
|
||||
parseInt(match[1], 10) * 1000 + 1
|
||||
);
|
||||
meta.parentNode.removeChild(meta);
|
||||
};
|
||||
|
||||
const morphNoscript = function(from) {
|
||||
if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) {
|
||||
const to = document.createElement('span');
|
||||
while ( from.firstChild !== null ) {
|
||||
to.appendChild(from.firstChild);
|
||||
}
|
||||
return to;
|
||||
}
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(
|
||||
'<span>' + from.textContent + '</span>',
|
||||
'text/html'
|
||||
);
|
||||
return document.adoptNode(doc.querySelector('span'));
|
||||
};
|
||||
|
||||
for ( const noscript of noscripts ) {
|
||||
const parent = noscript.parentNode;
|
||||
if ( parent === null ) { continue; }
|
||||
const span = morphNoscript(noscript);
|
||||
span.style.setProperty('display', 'inline', 'important');
|
||||
if ( redirectTimer === undefined ) {
|
||||
autoRefresh(span);
|
||||
}
|
||||
parent.replaceChild(span, noscript);
|
||||
}
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
49
uBlock0.chromium/js/scriptlets/scriptlet-loglevel-1.js
Normal file
49
uBlock0.chromium/js/scriptlets/scriptlet-loglevel-1.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2024-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
if ( self.uBO_bcSecret instanceof self.BroadcastChannel === false ) { return; }
|
||||
self.uBO_bcSecret.postMessage('setScriptletLogLevelToOne');
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
49
uBlock0.chromium/js/scriptlets/scriptlet-loglevel-2.js
Normal file
49
uBlock0.chromium/js/scriptlets/scriptlet-loglevel-2.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2024-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
if ( self.uBO_bcSecret instanceof self.BroadcastChannel === false ) { return; }
|
||||
self.uBO_bcSecret.postMessage('setScriptletLogLevelToTwo');
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
@@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2018-present 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
|
||||
*/
|
||||
|
||||
// If content scripts are already injected, we need to respond with `false`,
|
||||
// to "should inject content scripts?"
|
||||
//
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/403
|
||||
// If the content script was not bootstrapped, give it another try.
|
||||
|
||||
(( ) => {
|
||||
try {
|
||||
const status = vAPI.uBO !== true;
|
||||
if ( status === false && vAPI.bootstrap ) {
|
||||
self.requestIdleCallback(( ) => vAPI?.bootstrap?.());
|
||||
}
|
||||
return status;
|
||||
} catch(ex) {
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
113
uBlock0.chromium/js/scriptlets/subscriber.js
Normal file
113
uBlock0.chromium/js/scriptlets/subscriber.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2015-present 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
|
||||
*/
|
||||
|
||||
/* global HTMLDocument */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Injected into specific web pages, those which have been pre-selected
|
||||
// because they are known to contains `abp:subscribe` links.
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
// >>>>> start of local scope
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) { return; }
|
||||
|
||||
// Maybe uBO has gone away meanwhile.
|
||||
if ( typeof vAPI !== 'object' || vAPI === null ) { return; }
|
||||
|
||||
const onMaybeSubscriptionLinkClicked = function(target) {
|
||||
if ( vAPI instanceof Object === false ) {
|
||||
document.removeEventListener('click', onMaybeSubscriptionLinkClicked);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/763#issuecomment-691696716
|
||||
// Remove replacement patch if/when filterlists.com fixes encoded '&'.
|
||||
const subscribeURL = new URL(
|
||||
target.href.replace('&title=', '&title=')
|
||||
);
|
||||
if (
|
||||
/^(abp|ubo):$/.test(subscribeURL.protocol) === false &&
|
||||
subscribeURL.hostname !== 'subscribe.adblockplus.org'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const location = subscribeURL.searchParams.get('location') || '';
|
||||
const title = subscribeURL.searchParams.get('title') || '';
|
||||
if ( location === '' || title === '' ) { return true; }
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/1797
|
||||
if ( /^(file|https?):\/\//.test(location) === false ) { return true; }
|
||||
vAPI.messaging.send('scriptlets', {
|
||||
what: 'subscribeTo',
|
||||
location,
|
||||
title,
|
||||
});
|
||||
return true;
|
||||
} catch (_) {
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/easylist/EasyListHebrew/issues/89
|
||||
// Ensure trusted events only.
|
||||
|
||||
document.addEventListener('click', ev => {
|
||||
if ( ev.button !== 0 || ev.isTrusted === false ) { return; }
|
||||
const target = ev.target.closest('a');
|
||||
if ( target instanceof HTMLAnchorElement === false ) { return; }
|
||||
if ( onMaybeSubscriptionLinkClicked(target) === true ) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// <<<<< end of local scope
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
118
uBlock0.chromium/js/scriptlets/updater.js
Normal file
118
uBlock0.chromium/js/scriptlets/updater.js
Normal file
@@ -0,0 +1,118 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a comprehensive, efficient content blocker
|
||||
Copyright (C) 2014-present 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
|
||||
*/
|
||||
|
||||
/* global HTMLDocument */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Injected into specific webpages, those which have been pre-selected
|
||||
// because they are known to contain:
|
||||
// https://ublockorigin.github.io/uAssets/update-lists?listkeys=[...]
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
// >>>>> start of local scope
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( document instanceof HTMLDocument === false ) { return; }
|
||||
|
||||
// Maybe uBO has gone away meanwhile.
|
||||
if ( typeof vAPI !== 'object' || vAPI === null ) { return; }
|
||||
|
||||
function updateStockLists(target) {
|
||||
if ( vAPI instanceof Object === false ) {
|
||||
document.removeEventListener('click', updateStockLists);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const updateURL = new URL(target.href);
|
||||
if ( updateURL.hostname !== 'ublockorigin.github.io') { return; }
|
||||
if ( updateURL.pathname !== '/uAssets/update-lists.html') { return; }
|
||||
const listkeys = updateURL.searchParams.get('listkeys') || '';
|
||||
if ( listkeys === '' ) { return; }
|
||||
let auto = true;
|
||||
const manual = updateURL.searchParams.get('manual');
|
||||
if ( manual === '1' ) {
|
||||
auto = false;
|
||||
} else if ( /^\d{6}$/.test(`${manual}`) ) {
|
||||
const year = parseInt(manual.slice(0,2)) || 0;
|
||||
const month = parseInt(manual.slice(2,4)) || 0;
|
||||
const day = parseInt(manual.slice(4,6)) || 0;
|
||||
if ( year !== 0 && month !== 0 && day !== 0 ) {
|
||||
const date = new Date();
|
||||
date.setUTCFullYear(2000 + year, month - 1, day);
|
||||
date.setUTCHours(0);
|
||||
const then = date.getTime() / 1000 / 3600;
|
||||
const now = Date.now() / 1000 / 3600;
|
||||
auto = then < (now - 48) || then > (now + 48);
|
||||
}
|
||||
}
|
||||
vAPI.messaging.send('scriptlets', {
|
||||
what: 'updateLists',
|
||||
listkeys,
|
||||
auto,
|
||||
});
|
||||
return true;
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/easylist/EasyListHebrew/issues/89
|
||||
// Ensure trusted events only.
|
||||
|
||||
document.addEventListener('click', ev => {
|
||||
if ( ev.button !== 0 || ev.isTrusted === false ) { return; }
|
||||
const target = ev.target.closest('a');
|
||||
if ( target instanceof HTMLAnchorElement === false ) { return; }
|
||||
if ( updateStockLists(target) === true ) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// <<<<< end of local scope
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user