Bug 1350522: Part 7 - Merge pageAction/browserAction/sidebarAction/commands helper classes into API instances. r=aswan
MozReview-Commit-ID: 6aRQ1uybAmg
This commit is contained in:
@@ -48,10 +48,15 @@ XPCOMUtils.defineLazyGetter(this, "browserAreas", () => {
|
||||
};
|
||||
});
|
||||
|
||||
// Responsible for the browser_action section of the manifest as well
|
||||
// as the associated popup.
|
||||
function BrowserAction(options, extension) {
|
||||
this.extension = extension;
|
||||
this.browserAction = class extends ExtensionAPI {
|
||||
static for(extension) {
|
||||
return browserActionMap.get(extension);
|
||||
}
|
||||
|
||||
onManifestEntry(entryName) {
|
||||
let {extension} = this;
|
||||
|
||||
let options = extension.manifest.browser_action;
|
||||
|
||||
let widgetId = makeWidgetId(extension.id);
|
||||
this.id = `${widgetId}-browser-action`;
|
||||
@@ -83,9 +88,20 @@ function BrowserAction(options, extension) {
|
||||
extension);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
BrowserAction.prototype = {
|
||||
this.build();
|
||||
browserActionMap.set(extension, this);
|
||||
}
|
||||
|
||||
onShutdown(reason) {
|
||||
browserActionMap.delete(this.extension);
|
||||
|
||||
this.tabContext.shutdown();
|
||||
CustomizableUI.destroyWidget(this.id);
|
||||
|
||||
this.clearPopup();
|
||||
}
|
||||
|
||||
build() {
|
||||
let widget = CustomizableUI.createWidget({
|
||||
id: this.id,
|
||||
@@ -160,7 +176,7 @@ BrowserAction.prototype = {
|
||||
(evt, tab) => { this.updateWindow(tab.ownerGlobal); });
|
||||
|
||||
this.widget = widget;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers this browser action for the given window, with the same effects as
|
||||
@@ -168,8 +184,10 @@ BrowserAction.prototype = {
|
||||
*
|
||||
* 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();
|
||||
@@ -188,7 +206,7 @@ BrowserAction.prototype = {
|
||||
// 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});
|
||||
@@ -196,7 +214,7 @@ BrowserAction.prototype = {
|
||||
} else {
|
||||
this.emit("click");
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
let button = event.target;
|
||||
@@ -279,7 +297,7 @@ BrowserAction.prototype = {
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a potentially pre-loaded popup for the given URL in the given
|
||||
@@ -315,7 +333,7 @@ BrowserAction.prototype = {
|
||||
|
||||
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.
|
||||
@@ -330,7 +348,7 @@ BrowserAction.prototype = {
|
||||
this.pendingPopup = null;
|
||||
}
|
||||
this.tabToRevokeDuringClearPopup = null;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any pending timeouts to clear stale, pre-loaded popups.
|
||||
@@ -344,7 +362,7 @@ BrowserAction.prototype = {
|
||||
clearTimeout(this.pendingPopupTimeout);
|
||||
this.pendingPopupTimeout = null;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Update the toolbar button |node| with the tab context data
|
||||
// in |tabData|.
|
||||
@@ -402,7 +420,7 @@ BrowserAction.prototype = {
|
||||
--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) {
|
||||
@@ -411,7 +429,7 @@ BrowserAction.prototype = {
|
||||
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
|
||||
@@ -426,7 +444,7 @@ BrowserAction.prototype = {
|
||||
this.updateWindow(window);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// tab is allowed to be null.
|
||||
// prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor".
|
||||
@@ -440,7 +458,7 @@ BrowserAction.prototype = {
|
||||
}
|
||||
|
||||
this.updateOnChange(tab);
|
||||
},
|
||||
}
|
||||
|
||||
// tab is allowed to be null.
|
||||
// prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor".
|
||||
@@ -449,44 +467,13 @@ BrowserAction.prototype = {
|
||||
return this.defaults[prop];
|
||||
}
|
||||
return this.tabContext.get(tab)[prop];
|
||||
},
|
||||
|
||||
shutdown() {
|
||||
this.tabContext.shutdown();
|
||||
CustomizableUI.destroyWidget(this.id);
|
||||
|
||||
this.clearPopup();
|
||||
},
|
||||
};
|
||||
|
||||
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) {
|
||||
@@ -594,3 +581,6 @@ this.browserAction = class extends ExtensionAPI {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
global.browserActionFor = this.browserAction.for;
|
||||
|
||||
|
||||
@@ -9,22 +9,27 @@ var {
|
||||
|
||||
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
function CommandList(manifest, extension) {
|
||||
this.extension = extension;
|
||||
this.commands = class extends ExtensionAPI {
|
||||
onManifestEntry(entryName) {
|
||||
let {extension} = this;
|
||||
|
||||
this.id = makeWidgetId(extension.id);
|
||||
this.windowOpenListener = null;
|
||||
|
||||
// Map[{String} commandName -> {Object} commandProperties]
|
||||
this.commands = this.loadCommandsFromManifest(manifest);
|
||||
this.commands = this.loadCommandsFromManifest(this.extension.manifest);
|
||||
|
||||
// WeakMap[Window -> <xul:keyset>]
|
||||
this.keysetsMap = new WeakMap();
|
||||
|
||||
this.register();
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
}
|
||||
|
||||
onShutdown(reason) {
|
||||
this.unregister();
|
||||
}
|
||||
|
||||
CommandList.prototype = {
|
||||
/**
|
||||
* Registers the commands to all open windows and to any which
|
||||
* are later created.
|
||||
@@ -41,7 +46,7 @@ CommandList.prototype = {
|
||||
};
|
||||
|
||||
windowTracker.addOpenListener(this.windowOpenListener);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the commands from all open windows and stops commands
|
||||
@@ -55,7 +60,7 @@ CommandList.prototype = {
|
||||
}
|
||||
|
||||
windowTracker.removeOpenListener(this.windowOpenListener);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from commands for each command in the manifest.commands object.
|
||||
@@ -78,7 +83,7 @@ CommandList.prototype = {
|
||||
});
|
||||
}
|
||||
return commands;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the commands to a document.
|
||||
@@ -96,7 +101,7 @@ CommandList.prototype = {
|
||||
});
|
||||
doc.documentElement.appendChild(keyset);
|
||||
this.keysetsMap.set(window, keyset);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a XUL Key element and attaches an onCommand listener which
|
||||
@@ -141,7 +146,7 @@ CommandList.prototype = {
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
return keyElement;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a XUL Key element from the provided shortcut.
|
||||
@@ -171,7 +176,7 @@ CommandList.prototype = {
|
||||
}
|
||||
|
||||
return keyElement;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the corresponding XUL keycode from the given chrome key.
|
||||
@@ -188,7 +193,7 @@ CommandList.prototype = {
|
||||
*/
|
||||
getKeycodeAttribute(chromeKey) {
|
||||
return `VK${chromeKey.replace(/([A-Z])/g, "_$&").toUpperCase()}`;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the corresponding XUL modifiers from the chrome modifiers.
|
||||
@@ -214,26 +219,13 @@ CommandList.prototype = {
|
||||
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,
|
||||
@@ -246,9 +238,9 @@ this.commands = class extends ExtensionAPI {
|
||||
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(),
|
||||
},
|
||||
|
||||
@@ -15,10 +15,15 @@ var {
|
||||
// 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.pageAction = class extends ExtensionAPI {
|
||||
static for(extension) {
|
||||
return pageActionMap.get(extension);
|
||||
}
|
||||
|
||||
onManifestEntry(entryName) {
|
||||
let {extension} = this;
|
||||
let options = extension.manifest.page_action;
|
||||
|
||||
this.id = makeWidgetId(extension.id) + "-page-action";
|
||||
|
||||
this.tabManager = extension.tabManager;
|
||||
@@ -45,14 +50,28 @@ function PageAction(options, extension) {
|
||||
this.buttons = new WeakMap();
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
PageAction.prototype = {
|
||||
pageActionMap.set(extension, this);
|
||||
}
|
||||
|
||||
onShutdown(reason) {
|
||||
pageActionMap.delete(this.extension);
|
||||
|
||||
this.tabContext.shutdown();
|
||||
|
||||
for (let window of windowTracker.browserWindows()) {
|
||||
if (this.buttons.has(window)) {
|
||||
this.buttons.get(window).remove();
|
||||
window.document.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|.
|
||||
@@ -69,7 +88,7 @@ PageAction.prototype = {
|
||||
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:
|
||||
@@ -111,7 +130,7 @@ PageAction.prototype = {
|
||||
}
|
||||
|
||||
button.hidden = !tabData.show;
|
||||
},
|
||||
}
|
||||
|
||||
// Create an |image| node and add it to the |urlbar-icons|
|
||||
// container in the given window.
|
||||
@@ -128,7 +147,7 @@ PageAction.prototype = {
|
||||
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.
|
||||
@@ -139,7 +158,7 @@ PageAction.prototype = {
|
||||
}
|
||||
|
||||
return this.buttons.get(window);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers this page action for the given window, with the same effects as
|
||||
@@ -154,7 +173,7 @@ PageAction.prototype = {
|
||||
if (pageAction.getProperty(window.gBrowser.selectedTab, "show")) {
|
||||
pageAction.handleClick(window);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
const window = event.target.ownerGlobal;
|
||||
@@ -179,7 +198,7 @@ PageAction.prototype = {
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Handles a click event on the page action button for the given
|
||||
// window.
|
||||
@@ -202,52 +221,20 @@ PageAction.prototype = {
|
||||
} 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.document.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: {
|
||||
@@ -315,3 +302,5 @@ this.pageAction = class extends ExtensionAPI {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
global.pageActionFor = this.pageAction.for;
|
||||
|
||||
@@ -27,9 +27,17 @@ 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.
|
||||
@@ -52,6 +60,40 @@ class SidebarAction {
|
||||
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() {
|
||||
@@ -238,25 +280,6 @@ class SidebarAction {
|
||||
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.
|
||||
@@ -269,44 +292,10 @@ class SidebarAction {
|
||||
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) {
|
||||
@@ -368,3 +357,5 @@ this.sidebarAction = class extends ExtensionAPI {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
global.sidebarActionFor = this.sidebarAction.for;
|
||||
|
||||
@@ -966,6 +966,7 @@ this.Extension = class extends ExtensionData {
|
||||
await this.runManifest(this.manifest);
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user