Bug 1043612 - Persist the size of resizable in-content subdialogs. r=gijs, a=sledru
authorJared Wein <jwein@mozilla.com>
Thu, 09 Apr 2015 11:23:32 -0400
changeset 258447 9740c1d817f1
parent 258446 b54c44cfa07e
child 258448 afe57494b44d
push id4669
push userryanvm@gmail.com
push date2015-04-13 16:55 +0000
treeherdermozilla-beta@333017ad43a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs, sledru
bugs1043612
milestone38.0
Bug 1043612 - Persist the size of resizable in-content subdialogs. r=gijs, a=sledru
browser/components/preferences/in-content/subdialogs.js
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -11,16 +11,17 @@ let gSubDialog = {
   _frame: null,
   _overlay: null,
   _box: null,
   _injectedStyleSheets: ["chrome://mozapps/content/preferences/preferences.css",
                          "chrome://browser/skin/preferences/preferences.css",
                          "chrome://global/skin/in-content/common.css",
                          "chrome://browser/skin/preferences/in-content/preferences.css",
                          "chrome://browser/skin/preferences/in-content/dialog.css"],
+  _resizeObserver: null,
 
   init: function() {
     this._frame = document.getElementById("dialogFrame");
     this._overlay = document.getElementById("dialogOverlay");
     this._box = document.getElementById("dialogBox");
     this._closeButton = document.getElementById("dialogClose");
   },
 
@@ -176,18 +177,22 @@ let gSubDialog = {
 
     let groupBoxTitle = document.getAnonymousElementByAttribute(this._box, "class", "groupbox-title");
     let groupBoxTitleHeight = groupBoxTitle.clientHeight +
                               parseFloat(getComputedStyle(groupBoxTitle).borderBottomWidth);
 
     let groupBoxBody = document.getAnonymousElementByAttribute(this._box, "class", "groupbox-body");
     let boxVerticalPadding = 2 * parseFloat(getComputedStyle(groupBoxBody).paddingTop);
     let boxHorizontalPadding = 2 * parseFloat(getComputedStyle(groupBoxBody).paddingLeft);
-    let frameWidth = docEl.style.width || docEl.scrollWidth + "px";
-    let frameHeight = docEl.style.height || docEl.scrollHeight + "px";
+    let frameMinWidth = docEl.style.width || docEl.scrollWidth + "px";
+    let frameWidth = docEl.getAttribute("width") ? docEl.getAttribute("width") + "px" :
+                     frameMinWidth;
+    let frameMinHeight = docEl.style.height || docEl.scrollHeight + "px";
+    let frameHeight = docEl.getAttribute("height") ? docEl.getAttribute("height") + "px" :
+                      frameMinHeight;
     let boxVerticalBorder = 2 * parseFloat(getComputedStyle(this._box).borderTopWidth);
     let boxHorizontalBorder = 2 * parseFloat(getComputedStyle(this._box).borderLeftWidth);
 
     let frameRect = this._frame.getBoundingClientRect();
     let boxRect = this._box.getBoundingClientRect();
     let frameSizeDifference = (frameRect.top - boxRect.top) + (boxRect.bottom - frameRect.bottom);
 
     // Now check if the frame height we calculated is possible at this window size,
@@ -201,27 +206,55 @@ let gSubDialog = {
         container.classList.add("doScroll");
       }
     }
 
     this._frame.style.width = frameWidth;
     this._frame.style.height = frameHeight;
     this._box.style.minHeight = "calc(" +
                                 (boxVerticalBorder + groupBoxTitleHeight + boxVerticalPadding) +
-                                "px + " + frameHeight + ")";
+                                "px + " + frameMinHeight + ")";
     this._box.style.minWidth = "calc(" +
                                (boxHorizontalBorder + boxHorizontalPadding) +
-                               "px + " + frameWidth + ")";
+                               "px + " + frameMinWidth + ")";
 
     this._overlay.style.visibility = "visible";
     this._overlay.style.opacity = ""; // XXX: focus hack continued from _onContentLoaded
 
+    this._resizeObserver = new MutationObserver(this._onResize);
+    this._resizeObserver.observe(this._box, {attributes: true});
+
     this._trapFocus();
   },
 
+  _onResize: function(mutations) {
+    let frame = gSubDialog._frame;
+    // The width and height styles are needed for the initial
+    // layout of the frame, but afterward they need to be removed
+    // or their presence will restrict the contents of the <browser>
+    // from resizing to a smaller size.
+    frame.style.removeProperty("width");
+    frame.style.removeProperty("height");
+
+    let docEl = frame.contentDocument.documentElement;
+    let persistedAttributes = docEl.getAttribute("persist");
+    if (!persistedAttributes.contains("width") &&
+        !persistedAttributes.contains("height")) {
+      return;
+    }
+
+    for (let mutation of mutations) {
+      if (mutation.attributeName == "width") {
+        docEl.setAttribute("width", docEl.scrollWidth);
+      } else if (mutation.attributeName == "height") {
+        docEl.setAttribute("height", docEl.scrollHeight);
+      }
+    }
+  },
+
   _onDialogClosing: function(aEvent) {
     this._frame.contentWindow.removeEventListener("dialogclosing", this);
     this._closingEvent = aEvent;
   },
 
   _onKeyDown: function(aEvent) {
     if (aEvent.currentTarget == window && aEvent.keyCode == aEvent.DOM_VK_ESCAPE &&
         !aEvent.defaultPrevented) {
@@ -296,16 +329,20 @@ let gSubDialog = {
     chromeBrowser.removeEventListener("unload", this, true);
 
     this._closeButton.removeEventListener("command", this);
 
     window.removeEventListener("DOMFrameContentLoaded", this, true);
     this._frame.removeEventListener("load", this);
     this._frame.contentWindow.removeEventListener("dialogclosing", this);
     window.removeEventListener("keydown", this, true);
+    if (this._resizeObserver) {
+      this._resizeObserver.disconnect();
+      this._resizeObserver = null;
+    }
     this._untrapFocus();
   },
 
   _trapFocus: function() {
     let fm = Services.focus;
     fm.moveFocus(this._frame.contentWindow, null, fm.MOVEFOCUS_FIRST, 0);
     this._frame.contentDocument.addEventListener("keydown", this, true);
     this._closeButton.addEventListener("keydown", this);