diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 250c336e3..b42ae39d9 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -80,6 +80,7 @@ const toRegisterableScript = (context, fname, hostnames) => { : 'document_start'; const directive = { id: fname, + allFrames: true, matches, excludeMatches, js: [ `/rulesets/js/${fname.slice(0,2)}/${fname.slice(2)}.js` ], diff --git a/platform/mv3/scriptlets/no-setinterval-if.js b/platform/mv3/scriptlets/no-setinterval-if.js new file mode 100644 index 000000000..32e9bb20a --- /dev/null +++ b/platform/mv3/scriptlets/no-setinterval-if.js @@ -0,0 +1,123 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-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 + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +/* jshint esversion:11 */ + +'use strict'; + +/******************************************************************************/ + +/// name no-setInterval-if +/// alias nosiif + +/******************************************************************************/ + +// Important! +// Isolate from global scope +(function uBOL_noSetIntervalIf() { + +/******************************************************************************/ + +// $rulesetId$ + +const argsMap = new Map(self.$argsMap$); + +const hostnamesMap = new Map(self.$hostnamesMap$); + +/******************************************************************************/ + +const scriptlet = ( + needle = '', + delay = '', + +) => { + const needleNot = needle.charAt(0) === '!'; + if ( needleNot ) { needle = needle.slice(1); } + if ( delay === '' ) { delay = undefined; } + let delayNot = false; + if ( delay !== undefined ) { + delayNot = delay.charAt(0) === '!'; + if ( delayNot ) { delay = delay.slice(1); } + delay = parseInt(delay, 10); + } + if ( needle.startsWith('/') && needle.endsWith('/') ) { + needle = needle.slice(1,-1); + } else if ( needle !== '' ) { + needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + const reNeedle = new RegExp(needle); + const regexpTest = RegExp.prototype.test; + self.setInterval = new Proxy(self.setInterval, { + apply: function(target, thisArg, args) { + const a = String(args[0]); + const b = args[1]; + let defuse; + if ( needle !== '' ) { + defuse = regexpTest.call(reNeedle, a) !== needleNot; + } + if ( defuse !== false && delay !== undefined ) { + defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; + } + if ( defuse ) { + args[0] = function(){}; + } + return target.apply(thisArg, args); + } + }); +}; + +/******************************************************************************/ + +let hn; +try { hn = document.location.hostname; } catch(ex) { } +while ( hn ) { + if ( hostnamesMap.has(hn) ) { + let argsHashes = hostnamesMap.get(hn); + if ( typeof argsHashes === 'number' ) { argsHashes = [ argsHashes ]; } + for ( const argsHash of argsHashes ) { + const details = argsMap.get(argsHash); + if ( details.n && details.n.includes(hn) ) { continue; } + try { scriptlet(...details.a); } catch(ex) {} + } + } + if ( hn === '*' ) { break; } + const pos = hn.indexOf('.'); + if ( pos !== -1 ) { + hn = hn.slice(pos + 1); + } else { + hn = '*'; + } +} + +/******************************************************************************/ + +argsMap.clear(); +hostnamesMap.clear(); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ + diff --git a/platform/mv3/scriptlets/no-settimeout-if.js b/platform/mv3/scriptlets/no-settimeout-if.js index 39507025e..b8078a578 100644 --- a/platform/mv3/scriptlets/no-settimeout-if.js +++ b/platform/mv3/scriptlets/no-settimeout-if.js @@ -35,7 +35,7 @@ // Important! // Isolate from global scope -(function uBOL_noSetimeoutIf() { +(function uBOL_noSetTimeoutIf() { /******************************************************************************/ diff --git a/platform/mv3/scriptlets/no-windowopen-if.js b/platform/mv3/scriptlets/no-windowopen-if.js new file mode 100644 index 000000000..d3ed3417b --- /dev/null +++ b/platform/mv3/scriptlets/no-windowopen-if.js @@ -0,0 +1,160 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2019-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 + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +/* jshint esversion:11 */ + +'use strict'; + +/******************************************************************************/ + +/// name no-windowOpen-if +/// alias nowoif + +/******************************************************************************/ + +// Important! +// Isolate from global scope +(function uBOL_noWindowOpenIf() { + +/******************************************************************************/ + +// $rulesetId$ + +const argsMap = new Map(self.$argsMap$); + +const hostnamesMap = new Map(self.$hostnamesMap$); + +/******************************************************************************/ + +const scriptlet = ( + needle = '', + delay = '', + options = '' + +) => { + const newSyntax = /^[01]?$/.test(needle) === false; + let pattern = ''; + let targetResult = true; + let autoRemoveAfter = -1; + if ( newSyntax ) { + pattern = needle; + if ( pattern.startsWith('!') ) { + targetResult = false; + pattern = pattern.slice(1); + } + autoRemoveAfter = parseInt(delay); + if ( isNaN(autoRemoveAfter) ) { + autoRemoveAfter = -1; + } + } else { + pattern = delay; + if ( needle === '0' ) { + targetResult = false; + } + } + if ( pattern === '' ) { + pattern = '.?'; + } else if ( /^\/.+\/$/.test(pattern) ) { + pattern = pattern.slice(1,-1); + } else { + pattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + const rePattern = new RegExp(pattern); + const createDecoy = function(tag, urlProp, url) { + const decoy = document.createElement(tag); + decoy[urlProp] = url; + decoy.style.setProperty('height','1px', 'important'); + decoy.style.setProperty('position','fixed', 'important'); + decoy.style.setProperty('top','-1px', 'important'); + decoy.style.setProperty('width','1px', 'important'); + document.body.appendChild(decoy); + setTimeout(( ) => decoy.remove(), autoRemoveAfter * 1000); + return decoy; + }; + window.open = new Proxy(window.open, { + apply: function(target, thisArg, args) { + const url = args[0]; + if ( rePattern.test(url) !== targetResult ) { + return target.apply(thisArg, args); + } + if ( autoRemoveAfter < 0 ) { return null; } + const decoy = /\bobj\b/.test(options) + ? createDecoy('object', 'data', url) + : createDecoy('iframe', 'src', url); + let popup = decoy.contentWindow; + if ( typeof popup === 'object' && popup !== null ) { + Object.defineProperty(popup, 'closed', { value: false }); + } else { + const noopFunc = (function(){}).bind(self); + popup = new Proxy(self, { + get: function(target, prop) { + if ( prop === 'closed' ) { return false; } + const r = Reflect.get(...arguments); + if ( typeof r === 'function' ) { return noopFunc; } + return target[prop]; + }, + set: function() { + return Reflect.set(...arguments); + }, + }); + } + return popup; + } + }); +}; + +/******************************************************************************/ + +let hn; +try { hn = document.location.hostname; } catch(ex) { } +while ( hn ) { + if ( hostnamesMap.has(hn) ) { + let argsHashes = hostnamesMap.get(hn); + if ( typeof argsHashes === 'number' ) { argsHashes = [ argsHashes ]; } + for ( const argsHash of argsHashes ) { + const details = argsMap.get(argsHash); + if ( details.n && details.n.includes(hn) ) { continue; } + try { scriptlet(...details.a); } catch(ex) {} + } + } + if ( hn === '*' ) { break; } + const pos = hn.indexOf('.'); + if ( pos !== -1 ) { + hn = hn.slice(pos + 1); + } else { + hn = '*'; + } +} + +/******************************************************************************/ + +argsMap.clear(); +hostnamesMap.clear(); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ +