Path: blob/master/sites/bitcoin/js/contentscript.js
777 views
/*******************************************************************************12uBlock Origin - a browser extension to block requests.3Copyright (C) 2018 Raymond Hill45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see {http://www.gnu.org/licenses/}.1718Home: https://github.com/gorhill/uBlock19*/2021'use strict';2223// User stylesheets are always supported with Firefox/webext .2425if ( typeof vAPI === 'object' ) {26vAPI.supportsUserStylesheets = true;27}282930313233343536/*******************************************************************************3738DO NOT:39- Remove the following code40- Add code beyond the following code41Reason:42- https://github.com/gorhill/uBlock/pull/372143- uBO never uses the return value from injected content scripts4445**/4647void 0;4849/*******************************************************************************5051uBlock Origin - a browser extension to block requests.52Copyright (C) 2017-2018 Raymond Hill5354This program is free software: you can redistribute it and/or modify55it under the terms of the GNU General Public License as published by56the Free Software Foundation, either version 3 of the License, or57(at your option) any later version.5859This program is distributed in the hope that it will be useful,60but WITHOUT ANY WARRANTY; without even the implied warranty of61MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the62GNU General Public License for more details.6364You should have received a copy of the GNU General Public License65along with this program. If not, see {http://www.gnu.org/licenses/}.6667Home: https://github.com/gorhill/uBlock68*/697071// Packaging this file is optional: it is not necessary to package it if the72// platform is known to not support user stylesheets.7374// >>>>>>>> start of HUGE-IF-BLOCK75if ( typeof vAPI === 'object' && vAPI.supportsUserStylesheets ) {7677/******************************************************************************/78/******************************************************************************/7980vAPI.userStylesheet = {81added: new Set(),82removed: new Set(),83apply: function(callback) {84if ( this.added.size === 0 && this.removed.size === 0 ) { return; }85vAPI.messaging.send('vapi', {86what: 'userCSS',87add: Array.from(this.added),88remove: Array.from(this.removed)89}, callback);90this.added.clear();91this.removed.clear();92},93add: function(cssText, now) {94if ( cssText === '' ) { return; }95this.added.add(cssText);96if ( now ) { this.apply(); }97},98remove: function(cssText, now) {99if ( cssText === '' ) { return; }100this.removed.add(cssText);101if ( now ) { this.apply(); }102}103};104105/******************************************************************************/106107vAPI.DOMFilterer = function() {108this.commitTimer = new vAPI.SafeAnimationFrame(this.commitNow.bind(this));109this.domIsReady = document.readyState !== 'loading';110this.disabled = false;111this.listeners = [];112this.filterset = new Set();113this.excludedNodeSet = new WeakSet();114this.addedCSSRules = new Set();115116if ( this.domIsReady !== true ) {117document.addEventListener('DOMContentLoaded', () => {118this.domIsReady = true;119this.commit();120});121}122};123124vAPI.DOMFilterer.prototype = {125reOnlySelectors: /\n\{[^\n]+/g,126127// Here we will deal with:128// - Injecting low priority user styles;129// - Notifying listeners about changed filterset.130commitNow: function() {131this.commitTimer.clear();132var userStylesheet = vAPI.userStylesheet;133for ( var entry of this.addedCSSRules ) {134if (135this.disabled === false &&136entry.lazy &&137entry.injected === false138) {139userStylesheet.add(140entry.selectors + '\n{' + entry.declarations + '}'141);142}143}144this.addedCSSRules.clear();145userStylesheet.apply();146},147148commit: function(commitNow) {149if ( commitNow ) {150this.commitTimer.clear();151this.commitNow();152} else {153this.commitTimer.start();154}155},156157addCSSRule: function(selectors, declarations, details) {158if ( selectors === undefined ) { return; }159var selectorsStr = Array.isArray(selectors)160? selectors.join(',\n')161: selectors;162if ( selectorsStr.length === 0 ) { return; }163if ( details === undefined ) { details = {}; }164var entry = {165selectors: selectorsStr,166declarations,167lazy: details.lazy === true,168injected: details.injected === true169};170this.addedCSSRules.add(entry);171this.filterset.add(entry);172if (173this.disabled === false &&174entry.lazy !== true &&175entry.injected !== true176) {177vAPI.userStylesheet.add(selectorsStr + '\n{' + declarations + '}');178}179this.commit();180if ( this.hasListeners() ) {181this.triggerListeners({182declarative: [ [ selectorsStr, declarations ] ]183});184}185},186187addListener: function(listener) {188if ( this.listeners.indexOf(listener) !== -1 ) { return; }189this.listeners.push(listener);190},191192removeListener: function(listener) {193var pos = this.listeners.indexOf(listener);194if ( pos === -1 ) { return; }195this.listeners.splice(pos, 1);196},197198hasListeners: function() {199return this.listeners.length !== 0;200},201202triggerListeners: function(changes) {203var i = this.listeners.length;204while ( i-- ) {205this.listeners[i].onFiltersetChanged(changes);206}207},208209excludeNode: function(node) {210this.excludedNodeSet.add(node);211this.unhideNode(node);212},213214unexcludeNode: function(node) {215this.excludedNodeSet.delete(node);216},217218hideNode: function(node) {219if ( this.excludedNodeSet.has(node) ) { return; }220if ( this.hideNodeAttr === undefined ) { return; }221node.setAttribute(this.hideNodeAttr, '');222if ( this.hideNodeStyleSheetInjected === false ) {223this.hideNodeStyleSheetInjected = true;224this.addCSSRule(225'[' + this.hideNodeAttr + ']',226'display:none!important;'227);228}229},230231unhideNode: function(node) {232if ( this.hideNodeAttr === undefined ) { return; }233node.removeAttribute(this.hideNodeAttr);234},235236toggle: function(state, callback) {237if ( state === undefined ) { state = this.disabled; }238if ( state !== this.disabled ) { return; }239this.disabled = !state;240var userStylesheet = vAPI.userStylesheet;241for ( var entry of this.filterset ) {242var rule = entry.selectors + '\n{' + entry.declarations + '}';243if ( this.disabled ) {244userStylesheet.remove(rule);245} else {246userStylesheet.add(rule);247}248}249userStylesheet.apply(callback);250},251252getAllSelectors_: function(all) {253var out = {254declarative: []255};256var selectors;257for ( var entry of this.filterset ) {258selectors = entry.selectors;259if ( all !== true && this.hideNodeAttr !== undefined ) {260selectors = selectors261.replace('[' + this.hideNodeAttr + ']', '')262.replace(/^,\n|,\n$/gm, '');263if ( selectors === '' ) { continue; }264}265out.declarative.push([ selectors, entry.declarations ]);266}267return out;268},269270getFilteredElementCount: function() {271let details = this.getAllSelectors_(true);272if ( Array.isArray(details.declarative) === false ) { return 0; }273let selectors = details.declarative.reduce(function(acc, entry) {274acc.push(entry[0]);275return acc;276}, []);277if ( selectors.length === 0 ) { return 0; }278return document.querySelectorAll(selectors.join(',\n')).length;279},280281getAllSelectors: function() {282return this.getAllSelectors_(false);283}284};285286/******************************************************************************/287/******************************************************************************/288289}290// <<<<<<<< end of HUGE-IF-BLOCK291292293294295296297298299/*******************************************************************************300301DO NOT:302- Remove the following code303- Add code beyond the following code304Reason:305- https://github.com/gorhill/uBlock/pull/3721306- uBO never uses the return value from injected content scripts307308**/309310void 0;311312/*******************************************************************************313314uBlock Origin - a browser extension to block requests.315Copyright (C) 2014-2018 Raymond Hill316317This program is free software: you can redistribute it and/or modify318it under the terms of the GNU General Public License as published by319the Free Software Foundation, either version 3 of the License, or320(at your option) any later version.321322This program is distributed in the hope that it will be useful,323but WITHOUT ANY WARRANTY; without even the implied warranty of324MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the325GNU General Public License for more details.326327You should have received a copy of the GNU General Public License328along with this program. If not, see {http://www.gnu.org/licenses/}.329330Home: https://github.com/gorhill/uBlock331*/332333334/*******************************************************************************335336+--> domCollapser337|338|339domWatcher--+340| +-- domSurveyor341| |342+--> domFilterer --+-- domLogger343|344+-- domInspector345346domWatcher:347Watches for changes in the DOM, and notify the other components about these348changes.349350domCollapser:351Enforces the collapsing of DOM elements for which a corresponding352resource was blocked through network filtering.353354domFilterer:355Enforces the filtering of DOM elements, by feeding it cosmetic filters.356357domSurveyor:358Surveys the DOM to find new cosmetic filters to apply to the current page.359360domLogger:361Surveys the page to find and report the injected cosmetic filters blocking362actual elements on the current page. This component is dynamically loaded363IF AND ONLY IF uBO's logger is opened.364365If page is whitelisted:366- domWatcher: off367- domCollapser: off368- domFilterer: off369- domSurveyor: off370- domLogger: off371372I verified that the code in this file is completely flushed out of memory373when a page is whitelisted.374375If cosmetic filtering is disabled:376- domWatcher: on377- domCollapser: on378- domFilterer: off379- domSurveyor: off380- domLogger: off381382If generic cosmetic filtering is disabled:383- domWatcher: on384- domCollapser: on385- domFilterer: on386- domSurveyor: off387- domLogger: on if uBO logger is opened388389If generic cosmetic filtering is enabled:390- domWatcher: on391- domCollapser: on392- domFilterer: on393- domSurveyor: on394- domLogger: on if uBO logger is opened395396Additionally, the domSurveyor can turn itself off once it decides that397it has become pointless (repeatedly not finding new cosmetic filters).398399The domFilterer makes use of platform-dependent user stylesheets[1].400401At time of writing, only modern Firefox provides a custom implementation,402which makes for solid, reliable and low overhead cosmetic filtering on403Firefox.404405The generic implementation[2] performs as best as can be, but won't ever be406as reliable and accurate as real user stylesheets.407408[1] "user stylesheets" refer to local CSS rules which have priority over,409and can't be overriden by a web page's own CSS rules.410[2] below, see platformUserCSS / platformHideNode / platformUnhideNode411412*/413414// Abort execution if our global vAPI object does not exist.415// https://github.com/chrisaljoudi/uBlock/issues/456416// https://github.com/gorhill/uBlock/issues/2029417418if ( typeof vAPI === 'object' && !vAPI.contentScript ) { // >>>>>>>> start of HUGE-IF-BLOCK419420/******************************************************************************/421/******************************************************************************/422/******************************************************************************/423424vAPI.contentScript = true;425426/******************************************************************************/427/******************************************************************************/428/*******************************************************************************429430The purpose of SafeAnimationFrame is to take advantage of the behavior of431window.requestAnimationFrame[1]. If we use an animation frame as a timer,432then this timer is described as follow:433434- time events are throttled by the browser when the viewport is not visible --435there is no point for uBO to play with the DOM if the document is not436visible.437- time events are micro tasks[2].438- time events are synchronized to monitor refresh, meaning that they can fire439at most 1/60 (typically).440441If a delay value is provided, a plain timer is first used. Plain timers are442macro-tasks, so this is good when uBO wants to yield to more important tasks443on a page. Once the plain timer elapse, an animation frame is used to trigger444the next time at which to execute the job.445446[1] https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame447[2] https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/448449*/450451// https://github.com/gorhill/uBlock/issues/2147452453vAPI.SafeAnimationFrame = function(callback) {454this.fid = this.tid = null;455this.callback = callback;456this.boundMacroToMicro = this.macroToMicro.bind(this);457};458459vAPI.SafeAnimationFrame.prototype = {460start: function(delay) {461if ( delay === undefined ) {462if ( this.fid === null ) {463this.fid = requestAnimationFrame(this.callback);464}465if ( this.tid === null ) {466this.tid = vAPI.setTimeout(this.callback, 20000);467}468return;469}470if ( this.fid === null && this.tid === null ) {471this.tid = vAPI.setTimeout(this.boundMacroToMicro, delay);472}473},474clear: function() {475if ( this.fid !== null ) { cancelAnimationFrame(this.fid); }476if ( this.tid !== null ) { clearTimeout(this.tid); }477this.fid = this.tid = null;478},479macroToMicro: function() {480this.tid = null;481this.start();482}483};484485/******************************************************************************/486/******************************************************************************/487/******************************************************************************/488489vAPI.domWatcher = (function() {490491var addedNodeLists = [],492addedNodes = [],493domIsReady = false,494domLayoutObserver,495ignoreTags = new Set([ 'br', 'head', 'link', 'meta', 'script', 'style' ]),496listeners = [],497listenerIterator = [], listenerIteratorDirty = false,498removedNodeLists = [],499removedNodes = false,500safeObserverHandlerTimer;501502var safeObserverHandler = function() {503//console.time('dom watcher/safe observer handler');504safeObserverHandlerTimer.clear();505var i = addedNodeLists.length,506j = addedNodes.length,507nodeList, iNode, node;508while ( i-- ) {509nodeList = addedNodeLists[i];510iNode = nodeList.length;511while ( iNode-- ) {512node = nodeList[iNode];513if ( node.nodeType !== 1 ) { continue; }514if ( ignoreTags.has(node.localName) ) { continue; }515if ( node.parentElement === null ) { continue; }516addedNodes[j++] = node;517}518}519addedNodeLists.length = 0;520i = removedNodeLists.length;521while ( i-- && removedNodes === false ) {522nodeList = removedNodeLists[i];523iNode = nodeList.length;524while ( iNode-- ) {525if ( nodeList[iNode].nodeType !== 1 ) { continue; }526removedNodes = true;527break;528}529}530removedNodeLists.length = 0;531//console.timeEnd('dom watcher/safe observer handler');532if ( addedNodes.length === 0 && removedNodes === false ) { return; }533for ( var listener of getListenerIterator() ) {534listener.onDOMChanged(addedNodes, removedNodes);535}536addedNodes.length = 0;537removedNodes = false;538};539540// https://github.com/chrisaljoudi/uBlock/issues/205541// Do not handle added node directly from within mutation observer.542var observerHandler = function(mutations) {543//console.time('dom watcher/observer handler');544var nodeList, mutation,545i = mutations.length;546while ( i-- ) {547mutation = mutations[i];548nodeList = mutation.addedNodes;549if ( nodeList.length !== 0 ) {550addedNodeLists.push(nodeList);551}552if ( removedNodes ) { continue; }553nodeList = mutation.removedNodes;554if ( nodeList.length !== 0 ) {555removedNodeLists.push(nodeList);556}557}558if ( addedNodeLists.length !== 0 || removedNodes ) {559safeObserverHandlerTimer.start(560addedNodeLists.length < 100 ? 1 : undefined561);562}563//console.timeEnd('dom watcher/observer handler');564};565566var startMutationObserver = function() {567if ( domLayoutObserver !== undefined || !domIsReady ) { return; }568domLayoutObserver = new MutationObserver(observerHandler);569domLayoutObserver.observe(document.documentElement, {570//attributeFilter: [ 'class', 'id' ],571//attributes: true,572childList: true,573subtree: true574});575safeObserverHandlerTimer = new vAPI.SafeAnimationFrame(safeObserverHandler);576vAPI.shutdown.add(cleanup);577};578579var stopMutationObserver = function() {580if ( domLayoutObserver === undefined ) { return; }581cleanup();582vAPI.shutdown.remove(cleanup);583};584585var getListenerIterator = function() {586if ( listenerIteratorDirty ) {587listenerIterator = listeners.slice();588listenerIteratorDirty = false;589}590return listenerIterator;591};592593var addListener = function(listener) {594if ( listeners.indexOf(listener) !== -1 ) { return; }595listeners.push(listener);596listenerIteratorDirty = true;597if ( domIsReady !== true ) { return; }598listener.onDOMCreated();599startMutationObserver();600};601602var removeListener = function(listener) {603var pos = listeners.indexOf(listener);604if ( pos === -1 ) { return; }605listeners.splice(pos, 1);606listenerIteratorDirty = true;607if ( listeners.length === 0 ) {608stopMutationObserver();609}610};611612var cleanup = function() {613if ( domLayoutObserver !== undefined ) {614domLayoutObserver.disconnect();615domLayoutObserver = null;616}617if ( safeObserverHandlerTimer !== undefined ) {618safeObserverHandlerTimer.clear();619safeObserverHandlerTimer = undefined;620}621};622623var start = function() {624domIsReady = true;625for ( var listener of getListenerIterator() ) {626listener.onDOMCreated();627}628startMutationObserver();629};630631return {632start: start,633addListener: addListener,634removeListener: removeListener635};636})();637638/******************************************************************************/639/******************************************************************************/640/******************************************************************************/641642vAPI.matchesProp = (function() {643var docElem = document.documentElement;644if ( typeof docElem.matches !== 'function' ) {645if ( typeof docElem.mozMatchesSelector === 'function' ) {646return 'mozMatchesSelector';647} else if ( typeof docElem.webkitMatchesSelector === 'function' ) {648return 'webkitMatchesSelector';649} else if ( typeof docElem.msMatchesSelector === 'function' ) {650return 'msMatchesSelector';651}652}653return 'matches';654})();655656/******************************************************************************/657/******************************************************************************/658/******************************************************************************/659660vAPI.injectScriptlet = function(doc, text) {661if ( !doc ) { return; }662let script;663try {664script = doc.createElement('script');665script.appendChild(doc.createTextNode(text));666(doc.head || doc.documentElement).appendChild(script);667} catch (ex) {668}669if ( script ) {670if ( script.parentNode ) {671script.parentNode.removeChild(script);672}673script.textContent = '';674}675};676677/******************************************************************************/678/******************************************************************************/679/*******************************************************************************680681The DOM filterer is the heart of uBO's cosmetic filtering.682683DOMBaseFilterer: platform-specific684|685|686+---- DOMFilterer: adds procedural cosmetic filtering687688*/689690vAPI.DOMFilterer = (function() {691692// 'P' stands for 'Procedural'693694var PSelectorHasTextTask = function(task) {695var arg0 = task[1], arg1;696if ( Array.isArray(task[1]) ) {697arg1 = arg0[1]; arg0 = arg0[0];698}699this.needle = new RegExp(arg0, arg1);700};701PSelectorHasTextTask.prototype.exec = function(input) {702var output = [];703for ( var node of input ) {704if ( this.needle.test(node.textContent) ) {705output.push(node);706}707}708return output;709};710711var PSelectorIfTask = function(task) {712this.pselector = new PSelector(task[1]);713};714PSelectorIfTask.prototype.target = true;715PSelectorIfTask.prototype.exec = function(input) {716var output = [];717for ( var node of input ) {718if ( this.pselector.test(node) === this.target ) {719output.push(node);720}721}722return output;723};724725var PSelectorIfNotTask = function(task) {726PSelectorIfTask.call(this, task);727this.target = false;728};729PSelectorIfNotTask.prototype = Object.create(PSelectorIfTask.prototype);730PSelectorIfNotTask.prototype.constructor = PSelectorIfNotTask;731732var PSelectorMatchesCSSTask = function(task) {733this.name = task[1].name;734var arg0 = task[1].value, arg1;735if ( Array.isArray(arg0) ) {736arg1 = arg0[1]; arg0 = arg0[0];737}738this.value = new RegExp(arg0, arg1);739};740PSelectorMatchesCSSTask.prototype.pseudo = null;741PSelectorMatchesCSSTask.prototype.exec = function(input) {742var output = [], style;743for ( var node of input ) {744style = window.getComputedStyle(node, this.pseudo);745if ( style === null ) { return null; } /* FF */746if ( this.value.test(style[this.name]) ) {747output.push(node);748}749}750return output;751};752753var PSelectorMatchesCSSAfterTask = function(task) {754PSelectorMatchesCSSTask.call(this, task);755this.pseudo = ':after';756};757PSelectorMatchesCSSAfterTask.prototype = Object.create(PSelectorMatchesCSSTask.prototype);758PSelectorMatchesCSSAfterTask.prototype.constructor = PSelectorMatchesCSSAfterTask;759760var PSelectorMatchesCSSBeforeTask = function(task) {761PSelectorMatchesCSSTask.call(this, task);762this.pseudo = ':before';763};764PSelectorMatchesCSSBeforeTask.prototype = Object.create(PSelectorMatchesCSSTask.prototype);765PSelectorMatchesCSSBeforeTask.prototype.constructor = PSelectorMatchesCSSBeforeTask;766767var PSelectorXpathTask = function(task) {768this.xpe = document.createExpression(task[1], null);769this.xpr = null;770};771PSelectorXpathTask.prototype.exec = function(input) {772var output = [], j;773for ( var node of input ) {774this.xpr = this.xpe.evaluate(775node,776XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,777this.xpr778);779j = this.xpr.snapshotLength;780while ( j-- ) {781node = this.xpr.snapshotItem(j);782if ( node.nodeType === 1 ) {783output.push(node);784}785}786}787return output;788};789790var PSelector = function(o) {791if ( PSelector.prototype.operatorToTaskMap === undefined ) {792PSelector.prototype.operatorToTaskMap = new Map([793[ ':has', PSelectorIfTask ],794[ ':has-text', PSelectorHasTextTask ],795[ ':if', PSelectorIfTask ],796[ ':if-not', PSelectorIfNotTask ],797[ ':matches-css', PSelectorMatchesCSSTask ],798[ ':matches-css-after', PSelectorMatchesCSSAfterTask ],799[ ':matches-css-before', PSelectorMatchesCSSBeforeTask ],800[ ':xpath', PSelectorXpathTask ]801]);802}803this.budget = 200; // I arbitrary picked a 1/5 second804this.raw = o.raw;805this.cost = 0;806this.lastAllowanceTime = 0;807this.selector = o.selector;808this.tasks = [];809var tasks = o.tasks;810if ( !tasks ) { return; }811for ( var task of tasks ) {812this.tasks.push(new (this.operatorToTaskMap.get(task[0]))(task));813}814};815PSelector.prototype.operatorToTaskMap = undefined;816PSelector.prototype.prime = function(input) {817var root = input || document;818if ( this.selector !== '' ) {819return root.querySelectorAll(this.selector);820}821return [ root ];822};823PSelector.prototype.exec = function(input) {824var nodes = this.prime(input);825for ( var task of this.tasks ) {826if ( nodes.length === 0 ) { break; }827nodes = task.exec(nodes);828}829return nodes;830};831PSelector.prototype.test = function(input) {832var nodes = this.prime(input), AA = [ null ], aa;833for ( var node of nodes ) {834AA[0] = node; aa = AA;835for ( var task of this.tasks ) {836aa = task.exec(aa);837if ( aa.length === 0 ) { break; }838}839if ( aa.length !== 0 ) { return true; }840}841return false;842};843844var DOMProceduralFilterer = function(domFilterer) {845this.domFilterer = domFilterer;846this.domIsReady = false;847this.domIsWatched = false;848this.addedSelectors = new Map();849this.addedNodes = false;850this.removedNodes = false;851this.selectors = new Map();852};853854DOMProceduralFilterer.prototype = {855856addProceduralSelectors: function(aa) {857var raw, o, pselector,858mustCommit = this.domIsWatched;859for ( var i = 0, n = aa.length; i < n; i++ ) {860raw = aa[i];861o = JSON.parse(raw);862if ( o.style ) {863this.domFilterer.addCSSRule(o.style[0], o.style[1]);864mustCommit = true;865continue;866}867if ( o.pseudoclass ) {868this.domFilterer.addCSSRule(869o.raw,870'display:none!important;'871);872mustCommit = true;873continue;874}875if ( o.tasks ) {876if ( this.selectors.has(raw) === false ) {877pselector = new PSelector(o);878this.selectors.set(raw, pselector);879this.addedSelectors.set(raw, pselector);880mustCommit = true;881}882continue;883}884}885if ( mustCommit === false ) { return; }886this.domFilterer.commit();887if ( this.domFilterer.hasListeners() ) {888this.domFilterer.triggerListeners({889procedural: Array.from(this.addedSelectors.values())890});891}892},893894commitNow: function() {895if ( this.selectors.size === 0 || this.domIsReady === false ) {896return;897}898899if ( this.addedNodes || this.removedNodes ) {900this.addedSelectors.clear();901}902903var entry, nodes, i;904905if ( this.addedSelectors.size !== 0 ) {906//console.time('procedural selectors/filterset changed');907for ( entry of this.addedSelectors ) {908nodes = entry[1].exec();909i = nodes.length;910while ( i-- ) {911this.domFilterer.hideNode(nodes[i]);912}913}914this.addedSelectors.clear();915//console.timeEnd('procedural selectors/filterset changed');916return;917}918919//console.time('procedural selectors/dom layout changed');920921this.addedNodes = this.removedNodes = false;922923var t0 = Date.now(),924t1, pselector, allowance;925926for ( entry of this.selectors ) {927pselector = entry[1];928allowance = Math.floor((t0 - pselector.lastAllowanceTime) / 2000);929if ( allowance >= 1 ) {930pselector.budget += allowance * 50;931if ( pselector.budget > 200 ) { pselector.budget = 200; }932pselector.lastAllowanceTime = t0;933}934if ( pselector.budget <= 0 ) { continue; }935nodes = pselector.exec();936t1 = Date.now();937pselector.budget += t0 - t1;938if ( pselector.budget < -500 ) {939console.info('uBO: disabling %s', pselector.raw);940pselector.budget = -0x7FFFFFFF;941}942t0 = t1;943i = nodes.length;944while ( i-- ) {945this.domFilterer.hideNode(nodes[i]);946}947}948949//console.timeEnd('procedural selectors/dom layout changed');950},951952createProceduralFilter: function(o) {953return new PSelector(o);954},955956onDOMCreated: function() {957this.domIsReady = true;958this.domFilterer.commitNow();959},960961onDOMChanged: function(addedNodes, removedNodes) {962if ( this.selectors.size === 0 ) { return; }963this.addedNodes = this.addedNodes || addedNodes.length !== 0;964this.removedNodes = this.removedNodes || removedNodes;965this.domFilterer.commit();966}967};968969var DOMFiltererBase = vAPI.DOMFilterer;970971var domFilterer = function() {972DOMFiltererBase.call(this);973this.exceptions = [];974this.proceduralFilterer = new DOMProceduralFilterer(this);975this.hideNodeAttr = undefined;976this.hideNodeStyleSheetInjected = false;977978// May or may not exist: cache locally since this may be called often.979this.baseOnDOMChanged = DOMFiltererBase.prototype.onDOMChanged;980981if ( vAPI.domWatcher instanceof Object ) {982vAPI.domWatcher.addListener(this);983}984};985domFilterer.prototype = Object.create(DOMFiltererBase.prototype);986domFilterer.prototype.constructor = domFilterer;987988domFilterer.prototype.commitNow = function() {989DOMFiltererBase.prototype.commitNow.call(this);990this.proceduralFilterer.commitNow();991};992993domFilterer.prototype.addProceduralSelectors = function(aa) {994this.proceduralFilterer.addProceduralSelectors(aa);995};996997domFilterer.prototype.createProceduralFilter = function(o) {998return this.proceduralFilterer.createProceduralFilter(o);999};10001001domFilterer.prototype.getAllSelectors = function() {1002var out = DOMFiltererBase.prototype.getAllSelectors.call(this);1003out.procedural = Array.from(this.proceduralFilterer.selectors.values());1004return out;1005};10061007domFilterer.prototype.getAllExceptionSelectors = function() {1008return this.exceptions.join(',\n');1009};10101011domFilterer.prototype.onDOMCreated = function() {1012if ( DOMFiltererBase.prototype.onDOMCreated !== undefined ) {1013DOMFiltererBase.prototype.onDOMCreated.call(this);1014}1015this.proceduralFilterer.onDOMCreated();1016};10171018domFilterer.prototype.onDOMChanged = function() {1019if ( this.baseOnDOMChanged !== undefined ) {1020this.baseOnDOMChanged.apply(this, arguments);1021}1022this.proceduralFilterer.onDOMChanged.apply(1023this.proceduralFilterer,1024arguments1025);1026};10271028return domFilterer;1029})();10301031vAPI.domFilterer = new vAPI.DOMFilterer();10321033/******************************************************************************/1034/******************************************************************************/1035/******************************************************************************/10361037vAPI.domCollapser = (function() {1038var resquestIdGenerator = 1,1039processTimer,1040toProcess = [],1041toFilter = [],1042toCollapse = new Map(),1043cachedBlockedSet,1044cachedBlockedSetHash,1045cachedBlockedSetTimer;1046var src1stProps = {1047'embed': 'src',1048'iframe': 'src',1049'img': 'src',1050'object': 'data'1051};1052var src2ndProps = {1053'img': 'srcset'1054};1055var tagToTypeMap = {1056embed: 'object',1057iframe: 'sub_frame',1058img: 'image',1059object: 'object'1060};1061var netSelectorCacheCount = 0,1062messaging = vAPI.messaging;10631064var cachedBlockedSetClear = function() {1065cachedBlockedSet =1066cachedBlockedSetHash =1067cachedBlockedSetTimer = undefined;1068};10691070// https://github.com/chrisaljoudi/uBlock/issues/1741071// Do not remove fragment from src URL1072var onProcessed = function(response) {1073if ( !response ) { // This happens if uBO is disabled or restarted.1074toCollapse.clear();1075return;1076}10771078var targets = toCollapse.get(response.id);1079if ( targets === undefined ) { return; }1080toCollapse.delete(response.id);1081if ( cachedBlockedSetHash !== response.hash ) {1082cachedBlockedSet = new Set(response.blockedResources);1083cachedBlockedSetHash = response.hash;1084if ( cachedBlockedSetTimer !== undefined ) {1085clearTimeout(cachedBlockedSetTimer);1086}1087cachedBlockedSetTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);1088}1089if ( cachedBlockedSet === undefined || cachedBlockedSet.size === 0 ) {1090return;1091}1092var selectors = [],1093iframeLoadEventPatch = vAPI.iframeLoadEventPatch,1094netSelectorCacheCountMax = response.netSelectorCacheCountMax,1095tag, prop, src, value;10961097for ( var target of targets ) {1098tag = target.localName;1099prop = src1stProps[tag];1100if ( prop === undefined ) { continue; }1101src = target[prop];1102if ( typeof src !== 'string' || src.length === 0 ) {1103prop = src2ndProps[tag];1104if ( prop === undefined ) { continue; }1105src = target[prop];1106if ( typeof src !== 'string' || src.length === 0 ) { continue; }1107}1108if ( cachedBlockedSet.has(tagToTypeMap[tag] + ' ' + src) === false ) {1109continue;1110}1111// https://github.com/chrisaljoudi/uBlock/issues/3991112// Never remove elements from the DOM, just hide them1113target.style.setProperty('display', 'none', 'important');1114target.hidden = true;1115// https://github.com/chrisaljoudi/uBlock/issues/10481116// Use attribute to construct CSS rule1117if (1118netSelectorCacheCount <= netSelectorCacheCountMax &&1119(value = target.getAttribute(prop))1120) {1121selectors.push(tag + '[' + prop + '="' + value + '"]');1122netSelectorCacheCount += 1;1123}1124if ( iframeLoadEventPatch !== undefined ) {1125iframeLoadEventPatch(target);1126}1127}11281129if ( selectors.length !== 0 ) {1130messaging.send(1131'contentscript',1132{1133what: 'cosmeticFiltersInjected',1134type: 'net',1135hostname: window.location.hostname,1136selectors: selectors1137}1138);1139}1140};11411142var send = function() {1143processTimer = undefined;1144toCollapse.set(resquestIdGenerator, toProcess);1145var msg = {1146what: 'getCollapsibleBlockedRequests',1147id: resquestIdGenerator,1148frameURL: window.location.href,1149resources: toFilter,1150hash: cachedBlockedSetHash1151};1152messaging.send('contentscript', msg, onProcessed);1153toProcess = [];1154toFilter = [];1155resquestIdGenerator += 1;1156};11571158var process = function(delay) {1159if ( toProcess.length === 0 ) { return; }1160if ( delay === 0 ) {1161if ( processTimer !== undefined ) {1162clearTimeout(processTimer);1163}1164send();1165} else if ( processTimer === undefined ) {1166processTimer = vAPI.setTimeout(send, delay || 20);1167}1168};11691170var add = function(target) {1171toProcess[toProcess.length] = target;1172};11731174var addMany = function(targets) {1175var i = targets.length;1176while ( i-- ) {1177add(targets[i]);1178}1179};11801181var iframeSourceModified = function(mutations) {1182var i = mutations.length;1183while ( i-- ) {1184addIFrame(mutations[i].target, true);1185}1186process();1187};1188var iframeSourceObserver = new MutationObserver(iframeSourceModified);1189var iframeSourceObserverOptions = {1190attributes: true,1191attributeFilter: [ 'src' ]1192};11931194// The injected scriptlets are those which were injected in the current1195// document, from within `bootstrapPhase1`, and which scriptlets are1196// selectively looked-up from:1197// https://github.com/uBlockOrigin/uAssets/blob/master/filters/resources.txt1198var primeLocalIFrame = function(iframe) {1199if ( vAPI.injectedScripts ) {1200vAPI.injectScriptlet(iframe.contentDocument, vAPI.injectedScripts);1201}1202};12031204// https://github.com/gorhill/uBlock/issues/1621205// Be prepared to deal with possible change of src attribute.1206var addIFrame = function(iframe, dontObserve) {1207if ( dontObserve !== true ) {1208iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);1209}1210var src = iframe.src;1211if ( src === '' || typeof src !== 'string' ) {1212primeLocalIFrame(iframe);1213return;1214}1215if ( src.startsWith('http') === false ) { return; }1216toFilter[toFilter.length] = {1217type: 'sub_frame',1218url: iframe.src1219};1220add(iframe);1221};12221223var addIFrames = function(iframes) {1224var i = iframes.length;1225while ( i-- ) {1226addIFrame(iframes[i]);1227}1228};12291230var onResourceFailed = function(ev) {1231if ( tagToTypeMap[ev.target.localName] !== undefined ) {1232add(ev.target);1233process();1234}1235};12361237var domWatcherInterface = {1238onDOMCreated: function() {1239if ( vAPI instanceof Object === false ) { return; }1240if ( vAPI.domCollapser instanceof Object === false ) {1241if ( vAPI.domWatcher instanceof Object ) {1242vAPI.domWatcher.removeListener(domWatcherInterface);1243}1244return;1245}1246// Listener to collapse blocked resources.1247// - Future requests not blocked yet1248// - Elements dynamically added to the page1249// - Elements which resource URL changes1250// https://github.com/chrisaljoudi/uBlock/issues/71251// Preferring getElementsByTagName over querySelectorAll:1252// http://jsperf.com/queryselectorall-vs-getelementsbytagname/1451253var elems = document.images || document.getElementsByTagName('img'),1254i = elems.length, elem;1255while ( i-- ) {1256elem = elems[i];1257if ( elem.complete ) {1258add(elem);1259}1260}1261addMany(document.embeds || document.getElementsByTagName('embed'));1262addMany(document.getElementsByTagName('object'));1263addIFrames(document.getElementsByTagName('iframe'));1264process(0);12651266document.addEventListener('error', onResourceFailed, true);12671268vAPI.shutdown.add(function() {1269document.removeEventListener('error', onResourceFailed, true);1270if ( processTimer !== undefined ) {1271clearTimeout(processTimer);1272}1273});1274},1275onDOMChanged: function(addedNodes) {1276var ni = addedNodes.length;1277if ( ni === 0 ) { return; }1278for ( var i = 0, node; i < ni; i++ ) {1279node = addedNodes[i];1280if ( node.localName === 'iframe' ) {1281addIFrame(node);1282}1283if ( node.childElementCount === 0 ) { continue; }1284var iframes = node.getElementsByTagName('iframe');1285if ( iframes.length !== 0 ) {1286addIFrames(iframes);1287}1288}1289process();1290}1291};12921293if ( vAPI.domWatcher instanceof Object ) {1294vAPI.domWatcher.addListener(domWatcherInterface);1295}12961297return {1298add: add,1299addMany: addMany,1300addIFrame: addIFrame,1301addIFrames: addIFrames,1302process: process1303};1304})();13051306/******************************************************************************/1307/******************************************************************************/1308/******************************************************************************/13091310vAPI.domSurveyor = (function() {1311var messaging = vAPI.messaging,1312domFilterer,1313hostname = '',1314queriedIds = new Set(),1315queriedClasses = new Set(),1316pendingIdNodes = { nodes: [], added: [] },1317pendingClassNodes = { nodes: [], added: [] },1318surveyCost = 0;13191320// This is to shutdown the surveyor if result of surveying keeps being1321// fruitless. This is useful on long-lived web page. I arbitrarily1322// picked 5 minutes before the surveyor is allowed to shutdown. I also1323// arbitrarily picked 256 misses before the surveyor is allowed to1324// shutdown.1325var canShutdownAfter = Date.now() + 300000,1326surveyingMissCount = 0;13271328// Handle main process' response.13291330var surveyPhase3 = function(response) {1331var result = response && response.result,1332mustCommit = false;13331334if ( result ) {1335var selectors = result.simple;1336if ( Array.isArray(selectors) && selectors.length !== 0 ) {1337domFilterer.addCSSRule(1338selectors,1339'display:none!important;',1340{ type: 'simple' }1341);1342mustCommit = true;1343}1344selectors = result.complex;1345if ( Array.isArray(selectors) && selectors.length !== 0 ) {1346domFilterer.addCSSRule(1347selectors,1348'display:none!important;',1349{ type: 'complex' }1350);1351mustCommit = true;1352}1353selectors = result.injected;1354if ( typeof selectors === 'string' && selectors.length !== 0 ) {1355domFilterer.addCSSRule(1356selectors,1357'display:none!important;',1358{ injected: true }1359);1360mustCommit = true;1361}1362}13631364if ( hasChunk(pendingIdNodes) || hasChunk(pendingClassNodes) ) {1365surveyTimer.start(1);1366}13671368if ( mustCommit ) {1369surveyingMissCount = 0;1370canShutdownAfter = Date.now() + 300000;1371return;1372}13731374surveyingMissCount += 1;1375if ( surveyingMissCount < 256 || Date.now() < canShutdownAfter ) {1376return;1377}13781379//console.info('dom surveyor shutting down: too many misses');13801381surveyTimer.clear();1382vAPI.domWatcher.removeListener(domWatcherInterface);1383vAPI.domSurveyor = null;1384};13851386var surveyTimer = new vAPI.SafeAnimationFrame(function() {1387surveyPhase1();1388});13891390// The purpose of "chunkification" is to ensure the surveyor won't unduly1391// block the main event loop.13921393var hasChunk = function(pending) {1394return pending.nodes.length !== 0 ||1395pending.added.length !== 0;1396};13971398var addChunk = function(pending, added) {1399if ( added.length === 0 ) { return; }1400if (1401Array.isArray(added) === false ||1402pending.added.length === 0 ||1403Array.isArray(pending.added[0]) === false ||1404pending.added[0].length >= 10001405) {1406pending.added.push(added);1407} else {1408pending.added = pending.added.concat(added);1409}1410};14111412var nextChunk = function(pending) {1413var added = pending.added.length !== 0 ? pending.added.shift() : [],1414nodes;1415if ( pending.nodes.length === 0 ) {1416if ( added.length <= 1000 ) { return added; }1417nodes = Array.isArray(added)1418? added1419: Array.prototype.slice.call(added);1420pending.nodes = nodes.splice(1000);1421return nodes;1422}1423if ( Array.isArray(added) === false ) {1424added = Array.prototype.slice.call(added);1425}1426if ( pending.nodes.length < 1000 ) {1427nodes = pending.nodes.concat(added.splice(0, 1000 - pending.nodes.length));1428pending.nodes = added;1429} else {1430nodes = pending.nodes.splice(0, 1000);1431pending.nodes = pending.nodes.concat(added);1432}1433return nodes;1434};14351436// Extract all classes/ids: these will be passed to the cosmetic1437// filtering engine, and in return we will obtain only the relevant1438// CSS selectors.14391440// https://github.com/gorhill/uBlock/issues/6721441// http://www.w3.org/TR/2014/REC-html5-20141028/infrastructure.html#space-separated-tokens1442// http://jsperf.com/enumerate-classes/614431444var surveyPhase1 = function() {1445//console.time('dom surveyor/surveying');1446surveyTimer.clear();1447var t0 = window.performance.now();1448var rews = reWhitespace,1449qq, iout, nodes, i, node, v, vv, j;1450var ids = [];1451iout = 0;1452qq = queriedIds;1453nodes = nextChunk(pendingIdNodes);1454i = nodes.length;1455while ( i-- ) {1456node = nodes[i];1457v = node.id;1458if ( typeof v !== 'string' ) { continue; }1459v = v.trim();1460if ( qq.has(v) === false && v.length !== 0 ) {1461ids[iout++] = v; qq.add(v);1462}1463}1464var classes = [];1465iout = 0;1466qq = queriedClasses;1467nodes = nextChunk(pendingClassNodes);1468i = nodes.length;1469while ( i-- ) {1470node = nodes[i];1471vv = node.className;1472if ( typeof vv !== 'string' ) { continue; }1473if ( rews.test(vv) === false ) {1474if ( qq.has(vv) === false && vv.length !== 0 ) {1475classes[iout++] = vv; qq.add(vv);1476}1477} else {1478vv = node.classList;1479j = vv.length;1480while ( j-- ) {1481v = vv[j];1482if ( qq.has(v) === false ) {1483classes[iout++] = v; qq.add(v);1484}1485}1486}1487}1488surveyCost += window.performance.now() - t0;1489// Phase 2: Ask main process to lookup relevant cosmetic filters.1490if ( ids.length !== 0 || classes.length !== 0 ) {1491messaging.send(1492'contentscript',1493{1494what: 'retrieveGenericCosmeticSelectors',1495hostname: hostname,1496ids: ids.join('\n'),1497classes: classes.join('\n'),1498exceptions: domFilterer.exceptions,1499cost: surveyCost1500},1501surveyPhase31502);1503} else {1504surveyPhase3(null);1505}1506//console.timeEnd('dom surveyor/surveying');1507};1508var reWhitespace = /\s/;15091510var domWatcherInterface = {1511onDOMCreated: function() {1512if (1513vAPI instanceof Object === false ||1514vAPI.domSurveyor instanceof Object === false ||1515vAPI.domFilterer instanceof Object === false1516) {1517if ( vAPI instanceof Object ) {1518if ( vAPI.domWatcher instanceof Object ) {1519vAPI.domWatcher.removeListener(domWatcherInterface);1520}1521vAPI.domSurveyor = null;1522}1523return;1524}1525//console.time('dom surveyor/dom layout created');1526domFilterer = vAPI.domFilterer;1527addChunk(pendingIdNodes, document.querySelectorAll('[id]'));1528addChunk(pendingClassNodes, document.querySelectorAll('[class]'));1529surveyTimer.start();1530//console.timeEnd('dom surveyor/dom layout created');1531},1532onDOMChanged: function(addedNodes) {1533if ( addedNodes.length === 0 ) { return; }1534//console.time('dom surveyor/dom layout changed');1535var idNodes = [], iid = 0,1536classNodes = [], iclass = 0;1537var i = addedNodes.length,1538node, nodeList, j;1539while ( i-- ) {1540node = addedNodes[i];1541idNodes[iid++] = node;1542classNodes[iclass++] = node;1543if ( node.childElementCount === 0 ) { continue; }1544nodeList = node.querySelectorAll('[id]');1545j = nodeList.length;1546while ( j-- ) {1547idNodes[iid++] = nodeList[j];1548}1549nodeList = node.querySelectorAll('[class]');1550j = nodeList.length;1551while ( j-- ) {1552classNodes[iclass++] = nodeList[j];1553}1554}1555if ( idNodes.length !== 0 || classNodes.lengh !== 0 ) {1556addChunk(pendingIdNodes, idNodes);1557addChunk(pendingClassNodes, classNodes);1558surveyTimer.start(1);1559}1560//console.timeEnd('dom surveyor/dom layout changed');1561}1562};15631564var start = function(details) {1565if ( vAPI.domWatcher instanceof Object === false ) { return; }1566hostname = details.hostname;1567vAPI.domWatcher.addListener(domWatcherInterface);1568};15691570return {1571start: start1572};1573})();15741575/******************************************************************************/1576/******************************************************************************/1577/******************************************************************************/15781579// Bootstrapping allows all components of the content script to be launched1580// if/when needed.15811582(function bootstrap() {15831584var bootstrapPhase2 = function(ev) {1585// This can happen on Firefox. For instance:1586// https://github.com/gorhill/uBlock/issues/18931587if ( window.location === null ) { return; }15881589if ( ev ) {1590document.removeEventListener('DOMContentLoaded', bootstrapPhase2);1591}15921593if ( vAPI instanceof Object === false ) {1594return;1595}15961597if ( vAPI.domWatcher instanceof Object ) {1598vAPI.domWatcher.start();1599}16001601// Element picker works only in top window for now.1602if (1603window !== window.top ||1604vAPI.domFilterer instanceof Object === false1605) {1606return;1607}16081609// To send mouse coordinates to main process, as the chrome API fails1610// to provide the mouse position to context menu listeners.1611// https://github.com/chrisaljoudi/uBlock/issues/11431612// Also, find a link under the mouse, to try to avoid confusing new tabs1613// as nuisance popups.1614// Ref.: https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu16151616var onMouseClick = function(ev) {1617var elem = ev.target;1618while ( elem !== null && elem.localName !== 'a' ) {1619elem = elem.parentElement;1620}1621vAPI.messaging.send(1622'contentscript',1623{1624what: 'mouseClick',1625x: ev.clientX,1626y: ev.clientY,1627url: elem !== null && ev.isTrusted !== false ? elem.href : ''1628}1629);1630};16311632document.addEventListener('mousedown', onMouseClick, true);16331634// https://github.com/gorhill/uMatrix/issues/1441635vAPI.shutdown.add(function() {1636document.removeEventListener('mousedown', onMouseClick, true);1637});1638};16391640var bootstrapPhase1 = function(response) {1641// cosmetic filtering engine aka 'cfe'1642var cfeDetails = response && response.specificCosmeticFilters;1643if ( !cfeDetails || !cfeDetails.ready ) {1644vAPI.domWatcher = vAPI.domCollapser = vAPI.domFilterer =1645vAPI.domSurveyor = vAPI.domIsLoaded = null;1646return;1647}16481649if ( response.noCosmeticFiltering ) {1650vAPI.domFilterer = null;1651vAPI.domSurveyor = null;1652} else {1653var domFilterer = vAPI.domFilterer;1654if ( response.noGenericCosmeticFiltering || cfeDetails.noDOMSurveying ) {1655vAPI.domSurveyor = null;1656}1657domFilterer.exceptions = cfeDetails.exceptionFilters;1658domFilterer.hideNodeAttr = cfeDetails.hideNodeAttr;1659domFilterer.hideNodeStyleSheetInjected =1660cfeDetails.hideNodeStyleSheetInjected === true;1661domFilterer.addCSSRule(1662cfeDetails.declarativeFilters,1663'display:none!important;'1664);1665domFilterer.addCSSRule(1666cfeDetails.highGenericHideSimple,1667'display:none!important;',1668{ type: 'simple', lazy: true }1669);1670domFilterer.addCSSRule(1671cfeDetails.highGenericHideComplex,1672'display:none!important;',1673{ type: 'complex', lazy: true }1674);1675domFilterer.addCSSRule(1676cfeDetails.injectedHideFilters,1677'display:none!important;',1678{ injected: true }1679);1680domFilterer.addProceduralSelectors(cfeDetails.proceduralFilters);1681}16821683if ( cfeDetails.networkFilters.length !== 0 ) {1684vAPI.userStylesheet.add(1685cfeDetails.networkFilters + '\n{display:none!important;}');1686}16871688vAPI.userStylesheet.apply();16891690// Library of resources is located at:1691// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt1692if ( response.scriptlets ) {1693vAPI.injectScriptlet(document, response.scriptlets);1694vAPI.injectedScripts = response.scriptlets;1695}16961697if ( vAPI.domSurveyor instanceof Object ) {1698vAPI.domSurveyor.start(cfeDetails);1699}17001701// https://github.com/chrisaljoudi/uBlock/issues/5871702// If no filters were found, maybe the script was injected before1703// uBlock's process was fully initialized. When this happens, pages1704// won't be cleaned right after browser launch.1705if (1706typeof document.readyState === 'string' &&1707document.readyState !== 'loading'1708) {1709bootstrapPhase2();1710} else {1711document.addEventListener('DOMContentLoaded', bootstrapPhase2);1712}1713};17141715// This starts bootstrap process.1716vAPI.messaging.send(1717'contentscript',1718{1719what: 'retrieveContentScriptParameters',1720url: window.location.href,1721isRootFrame: window === window.top,1722charset: document.characterSet1723},1724bootstrapPhase11725);1726})();17271728/******************************************************************************/1729/******************************************************************************/1730/******************************************************************************/17311732} // <<<<<<<< end of HUGE-IF-BLOCK173317341735