Bug 1351111 - Add support for setTitle and getTitle r=mixedpuppy
authorMatthew Wein <mwein@mozilla.com>
Tue, 30 May 2017 22:40:57 -0400
changeset 409554 8e9c427717c8c5f294a48d63abb33e196695e631
parent 409553 c336400a21d7bbaf072553a4dfe39d4d297abea8
child 409555 f066295fa2d3d0be3063de361d509564fa77c42d
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1351111
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
+}