Bug 1459556 - Part 1 - Remove the implementation from the "handler" binding. r=jaws draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Wed, 16 May 2018 11:45:19 +0100
changeset 796209 3499235d4e87e2f163e97591e19fbecca8f8c397
parent 795657 3c9d69736f4a421218e5eb01b6571d535d38318a
child 796210 b8d11324dcc7de96abcf830d498051afe2e9d9b7
child 797241 1f1d2b43f738d291b3cbcd2c3a7dbc418235d21d
push id110180
push userpaolo.mozmail@amadzone.org
push dateThu, 17 May 2018 08:23:50 +0000
reviewersjaws
bugs1459556
milestone62.0a1
Bug 1459556 - Part 1 - Remove the implementation from the "handler" binding. r=jaws MozReview-Commit-ID: IN1C5NC9Rzb
browser/components/preferences/handlers.xml
browser/components/preferences/in-content/main.js
--- a/browser/components/preferences/handlers.xml
+++ b/browser/components/preferences/handlers.xml
@@ -6,39 +6,16 @@
 <!-- import-globals-from in-content/main.js -->
 
 <bindings id="handlerBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="handler" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
-    <implementation>
-      <property name="type" readonly="true">
-        <getter>
-          return this.getAttribute("type");
-        </getter>
-      </property>
-      <!-- Overriding listitem -->
-      <property name="selected" onget="return this.getAttribute('selected') == 'true';">
-        <setter><![CDATA[
-          if (val) {
-            this.setAttribute("selected", "true");
-            gMainPane.rebuildActionsMenu();
-          } else {
-            this.removeAttribute("selected");
-          }
-
-          document.getAnonymousElementByAttribute(this, "anonid", "selected").setAttribute("hidden", !val);
-          document.getAnonymousElementByAttribute(this, "anonid", "not-selected").setAttribute("hidden", !!val);
-
-          return val;
-        ]]></setter>
-      </property>
-    </implementation>
     <content>
       <xul:hbox flex="1" equalsize="always">
         <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription">
           <xul:image src="moz-icon://goat?size=16" class="typeIcon"
                      xbl:inherits="src=typeIcon" height="16" width="16"/>
           <xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/>
         </xul:hbox>
         <xul:hbox anonid="not-selected" flex="1" align="center" xbl:inherits="tooltiptext=actionDescription">
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -578,16 +578,17 @@ var gMainPane = {
 
   preInit() {
     promiseLoadHandlersList = new Promise((resolve, reject) => {
       // Load the data and build the list of handlers for applications pane.
       // By doing this after pageshow, we ensure it doesn't delay painting
       // of the preferences page.
       window.addEventListener("pageshow", async () => {
         try {
+          this._initListEventHandlers();
           this._loadData();
           await this._rebuildVisibleTypes();
           this._sortVisibleTypes();
           this._rebuildView();
           resolve();
         } catch (ex) {
           reject(ex);
         }
@@ -1443,16 +1444,41 @@ var gMainPane = {
 
       handlerInfoWrapper.handledOnlyByPlugin = false;
     }
   },
 
 
   // View Construction
 
+  selectedHandlerListItem: null,
+
+  _initListEventHandlers() {
+    this._list.addEventListener("select", event => {
+      if (event.target != this._list) {
+        return;
+      }
+
+      let handlerListItem = this._list.selectedItem &&
+                            HandlerListItem.forNode(this._list.selectedItem);
+      if (this.selectedHandlerListItem == handlerListItem) {
+        return;
+      }
+
+      if (this.selectedHandlerListItem) {
+        this.selectedHandlerListItem.showActionsMenu = false;
+      }
+      this.selectedHandlerListItem = handlerListItem;
+      if (handlerListItem) {
+        this.rebuildActionsMenu();
+        handlerListItem.showActionsMenu = true;
+      }
+    });
+  },
+
   async _rebuildVisibleTypes() {
     this._visibleTypes = [];
 
     // Map whose keys are string descriptions and values are references to the
     // first visible HandlerInfoWrapper that has this description. We use this
     // to determine whether or not to annotate descriptions with their types to
     // distinguish duplicate descriptions from each other.
     let visibleDescriptions = new Map();
@@ -1501,18 +1527,19 @@ var gMainPane = {
         // add the type to the description on both HandlerInfoWrapper objects.
         handlerInfo.disambiguateDescription = true;
         otherHandlerInfo.disambiguateDescription = true;
       }
     }
   },
 
   _rebuildView() {
-    let lastSelectedType = this._list.selectedItem &&
-      HandlerListItem.forNode(this._list.selectedItem).handlerInfoWrapper.type;
+    let lastSelectedType = this.selectedHandlerListItem &&
+                           this.selectedHandlerListItem.handlerInfoWrapper.type;
+    this.selectedHandlerListItem = null;
 
     // Clear the list of entries.
     while (this._list.childNodes.length > 1)
       this._list.removeChild(this._list.lastChild);
 
     var visibleTypes = this._visibleTypes;
 
     // If the user is filtering the list, then only show matching types.
@@ -1580,17 +1607,17 @@ var gMainPane = {
   },
 
   /**
    * Rebuild the actions menu for the selected entry.  Gets called by
    * the richlistitem constructor when an entry in the list gets selected.
    */
   rebuildActionsMenu() {
     var typeItem = this._list.selectedItem;
-    var handlerInfo = this._handledTypes[typeItem.type];
+    var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
     var menu =
       document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
     var menuPopup = menu.menupopup;
 
     // Clear out existing items.
     while (menuPopup.hasChildNodes())
       menuPopup.removeChild(menuPopup.lastChild);
 
@@ -1694,17 +1721,17 @@ var gMainPane = {
 
       menuPopup.appendChild(menuItem);
       possibleAppMenuItems.push(menuItem);
     }
     // Add gio handlers
     if (Cc["@mozilla.org/gio-service;1"]) {
       let gIOSvc = Cc["@mozilla.org/gio-service;1"].
                    getService(Ci.nsIGIOService);
-      var gioApps = gIOSvc.getAppsForURIScheme(typeItem.type);
+      var gioApps = gIOSvc.getAppsForURIScheme(handlerInfo.type);
       let enumerator = gioApps.enumerate();
       let possibleHandlers = handlerInfo.possibleApplicationHandlers;
       while (enumerator.hasMoreElements()) {
         let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
         // OS handler share the same name, it's most likely the same app, skipping...
         if (handler.name == handlerInfo.defaultDescription) {
           continue;
         }
@@ -1901,18 +1928,17 @@ var gMainPane = {
     try {
       this._storeAction(aActionItem);
     } finally {
       this._storingAction = false;
     }
   },
 
   _storeAction(aActionItem) {
-    var typeItem = this._list.selectedItem;
-    var handlerInfo = this._handledTypes[typeItem.type];
+    var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
 
     let action = parseInt(aActionItem.getAttribute("action"));
 
     // Set the plugin state if we're enabling or disabling a plugin.
     if (action == kActionUsePlugin)
       handlerInfo.enablePluginType();
     else if (handlerInfo.pluginName && !handlerInfo.isDisabledPluginType)
       handlerInfo.disablePluginType();
@@ -1937,34 +1963,33 @@ var gMainPane = {
 
     handlerInfo.store();
 
     // Make sure the handler info object is flagged to indicate that there is
     // now some user configuration for the type.
     handlerInfo.handledOnlyByPlugin = false;
 
     // Update the action label and image to reflect the new preferred action.
-    HandlerListItem.forNode(typeItem).refreshAction();
+    this.selectedHandlerListItem.refreshAction();
   },
 
   manageApp(aEvent) {
     // Don't let the normal "on select action" handler get this event,
     // as we handle it specially ourselves.
     aEvent.stopPropagation();
 
-    var typeItem = this._list.selectedItem;
-    var handlerInfo = this._handledTypes[typeItem.type];
+    var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
 
     let onComplete = () => {
       // Rebuild the actions menu so that we revert to the previous selection,
       // or "Always ask" if the previous default application has been removed
       this.rebuildActionsMenu();
 
       // update the richlistitem too. Will be visible when selecting another row
-      HandlerListItem.forNode(typeItem).refreshAction();
+      this.selectedHandlerListItem.refreshAction();
     };
 
     gSubDialog.open("chrome://browser/content/preferences/applicationManager.xul",
       "resizable=no", handlerInfo, onComplete);
 
   },
 
   chooseApp(aEvent) {
@@ -1993,17 +2018,17 @@ var gMainPane = {
             break;
           }
         }
       }
     };
 
     if (AppConstants.platform == "win") {
       var params = {};
-      var handlerInfo = this._handledTypes[this._list.selectedItem.type];
+      var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
 
       if (isFeedType(handlerInfo.type)) {
         // MIME info will be null, create a temp object.
         params.mimeInfo = gMIMEService.getFromTypeAndExtension(handlerInfo.type,
           handlerInfo.primaryExtension);
       } else {
         params.mimeInfo = handlerInfo.wrappedHandlerInfo;
       }
@@ -2033,17 +2058,17 @@ var gMainPane = {
         if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
           this._isValidHandlerExecutable(fp.file)) {
           handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
             createInstance(Ci.nsILocalHandlerApp);
           handlerApp.name = getFileDisplayName(fp.file);
           handlerApp.executable = fp.file;
 
           // Add the app to the type's list of possible handlers.
-          let handler = this._handledTypes[this._list.selectedItem.type];
+          let handler = this.selectedHandlerListItem.handlerInfoWrapper;
           handler.addPossibleApplicationHandler(handlerApp);
 
           chooseAppCallback(handlerApp);
         }
       };
 
       // Prompt the user to pick an app.  If they pick one, and it's a valid
       // selection, then add it to the list of possible handlers.
@@ -2459,16 +2484,23 @@ class HandlerListItem {
       this.node.setAttribute(APP_ICON_ATTR_NAME,
                              this.handlerInfoWrapper.actionIconClass);
       this.node.removeAttribute("actionIcon");
     } else {
       this.node.removeAttribute(APP_ICON_ATTR_NAME);
       this.node.setAttribute("actionIcon", this.handlerInfoWrapper.actionIcon);
     }
   }
+
+  set showActionsMenu(value) {
+    document.getAnonymousElementByAttribute(this.node, "anonid", "selected")
+            .setAttribute("hidden", !value);
+    document.getAnonymousElementByAttribute(this.node, "anonid", "not-selected")
+            .setAttribute("hidden", !!value);
+  }
 }
 
 /**
  * This object wraps nsIHandlerInfo with some additional functionality
  * the Applications prefpane needs to display and allow modification of
  * the list of handled types.
  *
  * We create an instance of this wrapper for each entry we might display