From c5d85881181aca19edb401f238634b0f0388d60e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 May 2018 07:33:21 -0400 Subject: [PATCH] inject scriptlets earlier (experimental) (ex. https://github.com/uBlockOrigin/uAssets/issues/2300) --- platform/chromium/vapi-background.js | 10 ---- src/js/background.js | 1 + src/js/messaging.js | 1 - src/js/scriptlet-filtering.js | 78 ++++++++++++++++++++-------- src/js/tab.js | 14 ++--- 5 files changed, 65 insertions(+), 39 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 415a12ea1785c..190c0cf8e9b5e 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -350,16 +350,7 @@ vAPI.tabs.registerListeners = function() { } }; - var onBeforeNavigate = function(details) { - if ( details.frameId !== 0 ) { - return; - } - }; - var onCommitted = function(details) { - if ( details.frameId !== 0 ) { - return; - } details.url = sanitizeURL(details.url); onNavigationClient(details); }; @@ -382,7 +373,6 @@ vAPI.tabs.registerListeners = function() { onUpdatedClient(tabId, changeInfo, tab); }; - chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate); chrome.webNavigation.onCommitted.addListener(onCommitted); // Not supported on Firefox WebExtensions yet. if ( chrome.webNavigation.onCreatedNavigationTarget instanceof Object ) { diff --git a/src/js/background.js b/src/js/background.js index 786be0d944c8f..11ec4c7e98ee5 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -42,6 +42,7 @@ var µBlock = (function() { // jshint ignore:line assetFetchTimeout: 30, autoUpdateAssetFetchPeriod: 120, autoUpdatePeriod: 7, + debugScriptlets: false, ignoreRedirectFilters: false, ignoreScriptInjectFilters: false, manualUpdateAssetFetchPeriod: 500, diff --git a/src/js/messaging.js b/src/js/messaging.js index d920b45c1832d..d2508a2f667a3 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -517,7 +517,6 @@ var onMessage = function(request, sender, callback) { request.entity = µb.URI.entityFromDomain(request.domain); response.specificCosmeticFilters = µb.cosmeticFilteringEngine.retrieveDomainSelectors(request, response); - response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); if ( request.isRootFrame && µb.logger.isEnabled() ) { µb.logCosmeticFilters(tabId); } diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index 0cd200f5ba662..caac2c62be0b4 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2017 Raymond Hill + Copyright (C) 2017-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 @@ -24,9 +24,9 @@ /******************************************************************************/ µBlock.scriptletFilteringEngine = (function() { - var api = {}; + let api = {}; - var µb = µBlock, + let µb = µBlock, scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(), duplicates = new Set(), acceptedCount = 0, @@ -36,15 +36,29 @@ scriptletsRegister = new Map(), reEscapeScriptArg = /[\\'"]/g; - var scriptletRemover = [ - '(function() {', - ' var c = document.currentScript, p = c && c.parentNode;', - ' if ( p ) { p.removeChild(c); }', - '})();' - ].join('\n'); - - - var lookupScriptlet = function(raw, reng, toInject) { + let contentscriptCodeParts = [ + '(', + function() { + let d = document; + let script = d.createElement('script'); + try { + script.appendChild(d.createTextNode( + decodeURIComponent(arguments[0])) + ); + (d.head || d.documentElement).appendChild(script); + } catch (ex) { + } + if ( script.parentNode ) { + script.parentNode.removeChild(script); + } + }.toString(), + ')("', 'scriptlets-slot', '");\n', + 'void 0;', + ]; + let contentscriptCodeScriptletsSlot = + contentscriptCodeParts.indexOf('scriptlets-slot'); + + let lookupScriptlet = function(raw, reng, toInject) { if ( toInject.has(raw) ) { return; } if ( scriptletCache.resetTime < reng.modifyTime ) { scriptletCache.reset(); @@ -77,7 +91,7 @@ // Fill template placeholders. Return falsy if: // - At least one argument contains anything else than /\w/ and `.` - var patchScriptlet = function(content, args) { + let patchScriptlet = function(content, args) { var i = 1, pos, arg; while ( args !== '' ) { @@ -91,7 +105,7 @@ return content; }; - var logOne = function(isException, token, details) { + let logOne = function(isException, token, details) { µb.logger.writeOne( details.tabId, 'cosmetic', @@ -256,16 +270,38 @@ if ( out.length === 0 ) { return; } - out.push(scriptletRemover); - return out.join('\n'); }; - api.apply = function(doc, details) { - var script = doc.createElement('script'); - script.textContent = details.scriptlets; - doc.head.insertBefore(script, doc.head.firstChild); - return true; + api.injectNow = function(details) { + if ( typeof details.frameId !== 'number' ) { return; } + if ( µb.URI.isNetworkURI(details.url) === false ) { return; } + let request = { + tabId: details.tabId, + frameId: details.frameId, + url: details.url, + hostname: µb.URI.hostnameFromURI(details.url), + domain: undefined, + entity: undefined + }; + request.domain = µb.URI.domainFromHostname(request.hostname); + request.entity = µb.URI.entityFromDomain(request.domain); + let scriptlets = µb.scriptletFilteringEngine.retrieve(request); + if ( scriptlets === undefined ) { return; } + if ( µb.hiddenSettings.debugScriptlets ) { + scriptlets = 'debugger;\n' + scriptlets; + } + contentscriptCodeParts[contentscriptCodeScriptletsSlot] = + encodeURIComponent(scriptlets); + chrome.tabs.executeScript( + details.tabId, + { + code: contentscriptCodeParts.join(''), + frameId: details.frameId, + matchAboutBlank: true, + runAt: 'document_start' + } + ); }; api.toSelfie = function() { diff --git a/src/js/tab.js b/src/js/tab.js index bf7cc5456601d..881ce2b3ff4a9 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -486,14 +486,14 @@ housekeep itself. // content has changed. vAPI.tabs.onNavigation = function(details) { - if ( details.frameId !== 0 ) { - return; - } - µb.tabContextManager.commit(details.tabId, details.url); - var pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted'); - if ( pageStore ) { - pageStore.journalAddRootFrame('committed', details.url); + if ( details.frameId === 0 ) { + µb.tabContextManager.commit(details.tabId, details.url); + let pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted'); + if ( pageStore ) { + pageStore.journalAddRootFrame('committed', details.url); + } } + µb.scriptletFilteringEngine.injectNow(details); }; /******************************************************************************/