This commit is contained in:
gorhill 2015-10-22 07:16:21 -04:00
parent 0ae483704f
commit 567b1007f8

View file

@ -71,7 +71,7 @@ vAPI.localStorage.setDefaultBool('forceLegacyToolbarButton', false);
var cleanupTasks = []; var cleanupTasks = [];
// This must be updated manually, every time a new task is added/removed // This must be updated manually, every time a new task is added/removed
var expectedNumberOfCleanups = 8; var expectedNumberOfCleanups = 9;
window.addEventListener('unload', function() { window.addEventListener('unload', function() {
if ( typeof vAPI.app.onShutdown === 'function' ) { if ( typeof vAPI.app.onShutdown === 'function' ) {
@ -524,6 +524,139 @@ vAPI.storage = (function() {
/******************************************************************************/ /******************************************************************************/
// This must be executed/setup early.
var winWatcher = (function() {
var chromeWindowType = vAPI.thunderbird ? 'mail:3pane' : 'navigator:browser';
var windowToIdMap = new Map();
var windowIdGenerator = 1;
var api = {
onOpenWindow: null,
onCloseWindow: null
};
api.getWindows = function() {
return windowToIdMap.keys();
};
api.idFromWindow = function(win) {
return windowToIdMap.get(win) || 0;
};
api.getCurrentWindow = function() {
return Services.wm.getMostRecentWindow(chromeWindowType) || null;
};
var addWindow = function(win) {
if ( !win || windowToIdMap.has(win) ) {
return;
}
windowToIdMap.set(win, windowIdGenerator++);
if ( typeof api.onOpenWindow === 'function' ) {
api.onOpenWindow(win);
}
};
var removeWindow = function(win) {
if ( !win || windowToIdMap.delete(win) !== true ) {
return;
}
if ( typeof api.onCloseWindow === 'function' ) {
api.onCloseWindow(win);
}
};
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowMediator
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher
// https://github.com/gorhill/uMatrix/issues/357
// Use nsIWindowMediator for being notified of opened/closed windows.
var listeners = {
onOpenWindow: function(aWindow) {
var win;
try {
win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch (ex) {
}
addWindow(win);
},
onCloseWindow: function(aWindow) {
var win;
try {
win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch (ex) {
}
removeWindow(win);
},
observe: function(aSubject, topic) {
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher#registerNotification%28%29
// "aSubject - the window being opened or closed, sent as an
// "nsISupports which can be ... QueryInterfaced to an
// "nsIDOMWindow."
var win;
try {
win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch (ex) {
}
if ( !win ) { return; }
if ( topic === 'domwindowopened' ) {
addWindow(win);
return;
}
if ( topic === 'domwindowclosed' ) {
removeWindow(win);
return;
}
}
};
(function() {
var win, winumerator, docElement;
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowMediator#getEnumerator%28%29
winumerator = Services.wm.getEnumerator(chromeWindowType);
while ( winumerator.hasMoreElements() ) {
win = winumerator.getNext();
if ( !win.closed ) {
windowToIdMap.set(win, windowIdGenerator++);
}
}
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher#getWindowEnumerator%28%29
winumerator = Services.ww.getWindowEnumerator();
while ( winumerator.hasMoreElements() ) {
win = winumerator.getNext()
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
if ( win.closed ) {
continue;
}
docElement = win.document && win.document.documentElement;
if ( !docElement ) {
continue;
}
if ( docElement.getAttribute('windowtype') === chromeWindowType ) {
windowToIdMap.set(win, windowIdGenerator++);
}
}
Services.wm.addListener(listeners);
Services.ww.registerNotification(listeners);
})();
cleanupTasks.push(function() {
Services.wm.removeListener(listeners);
Services.ww.unregisterNotification(listeners);
windowToIdMap.clear();
});
return api;
})();
/******************************************************************************/
var getTabBrowser = (function() { var getTabBrowser = (function() {
if ( vAPI.fennec ) { if ( vAPI.fennec ) {
return function(win) { return function(win) {
@ -550,7 +683,7 @@ var getOwnerWindow = function(target) {
} }
// Fennec // Fennec
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
for ( var tab of win.BrowserApp.tabs) { for ( var tab of win.BrowserApp.tabs) {
if ( tab === target || tab.window === target ) { if ( tab === target || tab.window === target ) {
return win; return win;
@ -574,15 +707,6 @@ vAPI.noTabId = '-1';
vAPI.tabs = {}; vAPI.tabs = {};
/******************************************************************************/
vAPI.tabs.chromeWindowType = (function() {
if ( vAPI.thunderbird ) {
return 'mail:3pane';
}
return 'navigator:browser';
})();
/******************************************************************************/ /******************************************************************************/
vAPI.tabs.registerListeners = function() { vAPI.tabs.registerListeners = function() {
@ -643,12 +767,11 @@ vAPI.tabs.get = function(tabId, callback) {
var win = getOwnerWindow(browser); var win = getOwnerWindow(browser);
var tabBrowser = getTabBrowser(win); var tabBrowser = getTabBrowser(win);
var windows = this.getWindows();
callback({ callback({
id: tabId, id: tabId,
index: tabWatcher.indexFromTarget(browser), index: tabWatcher.indexFromTarget(browser),
windowId: windows.indexOf(win), windowId: winWatcher.idFromWindow(win),
active: browser === tabBrowser.selectedBrowser, active: browser === tabBrowser.selectedBrowser,
url: browser.currentURI.asciiSpec, url: browser.currentURI.asciiSpec,
title: browser.contentTitle title: browser.contentTitle
@ -661,7 +784,7 @@ vAPI.tabs.getAll = function(window) {
var win, tab; var win, tab;
var tabs = []; var tabs = [];
for ( win of this.getWindows() ) { for ( win of winWatcher.getWindows() ) {
if ( window && window !== win ) { if ( window && window !== win ) {
continue; continue;
} }
@ -681,23 +804,6 @@ vAPI.tabs.getAll = function(window) {
/******************************************************************************/ /******************************************************************************/
vAPI.tabs.getWindows = function() {
var winumerator = Services.wm.getEnumerator(this.chromeWindowType);
var windows = [];
while ( winumerator.hasMoreElements() ) {
var win = winumerator.getNext();
if ( !win.closed ) {
windows.push(win);
}
}
return windows;
};
/******************************************************************************/
// properties of the details object: // properties of the details object:
// url: 'URL', // the address that will be opened // url: 'URL', // the address that will be opened
// tabId: 1, // the tab is used if set, instead of creating a new one // tabId: 1, // the tab is used if set, instead of creating a new one
@ -754,7 +860,7 @@ vAPI.tabs.open = function(details) {
} }
} }
var win = Services.wm.getMostRecentWindow(this.chromeWindowType); var win = winWatcher.getCurrentWindow();
var tabBrowser = getTabBrowser(win); var tabBrowser = getTabBrowser(win);
if ( vAPI.fennec ) { if ( vAPI.fennec ) {
@ -1023,7 +1129,7 @@ var tabWatcher = (function() {
}; };
var currentBrowser = function() { var currentBrowser = function() {
var win = Services.wm.getMostRecentWindow(vAPI.tabs.chromeWindowType); var win = winWatcher.getCurrentWindow();
// https://github.com/gorhill/uBlock/issues/399 // https://github.com/gorhill/uBlock/issues/399
// getTabBrowser() can return null at browser launch time. // getTabBrowser() can return null at browser launch time.
var tabBrowser = getTabBrowser(win); var tabBrowser = getTabBrowser(win);
@ -1213,65 +1319,10 @@ var tabWatcher = (function() {
} }
}; };
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowMediator
// https://github.com/gorhill/uMatrix/issues/357
// Use nsIWindowMediator for being notified of opened/closed windows.
var windowListener = {
onOpenWindow: function(aWindow) {
var win;
try {
win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch (ex) {
}
if ( win ) {
onWindowLoad(win);
}
},
onCloseWindow: function(aWindow) {
var win;
try {
win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch (ex) {
}
if ( win ) {
onWindowUnload(win);
}
}
};
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher
var windowWatcher = {
observe: function(aSubject, topic) {
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher#registerNotification%28%29
// "aSubject - the window being opened or closed, sent as an
// "nsISupports which can be ... QueryInterfaced to an
// "nsIDOMWindow."
var win;
try {
win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch (ex) {
}
if ( !win ) {
return;
}
if ( topic === 'domwindowopened' ) {
onWindowLoad(win);
return;
}
if ( topic === 'domwindowclosed' ) {
onWindowUnload(win);
return;
}
}
};
// Initialize map with existing active tabs // Initialize map with existing active tabs
var start = function() { var start = function() {
var tabBrowser, tabs, tab; var tabBrowser, tabs, tab;
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
onWindowLoad(win); onWindowLoad(win);
tabBrowser = getTabBrowser(win); tabBrowser = getTabBrowser(win);
if ( tabBrowser === null ) { if ( tabBrowser === null ) {
@ -1289,18 +1340,17 @@ var tabWatcher = (function() {
} }
} }
Services.wm.addListener(windowListener); winWatcher.onOpenWindow = onWindowLoad;
Services.ww.registerNotification(windowWatcher); winWatcher.onCloseWindow = onWindowUnload;
}; };
var stop = function() { var stop = function() {
Services.wm.removeListener(windowListener); winWatcher.onOpenWindow = null;
Services.ww.unregisterNotification(windowWatcher); winWatcher.onCloseWindow = null;
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
onWindowUnload(win); onWindowUnload(win);
} }
browserToTabIdMap.clear(); browserToTabIdMap.clear();
tabIdToBrowserMap.clear(); tabIdToBrowserMap.clear();
}; };
@ -1326,7 +1376,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
// If badge is undefined, then setIcon was called from the TabSelect event // If badge is undefined, then setIcon was called from the TabSelect event
var win = badge === undefined var win = badge === undefined
? iconStatus ? iconStatus
: Services.wm.getMostRecentWindow(vAPI.tabs.chromeWindowType); : winWatcher.getCurrentWindow();
var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab); var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab);
var tb = vAPI.toolbarButton; var tb = vAPI.toolbarButton;
@ -2216,7 +2266,7 @@ vAPI.toolbarButton = {
var menuItemIds = new WeakMap(); var menuItemIds = new WeakMap();
var shutdown = function() { var shutdown = function() {
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
var id = menuItemIds.get(win); var id = menuItemIds.get(win);
if ( !id ) { if ( !id ) {
continue; continue;
@ -2244,7 +2294,7 @@ vAPI.toolbarButton = {
}; };
tbb.onClick = function() { tbb.onClick = function() {
var win = Services.wm.getMostRecentWindow('navigator:browser'); var win = winWatcher.getCurrentWindow();
var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab); var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab);
vAPI.tabs.open({ vAPI.tabs.open({
url: 'popup.html?tabId=' + curTabId, url: 'popup.html?tabId=' + curTabId,
@ -2265,7 +2315,7 @@ vAPI.toolbarButton = {
tbb.init = function() { tbb.init = function() {
// Only actually expecting one window under Fennec (note, not tabs, windows) // Only actually expecting one window under Fennec (note, not tabs, windows)
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
var label = this.getMenuItemLabel(); var label = this.getMenuItemLabel();
var id = win.NativeWindow.menu.add({ var id = win.NativeWindow.menu.add({
name: label, name: label,
@ -2550,7 +2600,7 @@ vAPI.toolbarButton = {
}; };
var shutdown = function() { var shutdown = function() {
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
var toolbarButton = win.document.getElementById(tbb.id); var toolbarButton = win.document.getElementById(tbb.id);
if ( toolbarButton ) { if ( toolbarButton ) {
toolbarButton.parentNode.removeChild(toolbarButton); toolbarButton.parentNode.removeChild(toolbarButton);
@ -2631,7 +2681,7 @@ vAPI.toolbarButton = {
var shutdown = function() { var shutdown = function() {
CustomizableUI.destroyWidget(tbb.id); CustomizableUI.destroyWidget(tbb.id);
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
var panel = win.document.getElementById(tbb.viewId); var panel = win.document.getElementById(tbb.viewId);
panel.parentNode.removeChild(panel); panel.parentNode.removeChild(panel);
win.QueryInterface(Ci.nsIInterfaceRequestor) win.QueryInterface(Ci.nsIInterfaceRequestor)
@ -2759,7 +2809,7 @@ vAPI.toolbarButton = {
].join(';'); ].join(';');
var updateBadgeStyle = function() { var updateBadgeStyle = function() {
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
var button = win.document.getElementById(tbb.id); var button = win.document.getElementById(tbb.id);
if ( button === null ) { if ( button === null ) {
continue; continue;
@ -2816,7 +2866,7 @@ vAPI.toolbarButton = {
CustomizableUI.removeListener(CUIEvents); CustomizableUI.removeListener(CUIEvents);
CustomizableUI.destroyWidget(tbb.id); CustomizableUI.destroyWidget(tbb.id);
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
var panel = win.document.getElementById(tbb.viewId); var panel = win.document.getElementById(tbb.viewId);
panel.parentNode.removeChild(panel); panel.parentNode.removeChild(panel);
win.QueryInterface(Ci.nsIInterfaceRequestor) win.QueryInterface(Ci.nsIInterfaceRequestor)
@ -3103,7 +3153,7 @@ vAPI.contextMenu.create = function(details, callback) {
}); });
}; };
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
this.register(win.document); this.register(win.document);
} }
}; };
@ -3111,7 +3161,7 @@ vAPI.contextMenu.create = function(details, callback) {
/******************************************************************************/ /******************************************************************************/
vAPI.contextMenu.remove = function() { vAPI.contextMenu.remove = function() {
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of winWatcher.getWindows() ) {
this.unregister(win.document); this.unregister(win.document);
} }