mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-26 11:45:37 +00:00
e71d1d5a82
As we add more behaviors to EventManager, the signature of the constructor is going to get really clumsy. Head that off by converting it to take a general parameters object. This introduces a compatibility problem for existing webextension experiments, put in a backward-compatibility shim for now. MozReview-Commit-ID: 72QDfiwRm5j --HG-- extra : rebase_source : 31c3fd561f373a5d75c4336de830aa5a2abfe797
274 lines
7.8 KiB
JavaScript
274 lines
7.8 KiB
JavaScript
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set sts=2 sw=2 et tw=80: */
|
|
|
|
"use strict";
|
|
|
|
// The ext-* files are imported into the same scopes.
|
|
/* import-globals-from ext-utils.js */
|
|
|
|
ChromeUtils.defineModuleGetter(this, "Services",
|
|
"resource://gre/modules/Services.jsm");
|
|
|
|
// Import the android PageActions module.
|
|
ChromeUtils.defineModuleGetter(this, "PageActions",
|
|
"resource://gre/modules/PageActions.jsm");
|
|
|
|
ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
|
|
|
|
var {
|
|
IconDetails,
|
|
} = ExtensionParent;
|
|
|
|
// WeakMap[Extension -> PageAction]
|
|
let pageActionMap = new WeakMap();
|
|
|
|
class PageAction extends EventEmitter {
|
|
constructor(manifest, extension) {
|
|
super();
|
|
|
|
this.id = null;
|
|
|
|
this.extension = extension;
|
|
|
|
this.defaults = {
|
|
icons: IconDetails.normalize({path: manifest.default_icon}, extension),
|
|
popup: manifest.default_popup,
|
|
};
|
|
|
|
this.tabManager = extension.tabManager;
|
|
this.context = null;
|
|
|
|
this.tabContext = new TabContext(() => Object.create(this.defaults), extension);
|
|
|
|
this.options = {
|
|
title: manifest.default_title || extension.name,
|
|
id: `{${extension.uuid}}`,
|
|
clickCallback: () => {
|
|
let tab = tabTracker.activeTab;
|
|
|
|
this.tabManager.addActiveTabPermission(tab);
|
|
|
|
let popup = this.tabContext.get(tab.id).popup || this.defaults.popup;
|
|
if (popup) {
|
|
tabTracker.openExtensionPopupTab(popup);
|
|
} else {
|
|
this.emit("click", tab);
|
|
}
|
|
},
|
|
};
|
|
|
|
this.shouldShow = false;
|
|
|
|
this.tabContext.on("tab-selected", // eslint-disable-line mozilla/balanced-listeners
|
|
(evt, tabId) => { this.onTabSelected(tabId); });
|
|
this.tabContext.on("tab-closed", // eslint-disable-line mozilla/balanced-listeners
|
|
(evt, tabId) => { this.onTabClosed(tabId); });
|
|
}
|
|
|
|
/**
|
|
* Updates the page action whenever a tab is selected.
|
|
* @param {Integer} tabId The ID of the selected tab.
|
|
*/
|
|
onTabSelected(tabId) {
|
|
if (this.options.icon) {
|
|
this.hide();
|
|
let shouldShow = this.tabContext.get(tabId).show;
|
|
if (shouldShow) {
|
|
this.show();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the tab from the property map now that it is closed.
|
|
* @param {Integer} tabId The ID of the closed tab.
|
|
*/
|
|
onTabClosed(tabId) {
|
|
this.tabContext.clear(tabId);
|
|
}
|
|
|
|
/**
|
|
* Sets the context for the page action.
|
|
* @param {Object} context The extension context.
|
|
*/
|
|
setContext(context) {
|
|
this.context = context;
|
|
}
|
|
|
|
/**
|
|
* Sets a property for the page action for the specified tab. If the property is set
|
|
* for the active tab, the page action is also updated.
|
|
*
|
|
* @param {Object} tab The tab to set.
|
|
* @param {string} prop The property to update - either "show" or "popup".
|
|
* @param {string} value The value to set the property to. If falsy, the property is deleted.
|
|
* @returns {Object} Promise which resolves when the property is set and the page action is
|
|
* shown if necessary.
|
|
*/
|
|
setProperty(tab, prop, value) {
|
|
if (tab == null) {
|
|
throw new Error("Tab must not be null");
|
|
}
|
|
|
|
let properties = this.tabContext.get(tab.id);
|
|
if (value) {
|
|
properties[prop] = value;
|
|
} else {
|
|
delete properties[prop];
|
|
}
|
|
|
|
if (prop === "show" && tab.id == tabTracker.activeTab.id) {
|
|
if (this.id && !value) {
|
|
return this.hide();
|
|
} else if (!this.id && value) {
|
|
return this.show();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retreives a property of the page action for the specified tab.
|
|
*
|
|
* @param {Object} tab The tab to retrieve the property from. If null, the default value is returned.
|
|
* @param {string} prop The property to retreive - currently only "popup" is supported.
|
|
* @returns {string} the value stored for the specified property. If the value for the tab is undefined, then the
|
|
* default value is returned.
|
|
*/
|
|
getProperty(tab, prop) {
|
|
if (tab == null) {
|
|
return this.defaults[prop];
|
|
}
|
|
|
|
return this.tabContext.get(tab.id)[prop] || this.defaults[prop];
|
|
}
|
|
|
|
/**
|
|
* Show the page action for the active tab.
|
|
* @returns {Promise} resolves when the page action is shown.
|
|
*/
|
|
show() {
|
|
// The PageAction icon has been created or it is being converted.
|
|
if (this.id || this.shouldShow) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (this.options.icon) {
|
|
this.id = PageActions.add(this.options);
|
|
return Promise.resolve();
|
|
}
|
|
|
|
this.shouldShow = true;
|
|
|
|
// Bug 1372782: Remove dependency on contentWindow from this file. It should
|
|
// be put in a separate file called ext-c-pageAction.js.
|
|
// Note: Fennec is not going to be multi-process for the foreseaable future,
|
|
// so this layering violation has no immediate impact. However, it is should
|
|
// be done at some point.
|
|
let {contentWindow} = this.context.xulBrowser;
|
|
|
|
// Bug 1372783: Why is this contentWindow.devicePixelRatio, while
|
|
// convertImageURLToDataURL uses browserWindow.devicePixelRatio?
|
|
let {icon} = IconDetails.getPreferredIcon(this.defaults.icons, this.extension,
|
|
18 * contentWindow.devicePixelRatio);
|
|
|
|
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
|
return IconDetails.convertImageURLToDataURL(icon, contentWindow, browserWindow).then(dataURI => {
|
|
if (this.shouldShow) {
|
|
this.options.icon = dataURI;
|
|
this.id = PageActions.add(this.options);
|
|
}
|
|
}).catch(() => {
|
|
// The "icon conversion" promise has been rejected, set `this.shouldShow` to `false`
|
|
// so that we will try again on the next `pageAction.show` call.
|
|
this.shouldShow = false;
|
|
|
|
return Promise.reject({
|
|
message: "Failed to load PageAction icon",
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hides the page action for the active tab.
|
|
*/
|
|
hide() {
|
|
this.shouldShow = false;
|
|
|
|
if (this.id) {
|
|
PageActions.remove(this.id);
|
|
this.id = null;
|
|
}
|
|
}
|
|
|
|
shutdown() {
|
|
this.tabContext.shutdown();
|
|
this.hide();
|
|
}
|
|
}
|
|
|
|
this.pageAction = class extends ExtensionAPI {
|
|
onManifestEntry(entryName) {
|
|
let {extension} = this;
|
|
let {manifest} = extension;
|
|
|
|
let pageAction = new PageAction(manifest.page_action, extension);
|
|
pageActionMap.set(extension, pageAction);
|
|
}
|
|
|
|
onShutdown(reason) {
|
|
let {extension} = this;
|
|
|
|
if (pageActionMap.has(extension)) {
|
|
pageActionMap.get(extension).shutdown();
|
|
pageActionMap.delete(extension);
|
|
}
|
|
}
|
|
|
|
getAPI(context) {
|
|
const {extension} = context;
|
|
const {tabManager} = extension;
|
|
|
|
pageActionMap.get(extension).setContext(context);
|
|
|
|
return {
|
|
pageAction: {
|
|
onClicked: new EventManager({
|
|
context,
|
|
name: "pageAction.onClicked",
|
|
register: fire => {
|
|
let listener = (event, tab) => {
|
|
fire.async(tabManager.convert(tab));
|
|
};
|
|
pageActionMap.get(extension).on("click", listener);
|
|
return () => {
|
|
pageActionMap.get(extension).off("click", listener);
|
|
};
|
|
},
|
|
}).api(),
|
|
|
|
show(tabId) {
|
|
let tab = tabTracker.getTab(tabId);
|
|
return pageActionMap.get(extension).setProperty(tab, "show", true);
|
|
},
|
|
|
|
hide(tabId) {
|
|
let tab = tabTracker.getTab(tabId);
|
|
pageActionMap.get(extension).setProperty(tab, "show", false);
|
|
},
|
|
|
|
setPopup(details) {
|
|
let tab = tabTracker.getTab(details.tabId);
|
|
let url = details.popup && context.uri.resolve(details.popup);
|
|
pageActionMap.get(extension).setProperty(tab, "popup", url);
|
|
},
|
|
|
|
getPopup(details) {
|
|
let tab = tabTracker.getTab(details.tabId);
|
|
let popup = pageActionMap.get(extension).getProperty(tab, "popup");
|
|
return Promise.resolve(popup);
|
|
},
|
|
},
|
|
};
|
|
}
|
|
};
|