aboutsummaryrefslogtreecommitdiffstats
path: root/platform/firefox/vapi-background.js
diff options
context:
space:
mode:
Diffstat (limited to 'platform/firefox/vapi-background.js')
-rw-r--r--platform/firefox/vapi-background.js287
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);