Bug 1417042 - Remove the "panelview" binding. r=Gijs
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Wed, 06 Dec 2017 10:37:25 +0000
changeset 395337 6b5a357d277b83349223792361a0fdcc90c15305
parent 395336 5f90362bbc80cf7f9b09e41477c244f292fc9f1c
child 395338 7d1f9d97377c183637cc42dbc4ed2a517075efb3
push id33038
push userdluca@mozilla.com
push dateWed, 06 Dec 2017 22:02:18 +0000
treeherdermozilla-central@99a3b09ac189 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1417042
milestone59.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 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;
 }