diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 6f2c284c2..1b365e292 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -319,15 +319,31 @@ var contentObserver = { } sandbox.injectScript = function(script) { - var svc = Services; + let svc = Services; // Sandbox appears void. // I've seen this happens, need to investigate why. - if ( svc === undefined ) { - return; - } + if ( svc === undefined ) { return; } svc.scriptloader.loadSubScript(script, sandbox); }; + sandbox.injectCSS = function(sheetURI) { + try { + let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + wu.loadSheetUsingURIString(sheetURI, wu.USER_SHEET); + } catch(ex) { + } + }; + + sandbox.removeCSS = function(sheetURI) { + try { + let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + wu.removeSheetUsingURIString(sheetURI, wu.USER_SHEET); + } catch (ex) { + } + }; + // The goal is to have content scripts removed from web pages. This // helps remove traces of uBlock from memory when disabling/removing // the addon. @@ -337,8 +353,10 @@ var contentObserver = { sandbox.outerShutdown = function() { sandbox.removeMessageListener(); sandbox.addMessageListener = + sandbox.injectCSS = sandbox.injectScript = sandbox.outerShutdown = + sandbox.removeCSS = sandbox.removeMessageListener = sandbox.rpc = sandbox.sendAsyncMessage = function(){}; diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 6bff1b684..a7662a804 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -437,6 +437,46 @@ vAPI.messaging.start(); /******************************************************************************/ +vAPI.userCSS = (function() { + if ( !self.injectCSS ) { + return; + } + var injectCSS = self.injectCSS, + removeCSS = self.removeCSS, + userCSS = '', + sheetURI = ''; + var load = function() { + if ( userCSS === '' || sheetURI !== '' ) { return; } + sheetURI = 'data:text/css;charset=utf-8,' + encodeURIComponent(userCSS); + injectCSS(sheetURI); + }; + var unload = function() { + if ( sheetURI === '' ) { return; } + removeCSS(sheetURI); + sheetURI = ''; + }; + var add = function(cssText) { + if ( cssText === '' ) { return; } + if ( userCSS !== '' ) { userCSS += '\n'; } + userCSS += cssText; + unload(); + load(); + }; + var toggle = function(state) { + if ( userCSS === '' ) { return; } + if ( state === undefined ) { + state = sheetURI === ''; + } + return state ? load() : unload(); + }; + return { + add: add, + toggle: toggle + }; +})(); + +/******************************************************************************/ + // No need to have vAPI client linger around after shutdown if // we are not a top window (because element picker can still // be injected in top window). diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 675fdde18..1b8d32ff2 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -163,7 +163,8 @@ var allExceptions = Object.create(null), allSelectors = Object.create(null), commitTimer = null, stagedNodes = [], - matchesProp = vAPI.matchesProp; + matchesProp = vAPI.matchesProp, + userCSS = vAPI.userCSS; // Complex selectors, due to their nature may need to be "de-committed". A // Set() is used to implement this functionality. @@ -428,6 +429,9 @@ var domFilterer = { document.head.appendChild(styleTag); } this.styleTags.push(styleTag); + if ( userCSS ) { + userCSS.add(styleText); + } } // Simple selectors: incremental. @@ -580,10 +584,16 @@ var domFilterer = { }, toggleOff: function() { + if ( userCSS ) { + userCSS.toggle(false); + } this.enabled = false; }, toggleOn: function() { + if ( userCSS ) { + userCSS.toggle(true); + } this.enabled = true; },