diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 0e5ec4c3f..07e440e9e 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -264,6 +264,9 @@ const contentObserver = { }; sandbox.removeMessageListener = function() { + if ( !sandbox._messageListener_ ) { + return; + } try { messager.removeMessageListener( sandbox._sandboxId_, @@ -280,6 +283,22 @@ const contentObserver = { sandbox._messageListener_ = null; }; + // The goal is to have content scripts removed from web pages. This + // helps remove traces of uBlock from memory when disabling/removing + // the addon. + // For example, this takes care of: + // https://github.com/gorhill/uBlock/commit/ea4faff383789053f423498c1f1165c403fde7c7#commitcomment-11964137 + // > "gets the whole selected tab flashing" + sandbox.shutdownSandbox = function() { + sandbox.removeMessageListener(); + sandbox.addMessageListener = + sandbox.injectScript = + sandbox.removeMessageListener = + sandbox.sendAsyncMessage = + sandbox.shutdownSandbox = null; + messager = null; + }; + return sandbox; }, diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 5f29da896..227d68229 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -695,10 +695,6 @@ vAPI.tabs.open = function(details) { return; } - if ( details.index === -1 ) { - details.index = tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1; - } - // Open in a standalone window if ( details.popup === true ) { Services.ww.openWindow( @@ -711,6 +707,10 @@ vAPI.tabs.open = function(details) { return; } + if ( details.index === -1 ) { + details.index = tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1; + } + tab = tabBrowser.loadOneTab(details.url, { inBackground: !details.active }); if ( details.index !== undefined ) { @@ -1381,6 +1381,15 @@ vAPI.messaging.setup = function(defaultHandler) { cleanupTasks.push(function() { var gmm = vAPI.messaging.globalMessageManager; + gmm.broadcastAsyncMessage( + location.host + ':broadcast', + JSON.stringify({ + broadcast: true, + channelName: 'vAPI', + msg: { cmd: 'shutdownSandbox' } + }) + ); + gmm.removeDelayedFrameScript(vAPI.messaging.frameScript); gmm.removeMessageListener( location.host + ':background', diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 62fa77e07..0bdf61731 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global addMessageListener, removeMessageListener, sendAsyncMessage */ +/* global addMessageListener, removeMessageListener, sendAsyncMessage, shutdownSandbox */ // For non background pages @@ -76,32 +76,43 @@ vAPI.messaging = { messagingConnector(JSON.parse(msg)); }, - setup: function() { - addMessageListener(this.connector); - this.connected = true; - this.channels['vAPI'] = new MessagingChannel('vAPI', function(msg) { - if ( msg.cmd === 'injectScript' ) { - var details = msg.details; - if ( !details.allFrames && window !== window.top ) { - return; - } - // TODO: investigate why this happens, and if this happens - // legitimately (content scripts not injected I suspect, so - // that would make this legitimate). - // Case: open popup UI from icon in uBlock's logger - if ( typeof self.injectScript === 'function' ) { - self.injectScript(details.file); - } + builtinChannelHandler: function(msg) { + if ( msg.cmd === 'injectScript' ) { + var details = msg.details; + if ( !details.allFrames && window !== window.top ) { + return; } - }); + // TODO: investigate why this happens, and if this happens + // legitimately (content scripts not injected I suspect, so + // that would make this legitimate). + // Case: open popup UI from icon in uBlock's logger + if ( typeof self.injectScript === 'function' ) { + self.injectScript(details.file); + } + return; + } + if ( msg.cmd === 'shutdownSandbox' ) { + vAPI.shutdown.exec(); + vAPI.messaging.close(); + window.removeEventListener('pagehide', vAPI.messaging.toggleListener, true); + window.removeEventListener('pageshow', vAPI.messaging.toggleListener, true); + vAPI.messaging = null; + vAPI = {}; + shutdownSandbox(); + return; + } + }, + + setup: function() { + this.channels['vAPI'] = new MessagingChannel('vAPI', this.builtinChannelHandler); + window.addEventListener('pagehide', this.toggleListener, true); + window.addEventListener('pageshow', this.toggleListener, true); }, close: function() { - if ( !this.connected ) { - return; - } - removeMessageListener(); - this.connected = false; + window.removeEventListener('pagehide', this.toggleListener, true); + window.removeEventListener('pageshow', this.toggleListener, true); + this.disconnect(); this.channels = {}; this.pending = {}; }, @@ -120,25 +131,36 @@ vAPI.messaging = { return channel; }, + connect: function() { + if ( !this.connected ) { + addMessageListener(this.connector); + this.connected = true; + } + }, + + disconnect: function() { + if ( this.connected ) { + removeMessageListener(); + this.connected = false; + } + }, + toggleListener: function({type, persisted}) { - if ( !vAPI.messaging.connected ) { + if ( !vAPI.messaging ) { return; } if ( type === 'pagehide' ) { - removeMessageListener(); + vAPI.messaging.disconnect(); return; } if ( persisted ) { - addMessageListener(vAPI.messaging.connector); + vAPI.messaging.connect(); } } }; -window.addEventListener('pagehide', vAPI.messaging.toggleListener, true); -window.addEventListener('pageshow', vAPI.messaging.toggleListener, true); - /******************************************************************************/ var messagingConnector = function(details) { @@ -147,6 +169,12 @@ var messagingConnector = function(details) { } var messaging = vAPI.messaging; + + // Sandbox might have been shutdown + if ( !messaging ) { + return; + } + var channels = messaging.channels; var channel; @@ -195,10 +223,7 @@ var MessagingChannel = function(name, callback) { this.listeners = typeof callback === 'function' ? [callback] : []; this.refCount = 1; if ( typeof callback === 'function' ) { - var messaging = vAPI.messaging; - if ( !messaging.connected ) { - messaging.setup(); - } + vAPI.messaging.connect(); } }; @@ -208,9 +233,7 @@ MessagingChannel.prototype.send = function(message, callback) { MessagingChannel.prototype.sendTo = function(message, toTabId, toChannel, callback) { var messaging = vAPI.messaging; - if ( !messaging.connected ) { - messaging.setup(); - } + messaging.connect(); var auxProcessId; if ( callback ) { auxProcessId = messaging.auxProcessId++; @@ -241,10 +264,7 @@ MessagingChannel.prototype.addListener = function(callback) { throw new Error('Duplicate listener.'); } this.listeners.push(callback); - var messaging = vAPI.messaging; - if ( !messaging.connected ) { - messaging.setup(); - } + vAPI.messaging.connect(); }; MessagingChannel.prototype.removeListener = function(callback) { @@ -278,6 +298,10 @@ MessagingChannel.prototype.sendToListeners = function(msg) { /******************************************************************************/ +vAPI.messaging.setup(); + +/******************************************************************************/ + // 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).