Bug 1417042 - Remove the "panelview" binding. r=Gijs draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Wed, 06 Dec 2017 10:37:25 +0000
changeset 708193 503590d1052bc14ac13f1bd8419a7e0edd462b7b
parent 707503 b4cef8d1dff06a1ec2b9bb17211c0c3c7f5b76fa
child 743128 5814c80938e4f6479761b22395e4933dbfe226de
push id92318
push userpaolo.mozmail@amadzone.org
push dateWed, 06 Dec 2017 10:38:09 +0000
reviewersGijs
bugs1417042
milestone59.0a1
Bug 1417042 - Remove the "panelview" binding. r=Gijs MozReview-Commit-ID: 26uQb3pteQd
browser/base/content/browser-pageActions.js
browser/base/content/browser.css
browser/components/customizableui/PanelMultiView.jsm
browser/components/customizableui/content/panelUI.xml
browser/components/customizableui/test/browser_987640_charEncoding.js
browser/themes/shared/customizableui/panelUI.inc.css
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -946,17 +946,17 @@ BrowserPageActions.emailLink = {
 // send to device
 BrowserPageActions.sendToDevice = {
   onPlacedInPanel(buttonNode) {
     let action = PageActions.actionForID("sendToDevice");
     BrowserPageActions.takeActionTitleFromPanel(action);
   },
 
   onSubviewPlaced(panelViewNode) {
-    let bodyNode = panelViewNode.firstChild;
+    let bodyNode = panelViewNode.querySelector(".panel-subview-body");
     for (let node of bodyNode.childNodes) {
       BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
       BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
     }
   },
 
   onLocationChange() {
     let action = PageActions.actionForID("sendToDevice");
@@ -965,17 +965,17 @@ BrowserPageActions.sendToDevice = {
     action.setDisabled(!gSync.isSendableURI(url), window);
   },
 
   onShowingSubview(panelViewNode) {
     let browser = gBrowser.selectedBrowser;
     let url = browser.currentURI.spec;
     let title = browser.contentTitle;
 
-    let bodyNode = panelViewNode.firstChild;
+    let bodyNode = panelViewNode.querySelector(".panel-subview-body");
     let panelNode = panelViewNode.closest("panel");
 
     // This is on top because it also clears the device list between state
     // changes.
     gSync.populateSendTabToDevicesMenu(bodyNode, url, title, (clientId, name, clientType, lastModified) => {
       if (!name) {
         return document.createElement("toolbarseparator");
       }
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -68,31 +68,29 @@ toolbar[customizable="true"] {
 }
 
 panelmultiview {
   -moz-box-align: start;
   -moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelmultiview");
 }
 
 panelview {
-  -moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelview");
   -moz-box-orient: vertical;
 }
 
-panel[hidden] panelmultiview,
-panel[hidden] panelview {
+panel[hidden] panelmultiview {
   -moz-binding: none;
 }
 
 panelview:not([current]):not([in-transition]) {
   visibility: collapse;
 }
 
-panelview[mainview] > .panel-header,
-panelview:not([title]) > .panel-header {
+/* Hide the header when a subview is reused as a main view. */
+panelview[mainview] > .panel-header {
   display: none;
 }
 
 tabbrowser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
 }
 
 #tabbrowser-tabs {
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -363,19 +363,59 @@ this.PanelMultiView = class {
   }
 
   _placeSubView(viewNode) {
     this._viewStack.appendChild(viewNode);
     if (!this.panelViews.includes(viewNode))
       this.panelViews.push(viewNode);
   }
 
-  goBack(target) {
+  _setHeader(viewNode, titleText) {
+    // If the header already exists, update or remove it as requested.
+    let header = viewNode.firstChild;
+    if (header && header.classList.contains("panel-header")) {
+      if (titleText) {
+        header.querySelector("label").setAttribute("value", titleText);
+      } else {
+        header.remove();
+      }
+      return;
+    }
+
+    // The header doesn't exist, only create it if needed.
+    if (!titleText) {
+      return;
+    }
+
+    header = this.document.createElement("box");
+    header.classList.add("panel-header");
+
+    let backButton = this.document.createElement("toolbarbutton");
+    backButton.className =
+      "subviewbutton subviewbutton-iconic subviewbutton-back";
+    backButton.setAttribute("closemenu", "none");
+    backButton.setAttribute("tabindex", "0");
+    backButton.setAttribute("tooltip",
+      this.node.getAttribute("data-subviewbutton-tooltip"));
+    backButton.addEventListener("command", () => {
+      // The panelmultiview element may change if the view is reused.
+      viewNode.panelMultiView.goBack();
+      backButton.blur();
+    });
+
+    let label = this.document.createElement("label");
+    label.setAttribute("value", titleText);
+
+    header.append(backButton, label);
+    viewNode.prepend(header);
+  }
+
+  goBack() {
     let [current, previous] = this.panelViews.back();
-    return this.showSubView(current, target, previous);
+    return this.showSubView(current, null, previous);
   }
 
   /**
    * Checks whether it is possible to navigate backwards currently. Returns
    * false if this is the panelmultiview's mainview, true otherwise.
    *
    * @param  {panelview} view View to check, defaults to the currently active view.
    * @return {Boolean}
@@ -449,16 +489,20 @@ this.PanelMultiView = class {
           this._placeSubView(viewNode);
         } else {
           throw new Error(`Subview ${aViewId} doesn't exist!`);
         }
       } else if (viewNode.parentNode == this._panelViewCache) {
         this._placeSubView(viewNode);
       }
 
+      viewNode.panelMultiView = this.node;
+      this._setHeader(viewNode, viewNode.getAttribute("title") ||
+                                (aAnchor && aAnchor.getAttribute("label")));
+
       let reverse = !!aPreviousView;
       let previousViewNode = aPreviousView || this._currentSubView;
       // If the panelview to show is the same as the previous one, the 'ViewShowing'
       // event has already been dispatched. Don't do it twice.
       let showingSameView = viewNode == previousViewNode;
       let playTransition = (!!previousViewNode && !showingSameView && this._panel.state == "open");
       let isMainView = viewNode.id == this._mainViewId;
 
@@ -483,20 +527,17 @@ this.PanelMultiView = class {
       // Because the 'mainview' attribute may be out-of-sync, due to view node
       // reparenting in combination with ephemeral PanelMultiView instances,
       // this is the best place to correct it (just before showing).
       if (isMainView)
         viewNode.setAttribute("mainview", true);
       else
         viewNode.removeAttribute("mainview");
 
-      // Make sure that new panels always have a title set.
       if (aAnchor) {
-        if (!viewNode.hasAttribute("title"))
-          viewNode.setAttribute("title", aAnchor.getAttribute("label"));
         viewNode.classList.add("PanelUI-subView");
       }
       if (!isMainView && this._mainViewWidth)
         viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
 
       if (!showingSameView || !viewNode.hasAttribute("current")) {
         // Emit the ViewShowing event so that the widget definition has a chance
         // to lazily populate the subview with things or perhaps even cancel this
@@ -594,18 +635,18 @@ this.PanelMultiView = class {
       // reopening a subview, because its contents may have changed.
       viewRect = viewNode.__lastKnownBoundingRect;
       viewNode.setAttribute("in-transition", true);
     } else if (viewNode.customRectGetter) {
       // Can't use Object.assign directly with a DOM Rect object because its properties
       // aren't enumerable.
       let {height, width} = previousRect;
       viewRect = Object.assign({height, width}, viewNode.customRectGetter());
-      let {header} = viewNode;
-      if (header) {
+      let header = viewNode.firstChild;
+      if (header && header.classList.contains("panel-header")) {
         viewRect.height += this._dwu.getBoundsWithoutFlushing(header).height;
       }
       viewNode.setAttribute("in-transition", true);
     } else {
       let oldSibling = viewNode.nextSibling || null;
       this._offscreenViewStack.style.minHeight =
         this._viewContainer.style.height;
       this._offscreenViewStack.appendChild(viewNode);
@@ -1018,17 +1059,17 @@ this.PanelMultiView = class {
       }
       case "ArrowLeft":
       case "ArrowRight": {
         stop();
         let dir = this._dir;
         if ((dir == "ltr" && keyCode == "ArrowLeft") ||
             (dir == "rtl" && keyCode == "ArrowRight")) {
           if (this._canGoBack(view))
-            this.goBack(view.backButton);
+            this.goBack();
           break;
         }
         // If the current button is _not_ one that points to a subview, pressing
         // the arrow key shouldn't do anything.
         if (!navMap.selected || !navMap.selected.get() ||
             !navMap.selected.get().classList.contains("subviewbutton-nav")) {
           break;
         }
@@ -1084,18 +1125,16 @@ this.PanelMultiView = class {
    * Retrieve the button elements from a view node that can be used for navigation
    * using the keyboard; enabled buttons and the back button, if visible.
    *
    * @param  {nsIDOMNode} view
    * @return {Array}
    */
   _getNavigableElements(view) {
     let buttons = Array.from(view.querySelectorAll(".subviewbutton:not([disabled])"));
-    if (this._canGoBack(view))
-      buttons.unshift(view.backButton);
     let dwu = this._dwu;
     return buttons.filter(button => {
       let bounds = dwu.getBoundsWithoutFlushing(button);
       return bounds.width > 0 && bounds.height > 0;
     });
   }
 
   /**
--- a/browser/components/customizableui/content/panelUI.xml
+++ b/browser/components/customizableui/content/panelUI.xml
@@ -12,17 +12,17 @@
           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="panelmultiview">
     <resources>
       <stylesheet src="chrome://browser/content/customizableui/panelUI.css"/>
     </resources>
-    <content>
+    <content data-subviewbutton-tooltip="&backCmd.label;">
       <xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,transitioning">
         <xul:box anonid="viewStack" xbl:inherits="transitioning" class="panel-viewstack">
           <children includes="panelview"/>
         </xul:box>
       </xul:box>
       <xul:box class="panel-viewcontainer offscreen">
         <xul:box anonid="offscreenViewStack" class="panel-viewstack"/>
       </xul:box>
@@ -33,41 +33,9 @@
         this.instance = new PanelMultiView(this);
       ]]></constructor>
 
       <destructor><![CDATA[
         this.instance.destructor();
       ]]></destructor>
     </implementation>
   </binding>
-
-  <binding id="panelview">
-    <content>
-      <xul:box class="panel-header" anonid="header">
-        <xul:toolbarbutton anonid="back"
-                           class="subviewbutton subviewbutton-iconic subviewbutton-back"
-                           closemenu="none"
-                           tabindex="0"
-                           tooltip="&backCmd.label;"
-                           oncommand="document.getBindingParent(this).panelMultiView.goBack(); this.blur()"/>
-        <xul:label xbl:inherits="value=title"/>
-      </xul:box>
-      <children/>
-    </content>
-    <implementation>
-      <property name="header"
-                readonly="true"
-                onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'header');"/>
-      <property name="backButton"
-                readonly="true"
-                onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'back');"/>
-      <property name="panelMultiView" readonly="true">
-        <getter><![CDATA[
-          if (!this.parentNode.localName.endsWith("panelmultiview")) {
-            return document.getBindingParent(this.parentNode);
-          }
-
-          return this.parentNode;
-        ]]></getter>
-      </property>
-    </implementation>
-  </binding>
 </bindings>
--- a/browser/components/customizableui/test/browser_987640_charEncoding.js
+++ b/browser/components/customizableui/test/browser_987640_charEncoding.js
@@ -24,17 +24,17 @@ add_task(async function() {
   charEncodingButton.click();
   await subviewShownPromise;
 
   let checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
   let initialEncoding = checkedButtons[0];
   is(initialEncoding.getAttribute("label"), "Western", "The western encoding is initially selected");
 
   // change the encoding
-  let encodings = characterEncodingView.querySelectorAll("toolbarbutton");
+  let encodings = characterEncodingView.querySelectorAll("toolbarbutton:not(.subviewbutton-back)");
   let newEncoding = encodings[0].hasAttribute("checked") ? encodings[1] : encodings[0];
   let browserStopPromise = BrowserTestUtils.browserStopped(gBrowser, TEST_PAGE);
   newEncoding.click();
   await browserStopPromise;
   is(gBrowser.selectedBrowser.characterSet, "UTF-8", "The encoding should be changed to UTF-8");
   ok(!gBrowser.selectedBrowser.mayEnableCharacterEncodingMenu, "The encoding menu should be disabled");
 
   // check that the new encodng is applied
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1482,28 +1482,28 @@ menuitem[checked="true"].subviewbutton >
   font-size: 13px;
   font-weight: 500;
   margin: 0;
   /* Add the size of the back button to center properly. */
   margin-inline-end: 32px;
   text-align: center;
 }
 
-.PanelUI-subView .panel-header > .subviewbutton-back {
+.panel-header > .subviewbutton-back {
   -moz-context-properties: fill;
   fill: var(--arrowpanel-color);
   list-style-image: url(chrome://browser/skin/arrow-left.svg);
   padding: 8px;
 }
 
-.panel-header > .subviewbutton-back:-moz-locale-dir(rtl) {
+.subviewbutton-back:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
-.panel-header > .subviewbutton-back > .toolbarbutton-text {
+.subviewbutton-back > .toolbarbutton-text {
   /* !important to override .cui-widget-panel toolbarbutton:not([wrap]) > .toolbarbutton-text
    * selector further down. */
   display: none !important;
 }
 
 #panelMenu_pocket {
   display: none;
 }