Bug 1351111 - Add support for setTitle and getTitle r?mixedpuppy
MozReview-Commit-ID: 9B0RaOJjTxc
--- a/mobile/android/components/extensions/ext-browserAction.js
+++ b/mobile/android/components/extensions/ext-browserAction.js
@@ -10,33 +10,130 @@ XPCOMUtils.defineLazyModuleGetter(this,
// Import the android BrowserActions module.
XPCOMUtils.defineLazyModuleGetter(this, "BrowserActions",
"resource://gre/modules/BrowserActions.jsm");
// WeakMap[Extension -> BrowserAction]
var browserActionMap = new WeakMap();
+const {
+ DefaultMap,
+} = ExtensionUtils;
+
class BrowserAction {
constructor(options, extension) {
+ // Map[TabID -> Object]
+ this.tabIdToPropertyMap = new DefaultMap(() => ({}));
+ this.tabManager = extension.tabManager;
this.uuid = `{${extension.uuid}}`;
this.name = options.default_title || extension.name;
+
+ GlobalEventDispatcher.registerListener(this, ["Tab:Selected"]);
+ GlobalEventDispatcher.registerListener(this, ["Tab:Closed"]);
+
BrowserActions.register(this);
EventEmitter.decorate(this);
}
/**
+ * Retrieves the name for the active tab. Used for testing only.
+ * @returns {string} the name used for the active tab.
+ */
+ get activeName() {
+ let tab = tabTracker.activeTab;
+ return this.tabIdToPropertyMap.get(tab.id).name || this.name;
+ }
+
+ /**
* Required by the BrowserActions module. This event will get
* called whenever the browser action is clicked on.
*/
onClicked() {
this.emit("click", tabTracker.activeTab);
}
/**
+ * Required by the GlobalEventDispatcher module. This event will get
+ * called whenever one of the registered listeners fires.
+ * @param {string} event The event which fired.
+ * @param {object} data Information about the event which fired.
+ */
+ onEvent(event, data) {
+ switch (event) {
+ case "Tab:Selected":
+ this.onTabSelected(this.tabManager.get(data.id));
+ break;
+ case "Tab:Closed":
+ this.onTabClosed(this.tabManager.get(data.tabId));
+ break;
+ }
+ }
+
+ /**
+ * Updates the browser action whenever a tab is selected.
+ * @param {Object} tab The tab to update.
+ */
+ onTabSelected(tab) {
+ let name = this.tabIdToPropertyMap.get(tab.id).name || this.name;
+ BrowserActions.update(this.uuid, {name});
+ }
+
+ /**
+ * Removes the tab from the property map now that it is closed.
+ * @param {Object} tab The tab which closed.
+ */
+ onTabClosed(tab) {
+ this.tabIdToPropertyMap.delete(tab.id);
+ }
+
+ /**
+ * Sets a property for the browser action for the specified tab. If the property is set
+ * for the active tab, the browser action is also updated.
+ *
+ * @param {Object} tab The tab to set. If null, the browser action's default value is set.
+ * @param {string} prop The property to update. Currently only "name" is supported.
+ * @param {string} value The value to set the property to.
+ */
+ setProperty(tab, prop, value) {
+ if (tab == null) {
+ if (value) {
+ this[prop] = value;
+ }
+ } else {
+ let properties = this.tabIdToPropertyMap.get(tab.id);
+ if (value) {
+ properties[prop] = value;
+ } else {
+ delete properties[prop];
+ }
+ }
+
+ if (tab && tab.selected) {
+ BrowserActions.update(this.uuid, {[prop]: value});
+ }
+ }
+
+ /**
+ * Retreives a property of the browser 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 "name" is supported.
+ * @returns {string} the value stored for the specified property. If the value is undefined, then the
+ * default value is returned.
+ */
+ getProperty(tab, prop) {
+ if (tab == null) {
+ return this[prop];
+ }
+
+ return this.tabIdToPropertyMap.get(tab.id)[prop] || this[prop];
+ }
+
+ /**
* Unregister the browser action from the BrowserActions module.
*/
shutdown() {
BrowserActions.unregister(this.uuid);
}
}
this.browserAction = class extends ExtensionAPI {
@@ -67,12 +164,25 @@ this.browserAction = class extends Exten
let listener = (event, tab) => {
fire.async(tabManager.convert(tab));
};
browserActionMap.get(extension).on("click", listener);
return () => {
browserActionMap.get(extension).off("click", listener);
};
}).api(),
+
+ setTitle: function(details) {
+ let {tabId, title} = details;
+ let tab = tabId ? tabTracker.getTab(tabId) : null;
+ browserActionMap.get(extension).setProperty(tab, "name", title);
+ },
+
+ getTitle: function(details) {
+ let {tabId} = details;
+ let tab = tabId ? tabTracker.getTab(tabId) : null;
+ let title = browserActionMap.get(extension).getProperty(tab, "name");
+ return Promise.resolve(title);
+ },
},
};
}
};
--- a/mobile/android/components/extensions/schemas/browser_action.json
+++ b/mobile/android/components/extensions/schemas/browser_action.json
@@ -72,17 +72,16 @@
"additionalProperties": { "type": "any" },
"postprocess": "convertImageDataToURL",
"description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
}
],
"functions": [
{
"name": "setTitle",
- "unsupported": true,
"type": "function",
"description": "Sets the title of the browser action. This shows up in the tooltip.",
"async": "callback",
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
@@ -102,17 +101,16 @@
"name": "callback",
"optional": true,
"parameters": []
}
]
},
{
"name": "getTitle",
- "unsupported": true,
"type": "function",
"description": "Gets the title of the browser action.",
"async": "callback",
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
--- a/mobile/android/modules/BrowserActions.jsm
+++ b/mobile/android/modules/BrowserActions.jsm
@@ -28,17 +28,17 @@ var BrowserActions = {
EventDispatcher.instance.registerListener(this, "Menu:BrowserActionClicked");
}
},
/**
* Unregisters the listeners if they are already initizliaed and
* all of the browser actions have been removed.
*/
- _maybeUnregisterListeners: function() {
+ _maybeUnregisterListeners() {
if (this._initialized && !Object.keys(this._browserActions).length) {
this._initialized = false;
EventDispatcher.instance.unregisterListener(this, "Menu:BrowserActionClicked");
}
},
/**
* Called when a browser action is clicked on.
@@ -68,33 +68,59 @@ var BrowserActions = {
EventDispatcher.instance.sendRequest({
type: "Menu:AddBrowserAction",
id: this._nextMenuId++,
uuid: browserAction.uuid,
name: browserAction.name,
});
this._browserActions[browserAction.uuid] = browserAction;
+
this._maybeRegisterListeners();
},
/**
+ * Updates the browser action with the specified UUID.
+ * @param {string} uuid The UUID of the browser action.
+ * @param {Object} options The properties to update.
+ */
+ update(uuid, options) {
+ if (options.name) {
+ EventDispatcher.instance.sendRequest({
+ type: "Menu:UpdateBrowserAction",
+ uuid,
+ options,
+ });
+ }
+ },
+
+ /**
+ * Retrieves the name currently used for the browser action with the
+ * specified UUID. Used for testing only.
+ * @param {string} uuid The UUID of the browser action.
+ * @returns {string} the name currently used for the browser action.
+ */
+ getNameForActiveTab(uuid) {
+ return this._browserActions[uuid].activeName;
+ },
+
+ /**
* Checks to see if the browser action is shown. Used for testing only.
* @param {string} uuid The UUID of the browser action.
- * @returns True if the browser action is shown; false otherwise.
+ * @returns {boolean} true if the browser action is shown; false otherwise.
*/
- isShown: function(uuid) {
+ isShown(uuid) {
return !!this._browserActions[uuid];
},
/**
* Synthesizes a click on the browser action. Used for testing only.
* @param {string} uuid The UUID of the browser action.
*/
- synthesizeClick: function(uuid) {
+ synthesizeClick(uuid) {
let browserAction = this._browserActions[uuid];
if (!browserAction) {
throw new Error(`No BrowserAction with UUID ${uuid} was found`);
}
browserAction.onClicked();
},
/**
@@ -108,9 +134,9 @@ var BrowserActions = {
}
EventDispatcher.instance.sendRequest({
type: "Menu:RemoveBrowserAction",
uuid,
});
delete this._browserActions[uuid];
this._maybeUnregisterListeners();
}
-}
\ No newline at end of file
+}