Bug 961431 - Autocomplete suggestions in split console are sliding down. r=robcee
authorMihai Sucan <mihai.sucan@gmail.com>
Tue, 28 Jan 2014 15:55:19 +0200
changeset 165632 ea49291871ef8e22ef8ba632787779c5df834a92
parent 165631 afed99fd2c9f6ade5ff025723108cf4c2f301172
child 165633 24979a717e7d9eed81675a4e57a3e5ae62458b33
push id4624
push userryanvm@gmail.com
push dateTue, 28 Jan 2014 23:09:06 +0000
treeherderfx-team@ea49291871ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrobcee
bugs961431
milestone29.0a1
Bug 961431 - Autocomplete suggestions in split console are sliding down. r=robcee
browser/devtools/inspector/selector-search.js
browser/devtools/markupview/markup-view.js
browser/devtools/shared/autocomplete-popup.js
browser/devtools/styleinspector/rule-view.js
--- a/browser/devtools/inspector/selector-search.js
+++ b/browser/devtools/inspector/selector-search.js
@@ -45,17 +45,16 @@ function SelectorSearch(aInspector, aCon
   this._onHTMLSearch = this._onHTMLSearch.bind(this);
   this._onSearchKeypress = this._onSearchKeypress.bind(this);
   this._onListBoxKeypress = this._onListBoxKeypress.bind(this);
 
   // Options for the AutocompletePopup.
   let options = {
     panelId: "inspector-searchbox-panel",
     listBoxId: "searchbox-panel-listbox",
-    fixedWidth: true,
     autoSelect: true,
     position: "before_start",
     direction: "ltr",
     onClick: this._onListBoxKeypress,
     onKeypress: this._onListBoxKeypress,
   };
   this.searchPopup = new AutocompletePopup(this.panelDoc, options);
 
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -71,17 +71,16 @@ function MarkupView(aInspector, aFrame, 
   try {
     this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
   } catch(ex) {
     this.maxChildren = DEFAULT_MAX_CHILDREN;
   }
 
   // Creating the popup to be used to show CSS suggestions.
   let options = {
-    fixedWidth: true,
     autoSelect: true,
     theme: "auto"
   };
   this.popup = new AutocompletePopup(this.doc.defaultView.parent.document, options);
 
   this.undo = new UndoStack();
   this.undo.installController(aControllerWindow);
 
--- a/browser/devtools/shared/autocomplete-popup.js
+++ b/browser/devtools/shared/autocomplete-popup.js
@@ -20,27 +20,25 @@ loader.lazyImporter(this, "gDevTools", "
  * @param Object aOptions
  *        An object consiting any of the following options:
  *        - panelId {String} The id for the popup panel.
  *        - listBoxId {String} The id for the richlistbox inside the panel.
  *        - position {String} The position for the popup panel.
  *        - theme {String} String related to the theme of the popup.
  *        - autoSelect {Boolean} Boolean to allow the first entry of the popup
  *                     panel to be automatically selected when the popup shows.
- *        - fixedWidth {Boolean} Boolean to control dynamic width of the popup.
  *        - direction {String} The direction of the text in the panel. rtl or ltr
  *        - onSelect {String} The select event handler for the richlistbox
  *        - onClick {String} The click event handler for the richlistbox.
  *        - onKeypress {String} The keypress event handler for the richlistitems.
  */
 function AutocompletePopup(aDocument, aOptions = {})
 {
   this._document = aDocument;
 
-  this.fixedWidth = aOptions.fixedWidth || false;
   this.autoSelect = aOptions.autoSelect || false;
   this.position = aOptions.position || "after_start";
   this.direction = aOptions.direction || "ltr";
 
   this.onSelect = aOptions.onSelect;
   this.onClick = aOptions.onClick;
   this.onKeypress = aOptions.onKeypress;
 
@@ -70,17 +68,16 @@ function AutocompletePopup(aDocument, aO
 
     let mainPopupSet = this._document.getElementById("mainPopupSet");
     if (mainPopupSet) {
       mainPopupSet.appendChild(this._panel);
     }
     else {
       this._document.documentElement.appendChild(this._panel);
     }
-    this._list = null;
   }
   else {
     this._list = this._panel.firstChild;
   }
 
   if (!this._list) {
     this._list = this._document.createElementNS(XUL_NS, "richlistbox");
     this._panel.appendChild(this._list);
@@ -132,24 +129,23 @@ AutocompletePopup.prototype = {
    *        Horizontal offset in pixels from the left of the node to the left
    *        of the popup.
    * @param Number aYOffset
    *        Vertical offset in pixels from the top of the node to the starting
    *        of the popup.
    */
   openPopup: function AP_openPopup(aAnchor, aXOffset = 0, aYOffset = 0)
   {
+    this.__maxLabelLength = -1;
+    this._updateSize();
     this._panel.openPopup(aAnchor, this.position, aXOffset, aYOffset);
 
     if (this.autoSelect) {
       this.selectFirstItem();
     }
-    if (!this.fixedWidth) {
-      this._updateSize();
-    }
   },
 
   /**
    * Hide the autocomplete popup panel.
    */
   hidePopup: function AP_hidePopup()
   {
     this._panel.hidePopup();
@@ -236,90 +232,95 @@ AutocompletePopup.prototype = {
     this.clearItems();
     aItems.forEach(this.appendItem, this);
 
     // Make sure that the new content is properly fitted by the XUL richlistbox.
     if (this.isOpen) {
       if (this.autoSelect) {
         this.selectFirstItem();
       }
-      if (!this.fixedWidth) {
-        this._updateSize();
-      }
+      this._updateSize();
     }
   },
 
   /**
    * Selects the first item of the richlistbox. Note that first item here is the
    * item closes to the input element, which means that 0th index if position is
    * below, and last index if position is above.
    */
   selectFirstItem: function AP_selectFirstItem()
   {
     if (this.position.contains("before")) {
       this.selectedIndex = this.itemCount - 1;
     }
     else {
       this.selectedIndex = 0;
     }
+    this._list.ensureIndexIsVisible(this._list.selectedIndex);
+  },
+
+  __maxLabelLength: -1,
+
+  get _maxLabelLength() {
+    if (this.__maxLabelLength != -1) {
+      return this.__maxLabelLength;
+    }
+
+    let max = 0;
+    for (let i = 0; i < this._list.childNodes.length; i++) {
+      let item = this._list.childNodes[i]._autocompleteItem;
+      let str = item.label;
+      if (item.count) {
+        str += (item.count + "");
+      }
+      max = Math.max(str.length, max);
+    }
+
+    this.__maxLabelLength = max;
+    return this.__maxLabelLength;
   },
 
   /**
    * Update the panel size to fit the content.
    *
    * @private
    */
   _updateSize: function AP__updateSize()
   {
     if (!this._panel) {
       return;
     }
-    // Flush the layout so that we get the latest height.
-    this._panel.boxObject.height;
-    let height = {};
-    this._list.scrollBoxObject.getScrolledSize({}, height);
-    // Change the width of the popup only if the scrollbar is visible.
-    if (height.value > this._panel.clientHeight) {
-      this._list.width = this._panel.clientWidth + this._scrollbarWidth;
-    }
-    // Height change is required, otherwise the panel is drawn at an offset
-    // the first time.
-    this._list.height = this._list.clientHeight;
-    // This brings the panel back at right position.
-    this._list.top = 0;
-    // Move the panel to -1,-1 to realign the popup with its anchor node when
-    // decreasing the panel height.
-    this._panel.moveTo(-1, -1);
-    // Changing panel height might make the selected item out of view, so
-    // bring it back to view.
+
+    this._list.style.width = (this._maxLabelLength + 3) +"ch";
     this._list.ensureIndexIsVisible(this._list.selectedIndex);
   },
 
   /**
    * Clear all the items from the autocomplete list.
    */
   clearItems: function AP_clearItems()
   {
     // Reset the selectedIndex to -1 before clearing the list
     this.selectedIndex = -1;
 
     while (this._list.hasChildNodes()) {
       this._list.removeChild(this._list.firstChild);
     }
 
-    if (!this.fixedWidth) {
-      // Reset the panel and list dimensions. New dimensions are calculated when
-      // a new set of items is added to the autocomplete popup.
-      this._list.width = "";
-      this._list.height = "";
-      this._panel.width = "";
-      this._panel.height = "";
-      this._panel.top = "";
-      this._panel.left = "";
-    }
+    this.__maxLabelLength = -1;
+
+    // Reset the panel and list dimensions. New dimensions are calculated when
+    // a new set of items is added to the autocomplete popup.
+    this._list.width = "";
+    this._list.style.width = "";
+    this._list.height = "";
+    this._panel.width = "";
+    this._panel.height = "";
+    this._panel.top = "";
+    this._panel.left = "";
   },
 
   /**
    * Getter for the index of the selected item.
    *
    * @type number
    */
   get selectedIndex() {
@@ -492,48 +493,16 @@ AutocompletePopup.prototype = {
    * Focuses the richlistbox.
    */
   focus: function AP_focus()
   {
     this._list.focus();
   },
 
   /**
-   * Determine the scrollbar width in the current document.
-   *
-   * @private
-   */
-  get _scrollbarWidth()
-  {
-    if (this.__scrollbarWidth !== null) {
-      return this.__scrollbarWidth;
-    }
-
-    let doc = this._document;
-    if (doc.defaultView.matchMedia("(-moz-overlay-scrollbars)").matches) {
-      // This is for the Mac's floating scrollbar, which actually is drawn over
-      // the content, thus taking no extra width.
-      return (this.__scrollbarWidth = 0);
-    }
-
-    let hbox = doc.createElementNS(XUL_NS, "hbox");
-    hbox.setAttribute("style", "height: 0%; overflow: hidden");
-
-    let scrollbar = doc.createElementNS(XUL_NS, "scrollbar");
-    scrollbar.setAttribute("orient", "vertical");
-    hbox.appendChild(scrollbar);
-    doc.documentElement.appendChild(hbox);
-
-    this.__scrollbarWidth = scrollbar.clientWidth;
-    doc.documentElement.removeChild(hbox);
-
-    return this.__scrollbarWidth;
-  },
-
-  /**
    * Manages theme switching for the popup based on the devtools.theme pref.
    *
    * @private
    *
    * @param String aEvent
    *        The name of the event. In this case, "pref-changed".
    * @param Object aData
    *        An object passed by the emitter of the event. In this case, the
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1084,17 +1084,16 @@ function CssRuleView(aInspector, aDoc, a
   this._handlePrefChange = this._handlePrefChange.bind(this);
   gDevTools.on("pref-changed", this._handlePrefChange);
 
   this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
 
   let options = {
-    fixedWidth: true,
     autoSelect: true,
     theme: "auto"
   };
   this.popup = new AutocompletePopup(aDoc.defaultView.parent.document, options);
 
   // Create a tooltip for previewing things in the rule view (images for now)
   this.previewTooltip = new Tooltip(this.inspector.panelDoc);
   this.previewTooltip.startTogglingOnHover(this.element,