Bug 1480338 - Prevent event/task dialog being too small for the iframe inside; r=MakeMyDay
authorGeoff Lankow <geoff@darktrojan.net>
Thu, 27 Sep 2018 22:33:40 +1200
changeset 33244 5b5a269c9cdee41f53623588c233331665495cfc
parent 33243 102bf0b97b0ab0cab19a71ca61d11fe35639d354
child 33245 e6adb6f718934c05bc287a8247760f81ef640a83
push id387
push userclokep@gmail.com
push dateMon, 10 Dec 2018 21:30:47 +0000
reviewersMakeMyDay
bugs1480338
Bug 1480338 - Prevent event/task dialog being too small for the iframe inside; r=MakeMyDay
calendar/base/content/dialogs/calendar-dialog-utils.js
calendar/base/content/dialogs/calendar-event-dialog.xul
calendar/lightning/content/lightning-item-panel.js
--- a/calendar/base/content/dialogs/calendar-dialog-utils.js
+++ b/calendar/base/content/dialogs/calendar-dialog-utils.js
@@ -35,67 +35,66 @@ function intializeTabOrWindowVariables()
  * Dispose of controlling operations of this event dialog. Uses
  * window.arguments[0].job.dispose()
  */
 function dispose() {
     let args = window.arguments[0];
     if (args.job && args.job.dispose) {
         args.job.dispose();
     }
-    resetDialogId(document.documentElement);
 }
 
 /**
  * Sets the id of a Dialog to another value to allow different window-icons to be displayed.
  * The original name is stored as new Attribute of the Dialog to set it back later.
  *
  * @param aDialog               The Dialog to be changed.
  * @param aNewId                The new ID as String.
  */
 function setDialogId(aDialog, aNewId) {
-    aDialog.setAttribute("originalId", aDialog.getAttribute("id"));
     aDialog.setAttribute("id", aNewId);
-    applyPersitedProperties(aDialog);
-}
-
-/**
- * Sets the Dialog id back to previously stored one,
- * so that the persisted values are correctly saved.
- *
- * @param aDialog               The Dialog which is to be restored.
- */
-function resetDialogId(aDialog) {
-    let id = aDialog.getAttribute("originalId");
-    if (id != "") {
-        aDialog.setAttribute("id", id);
-    }
-    aDialog.removeAttribute("originalId");
+    applyPersistedProperties(aDialog);
 }
 
 /**
  * Apply the persisted properties from xulstore.json on a dialog based on the current dialog id.
  * This needs to be invoked after changing a dialog id while loading to apply the values for the
  * new dialog id.
  *
  * @param aDialog               The Dialog to apply the property values for
  */
-function applyPersitedProperties(aDialog) {
+function applyPersistedProperties(aDialog) {
     let xulStore = Services.xulStore;
     // first we need to detect which properties are persisted
     let persistedProps = aDialog.getAttribute("persist") || "";
     if (persistedProps == "") {
         return;
     }
     let propNames = persistedProps.split(" ");
+    let { outerWidth: width, outerHeight: height } = aDialog;
+    let doResize = false;
     // now let's apply persisted values if applicable
     for (let propName of propNames) {
         if (xulStore.hasValue(aDialog.baseURI, aDialog.id, propName)) {
-            aDialog.setAttribute(propName, xulStore.getValue(aDialog.baseURI, aDialog.id, propName));
+            let propValue = xulStore.getValue(aDialog.baseURI, aDialog.id, propName);
+            if (propName == "width") {
+                width = propValue;
+                doResize = true;
+            } else if (propName == "height") {
+                height = propValue;
+                doResize = true;
+            } else {
+                aDialog.setAttribute(propName, propValue);
+            }
         }
     }
+
+    if (doResize) {
+        aDialog.ownerGlobal.resizeTo(width, height);
+    }
 }
 
 /**
  * Create a calIAlarm from the given menuitem. The menuitem must have the
  * following attributes: unit, length, origin, relation.
  *
  * @param menuitem      The menuitem to create the alarm from.
  * @return              The calIAlarm with information from the menuitem.
--- a/calendar/base/content/dialogs/calendar-event-dialog.xul
+++ b/calendar/base/content/dialogs/calendar-event-dialog.xul
@@ -25,18 +25,17 @@
     %calendarDTD;
     %eventDialogDTD;
 ]>
 
 <?xul-overlay href="chrome://lightning/content/lightning-item-toolbar.xul"?>
 
 <!-- Dialog id is changed during excution to allow different Window-icons
      on this dialog. document.loadOverlay() will not work on this one. -->
-<dialog id="calendar-event-dialog"
-        title="&event.title.label;"
+<dialog title="&event.title.label;"
         windowtype="Calendar:EventDialog"
         onload="onLoadLightningItemPanel();"
         onunload="onUnloadLightningItemPanel();"
         ondialogaccept="return onAccept();"
         ondialogcancel="return onCancel();"
         persist="screenX screenY width height"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
--- a/calendar/lightning/content/lightning-item-panel.js
+++ b/calendar/lightning/content/lightning-item-panel.js
@@ -227,21 +227,20 @@ function onLoadLightningItemPanel(aIfram
         iframe = document.createElement("iframe");
         iframeSrc = window.arguments[0].useNewItemUI
             ? "chrome://lightning/content/html-item-editing/lightning-item-iframe.html"
             : "chrome://lightning/content/lightning-item-iframe.xul";
 
         iframe.setAttribute("id", "lightning-item-panel-iframe");
         iframe.setAttribute("flex", "1");
 
-        let dialog = document.getElementById("calendar-event-dialog");
         let statusbar = document.getElementById("status-bar");
 
         // Note: iframe.contentWindow is undefined before the iframe is inserted here.
-        dialog.insertBefore(iframe, statusbar);
+        document.documentElement.insertBefore(iframe, statusbar);
 
         // Move the args so they are positioned relative to the iframe,
         // for the window dialog just as they are for the tab.
         // XXX Should we delete the arguments here in the parent context
         // so they are only accessible in one place?
         iframe.contentWindow.arguments = [window.arguments[0]];
 
         // hide the ok and cancel dialog buttons
@@ -250,25 +249,63 @@ function onLoadLightningItemPanel(aIfram
         accept.setAttribute("collapsed", "true");
         cancel.setAttribute("collapsed", "true");
         cancel.parentNode.setAttribute("collapsed", "true");
 
         // set toolbar icon color for light or dark themes
         if (typeof ToolbarIconColor !== "undefined") {
             ToolbarIconColor.init();
         }
+
+        // Enlarge the dialog window so the iframe content fits, and prevent it
+        // getting smaller. We don't know the minimum size of the content unless
+        // it's overflowing, so don't attempt to enforce what we don't know.
+        let docEl = document.documentElement;
+        let overflowListener = () => {
+            let { scrollWidth, scrollHeight } = iframe.contentDocument.documentElement;
+            let { clientWidth, clientHeight } = iframe;
+
+            let diffX = scrollWidth - clientWidth;
+            let diffY = scrollHeight - clientHeight;
+            // If using a scaled screen resolution, rounding might cause
+            // scrollWidth/scrollHeight to be 1px larger than
+            // clientWidth/clientHeight, so we check for a difference
+            // greater than 1 here, not 0.
+            if (diffX > 1) {
+                window.resizeBy(diffX, 0);
+                docEl.setAttribute("minwidth", docEl.getAttribute("width"));
+            }
+            if (diffY > 1) {
+                window.resizeBy(0, diffY);
+                docEl.setAttribute("minheight", docEl.getAttribute("height"));
+            }
+            if (docEl.hasAttribute("minwidth") && docEl.hasAttribute("minheight")) {
+                iframe.contentWindow.removeEventListener("resize", overflowListener);
+            }
+        };
+        iframe.contentWindow.addEventListener("load", () => {
+            // This is the first listener added, but it should run after all the others,
+            // so that they can properly set up the layout they might need.
+            // Push to the end of the event queue.
+            setTimeout(overflowListener, 0);
+        }, { once: true });
+        iframe.contentWindow.addEventListener("resize", overflowListener);
     }
 
     // event or task
     let calendarItem = iframe.contentWindow.arguments[0].calendarEvent;
     gConfig.isEvent = cal.item.isEvent(calendarItem);
 
     // for tasks in a window dialog, set the dialog id for CSS selection, etc.
-    if (!gTabmail && !gConfig.isEvent) {
-        setDialogId(document.documentElement, "calendar-task-dialog");
+    if (!gTabmail) {
+        if (gConfig.isEvent) {
+            setDialogId(document.documentElement, "calendar-event-dialog");
+        } else {
+            setDialogId(document.documentElement, "calendar-task-dialog");
+        }
     }
 
     // timezones enabled
     gConfig.timezonesEnabled = getTimezoneCommandState();
     iframe.contentWindow.gTimezonesEnabled = gConfig.timezonesEnabled;
 
     // toggle link
     let cmdToggleLink = document.getElementById("cmd_toggle_link");