1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-03 17:49:39 +02:00

this should fix #266 + fixes #212

This commit is contained in:
gorhill 2015-05-31 17:43:19 -04:00
parent b084d796b5
commit 6470216530

View File

@ -69,9 +69,13 @@ var cleanupTasks = [];
// Fixed by github.com/AlexVallat: // Fixed by github.com/AlexVallat:
// https://github.com/AlexVallat/uBlock/commit/7b781248f00cbe3d61b1cc367c440db80fa06049 // https://github.com/AlexVallat/uBlock/commit/7b781248f00cbe3d61b1cc367c440db80fa06049
// 7 instances of cleanupTasks.push, but one is unique to fennec, and one to desktop. // 7 instances of cleanupTasks.push, but one is unique to fennec, and one to desktop.
var expectedNumberOfCleanups = 7; var expectedNumberOfCleanups = 6;
window.addEventListener('unload', function() { window.addEventListener('unload', function() {
if ( typeof vAPI.app.onShutdown === 'function' ) {
vAPI.app.onShutdown();
}
for ( var cleanup of cleanupTasks ) { for ( var cleanup of cleanupTasks ) {
cleanup(); cleanup();
} }
@ -362,15 +366,6 @@ var getTabBrowser = function(win) {
/******************************************************************************/ /******************************************************************************/
var getBrowserForTab = function(tab) {
if ( !tab ) {
return null;
}
return vAPI.fennec && tab.browser || tab.linkedBrowser || null;
};
/******************************************************************************/
var getOwnerWindow = function(target) { var getOwnerWindow = function(target) {
if ( target.ownerDocument ) { if ( target.ownerDocument ) {
return target.ownerDocument.defaultView; return target.ownerDocument.defaultView;
@ -403,100 +398,67 @@ vAPI.tabs = {};
/******************************************************************************/ /******************************************************************************/
vAPI.tabs.registerListeners = function() { vAPI.tabs.registerListeners = function() {
// onClosed - handled in tabWatcher.onTabClose tabWatcher.start();
// onPopup - handled in httpObserver.handlePopup
for ( var win of this.getWindows() ) {
windowWatcher.onReady.call(win);
}
Services.ww.registerNotification(windowWatcher);
cleanupTasks.push(function() {
Services.ww.unregisterNotification(windowWatcher);
for ( var win of vAPI.tabs.getWindows() ) {
vAPI.contextMenu.unregister(win.document);
win.removeEventListener('DOMContentLoaded', windowWatcher.onReady);
var tabContainer;
var tabBrowser = getTabBrowser(win);
if ( !tabBrowser ) {
continue;
}
if ( tabBrowser.deck ) {
// Fennec
tabContainer = tabBrowser.deck;
} else if ( tabBrowser.tabContainer ) {
tabContainer = tabBrowser.tabContainer;
}
tabContainer.removeEventListener('TabOpen', tabWatcher.onOpen);
tabContainer.removeEventListener('TabShow', tabWatcher.onShow);
tabContainer.removeEventListener('TabClose', tabWatcher.onClose);
tabContainer.removeEventListener('TabSelect', tabWatcher.onSelect);
// Close extension tabs
for ( var tab of tabBrowser.tabs ) {
var browser = getBrowserForTab(tab);
if ( browser === null ) {
continue;
}
var URI = browser.currentURI;
if ( URI.schemeIs('chrome') && URI.host === location.host ) {
vAPI.tabs._remove(tab, getTabBrowser(win));
}
}
}
});
}; };
/******************************************************************************/ /******************************************************************************/
// Firefox:
//
// browser --> ownerDocument --> defaultView --> gBrowser --> browsers --+
// ^ |
// | |
// +-------------------------------------------------------------------
//
// browser (browser)
// contentTitle
// currentURI
// ownerDocument (XULDocument)
// defaultView (ChromeWindow)
// gBrowser (tabbrowser)
// browsers (browser)
// selectedBrowser
// selectedTab
// tabs (tab.tabbrowser-tab)
//
// Fennec:
//
// ???
vAPI.tabs.get = function(tabId, callback) { vAPI.tabs.get = function(tabId, callback) {
var tab, win; var win, browser;
if ( tabId === null ) { if ( tabId === null ) {
win = Services.wm.getMostRecentWindow('navigator:browser'); win = Services.wm.getMostRecentWindow('navigator:browser');
tab = getTabBrowser(win).selectedTab; browser = tabWatcher.browserFromTarget(getTabBrowser(win).selectedTab);
tabId = tabWatcher.tabIdFromTab(tab); tabId = tabWatcher.tabIdFromTarget(browser);
} else { } else {
tab = tabWatcher.tabFromTabId(tabId); browser = tabWatcher.browserFromTabId(tabId);
if ( tab ) { if ( browser ) {
win = getOwnerWindow(tab); win = getOwnerWindow(browser);
} }
} }
// For internal use // For internal use
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
return tab; return browser;
} }
if ( !tab ) { if ( !browser ) {
callback(); callback();
return; return;
} }
var windows = this.getWindows(); var windows = this.getWindows();
var browser = getBrowserForTab(tab);
var tabBrowser = getTabBrowser(win); var tabBrowser = getTabBrowser(win);
var tabIndex, tabTitle;
if ( vAPI.fennec ) {
tabIndex = tabBrowser.tabs.indexOf(tab);
tabTitle = browser.contentTitle;
} else {
tabIndex = tabBrowser.browsers.indexOf(browser);
tabTitle = tab.label;
}
callback({ callback({
id: tabId, id: tabId,
index: tabIndex, index: tabWatcher.indexFromTarget(browser),
windowId: windows.indexOf(win), windowId: windows.indexOf(win),
active: tab === tabBrowser.selectedTab, active: browser === tabBrowser.selectedBrowser,
url: browser.currentURI.asciiSpec, url: browser.currentURI.asciiSpec,
title: tabTitle title: browser.contentTitle
}); });
}; };
@ -517,9 +479,6 @@ vAPI.tabs.getAll = function(window) {
} }
for ( tab of tabBrowser.tabs ) { for ( tab of tabBrowser.tabs ) {
if ( !vAPI.fennec && tab.hasAttribute('pending') ) {
continue;
}
tabs.push(tab); tabs.push(tab);
} }
} }
@ -568,7 +527,7 @@ vAPI.tabs.open = function(details) {
var URI = Services.io.newURI(details.url, null, null); var URI = Services.io.newURI(details.url, null, null);
for ( tab of this.getAll() ) { for ( tab of this.getAll() ) {
var browser = getBrowserForTab(tab); var browser = tabWatcher.browserFromTarget(tab);
// Or simply .equals if we care about the fragment // Or simply .equals if we care about the fragment
if ( URI.equalsExceptRef(browser.currentURI) === false ) { if ( URI.equalsExceptRef(browser.currentURI) === false ) {
@ -585,9 +544,9 @@ vAPI.tabs.open = function(details) {
} }
if ( details.tabId ) { if ( details.tabId ) {
tab = tabWatcher.tabFromTabId(details.tabId); tab = tabWatcher.browserFromTabId(details.tabId);
if ( tab ) { if ( tab ) {
getBrowserForTab(tab).loadURI(details.url); tabWatcher.browserFromTarget(tab).loadURI(details.url);
return; return;
} }
} }
@ -624,9 +583,9 @@ vAPI.tabs.replace = function(tabId, url) {
targetURL = vAPI.getURL(targetURL); targetURL = vAPI.getURL(targetURL);
} }
var tab = tabWatcher.tabFromTabId(tabId); var tab = tabWatcher.browserFromTabId(tabId);
if ( tab ) { if ( tab ) {
getBrowserForTab(tab).loadURI(targetURL); tabWatcher.browserFromTarget(tab).loadURI(targetURL);
} }
}; };
@ -643,7 +602,7 @@ vAPI.tabs._remove = function(tab, tabBrowser) {
/******************************************************************************/ /******************************************************************************/
vAPI.tabs.remove = function(tabId) { vAPI.tabs.remove = function(tabId) {
var tabs = tabWatcher.tabFromTabId(tabId); var tabs = tabWatcher.browserFromTabId(tabId);
if ( tabs.length === 0 ) { if ( tabs.length === 0 ) {
return; return;
} }
@ -661,7 +620,7 @@ vAPI.tabs.reload = function(tabId) {
return; return;
} }
getBrowserForTab(tab).webNavigation.reload( tabWatcher.browserFromTarget(tab).webNavigation.reload(
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
); );
}; };
@ -698,7 +657,7 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
} }
details.file = vAPI.getURL(details.file); details.file = vAPI.getURL(details.file);
getBrowserForTab(tab).messageManager.sendAsyncMessage( tabWatcher.browserFromTarget(tab).messageManager.sendAsyncMessage(
location.host + ':broadcast', location.host + ':broadcast',
JSON.stringify({ JSON.stringify({
broadcast: true, broadcast: true,
@ -717,25 +676,130 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
/******************************************************************************/ /******************************************************************************/
var windowWatcher = { var tabWatcher = (function() {
onReady: function(e) { // TODO: find out whether we need a janitor to take care of stale entries.
if ( e ) { var browserToTabIdMap = new Map();
this.removeEventListener(e.type, windowWatcher.onReady); var tabIdToBrowserMap = new Map();
var tabIdGenerator = 1;
var indexFromBrowser = function(browser) {
var win = getOwnerWindow(browser);
if ( !win ) {
return -1;
}
var tabbrowser = getTabBrowser(win);
if ( !tabbrowser ) {
return -1;
}
return vAPI.fennec ?
tabbrowser.tabs.indexOf(browser) :
tabbrowser.browsers.indexOf(browser);
};
var indexFromTarget = function(target) {
return indexFromBrowser(browserFromTarget(target));
};
var browserFromTarget = function(target) {
if ( !target ) {
return null;
}
if ( vAPI.fennec ) {
if ( target.browser ) { // target is a tab
target = target.browser;
}
} else if ( target.linkedPanel ) { // target is a tab
target = target.linkedBrowser;
}
if ( target.localName !== 'browser' ) {
return null;
}
return target;
};
var tabIdFromTarget = function(target) {
var browser = browserFromTarget(target);
if ( browser === null ) {
return vAPI.noTabId;
}
var tabId = browserToTabIdMap.get(browser);
if ( tabId === undefined ) {
tabId = 't' + tabIdGenerator++;
browserToTabIdMap.set(browser, tabId);
tabIdToBrowserMap.set(tabId, browser);
}
return tabId;
};
var browserFromTabId = function(tabId) {
var browser = tabIdToBrowserMap.get(tabId);
if ( browser === undefined ) {
return null;
}
// Verify that the browser is still live
if ( indexFromBrowser(browser) !== -1 ) {
return browser;
}
removeBrowserEntry(tabId, browser);
return null;
};
var removeBrowserEntry = function(tabId, browser) {
if ( tabId && tabId !== vAPI.noTabId ) {
vAPI.tabs.onClosed(tabId);
delete vAPI.toolbarButton.tabs[tabId];
tabIdToBrowserMap.delete(tabId);
}
if ( browser ) {
browserToTabIdMap.delete(browser);
}
};
// 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);
};
// https://developer.mozilla.org/en-US/docs/Web/Events/TabClose
var onClose = function({target}) {
// target is tab in Firefox, browser in Fennec
var browser = browserFromTarget(target);
var tabId = browserToTabIdMap.get(browser);
removeBrowserEntry(tabId, browser);
};
// https://developer.mozilla.org/en-US/docs/Web/Events/TabSelect
var onSelect = function({target}) {
vAPI.setIcon(tabIdFromTarget(target), getOwnerWindow(target));
};
var onWindowLoad = function(ev) {
if ( ev ) {
this.removeEventListener(ev.type, onWindowLoad);
} }
var wintype = this.document.documentElement.getAttribute('windowtype'); var wintype = this.document.documentElement.getAttribute('windowtype');
if ( wintype !== 'navigator:browser' ) { if ( wintype !== 'navigator:browser' ) {
return; return;
} }
var tabContainer;
var tabBrowser = getTabBrowser(this); var tabBrowser = getTabBrowser(this);
if ( !tabBrowser ) { if ( !tabBrowser ) {
return; return;
} }
var tabContainer;
if ( tabBrowser.deck ) { if ( tabBrowser.deck ) {
// Fennec // Fennec
tabContainer = tabBrowser.deck; tabContainer = tabBrowser.deck;
@ -746,118 +810,105 @@ var windowWatcher = {
} else { } else {
return; return;
} }
tabContainer.addEventListener('TabOpen', onOpen);
tabContainer.addEventListener('TabOpen', tabWatcher.onOpen); tabContainer.addEventListener('TabShow', onShow);
tabContainer.addEventListener('TabShow', tabWatcher.onShow); tabContainer.addEventListener('TabClose', onClose);
tabContainer.addEventListener('TabClose', tabWatcher.onClose); tabContainer.addEventListener('TabSelect', onSelect);
tabContainer.addEventListener('TabSelect', tabWatcher.onSelect);
// when new window is opened TabSelect doesn't run on the selected tab? // when new window is opened TabSelect doesn't run on the selected tab?
},
observe: function(win, topic) {
if ( topic === 'domwindowopened' ) {
win.addEventListener('DOMContentLoaded', this.onReady);
}
}
};
/******************************************************************************/
var tabWatcher = (function() {
var knownTabs = new Set();
var stack = new WeakMap();
var stackId = 1;
// If needed, we can optimize further by having a matching tabid->tab map.
var tabIdFromTab = function(target) {
if ( !target ) {
return vAPI.noTabId;
}
if ( vAPI.fennec ) {
if ( target.browser ) {
// target is a tab
target = target.browser;
}
} else if ( target.linkedPanel ) {
// target is a tab
target = target.linkedBrowser;
}
if ( target.localName !== 'browser' ) {
return vAPI.noTabId;
}
var tabId = stack.get(target);
if ( !tabId ) {
tabId = '' + stackId++;
stack.set(target, tabId);
}
return tabId;
}; };
var tabFromTabId = function(tabId) { var onWindowUnload = function() {
for ( var tab of knownTabs ) { vAPI.contextMenu.unregister(this.document);
if ( tabIdFromTab(tab) === tabId ) { this.removeEventListener('DOMContentLoaded', onWindowLoad);
return tab;
var tabBrowser = getTabBrowser(this);
if ( !tabBrowser ) {
return;
}
var tabContainer = null;
if ( tabBrowser.deck ) {
// Fennec
tabContainer = tabBrowser.deck;
} else if ( tabBrowser.tabContainer ) {
tabContainer = tabBrowser.tabContainer;
}
if ( tabContainer ) {
tabContainer.removeEventListener('TabOpen', onOpen);
tabContainer.removeEventListener('TabShow', onShow);
tabContainer.removeEventListener('TabClose', onClose);
tabContainer.removeEventListener('TabSelect', onSelect);
}
// Close extension tabs
var browser, URI, tabId;
for ( var tab of tabBrowser.tabs ) {
browser = tabWatcher.browserFromTarget(tab);
if ( browser === null ) {
continue;
}
URI = browser.currentURI;
if ( URI.schemeIs('chrome') && URI.host === location.host ) {
vAPI.tabs._remove(tab, getTabBrowser(this));
}
browser = browserFromTarget(tab);
tabId = browserToTabIdMap.get(browser);
if ( tabId !== undefined ) {
tabIdToBrowserMap.delete(tabId);
}
browserToTabIdMap.delete(browser);
}
};
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher
var windowWatcher = {
observe: function(win, topic) {
if ( topic === 'domwindowopened' ) {
win.addEventListener('DOMContentLoaded', onWindowLoad);
} }
} }
return null;
}; };
// Initialize map with existing active tabs // Initialize map with existing active tabs
var tabBrowser; var start = function() {
for ( var win of vAPI.tabs.getWindows() ) { var tabBrowser, tab;
tabBrowser = getTabBrowser(win); for ( var win of vAPI.tabs.getWindows() ) {
if ( tabBrowser === null ) { onWindowLoad.call(win);
continue; tabBrowser = getTabBrowser(win);
} if ( tabBrowser === null ) {
for ( var tab of tabBrowser.tabs ) {
if ( !vAPI.fennec && tab.hasAttribute('pending') ) {
continue; continue;
} }
knownTabs.add(tab, true); for ( tab of tabBrowser.tabs ) {
tabIdFromTab(tab); if ( vAPI.fennec || !tab.hasAttribute('pending') ) {
tabIdFromTarget(tab);
}
}
} }
}
cleanupTasks.push(function() { Services.ww.registerNotification(windowWatcher);
knownTabs.clear();
});
// https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen
var onOpen = function({target}) {
knownTabs.add(target, true);
tabIdFromTab(tab);
}; };
// https://developer.mozilla.org/en-US/docs/Web/Events/TabShow var stop = function() {
var onShow = function({target}) { Services.ww.unregisterNotification(windowWatcher);
knownTabs.add(target, true);
tabIdFromTab(tab); for ( var win of vAPI.tabs.getWindows() ) {
onWindowUnload.call(win);
}
browserToTabIdMap.clear();
tabIdToBrowserMap.clear();
}; };
// https://developer.mozilla.org/en-US/docs/Web/Events/TabClose cleanupTasks.push(stop);
var onClose = function({target}) {
// target is tab in Firefox, browser in Fennec
var tabId = tabIdFromTab(target);
vAPI.tabs.onClosed(tabId);
delete vAPI.toolbarButton.tabs[tabId];
knownTabs.delete(target);
};
// https://developer.mozilla.org/en-US/docs/Web/Events/TabSelect
var onSelect = function({target}) {
knownTabs.add(target, true);
vAPI.setIcon(tabIdFromTab(target), getOwnerWindow(target));
};
return { return {
onOpen: onOpen, start: start,
onShow: onShow, browserFromTarget: browserFromTarget,
onClose: onClose, tabs: function() { return browserToTabIdMap.keys(); },
onSelect: onSelect, tabIdFromTarget: tabIdFromTarget,
tabs: knownTabs, browserFromTabId: browserFromTabId,
tabIdFromTab: tabIdFromTab, indexFromTarget: indexFromTarget
tabFromTabId: tabFromTabId
}; };
})(); })();
@ -868,7 +919,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
var win = badge === undefined var win = badge === undefined
? iconStatus ? iconStatus
: Services.wm.getMostRecentWindow('navigator:browser'); : Services.wm.getMostRecentWindow('navigator:browser');
var curTabId = tabWatcher.tabIdFromTab(getTabBrowser(win).selectedTab); var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab);
var tb = vAPI.toolbarButton; var tb = vAPI.toolbarButton;
// from 'TabSelect' event // from 'TabSelect' event
@ -932,7 +983,7 @@ vAPI.messaging.onMessage = function({target, data}) {
var sender = { var sender = {
tab: { tab: {
id: tabWatcher.tabIdFromTab(target) id: tabWatcher.tabIdFromTarget(target)
} }
}; };
@ -988,6 +1039,8 @@ vAPI.messaging.setup = function(defaultHandler) {
location.host + ':background', location.host + ':background',
vAPI.messaging.onMessage vAPI.messaging.onMessage
); );
vAPI.messaging.defaultHandler = null;
}); });
}; };
@ -1444,20 +1497,20 @@ vAPI.net.registerListeners = function() {
// a request would end up being categorized as a behind-the-scene // a request would end up being categorized as a behind-the-scene
// requests. // requests.
var details = e.data; var details = e.data;
var tabId = tabWatcher.tabIdFromTab(e.target); var tabId = tabWatcher.tabIdFromTarget(e.target);
var sourceTabId = null; var sourceTabId = null;
// Popup candidate // Popup candidate
if ( details.openerURL ) { if ( details.openerURL ) {
for ( var tab of tabWatcher.tabs ) { for ( var tab of tabWatcher.tabs() ) {
var URI = getBrowserForTab(tab).currentURI; var URI = tab.currentURI;
// Probably isn't the best method to identify the source tab // Probably isn't the best method to identify the source tab
if ( URI.spec !== details.openerURL ) { if ( URI.spec !== details.openerURL ) {
continue; continue;
} }
sourceTabId = tabWatcher.tabIdFromTab(tab); sourceTabId = tabWatcher.tabIdFromTarget(tab);
if ( sourceTabId === tabId ) { if ( sourceTabId === tabId ) {
sourceTabId = null; sourceTabId = null;
@ -1493,7 +1546,7 @@ vAPI.net.registerListeners = function() {
var locationChangedListener = function(e) { var locationChangedListener = function(e) {
var details = e.data; var details = e.data;
var browser = e.target; var browser = e.target;
var tabId = tabWatcher.tabIdFromTab(browser); var tabId = tabWatcher.tabIdFromTarget(browser);
// Ignore notifications related to our popup // Ignore notifications related to our popup
if ( details.url.lastIndexOf(vAPI.getURL('popup.html'), 0) === 0 ) { if ( details.url.lastIndexOf(vAPI.getURL('popup.html'), 0) === 0 ) {
@ -1586,7 +1639,7 @@ vAPI.toolbarButton.init = function() {
tb.onClick = function() { tb.onClick = function() {
var win = Services.wm.getMostRecentWindow('navigator:browser'); var win = Services.wm.getMostRecentWindow('navigator:browser');
var curTabId = tabWatcher.tabIdFromTab(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,
index: -1, index: -1,
@ -2021,7 +2074,7 @@ vAPI.contextMenu.create = function(details, callback) {
} }
callback(details, { callback(details, {
id: tabWatcher.tabIdFromTab(gContextMenu.browser), id: tabWatcher.tabIdFromTarget(gContextMenu.browser),
url: gContextMenu.browser.currentURI.asciiSpec url: gContextMenu.browser.currentURI.asciiSpec
}); });
}; };
@ -2053,7 +2106,7 @@ var optionsObserver = {
Services.obs.addObserver(this, 'addon-options-displayed', false); Services.obs.addObserver(this, 'addon-options-displayed', false);
cleanupTasks.push(this.unregister.bind(this)); cleanupTasks.push(this.unregister.bind(this));
var browser = getBrowserForTab(vAPI.tabs.get(null)); var browser = tabWatcher.browserFromTarget(vAPI.tabs.get(null));
if ( browser && browser.currentURI && browser.currentURI.spec === 'about:addons' ) { if ( browser && browser.currentURI && browser.currentURI.spec === 'about:addons' ) {
this.observe(browser.contentDocument, 'addon-enabled', this.addonId); this.observe(browser.contentDocument, 'addon-enabled', this.addonId);
} }
@ -2101,12 +2154,12 @@ vAPI.lastError = function() {
vAPI.onLoadAllCompleted = function() { vAPI.onLoadAllCompleted = function() {
var µb = µBlock; var µb = µBlock;
for ( var tab of tabWatcher.tabs ) { var tabId;
var tabId = tabWatcher.tabIdFromTab(tab); for ( var tab of tabWatcher.tabs() ) {
var browser = getBrowserForTab(tab); tabId = tabWatcher.tabIdFromTarget(tab);
µb.tabContextManager.commit(tabId, browser.currentURI.asciiSpec); µb.tabContextManager.commit(tabId, tab.currentURI.asciiSpec);
µb.bindTabToPageStats(tabId); µb.bindTabToPageStats(tabId);
browser.messageManager.sendAsyncMessage( tab.messageManager.sendAsyncMessage(
location.host + '-load-completed' location.host + '-load-completed'
); );
} }