Bug 1260548: Part 4 - Factor out tab status listener logic into shared module. r?aswan
MozReview-Commit-ID: Ff9gLKdGQHX
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -279,24 +279,20 @@ extensions.registerSchemaAPI("tabs", "ad
if (extension.hasPermission("tabs") || !restricted.includes(prop)) {
nonempty = true;
result[prop] = changeInfo[prop];
}
}
return [nonempty, result];
}
- let fireForBrowser = (browser, changed) => {
+ let fireForTab = (tab, changed) => {
let [needed, changeInfo] = sanitize(extension, changed);
if (needed) {
- let gBrowser = browser.ownerGlobal.gBrowser;
- let tabElem = gBrowser.getTabForBrowser(browser);
-
- let tab = tabManager.convert(tabElem);
- fire(tab.id, changeInfo, tab);
+ fire(tab.id, changeInfo, tab.convert());
}
};
let listener = event => {
let needed = [];
if (event.type == "TabAttrModified") {
let changed = event.detail.changed;
if (changed.includes("image")) {
@@ -312,70 +308,45 @@ extensions.registerSchemaAPI("tabs", "ad
needed.push("title");
}
} else if (event.type == "TabPinned") {
needed.push("pinned");
} else if (event.type == "TabUnpinned") {
needed.push("pinned");
}
- if (needed.length && !extension.hasPermission("tabs")) {
- needed = needed.filter(attr => !restricted.includes(attr));
+ let tab = tabManager.getWrapper(event.originalTarget);
+ let changeInfo = {};
+ for (let prop of needed) {
+ changeInfo[prop] = tab[prop];
}
- if (needed.length) {
- let tab = tabManager.convert(event.originalTarget);
+ fireForTab(tab, changeInfo);
+ };
- let changeInfo = {};
- for (let prop of needed) {
- changeInfo[prop] = tab[prop];
- }
- fire(tab.id, changeInfo, tab);
- }
- };
- let progressListener = {
- onStateChange(browser, webProgress, request, stateFlags, statusCode) {
- if (!webProgress.isTopLevel) {
- return;
+ let statusListener = ({browser, status, url}) => {
+ let {gBrowser} = browser.ownerGlobal;
+ let tabElem = gBrowser.getTabForBrowser(browser);
+ if (tabElem) {
+ let changed = {status};
+ if (url) {
+ changed.url = url;
}
- let status;
- if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
- if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
- status = "loading";
- } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
- status = "complete";
- }
- } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
- statusCode == Cr.NS_BINDING_ABORTED) {
- status = "complete";
- }
-
- fireForBrowser(browser, {status});
- },
-
- onLocationChange(browser, webProgress, request, locationURI, flags) {
- if (!webProgress.isTopLevel) {
- return;
- }
-
- fireForBrowser(browser, {
- status: webProgress.isLoadingDocument ? "loading" : "complete",
- url: locationURI.spec,
- });
- },
+ fireForTab(tabManager.wrapTab(tabElem), changed);
+ }
};
- windowTracker.addListener("progress", progressListener);
+ windowTracker.addListener("status", statusListener);
windowTracker.addListener("TabAttrModified", listener);
windowTracker.addListener("TabPinned", listener);
windowTracker.addListener("TabUnpinned", listener);
return () => {
- windowTracker.removeListener("progress", progressListener);
+ windowTracker.removeListener("status", statusListener);
windowTracker.removeListener("TabAttrModified", listener);
windowTracker.removeListener("TabPinned", listener);
windowTracker.removeListener("TabUnpinned", listener);
};
}).api(),
create(createProperties) {
return new Promise((resolve, reject) => {
--- a/toolkit/components/extensions/ExtensionTabs.jsm
+++ b/toolkit/components/extensions/ExtensionTabs.jsm
@@ -305,27 +305,66 @@ class TabTrackerBase extends EventEmitte
if (!this.initialized) {
this.init();
}
return super.on(...args); // eslint-disable-line mozilla/balanced-listeners
}
}
+class StatusListener {
+ constructor(listener) {
+ this.listener = listener;
+ }
+
+ onStateChange(browser, webProgress, request, stateFlags, statusCode) {
+ if (!webProgress.isTopLevel) {
+ return;
+ }
+
+ let status;
+ if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
+ if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
+ status = "loading";
+ } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ status = "complete";
+ }
+ } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ statusCode == Cr.NS_BINDING_ABORTED) {
+ status = "complete";
+ }
+
+ if (status) {
+ this.listener({browser, status});
+ }
+ }
+
+ onLocationChange(browser, webProgress, request, locationURI, flags) {
+ if (webProgress.isTopLevel) {
+ let status = webProgress.isLoadingDocument ? "loading" : "complete";
+ this.listener({browser, status, url: locationURI.spec});
+ }
+ }
+}
+
class WindowTrackerBase extends EventEmitter {
constructor() {
super();
this.handleWindowOpened = this.handleWindowOpened.bind(this);
this._openListeners = new Set();
this._closeListeners = new Set();
this._listeners = new DefaultMap(() => new Set());
+ this._statusListeners = new DefaultWeakMap(listener => {
+ return new StatusListener(listener);
+ });
+
this._windowIds = new DefaultWeakMap(window => {
window.QueryInterface(Ci.nsIInterfaceRequestor);
return window.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
});
}
isBrowserWindow(window) {
@@ -479,31 +518,41 @@ class WindowTrackerBase extends EventEmi
} else if (type === "domwindowclosed") {
return this.addCloseListener(listener);
}
if (this._listeners.size === 0) {
this.addOpenListener(this.handleWindowOpened);
}
+ if (type === "status") {
+ listener = this._statusListeners.get(listener);
+ type = "progress";
+ }
+
this._listeners.get(type).add(listener);
// Register listener on all existing windows.
for (let window of this.browserWindows()) {
this.addWindowListener(window, type, listener);
}
}
removeListener(eventType, listener) {
if (eventType === "domwindowopened") {
return this.removeOpenListener(listener);
} else if (eventType === "domwindowclosed") {
return this.removeCloseListener(listener);
}
+ if (eventType === "status") {
+ listener = this._statusListeners.get(listener);
+ eventType = "progress";
+ }
+
let listeners = this._listeners.get(eventType);
listeners.delete(listener);
if (listeners.size === 0) {
this._listeners.delete(eventType);
if (this._listeners.size === 0) {
this.removeOpenListener(this.handleWindowOpened);
}