Bug 1351111 - Add support for setTitle and getTitle r?mixedpuppy draft
authorMatthew Wein <mwein@mozilla.com>
Tue, 30 May 2017 22:40:57 -0400
changeset 586783 885095dd7c55b9607f0d85a1e24396e73b249a85
parent 586782 b6d0d9fa05265912d5f556683496507dc06c4f2c
child 586784 9b5d655d227caa79c8566e35ca0c3e8e2e7fc467
child 586787 a0f0fb60ac80e084e9e8a87d64d15ed9a0c681b4
push id61521
push usermwein@mozilla.com
push dateWed, 31 May 2017 03:08:36 +0000
reviewersmixedpuppy
bugs1351111
milestone55.0a1
Bug 1351111 - Add support for setTitle and getTitle r?mixedpuppy MozReview-Commit-ID: 9B0RaOJjTxc
mobile/android/components/extensions/ext-browserAction.js
mobile/android/components/extensions/schemas/browser_action.json
mobile/android/modules/BrowserActions.jsm
--- 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
+}