diff options
Diffstat (limited to 'platform/firefox/vapi-background.js')
-rw-r--r-- | platform/firefox/vapi-background.js | 287 |
1 files changed, 223 insertions, 64 deletions
diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index a74fd23..0a4b98d 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -40,6 +40,7 @@ const {Services} = Cu.import('resource://gre/modules/Services.jsm', null); var vAPI = self.vAPI = self.vAPI || {}; vAPI.firefox = true; vAPI.fennec = Services.appinfo.ID === '{aa3c5121-dab2-40e2-81ca-7ea25febc110}'; +vAPI.thunderbird = Services.appinfo.ID === '{3550f703-e582-4d05-9a08-453d09bdfdc6}'; /******************************************************************************/ @@ -70,7 +71,7 @@ vAPI.localStorage.setDefaultBool('forceLegacyToolbarButton', false); var cleanupTasks = []; // This must be updated manually, every time a new task is added/removed -var expectedNumberOfCleanups = 7; +var expectedNumberOfCleanups = 8; window.addEventListener('unload', function() { if ( typeof vAPI.app.onShutdown === 'function' ) { @@ -90,10 +91,11 @@ window.addEventListener('unload', function() { } // frameModule needs to be cleared too + var frameModuleURL = vAPI.getURL('frameModule.js'); var frameModule = {}; - Cu.import(vAPI.getURL('frameModule.js'), frameModule); + Cu.import(frameModuleURL, frameModule); frameModule.contentObserver.unregister(); - Cu.unload(vAPI.getURL('frameModule.js')); + Cu.unload(frameModuleURL); }); /******************************************************************************/ @@ -522,9 +524,23 @@ vAPI.storage = (function() { /******************************************************************************/ -var getTabBrowser = function(win) { - return vAPI.fennec && win.BrowserApp || win.gBrowser || null; -}; +var getTabBrowser = (function() { + if ( vAPI.fennec ) { + return function(win) { + return win.BrowserApp || null; + }; + } + + if ( vAPI.thunderbird ) { + return function(win) { + return win.document.getElementById('tabmail') || null; + }; + } + + return function(win) { + return win.gBrowser || null; + }; +})(); /******************************************************************************/ @@ -557,6 +573,16 @@ vAPI.noTabId = '-1'; vAPI.tabs = {}; + +/******************************************************************************/ + +vAPI.tabs.mostRecentWindowId = (function() { + if ( vAPI.thunderbird ) { + return 'mail:3pane'; + } + return 'navigator:browser'; +})(); + /******************************************************************************/ vAPI.tabs.registerListeners = function() { @@ -656,7 +682,7 @@ vAPI.tabs.getAll = function(window) { /******************************************************************************/ vAPI.tabs.getWindows = function() { - var winumerator = Services.wm.getEnumerator('navigator:browser'); + var winumerator = Services.wm.getEnumerator(this.mostRecentWindowId); var windows = []; while ( winumerator.hasMoreElements() ) { @@ -728,11 +754,13 @@ vAPI.tabs.open = function(details) { } } - var win = Services.wm.getMostRecentWindow('navigator:browser'); + var win = Services.wm.getMostRecentWindow(this.mostRecentWindowId); var tabBrowser = getTabBrowser(win); if ( vAPI.fennec ) { - tabBrowser.addTab(details.url, {selected: details.active !== false}); + tabBrowser.addTab(details.url, { + selected: details.active !== false + }); // Note that it's impossible to move tabs on Fennec, so don't bother return; } @@ -749,6 +777,15 @@ vAPI.tabs.open = function(details) { return; } + if ( vAPI.thunderbird ) { + tabBrowser.openTab('contentTab', { + contentPage: details.url, + background: !details.active + }); + // TODO: Should be possible to move tabs on Thunderbird + return; + } + if ( details.index === -1 ) { details.index = tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1; } @@ -780,13 +817,16 @@ vAPI.tabs.replace = function(tabId, url) { /******************************************************************************/ -vAPI.tabs._remove = function(tab, tabBrowser) { - if ( vAPI.fennec ) { - tabBrowser.closeTab(tab); - return; +vAPI.tabs._remove = (function() { + if ( vAPI.fennec || vAPI.thunderbird ) { + return function(tab, tabBrowser) { + tabBrowser.closeTab(tab); + }; } - tabBrowser.removeTab(tab); -}; + return function(tab, tabBrowser) { + tabBrowser.removeTab(tab); + }; +})(); /******************************************************************************/ @@ -875,6 +915,10 @@ var tabWatcher = (function() { var tabIdGenerator = 1; var indexFromBrowser = function(browser) { + // TODO: Add support for this + if ( vAPI.thunderbird ) { + return -1; + } var win = getOwnerWindow(browser); if ( !win ) { return -1; @@ -920,22 +964,36 @@ var tabWatcher = (function() { return tabbrowser.tabs[i]; }; - var browserFromTarget = function(target) { - if ( !target ) { - return null; - } + var browserFromTarget = (function() { if ( vAPI.fennec ) { - if ( target.browser ) { // target is a tab - target = target.browser; - } - } else if ( target.linkedPanel ) { // target is a tab - target = target.linkedBrowser; + return function(target) { + if ( !target ) { return null; } + if ( target.browser ) { // target is a tab + target = target.browser; + } + return target.localName === 'browser' ? target : null; + }; } - if ( target.localName !== 'browser' ) { - return null; + if ( vAPI.thunderbird ) { + return function(target) { + if ( !target ) { return null; } + if ( target.mode ) { // target is object with tab info + var browserFunc = target.mode.getBrowser || target.mode.tabType.getBrowser; + if ( browserFunc ) { + return browserFunc.call(target.mode.tabType, target); + } + } + return target.localName === 'browser' ? target : null; + }; } - return target; - }; + return function(target) { + if ( !target ) { return null; } + if ( target.linkedPanel ) { // target is a tab + target = target.linkedBrowser; + } + return target.localName === 'browser' ? target : null; + }; + })(); var tabIdFromTarget = function(target) { var browser = browserFromTarget(target); @@ -965,13 +1023,20 @@ var tabWatcher = (function() { }; var currentBrowser = function() { - var win = Services.wm.getMostRecentWindow('navigator:browser'); + var win = Services.wm.getMostRecentWindow(vAPI.tabs.mostRecentWindowId); // https://github.com/gorhill/uBlock/issues/399 // getTabBrowser() can return null at browser launch time. var tabBrowser = getTabBrowser(win); if ( tabBrowser === null ) { return null; } + if ( vAPI.thunderbird ) { + // Directly at startup the first tab may not be initialized + if ( tabBrowser.tabInfo.length === 0 ) { + return null; + } + return tabBrowser.getBrowserForSelectedTab() || null; + } return browserFromTarget(tabBrowser.selectedTab); }; @@ -986,17 +1051,21 @@ var tabWatcher = (function() { } }; - // https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen - var onOpen = function({target}) { - var tabId = tabIdFromTarget(target); - var browser = browserFromTabId(tabId); - vAPI.tabs.onNavigation({ - frameId: 0, - tabId: tabId, - url: browser.currentURI.asciiSpec, - }); + var removeTarget = function(target) { + onClose({ target: target }); }; + // https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen + //var onOpen = function({target}) { + // var tabId = tabIdFromTarget(target); + // var browser = browserFromTabId(tabId); + // vAPI.tabs.onNavigation({ + // frameId: 0, + // tabId: tabId, + // url: browser.currentURI.asciiSpec, + // }); + //}; + // https://developer.mozilla.org/en-US/docs/Web/Events/TabShow var onShow = function({target}) { tabIdFromTarget(target); @@ -1021,29 +1090,29 @@ var tabWatcher = (function() { return false; } + if ( typeof vAPI.toolbarButton.attachToNewWindow === 'function' ) { + vAPI.toolbarButton.attachToNewWindow(window); + } + var tabContainer; if ( tabBrowser.deck ) { // Fennec tabContainer = tabBrowser.deck; } else if ( tabBrowser.tabContainer ) { // Firefox tabContainer = tabBrowser.tabContainer; vAPI.contextMenu.register(window.document); - } else { - return true; - } - - if ( typeof vAPI.toolbarButton.attachToNewWindow === 'function' ) { - vAPI.toolbarButton.attachToNewWindow(window); } // https://github.com/gorhill/uBlock/issues/697 // Ignore `TabShow` events: unfortunately the `pending` attribute is // not set when a tab is opened as a result of session restore -- it is // set *after* the event is fired in such case. - //tabContainer.addEventListener('TabOpen', onOpen); - tabContainer.addEventListener('TabShow', onShow); - tabContainer.addEventListener('TabClose', onClose); - // when new window is opened TabSelect doesn't run on the selected tab? - tabContainer.addEventListener('TabSelect', onSelect); + if ( tabContainer ) { + //tabContainer.addEventListener('TabOpen', onOpen); + tabContainer.addEventListener('TabShow', onShow); + tabContainer.addEventListener('TabClose', onClose); + // when new window is opened TabSelect doesn't run on the selected tab? + tabContainer.addEventListener('TabSelect', onSelect); + } return true; }; @@ -1092,7 +1161,9 @@ var tabWatcher = (function() { // To keep in mind: not all windows are tab containers, // sometimes the window IS the tab. var tabs; - if ( tabBrowser.tabs ) { + if ( vAPI.thunderbird ) { + tabs = tabBrowser.tabInfo; + } else if ( tabBrowser.tabs ) { tabs = tabBrowser.tabs; } else if ( tabBrowser.localName === 'browser' ) { tabs = [tabBrowser]; @@ -1101,8 +1172,10 @@ var tabWatcher = (function() { } var browser, URI, tabId; - for ( var tab of tabs ) { - browser = tabWatcher.browserFromTarget(tab); + var tabindex = tabs.length, tab; + while ( tabindex-- ) { + tab = tabs[tabindex]; + browser = browserFromTarget(tab); if ( browser === null ) { continue; } @@ -1111,7 +1184,6 @@ var tabWatcher = (function() { if ( URI.schemeIs('chrome') && URI.host === location.host ) { vAPI.tabs._remove(tab, getTabBrowser(this)); } - browser = browserFromTarget(tab); tabId = browserToTabIdMap.get(browser); if ( tabId !== undefined ) { removeBrowserEntry(tabId, browser); @@ -1137,14 +1209,19 @@ var tabWatcher = (function() { // Initialize map with existing active tabs var start = function() { - var tabBrowser, tab; + var tabBrowser, tabs, tab; for ( var win of vAPI.tabs.getWindows() ) { onWindowLoad.call(win); tabBrowser = getTabBrowser(win); if ( tabBrowser === null ) { continue; } - for ( tab of tabBrowser.tabs ) { + // `tabBrowser.tabs` may not exist (Thunderbird). + tabs = tabBrowser.tabs; + if ( !tabs ) { + continue; + } + for ( tab of tabs ) { if ( vAPI.fennec || !tab.hasAttribute('pending') ) { tabIdFromTarget(tab); } @@ -1173,6 +1250,7 @@ var tabWatcher = (function() { browserFromTarget: browserFromTarget, currentBrowser: currentBrowser, indexFromTarget: indexFromTarget, + removeTarget: removeTarget, start: start, tabFromBrowser: tabFromBrowser, tabIdFromTarget: tabIdFromTarget @@ -1185,7 +1263,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) { // If badge is undefined, then setIcon was called from the TabSelect event var win = badge === undefined ? iconStatus - : Services.wm.getMostRecentWindow('navigator:browser'); + : Services.wm.getMostRecentWindow(vAPI.tabs.mostRecentWindowId); var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab); var tb = vAPI.toolbarButton; @@ -1208,7 +1286,7 @@ vAPI.messaging = { return Cc['@mozilla.org/globalmessagemanager;1'] .getService(Ci.nsIMessageListenerManager); }, - frameScript: vAPI.getURL('frameScript.js'), + frameScriptURL: vAPI.getURL('frameScript.js'), listeners: {}, defaultHandler: null, NOOPFUNC: function(){}, @@ -1438,7 +1516,7 @@ vAPI.messaging.setup = function(defaultHandler) { this.onMessage ); - this.globalMessageManager.loadFrameScript(this.frameScript, true); + this.globalMessageManager.loadFrameScript(this.frameScriptURL, true); cleanupTasks.push(function() { var gmm = vAPI.messaging.globalMessageManager; @@ -1452,7 +1530,7 @@ vAPI.messaging.setup = function(defaultHandler) { }) ); - gmm.removeDelayedFrameScript(vAPI.messaging.frameScript); + gmm.removeDelayedFrameScript(vAPI.messaging.frameScriptURL); gmm.removeMessageListener( location.host + ':background', vAPI.messaging.onMessage @@ -1472,6 +1550,60 @@ vAPI.messaging.broadcast = function(message) { }; /******************************************************************************/ +/******************************************************************************/ + +// Synchronous messaging: Firefox allows this. Chromium does not allow this. + +// Sometimes there is no way around synchronous messaging, as long as: +// - the code at the other end execute fast and return quickly. +// - it's not abused. +// Original rationale is <https://github.com/gorhill/uBlock/issues/756>. +// Synchronous messaging is a good solution for this case because: +// - It's done only *once* per page load. (Keep in mind there is already a +// sync message sent for each single network request on a page and it's not +// an issue, because the code executed is trivial, which is the key -- see +// shouldLoadListener below). +// - The code at the other end is fast. +// Though vAPI.rpcReceiver was brought forth because of this one case, I +// generalized the concept for whatever future need for synchronous messaging +// which might arise. + +// https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Content_frame_message_manager + +vAPI.rpcReceiver = (function() { + var calls = Object.create(null); + var childProcessMessageName = location.host + ':child-process-message'; + + var onChildProcessMessage = function(ev) { + var msg = ev.data; + if ( !msg ) { return; } + var fn = calls[msg.fnName]; + if ( typeof fn === 'function' ) { + return fn(msg); + } + }; + + if ( Services.ppmm ) { + Services.ppmm.addMessageListener( + childProcessMessageName, + onChildProcessMessage + ); + } + + cleanupTasks.push(function() { + if ( Services.ppmm ) { + Services.ppmm.removeMessageListener( + childProcessMessageName, + onChildProcessMessage + ); + } + }); + + return calls; +})(); + +/******************************************************************************/ +/******************************************************************************/ var httpObserver = { classDescription: 'net-channel-event-sinks for ' + location.host, @@ -1865,7 +1997,11 @@ vAPI.net.registerListeners = function() { var tabId = tabWatcher.tabIdFromTarget(e.target); var sourceTabId = null; - // Popup candidate + // Popup candidate: this code path is taken only for when a new top + // document loads, i.e. only once per document load. TODO: evaluate for + // popup filtering in an asynchrous manner -- it's not really required + // to evaluate on the spot. Still, there is currently no harm given + // this code path is typically taken only once per page load. if ( details.openerURL ) { for ( var browser of tabWatcher.browsers() ) { var URI = browser.currentURI; @@ -1899,8 +2035,9 @@ vAPI.net.registerListeners = function() { } } - //console.log('shouldLoadListener:', details.url); - + // We are being called synchronously from the content process, so we + // must return ASAP. The code below merely record the details of the + // request into a ring buffer for later retrieval by the HTTP observer. var pendingReq = httpObserver.createPendingRequest(details.url); pendingReq.frameId = details.frameId; pendingReq.parentFrameId = details.parentFrameId; @@ -1918,10 +2055,22 @@ vAPI.net.registerListeners = function() { var locationChangedListener = function(e) { var browser = e.target; + // I have seen this happens (at startup time) + if ( !browser.currentURI ) { + return; + } + // https://github.com/gorhill/uBlock/issues/697 // Dismiss event if the associated tab is pending. var tab = tabWatcher.tabFromBrowser(browser); if ( !vAPI.fennec && tab && tab.hasAttribute('pending') ) { + // https://github.com/gorhill/uBlock/issues/820 + // Firefox quirk: it happens the `pending` attribute was not + // present for certain tabs at startup -- and this can cause + // unwanted [browser <--> tab id] associations internally. + // Dispose of these if it is found the `pending` attribute is + // set. + tabWatcher.removeTarget(tab); return; } @@ -2131,9 +2280,12 @@ vAPI.toolbarButton = { resizeTimer = null; var body = iframe.contentDocument.body; panel.parentNode.style.maxWidth = 'none'; + + // https://github.com/gorhill/uMatrix/issues/362 + panel.parentNode.style.opacity = '1'; + // https://github.com/chrisaljoudi/uBlock/issues/730 // Voodoo programming: this recipe works - var clientHeight = body.clientHeight; iframe.style.height = toPx(clientHeight); panel.style.height = toPx(clientHeight + panel.boxObject.height - panel.clientHeight); @@ -2202,7 +2354,8 @@ vAPI.toolbarButton = { var addLegacyToolbarButton = function(window) { var document = window.document; - var toolbox = document.getElementById('navigator-toolbox') || document.getElementById('mail-toolbox'); + var toolbox = document.getElementById('navigator-toolbox') || + document.getElementById('mail-toolbox'); if ( !toolbox ) { return; } @@ -2766,6 +2919,12 @@ vAPI.contextMenu.register = function(doc) { } var contextMenu = doc.getElementById('contentAreaContextMenu'); + + // This can happen (Thunderbird). + if ( contextMenu === null ) { + return; + } + var menuitem = doc.createElement('menuitem'); menuitem.setAttribute('id', this.menuItemId); menuitem.setAttribute('label', this.menuLabel); |