Bug 1424264 - Part 2 - Move state related to view sizing to the PanelView class. r=Gijs draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Fri, 26 Jan 2018 14:19:53 +0000
changeset 747615 8d989e9ce8fd973fb31d57d7085a1c8c2ce397d6
parent 747614 fba009a10b2b920e0da2c1d8b8dc57b99697bea5
child 747616 cc59c5af53807a8ed13d50ffeb2c12de002843ed
push id96955
push userpaolo.mozmail@amadzone.org
push dateFri, 26 Jan 2018 14:22:08 +0000
reviewersGijs
bugs1424264
milestone60.0a1
Bug 1424264 - Part 2 - Move state related to view sizing to the PanelView class. r=Gijs MozReview-Commit-ID: IsywTHd3qVV
browser/components/customizableui/PanelMultiView.jsm
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -357,26 +357,20 @@ this.PanelMultiView = class extends this
 
       viewNode.panelMultiView = this.node;
 
       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 previousRect = previousViewNode.__lastKnownBoundingRect =
-          this._dwu.getBoundsWithoutFlushing(previousViewNode);
-      // Cache the measures that have the same caching lifetime as the width
-      // or height of the main view, i.e. whilst the panel is shown and/ or
-      // visible.
-      if (!this._mainViewWidth) {
-        this._mainViewWidth = previousRect.width;
-      }
+      let prevPanelView = PanelView.forNode(previousViewNode);
+      prevPanelView.captureKnownSize();
       if (!this._mainViewHeight) {
-        this._mainViewHeight = previousRect.height;
+        this._mainViewHeight = prevPanelView.knownHeight;
         this._viewContainer.style.minHeight = this._mainViewHeight + "px";
       }
 
       this._viewShowing = viewNode;
 
       let reverse = !!aPreviousView;
       if (!reverse) {
         // We are opening a new view, either because we are navigating forward
@@ -384,17 +378,17 @@ this.PanelMultiView = class extends this
         // may vary between panels, so we make sure to update them every time.
         // Firstly, make sure that the header matches how the view was opened.
         nextPanelView.headerText = viewNode.getAttribute("title") ||
                                    (aAnchor && aAnchor.getAttribute("label"));
         // The main view of a panel can be a subview in another one.
         let isMainView = viewNode.id == this._mainViewId;
         nextPanelView.mainview = isMainView;
         // The constrained width of subviews may also vary between panels.
-        nextPanelView.minMaxWidth = isMainView ? 0 : this._mainViewWidth;
+        nextPanelView.minMaxWidth = isMainView ? 0 : prevPanelView.knownWidth;
       }
 
       if (aAnchor) {
         viewNode.classList.add("PanelUI-subView");
       }
 
       if (!showingSameView || !viewNode.hasAttribute("current")) {
         // Emit the ViewShowing event so that the widget definition has a chance
@@ -422,17 +416,17 @@ this.PanelMultiView = class extends this
           return;
         }
       }
 
       // Now we have to transition the panel. If we've got an older transition
       // still running, make sure to clean it up.
       await this._cleanupTransitionPhase();
       if (!showingSameView && this._panel.state == "open") {
-        await this._transitionViews(previousViewNode, viewNode, reverse, previousRect, aAnchor);
+        await this._transitionViews(previousViewNode, viewNode, reverse, aAnchor);
         nextPanelView.focusSelectedElement();
       } else {
         this.hideAllViewsExcept(nextPanelView);
       }
     })().catch(e => Cu.reportError(e));
     return this._currentShowPromise;
   }
 
@@ -444,31 +438,30 @@ this.PanelMultiView = class extends this
    * RTL mode.
    *
    * @param {panelview} previousViewNode Node that is currently shown as active,
    *                                     but is about to be transitioned away.
    * @param {panelview} viewNode         Node that will becode the active view,
    *                                     after the transition has finished.
    * @param {Boolean}   reverse          Whether we're navigation back to a
    *                                     previous view or forward to a next view.
-   * @param {Object}    previousRect     Rect object, with the same structure as
-   *                                     a DOMRect, of the `previousViewNode`.
    * @param {Element}   anchor           the anchor for which we're opening
    *                                     a new panelview, if any
    */
-  async _transitionViews(previousViewNode, viewNode, reverse, previousRect, anchor) {
+  async _transitionViews(previousViewNode, viewNode, reverse, anchor) {
     // There's absolutely no need to show off our epic animation skillz when
     // the panel's not even open.
     if (this._panel.state != "open") {
       return;
     }
 
     const {window, document} = this;
 
     let nextPanelView = PanelView.forNode(viewNode);
+    let prevPanelView = PanelView.forNode(previousViewNode);
 
     if (this._autoResizeWorkaroundTimer)
       window.clearTimeout(this._autoResizeWorkaroundTimer);
 
     let details = this._transitionDetails = {
       phase: TRANSITION_PHASES.START,
       previousViewNode, viewNode, reverse, anchor
     };
@@ -477,33 +470,35 @@ this.PanelMultiView = class extends this
       anchor.setAttribute("open", "true");
 
     // Since we're going to show two subview at the same time, don't abuse the
     // 'current' attribute, since it's needed for other state-keeping, but use
     // a separate 'in-transition' attribute instead.
     previousViewNode.setAttribute("in-transition", true);
     // Set the viewContainer dimensions to make sure only the current view is
     // visible.
-    this._viewContainer.style.height = Math.max(previousRect.height, this._mainViewHeight) + "px";
-    this._viewContainer.style.width = previousRect.width + "px";
+    this._viewContainer.style.height = Math.max(prevPanelView.knownHeight, this._mainViewHeight) + "px";
+    this._viewContainer.style.width = prevPanelView.knownWidth + "px";
     // Lock the dimensions of the window that hosts the popup panel.
     let rect = this._panel.popupBoxObject.getOuterScreenRect();
     this._panel.setAttribute("width", rect.width);
     this._panel.setAttribute("height", rect.height);
 
     let viewRect;
-    if (reverse && viewNode.__lastKnownBoundingRect) {
+    if (reverse) {
       // Use the cached size when going back to a previous view, but not when
       // reopening a subview, because its contents may have changed.
-      viewRect = viewNode.__lastKnownBoundingRect;
+      viewRect = { width: nextPanelView.knownWidth,
+                   height: nextPanelView.knownHeight };
       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;
+      let width = prevPanelView.knownWidth;
+      let height = prevPanelView.knownHeight;
       viewRect = Object.assign({height, width}, viewNode.customRectGetter());
       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;
@@ -529,17 +524,17 @@ this.PanelMultiView = class extends this
       this._offscreenViewStack.style.removeProperty("min-height");
     }
 
     this._transitioning = true;
     details.phase = TRANSITION_PHASES.PREPARE;
 
     // The 'magic' part: build up the amount of pixels to move right or left.
     let moveToLeft = (this._dir == "rtl" && !reverse) || (this._dir == "ltr" && reverse);
-    let deltaX = previousRect.width;
+    let deltaX = prevPanelView.knownWidth;
     let deepestNode = reverse ? previousViewNode : viewNode;
 
     // With a transition when navigating backwards - user hits the 'back'
     // button - we need to make sure that the views are positioned in a way
     // that a translateX() unveils the previous view from the right direction.
     if (reverse)
       this._viewStack.style.marginInlineStart = "-" + deltaX + "px";
 
@@ -753,30 +748,24 @@ this.PanelMultiView = class extends this
         break;
       case "popuphidden": {
         // WebExtensions consumers can hide the popup from viewshowing, or
         // mid-transition, which disrupts our state:
         this._viewShowing = null;
         this._transitioning = false;
         this.node.removeAttribute("panelopen");
         this.showMainView();
-        for (let panelView of this._viewStack.children) {
-          if (panelView.nodeName != "children") {
-            panelView.__lastKnownBoundingRect = null;
-          }
-        }
         this.window.removeEventListener("keydown", this);
         this._panel.removeEventListener("mousemove", this);
         this.openViews.forEach(panelView => panelView.clearNavigation());
         this.openViews = [];
 
         // Clear the main view size caches. The dimensions could be different
         // when the popup is opened again, e.g. through touch mode sizing.
         this._mainViewHeight = 0;
-        this._mainViewWidth = 0;
         this._viewContainer.style.removeProperty("min-height");
         this._viewStack.style.removeProperty("max-height");
         this._viewContainer.style.removeProperty("min-width");
         this._viewContainer.style.removeProperty("max-width");
 
         this.dispatchCustomEvent("PanelMultiViewHidden");
         break;
       }
@@ -876,16 +865,26 @@ this.PanelView = class extends this.Asso
    * Also make sure that the correct method is called on CustomizableWidget.
    */
   dispatchCustomEvent(...args) {
     CustomizableUI.ensureSubviewListeners(this.node);
     return super.dispatchCustomEvent(...args);
   }
 
   /**
+   * Populates the "knownWidth" and "knownHeight" properties with the current
+   * dimensions of the view. These may be zero if the view is invisible.
+   */
+  captureKnownSize() {
+    let rect = this._dwu.getBoundsWithoutFlushing(this.node);
+    this.knownWidth = rect.width;
+    this.knownHeight = rect.height;
+  }
+
+  /**
    * If the main view or a subview contains wrapping elements, the attribute
    * "descriptionheightworkaround" should be set on the view to force all the
    * wrapping "description", "label" or "toolbarbutton" elements to a fixed
    * height. If the attribute is set and the visibility, contents, or width
    * of any of these elements changes, this function should be called to
    * refresh the calculated heights.
    *
    * This may trigger a synchronous layout.