Big Change Update

This commit is contained in:
yeongpin
2025-01-14 14:47:41 +08:00
parent 380ea0b81d
commit 19fe4c85f8
651 changed files with 366654 additions and 17 deletions

View 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;

View 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;

View 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;

View 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
})();

View 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;

View 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;
})();

View 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;
}
})();

File diff suppressed because it is too large Load Diff

View 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;

View 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;

View 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;

View 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);
}
})();
/******************************************************************************/

View 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;

View 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;

View File

@@ -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;
})();

View 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('&amp;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;

View 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;