--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -43,54 +43,68 @@ XPCOMUtils.defineLazyGetter(this, "brows
return {
"navbar": CustomizableUI.AREA_NAVBAR,
"menupanel": CustomizableUI.AREA_PANEL,
"tabstrip": CustomizableUI.AREA_TABSTRIP,
"personaltoolbar": CustomizableUI.AREA_BOOKMARKS,
};
});
-// Responsible for the browser_action section of the manifest as well
-// as the associated popup.
-function BrowserAction(options, extension) {
- this.extension = extension;
-
- let widgetId = makeWidgetId(extension.id);
- this.id = `${widgetId}-browser-action`;
- this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
- this.widget = null;
-
- this.pendingPopup = null;
- this.pendingPopupTimeout = null;
-
- this.tabManager = extension.tabManager;
-
- this.defaults = {
- enabled: true,
- title: options.default_title || extension.name,
- badgeText: "",
- badgeBackgroundColor: null,
- icon: IconDetails.normalize({path: options.default_icon}, extension),
- popup: options.default_popup || "",
- area: browserAreas[options.default_area || "navbar"],
- };
-
- this.browserStyle = options.browser_style || false;
- if (options.browser_style === null) {
- this.extension.logger.warn("Please specify whether you want browser_style " +
- "or not in your browser_action options.");
+this.browserAction = class extends ExtensionAPI {
+ static for(extension) {
+ return browserActionMap.get(extension);
}
- this.tabContext = new TabContext(tab => Object.create(this.defaults),
- extension);
+ onManifestEntry(entryName) {
+ let {extension} = this;
+
+ let options = extension.manifest.browser_action;
+
+ let widgetId = makeWidgetId(extension.id);
+ this.id = `${widgetId}-browser-action`;
+ this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
+ this.widget = null;
+
+ this.pendingPopup = null;
+ this.pendingPopupTimeout = null;
+
+ this.tabManager = extension.tabManager;
- EventEmitter.decorate(this);
-}
+ this.defaults = {
+ enabled: true,
+ title: options.default_title || extension.name,
+ badgeText: "",
+ badgeBackgroundColor: null,
+ icon: IconDetails.normalize({path: options.default_icon}, extension),
+ popup: options.default_popup || "",
+ area: browserAreas[options.default_area || "navbar"],
+ };
+
+ this.browserStyle = options.browser_style || false;
+ if (options.browser_style === null) {
+ this.extension.logger.warn("Please specify whether you want browser_style " +
+ "or not in your browser_action options.");
+ }
-BrowserAction.prototype = {
+ this.tabContext = new TabContext(tab => Object.create(this.defaults),
+ extension);
+
+ EventEmitter.decorate(this);
+
+ this.build();
+ browserActionMap.set(extension, this);
+ }
+
+ onShutdown(reason) {
+ browserActionMap.delete(this.extension);
+
+ this.tabContext.shutdown();
+ CustomizableUI.destroyWidget(this.id);
+ }
+
build() {
let widget = CustomizableUI.createWidget({
id: this.id,
viewId: this.viewId,
type: "view",
removable: true,
label: this.defaults.title || this.extension.name,
tooltiptext: this.defaults.title || "",
@@ -154,26 +168,28 @@ BrowserAction.prototype = {
}
},
});
this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
(evt, tab) => { this.updateWindow(tab.ownerGlobal); });
this.widget = widget;
- },
+ }
/**
* Triggers this browser action for the given window, with the same effects as
* if it were clicked by a user.
*
* This has no effect if the browser action is disabled for, or not
* present in, the given window.
+ *
+ * @param {Window} window
*/
- triggerAction: Task.async(function* (window) {
+ async triggerAction(window) {
let popup = ViewPopup.for(this.extension, window);
if (popup) {
popup.closePopup();
return;
}
let widget = this.widget.forWindow(window);
let tab = window.gBrowser.selectedTab;
@@ -182,25 +198,25 @@ BrowserAction.prototype = {
return;
}
// Popups are shown only if a popup URL is defined; otherwise
// a "click" event is dispatched. This is done for compatibility with the
// Google Chrome onClicked extension API.
if (this.getProperty(tab, "popup")) {
if (this.widget.areaType == CustomizableUI.TYPE_MENU_PANEL) {
- yield window.PanelUI.show();
+ await window.PanelUI.show();
}
let event = new window.CustomEvent("command", {bubbles: true, cancelable: true});
widget.node.dispatchEvent(event);
} else {
this.emit("click");
}
- }),
+ }
handleEvent(event) {
let button = event.target;
let window = button.ownerGlobal;
switch (event.type) {
case "mousedown":
if (event.button == 0) {
@@ -273,17 +289,17 @@ BrowserAction.prototype = {
global.actionContextMenu({
extension: this.extension,
onBrowserAction: true,
menu: menu,
});
}
break;
}
- },
+ }
/**
* Returns a potentially pre-loaded popup for the given URL in the given
* window. If a matching pre-load popup already exists, returns that.
* Otherwise, initializes a new one.
*
* If a pre-load popup exists which does not match, it is destroyed before a
* new one is created.
@@ -309,46 +325,46 @@ BrowserAction.prototype = {
return pendingPopup;
}
pendingPopup.destroy();
}
let fixedWidth = this.widget.areaType == CustomizableUI.TYPE_MENU_PANEL;
return new ViewPopup(this.extension, window, popupURL, this.browserStyle, fixedWidth, blockParser);
- },
+ }
/**
* Clears any pending pre-loaded popup and related timeouts.
*/
clearPopup() {
this.clearPopupTimeout();
if (this.pendingPopup) {
if (this.tabToRevokeDuringClearPopup) {
this.tabManager.revokeActiveTabPermission(this.tabToRevokeDuringClearPopup);
this.tabToRevokeDuringClearPopup = null;
}
this.pendingPopup.destroy();
this.pendingPopup = null;
}
- },
+ }
/**
* Clears any pending timeouts to clear stale, pre-loaded popups.
*/
clearPopupTimeout() {
if (this.pendingPopup) {
this.pendingPopup.window.removeEventListener("mouseup", this, true);
}
if (this.pendingPopupTimeout) {
clearTimeout(this.pendingPopupTimeout);
this.pendingPopupTimeout = null;
}
- },
+ }
// Update the toolbar button |node| with the tab context data
// in |tabData|.
updateButton(node, tabData) {
let title = tabData.title || this.extension.name;
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
@@ -396,99 +412,70 @@ BrowserAction.prototype = {
IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
node.setAttribute("style", `
--webextension-menupanel-image: url("${getIcon(32)}");
--webextension-menupanel-image-2x: url("${getIcon(64)}");
--webextension-toolbar-image: url("${IconDetails.escapeUrl(icon)}");
--webextension-toolbar-image-2x: url("${getIcon(baseSize * 2)}");
`);
- },
+ }
// Update the toolbar button for a given window.
updateWindow(window) {
let widget = this.widget.forWindow(window);
if (widget) {
let tab = window.gBrowser.selectedTab;
this.updateButton(widget.node, this.tabContext.get(tab));
}
- },
+ }
// Update the toolbar button when the extension changes the icon,
// title, badge, etc. If it only changes a parameter for a single
// tab, |tab| will be that tab. Otherwise it will be null.
updateOnChange(tab) {
if (tab) {
if (tab.selected) {
this.updateWindow(tab.ownerGlobal);
}
} else {
for (let window of windowTracker.browserWindows()) {
this.updateWindow(window);
}
}
- },
+ }
// tab is allowed to be null.
// prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor".
setProperty(tab, prop, value) {
if (tab == null) {
this.defaults[prop] = value;
} else if (value != null) {
this.tabContext.get(tab)[prop] = value;
} else {
delete this.tabContext.get(tab)[prop];
}
this.updateOnChange(tab);
- },
+ }
// tab is allowed to be null.
// prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor".
getProperty(tab, prop) {
if (tab == null) {
return this.defaults[prop];
}
return this.tabContext.get(tab)[prop];
- },
-
- shutdown() {
- this.tabContext.shutdown();
- CustomizableUI.destroyWidget(this.id);
- },
-};
-
-BrowserAction.for = (extension) => {
- return browserActionMap.get(extension);
-};
-
-global.browserActionFor = BrowserAction.for;
-
-this.browserAction = class extends ExtensionAPI {
- onManifestEntry(entryName) {
- let {extension} = this;
- let {manifest} = extension;
-
- this.browserAction = new BrowserAction(manifest.browser_action, extension);
- this.browserAction.build();
- browserActionMap.set(extension, this.browserAction);
- }
-
- onShutdown(reason) {
- let {extension} = this;
-
- browserActionMap.delete(extension);
- this.browserAction.shutdown();
}
getAPI(context) {
let {extension} = context;
let {tabManager} = extension;
- let {browserAction} = this;
+ let browserAction = this;
function getTab(tabId) {
if (tabId !== null) {
return tabTracker.getTab(tabId);
}
return null;
}
@@ -586,8 +573,11 @@ this.browserAction = class extends Exten
let color = browserAction.getProperty(tab, "badgeBackgroundColor");
return Promise.resolve(color || [0xd9, 0, 0, 255]);
},
},
};
}
};
+
+global.browserActionFor = this.browserAction.for;
+
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/ext-commands.js
@@ -4,63 +4,68 @@
var {
SingletonEventManager,
PlatformInfo,
} = ExtensionUtils;
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-function CommandList(manifest, extension) {
- this.extension = extension;
- this.id = makeWidgetId(extension.id);
- this.windowOpenListener = null;
+this.commands = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
- // Map[{String} commandName -> {Object} commandProperties]
- this.commands = this.loadCommandsFromManifest(manifest);
+ this.id = makeWidgetId(extension.id);
+ this.windowOpenListener = null;
+
+ // Map[{String} commandName -> {Object} commandProperties]
+ this.commands = this.loadCommandsFromManifest(this.extension.manifest);
- // WeakMap[Window -> <xul:keyset>]
- this.keysetsMap = new WeakMap();
+ // WeakMap[Window -> <xul:keyset>]
+ this.keysetsMap = new WeakMap();
- this.register();
- EventEmitter.decorate(this);
-}
+ this.register();
+ EventEmitter.decorate(this);
+ }
-CommandList.prototype = {
+ onShutdown(reason) {
+ this.unregister();
+ }
+
/**
* Registers the commands to all open windows and to any which
* are later created.
*/
register() {
for (let window of windowTracker.browserWindows()) {
this.registerKeysToDocument(window);
}
this.windowOpenListener = (window) => {
if (!this.keysetsMap.has(window)) {
this.registerKeysToDocument(window);
}
};
windowTracker.addOpenListener(this.windowOpenListener);
- },
+ }
/**
* Unregisters the commands from all open windows and stops commands
* from being registered to windows which are later created.
*/
unregister() {
for (let window of windowTracker.browserWindows()) {
if (this.keysetsMap.has(window)) {
this.keysetsMap.get(window).remove();
}
}
windowTracker.removeOpenListener(this.windowOpenListener);
- },
+ }
/**
* Creates a Map from commands for each command in the manifest.commands object.
*
* @param {Object} manifest The manifest JSON object.
* @returns {Map<string, object>}
*/
loadCommandsFromManifest(manifest) {
@@ -73,17 +78,17 @@ CommandList.prototype = {
let shortcut = suggested_key[os] || suggested_key.default;
shortcut = shortcut ? shortcut.replace(/\s+/g, "") : null;
commands.set(name, {
description: command.description,
shortcut,
});
}
return commands;
- },
+ }
/**
* Registers the commands to a document.
* @param {ChromeWindow} window The XUL window to insert the Keyset.
*/
registerKeysToDocument(window) {
let doc = window.document;
let keyset = doc.createElementNS(XUL_NS, "keyset");
@@ -91,17 +96,17 @@ CommandList.prototype = {
this.commands.forEach((command, name) => {
if (command.shortcut) {
let keyElement = this.buildKey(doc, name, command.shortcut);
keyset.appendChild(keyElement);
}
});
doc.documentElement.appendChild(keyset);
this.keysetsMap.set(window, keyset);
- },
+ }
/**
* Builds a XUL Key element and attaches an onCommand listener which
* emits a command event with the provided name when fired.
*
* @param {Document} doc The XUL document.
* @param {string} name The name of the command.
* @param {string} shortcut The shortcut provided in the manifest.
@@ -136,17 +141,17 @@ CommandList.prototype = {
if (action) {
let win = event.target.ownerGlobal;
action.triggerAction(win);
}
});
/* eslint-enable mozilla/balanced-listeners */
return keyElement;
- },
+ }
/**
* Builds a XUL Key element from the provided shortcut.
*
* @param {Document} doc The XUL document.
* @param {string} shortcut The shortcut provided in the manifest.
*
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
@@ -166,34 +171,34 @@ CommandList.prototype = {
if (/^[A-Z0-9]$/.test(chromeKey)) {
// We use the key attribute for all single digits and characters.
keyElement.setAttribute("key", chromeKey);
} else {
keyElement.setAttribute("keycode", this.getKeycodeAttribute(chromeKey));
}
return keyElement;
- },
+ }
/**
* Determines the corresponding XUL keycode from the given chrome key.
*
* For example:
*
* input | output
* ---------------------------------------
* "PageUP" | "VK_PAGE_UP"
* "Delete" | "VK_DELETE"
*
* @param {string} chromeKey The chrome key (e.g. "PageUp", "Space", ...)
* @returns {string} The constructed value for the Key's 'keycode' attribute.
*/
getKeycodeAttribute(chromeKey) {
return `VK${chromeKey.replace(/([A-Z])/g, "_$&").toUpperCase()}`;
- },
+ }
/**
* Determines the corresponding XUL modifiers from the chrome modifiers.
*
* For example:
*
* input | output
* ---------------------------------------
@@ -209,49 +214,36 @@ CommandList.prototype = {
"Command": "accel",
"Ctrl": "accel",
"MacCtrl": "control",
"Shift": "shift",
};
return Array.from(chromeModifiers, modifier => {
return modifiersMap[modifier];
}).join(" ");
- },
-};
-
-this.commands = class extends ExtensionAPI {
- onManifestEntry(entryName) {
- let {extension} = this;
- let {manifest} = extension;
-
- this.commandList = new CommandList(manifest, extension);
- }
-
- onShutdown(reason) {
- this.commandList.unregister();
}
getAPI(context) {
return {
commands: {
getAll: () => {
- let commands = this.commandList.commands;
+ let commands = this.commands;
return Promise.resolve(Array.from(commands, ([name, command]) => {
return ({
name,
description: command.description,
shortcut: command.shortcut,
});
}));
},
onCommand: new SingletonEventManager(context, "commands.onCommand", fire => {
let listener = (eventName, commandName) => {
fire.async(commandName);
};
- this.commandList.on("command", listener);
+ this.on("command", listener);
return () => {
- this.commandList.off("command", listener);
+ this.off("command", listener);
};
}).api(),
},
};
}
};
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -10,71 +10,90 @@ Cu.import("resource://gre/modules/Task.j
var {
SingletonEventManager,
IconDetails,
} = ExtensionUtils;
// WeakMap[Extension -> PageAction]
let pageActionMap = new WeakMap();
-// Handles URL bar icons, including the |page_action| manifest entry
-// and associated API.
-function PageAction(options, extension) {
- this.extension = extension;
- this.id = makeWidgetId(extension.id) + "-page-action";
-
- this.tabManager = extension.tabManager;
-
- this.defaults = {
- show: false,
- title: options.default_title || extension.name,
- icon: IconDetails.normalize({path: options.default_icon}, extension),
- popup: options.default_popup || "",
- };
-
- this.browserStyle = options.browser_style || false;
- if (options.browser_style === null) {
- this.extension.logger.warn("Please specify whether you want browser_style " +
- "or not in your page_action options.");
+this.pageAction = class extends ExtensionAPI {
+ static for(extension) {
+ return pageActionMap.get(extension);
}
- this.tabContext = new TabContext(tab => Object.create(this.defaults),
- extension);
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let options = extension.manifest.page_action;
+
+ this.id = makeWidgetId(extension.id) + "-page-action";
+
+ this.tabManager = extension.tabManager;
- this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
+ this.defaults = {
+ show: false,
+ title: options.default_title || extension.name,
+ icon: IconDetails.normalize({path: options.default_icon}, extension),
+ popup: options.default_popup || "",
+ };
+
+ this.browserStyle = options.browser_style || false;
+ if (options.browser_style === null) {
+ this.extension.logger.warn("Please specify whether you want browser_style " +
+ "or not in your page_action options.");
+ }
+
+ this.tabContext = new TabContext(tab => Object.create(this.defaults),
+ extension);
- // WeakMap[ChromeWindow -> <xul:image>]
- this.buttons = new WeakMap();
+ this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
+
+ // WeakMap[ChromeWindow -> <xul:image>]
+ this.buttons = new WeakMap();
+
+ EventEmitter.decorate(this);
+
+ pageActionMap.set(extension, this);
+ }
- EventEmitter.decorate(this);
-}
+ onShutdown(reason) {
+ pageActionMap.delete(this.extension);
+
+ this.tabContext.shutdown();
-PageAction.prototype = {
+ for (let window of windowTracker.browserWindows()) {
+ if (this.buttons.has(window)) {
+ this.buttons.get(window).remove();
+ window.removeEventListener("popupshowing", this);
+ }
+ }
+ }
+
// Returns the value of the property |prop| for the given tab, where
// |prop| is one of "show", "title", "icon", "popup".
getProperty(tab, prop) {
return this.tabContext.get(tab)[prop];
- },
+ }
// Sets the value of the property |prop| for the given tab to the
// given value, symmetrically to |getProperty|.
//
// If |tab| is currently selected, updates the page action button to
// reflect the new value.
setProperty(tab, prop, value) {
if (value != null) {
this.tabContext.get(tab)[prop] = value;
} else {
delete this.tabContext.get(tab)[prop];
}
if (tab.selected) {
this.updateButton(tab.ownerGlobal);
}
- },
+ }
// Updates the page action button in the given window to reflect the
// properties of the currently selected tab:
//
// Updates "tooltiptext" and "aria-label" to match "title" property.
// Updates "image" to match the "icon" property.
// Shows or hides the icon, based on the "show" property.
updateButton(window) {
@@ -106,60 +125,60 @@ PageAction.prototype = {
--webextension-urlbar-image: url("${getIcon(16)}");
--webextension-urlbar-image-2x: url("${getIcon(32)}");
`);
button.classList.add("webextension-page-action");
}
button.hidden = !tabData.show;
- },
+ }
// Create an |image| node and add it to the |urlbar-icons|
// container in the given window.
addButton(window) {
let document = window.document;
let button = document.createElement("image");
button.id = this.id;
button.setAttribute("class", "urlbar-icon");
button.addEventListener("click", this); // eslint-disable-line mozilla/balanced-listeners
document.addEventListener("popupshowing", this);
document.getElementById("urlbar-icons").appendChild(button);
return button;
- },
+ }
// Returns the page action button for the given window, creating it if
// it doesn't already exist.
getButton(window) {
if (!this.buttons.has(window)) {
let button = this.addButton(window);
this.buttons.set(window, button);
}
return this.buttons.get(window);
- },
+ }
/**
* Triggers this page action for the given window, with the same effects as
* if it were clicked by a user.
*
* This has no effect if the page action is hidden for the selected tab.
*
* @param {Window} window
*/
triggerAction(window) {
let pageAction = pageActionMap.get(this.extension);
if (pageAction.getProperty(window.gBrowser.selectedTab, "show")) {
pageAction.handleClick(window);
}
- },
+ }
handleEvent(event) {
const window = event.target.ownerGlobal;
switch (event.type) {
case "click":
if (event.button === 0) {
this.handleClick(window);
@@ -174,17 +193,17 @@ PageAction.prototype = {
global.actionContextMenu({
extension: this.extension,
onPageAction: true,
menu: menu,
});
}
break;
}
- },
+ }
// Handles a click event on the page action button for the given
// window.
// If the page action has a |popup| property, a panel is opened to
// that URL. Otherwise, a "click" event is emitted, and dispatched to
// the any click listeners in the add-on.
handleClick(window) {
let tab = window.gBrowser.selectedTab;
@@ -197,62 +216,30 @@ PageAction.prototype = {
// If it has no popup URL defined, we dispatch a click event, but do not
// open a popup.
if (popupURL) {
new PanelPopup(this.extension, this.getButton(window), popupURL,
this.browserStyle);
} else {
this.emit("click", tab);
}
- },
+ }
handleLocationChange(eventType, tab, fromBrowse) {
if (fromBrowse) {
this.tabContext.clear(tab);
}
this.updateButton(tab.ownerGlobal);
- },
-
- shutdown() {
- this.tabContext.shutdown();
-
- for (let window of windowTracker.browserWindows()) {
- if (this.buttons.has(window)) {
- this.buttons.get(window).remove();
- window.removeEventListener("popupshowing", this);
- }
- }
- },
-};
-
-PageAction.for = extension => {
- return pageActionMap.get(extension);
-};
-
-global.pageActionFor = PageAction.for;
-
-this.pageAction = class extends ExtensionAPI {
- onManifestEntry(entryName) {
- let {extension} = this;
- let {manifest} = extension;
-
- this.pageAction = new PageAction(manifest.page_action, extension);
- pageActionMap.set(extension, this.pageAction);
- }
-
- onShutdown(reason) {
- pageActionMap.delete(this.extension);
- this.pageAction.shutdown();
}
getAPI(context) {
let {extension} = context;
const {tabManager} = extension;
- const {pageAction} = this;
+ const pageAction = this;
return {
pageAction: {
onClicked: new SingletonEventManager(context, "pageAction.onClicked", fire => {
let listener = (evt, tab) => {
fire.async(tabManager.convert(tab));
};
@@ -310,8 +297,10 @@ this.pageAction = class extends Extensio
let popup = pageAction.getProperty(tab, "popup");
return Promise.resolve(popup);
},
},
};
}
};
+
+global.pageActionFor = this.pageAction.for;
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/ext-sidebarAction.js
@@ -22,19 +22,27 @@ var XUL_NS = "http://www.mozilla.org/key
let sidebarActionMap = new WeakMap();
const sidebarURL = "chrome://browser/content/webext-panels.xul";
/**
* Responsible for the sidebar_action section of the manifest as well
* as the associated sidebar browser.
*/
-class SidebarAction {
- constructor(options, extension) {
- this.extension = extension;
+this.sidebarAction = class extends ExtensionAPI {
+ static for(extension) {
+ return sidebarActionMap.get(extension);
+ }
+
+ onManifestEntry(entryName) {
+ let {extension} = this;
+
+ extension.once("ready", this.onReady.bind(this));
+
+ let options = extension.manifest.sidebar_action;
// Add the extension to the sidebar menu. The sidebar widget will copy
// from that when it is viewed, so we shouldn't need to update that.
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-sidebar-action`;
this.menuId = `menu_${this.id}`;
this.defaults = {
@@ -47,16 +55,50 @@ class SidebarAction {
this.tabContext = new TabContext(tab => Object.create(this.defaults),
extension);
// We need to ensure our elements are available before session restore.
this.windowOpenListener = (window) => {
this.createMenuItem(window, this.defaults);
};
windowTracker.addOpenListener(this.windowOpenListener);
+
+ sidebarActionMap.set(extension, this);
+ }
+
+ onReady() {
+ this.build();
+ }
+
+ onShutdown(reason) {
+ sidebarActionMap.delete(this.this);
+
+ this.tabContext.shutdown();
+
+ // Don't remove everything on app shutdown so session restore can handle
+ // restoring open sidebars.
+ if (reason === "APP_SHUTDOWN") {
+ return;
+ }
+
+ for (let window of windowTracker.browserWindows()) {
+ let {document, SidebarUI} = window;
+ if (SidebarUI.currentID === this.id) {
+ SidebarUI.hide();
+ }
+ let menu = document.getElementById(this.menuId);
+ if (menu) {
+ menu.remove();
+ }
+ let broadcaster = document.getElementById(this.id);
+ if (broadcaster) {
+ broadcaster.remove();
+ }
+ }
+ windowTracker.removeOpenListener(this.windowOpenListener);
}
build() {
this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
(evt, tab) => { this.updateWindow(tab.ownerGlobal); });
let install = this.extension.startupReason === "ADDON_INSTALL";
for (let window of windowTracker.browserWindows()) {
@@ -233,85 +275,32 @@ class SidebarAction {
*/
getProperty(nativeTab, prop) {
if (nativeTab === null) {
return this.defaults[prop];
}
return this.tabContext.get(nativeTab)[prop];
}
- shutdown() {
- this.tabContext.shutdown();
- for (let window of windowTracker.browserWindows()) {
- let {document, SidebarUI} = window;
- if (SidebarUI.currentID === this.id) {
- SidebarUI.hide();
- }
- let menu = document.getElementById(this.menuId);
- if (menu) {
- menu.remove();
- }
- let broadcaster = document.getElementById(this.id);
- if (broadcaster) {
- broadcaster.remove();
- }
- }
- windowTracker.removeOpenListener(this.windowOpenListener);
- }
-
/**
* Triggers this sidebar action for the given window, with the same effects as
* if it were toggled via menu or toolbarbutton by a user.
*
* @param {ChromeWindow} window
*/
triggerAction(window) {
let {SidebarUI} = window;
if (SidebarUI) {
SidebarUI.toggle(this.id);
}
}
-}
-
-SidebarAction.for = (extension) => {
- return sidebarActionMap.get(extension);
-};
-
-global.sidebarActionFor = SidebarAction.for;
-
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("ready", (type, extension) => {
- // We build sidebars during ready to ensure the background scripts are ready.
- if (sidebarActionMap.has(extension)) {
- sidebarActionMap.get(extension).build();
- }
-});
-/* eslint-enable mozilla/balanced-listeners */
-
-this.sidebarAction = class extends ExtensionAPI {
- onManifestEntry(entryName) {
- let {extension} = this;
- let {manifest} = extension;
-
- this.sidebarAction = new SidebarAction(manifest.sidebar_action, extension);
- sidebarActionMap.set(extension, this.sidebarAction);
- }
-
- onShutdown(reason) {
- // Don't remove everything on app shutdown so session restore can handle
- // restoring open sidebars.
- if (reason !== "APP_SHUTDOWN") {
- this.sidebarAction.shutdown();
- }
- sidebarActionMap.delete(this.extension);
- }
getAPI(context) {
let {extension} = context;
- const {sidebarAction} = this;
+ const sidebarAction = this;
function getTab(tabId) {
if (tabId !== null) {
return tabTracker.getTab(tabId);
}
return null;
}
@@ -363,8 +352,10 @@ this.sidebarAction = class extends Exten
let panel = sidebarAction.getProperty(nativeTab, "panel");
return Promise.resolve(panel);
},
},
};
}
};
+
+global.sidebarActionFor = this.sidebarAction.for;
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -924,16 +924,17 @@ this.Extension = class extends Extension
// and it is used to run code that needs to be executed before
// any of the "startup" listeners.
this.emit("startup", this);
Management.emit("startup", this);
return this.runManifest(this.manifest);
}).then(() => {
Management.emit("ready", this);
+ this.emit("ready");
}).catch(e => {
dump(`Extension error: ${e.message} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`);
Cu.reportError(e);
if (started) {
ExtensionManagement.shutdownExtension(this.uuid);
}