diff --git a/browser/components/places/PlacesUIUtils.jsm b/browser/components/places/PlacesUIUtils.jsm index 8faaa9d53400..07a4126ab844 100644 --- a/browser/components/places/PlacesUIUtils.jsm +++ b/browser/components/places/PlacesUIUtils.jsm @@ -461,21 +461,17 @@ this.PlacesUIUtils = { showBookmarkDialog: function PUIU_showBookmarkDialog(aInfo, aParentWindow) { // Preserve size attributes differently based on the fact the dialog has - // a folder picker or not. If the picker is visible, the dialog should - // be resizable since it may not show enough content for the folders - // hierarchy. + // a folder picker or not, since it needs more horizontal space than the + // other controls. let hasFolderPicker = !("hiddenRows" in aInfo) || aInfo.hiddenRows.indexOf("folderPicker") == -1; - // Use a different chrome url, since this allows to persist different sizes, - // based on resizability of the dialog. + // Use a different chrome url to persist different sizes. let dialogURL = hasFolderPicker ? "chrome://browser/content/places/bookmarkProperties2.xul" : "chrome://browser/content/places/bookmarkProperties.xul"; - let features = - "centerscreen,chrome,modal,resizable=" + (hasFolderPicker ? "yes" : "no"); - - aParentWindow.openDialog(dialogURL, "", features, aInfo); + let features = "centerscreen,chrome,modal,resizable=yes"; + aParentWindow.openDialog(dialogURL, "", features, aInfo); return ("performed" in aInfo && aInfo.performed); }, diff --git a/browser/components/places/content/bookmarkProperties.js b/browser/components/places/content/bookmarkProperties.js index 940443dd7f47..016a3578fd32 100644 --- a/browser/components/places/content/bookmarkProperties.js +++ b/browser/components/places/content/bookmarkProperties.js @@ -72,6 +72,8 @@ const LIVEMARK_CONTAINER = 2; const ACTION_EDIT = 0; const ACTION_ADD = 1; +let elementsHeight = new Map(); + var BookmarkPropertiesPanel = { /** UI Text Strings */ @@ -271,6 +273,43 @@ var BookmarkPropertiesPanel = { var acceptButton = document.documentElement.getButton("accept"); acceptButton.label = this._getAcceptLabel(); + // Do not use sizeToContent, otherwise, due to bug 90276, the dialog will + // grow at every opening. + // Since elements can be uncollapsed asynchronously, we must observe their + // mutations and resize the dialog using a cached element size. + this._height = window.outerHeight; + this._mutationObserver = new MutationObserver(mutations => { + for (let mutation of mutations) { + let target = mutation.target; + let id = target.id; + if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id)) + continue; + + let collapsed = target.getAttribute("collapsed") === "true"; + let wasCollapsed = mutation.oldValue === "true"; + if (collapsed == wasCollapsed) + continue; + + if (collapsed) { + this._height -= elementsHeight.get(id); + elementsHeight.delete(id); + } else { + elementsHeight.set(id, target.boxObject.height); + this._height += elementsHeight.get(id); + } + window.resizeTo(window.outerWidth, this._height); + } + }); + + this._mutationObserver.observe(document, + { subtree: true, + attributeOldValue: true, + attributeFilter: ["collapsed"] }); + + // Some controls are flexible and we want to update their cached size when + // the dialog is resized. + window.addEventListener("resize", this); + this._beginBatch(); switch (this._action) { @@ -300,25 +339,6 @@ var BookmarkPropertiesPanel = { break; } - // Adjust the dialog size to the changes done by initPanel. This is necessary because - // initPanel, which shows and hides elements, may run after some async work was done - // here - i.e. after the DOM load event was processed. - window.sizeToContent(); - - // When collapsible elements change their collapsed attribute we must - // resize the dialog. - // sizeToContent is not usable due to bug 90276, so we'll use resizeTo - // instead and cache the element size. See WSucks in the legacy - // UI code (addBookmark2.js). - if (!this._element("tagsRow").collapsed) { - this._element("tagsSelectorRow") - .addEventListener("DOMAttrModified", this, false); - } - if (!this._element("folderRow").collapsed) { - this._element("folderTreeRow") - .addEventListener("DOMAttrModified", this, false); - } - if (!gEditItemOverlay.readOnly) { // Listen on uri fields to enable accept button if input is valid if (this._itemType == BOOKMARK_ITEM) { @@ -330,12 +350,9 @@ var BookmarkPropertiesPanel = { } } } - - window.sizeToContent(); }), // nsIDOMEventListener - _elementsHeight: [], handleEvent: function BPP_handleEvent(aEvent) { var target = aEvent.target; switch (aEvent.type) { @@ -347,24 +364,11 @@ var BookmarkPropertiesPanel = { .getButton("accept").disabled = !this._inputIsValid(); } break; - - case "DOMAttrModified": - // this is called when collapsing a node, but also its direct children, - // we only need to resize when the original node changes. - if ((target.id == "editBMPanel_tagsSelectorRow" || - target.id == "editBMPanel_folderTreeRow") && - aEvent.attrName == "collapsed" && - target == aEvent.originalTarget) { - var id = target.id; - var newHeight = window.outerHeight; - if (aEvent.newValue) // is collapsed - newHeight -= this._elementsHeight[id]; - else { - this._elementsHeight[id] = target.boxObject.height; - newHeight += this._elementsHeight[id]; - } - - window.resizeTo(window.outerWidth, newHeight); + case "resize": + for (let [id, oldHeight] of elementsHeight) { + let newHeight = document.getElementById(id).boxObject.height; + this._height += - oldHeight + newHeight; + elementsHeight.set(id, newHeight); } break; } @@ -418,12 +422,13 @@ var BookmarkPropertiesPanel = { onDialogUnload() { // gEditItemOverlay does not exist anymore here, so don't rely on it. + this._mutationObserver.disconnect(); + delete this._mutationObserver; + + window.removeEventListener("resize", this); + // Calling removeEventListener with arguments which do not identify any // currently registered EventListener on the EventTarget has no effect. - this._element("tagsSelectorRow") - .removeEventListener("DOMAttrModified", this, false); - this._element("folderTreeRow") - .removeEventListener("DOMAttrModified", this, false); this._element("locationField") .removeEventListener("input", this, false); }, diff --git a/browser/components/places/content/editBookmarkOverlay.xul b/browser/components/places/content/editBookmarkOverlay.xul index 2f6f8e7bc007..140e752c0484 100644 --- a/browser/components/places/content/editBookmarkOverlay.xul +++ b/browser/components/places/content/editBookmarkOverlay.xul @@ -24,7 +24,9 @@ - + - + - + - + - + - + - +