Bug 1316404 Dropdown menu list is using wrong font family. r?jaws,mconley draft
authorMark Golbeck <markgolbeck08@gmail.com>
Thu, 17 Nov 2016 12:56:30 -0800
changeset 441127 9ecdf8adaa287a655b22c88819bb7c71432be913
parent 440515 cfc5fb7830be3f347963ef43b861d4592ae5b8f0
child 537503 213ca9ae4bf40947418bfa754789f72240623fb1
push id36368
push usermarkgolbeck08@gmail.com
push dateFri, 18 Nov 2016 14:53:04 +0000
reviewersjaws, mconley
bugs1316404
milestone52.0a1
Bug 1316404 Dropdown menu list is using wrong font family. r?jaws,mconley MozReview-Commit-ID: FFPXd27Gac1
browser/themes/windows/browser.css
toolkit/modules/SelectParentHelper.jsm
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2649,45 +2649,41 @@ notification.pluginVulnerable > .notific
     border-radius: 4px;
   }
 }
 
 .webextension-popup-browser {
   border-radius: inherit;
 }
 
-/* select dropdown changes listed below: */
-
 .contentSelectDropdown-ingroup > .menu-iconic-text {
   padding-inline-start: 20px;
 }
 
 #ContentSelectDropdown > menupopup {
   background-color: -moz-field;
   -moz-border-top-colors: GrayText;
   -moz-border-right-colors: GrayText;
   -moz-border-bottom-colors: GrayText;
   -moz-border-left-colors: GrayText;
 }
 
 #ContentSelectDropdown > menupopup > menucaption,
 #ContentSelectDropdown > menupopup > menuitem {
-  padding: 4px 6px;
+  padding: 0 6px;
+  border-width: 0;
 }
 
 #ContentSelectDropdown > menupopup > menucaption > .menu-iconic-text,
 #ContentSelectDropdown > menupopup > menuitem > .menu-iconic-text {
-  font: message-box;
-  font-size: 11px;
-  /**
-   * Remove the extra vertical padding set by menu.css since
-   * the menuitem itself will include enough padding.
-   */
-  padding-top: 0px;
-  padding-bottom: 0px;
+  padding-top: 0.3636em;   
+  padding-bottom: 0.3636em;
+  /** em = desired element pixel value / parent element font-size in pixels
+      0.3636em = 4px / 11px
+  */
 }
 
 #ContentSelectDropdown > menupopup > menucaption > .menu-iconic-text {
   font-weight: bold;
 }
 
 #ContentSelectDropdown > menupopup > menuitem[_moz-menuactive="true"][disabled="true"] {
   color: GrayText;
@@ -2697,62 +2693,13 @@ notification.pluginVulnerable > .notific
 #ContentSelectDropdown > menupopup > menucaption {
   background-color: buttonface;
 }
 
 #ContentSelectDropdown > menupopup > menucaption[disabled="true"] {
   color: GrayText;
 }
 
-#ContentSelectDropdown > .isOpenedViaTouch > menucaption,
-#ContentSelectDropdown > .isOpenedViaTouch > menuitem {
-  padding-top: 11px;
-  padding-bottom: 11px;
-}
-
-.contentSelectDropdown-ingroup > .menu-iconic-text {
-  padding-inline-start: 20px;
-}
-
-#ContentSelectDropdown > menupopup {
-  background-color: -moz-field;
-  -moz-border-top-colors: GrayText;
-  -moz-border-right-colors:GrayText;
-  -moz-border-bottom-colors:GrayText;
-  -moz-border-left-colors:GrayText;
-}
-
-#ContentSelectDropdown > menupopup > menucaption,
-#ContentSelectDropdown > menupopup > menuitem {
-  padding: 4px 6px;
-}
-
-#ContentSelectDropdown > menupopup > menucaption > .menu-iconic-text,
-#ContentSelectDropdown > menupopup > menuitem > .menu-iconic-text {
-  font: message-box;
-  font-size: 11px; /* use rem here instead of px proportional to 11 px
-  /**
-   * Remove the extra vertical padding set by menu.css since
-   * the menuitem itself will include enough padding.
-   */
-  padding-top: 0px;      /* make proportional to 4 px */ 
-  padding-bottom: 0px;
-}
-
-#ContentSelectDropdown > menupopup > menuitem[_moz-menuactive="true"][disabled="true"] {
-    color: Graytext;
-    background-color: unset;
-}
-
-#ContentSelectDropdown > menupopup > menucaption {
-  background-color: buttonface;
-}
-
-#ContentSelectDropdown > menupopup > menucaption[disabled="true"] {
-  color: Graytext;
-}
-
-@media (-moz-touch-enabled) {
-  #ContentSelectDropdown > menupopup > menuitem {
-    padding-top: 11px;
-    padding-bottom: 11px;
-  }
+#ContentSelectDropdown > .isOpenedViaTouch > menucaption > .menu-iconic-text,
+#ContentSelectDropdown > .isOpenedViaTouch > menuitem > .menu-iconic-text {
+  padding-top: 0.9167em;
+  padding-bottom: 0.9167em;
 }
\ No newline at end of file
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -3,37 +3,52 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
   "SelectParentHelper"
 ];
 
+const {utils: Cu} = Components;
+const { AppConstants } = Cu.import("resource://gre/modules/AppConstants.jsm");
+
 // Maximum number of rows to display in the select dropdown.
 const MAX_ROWS = 20;
 
 var currentBrowser = null;
 var currentMenulist = null;
 var currentZoom = 1;
 var closedWithEnter = false;
 
+//CPST
+var lastColor = "";
+var lastBackground = "";
+//END CPST
+
 this.SelectParentHelper = {
+
   populate: function(menulist, items, selectedIndex, zoom) {
     // Clear the current contents of the popup
     menulist.menupopup.textContent = "";
     currentZoom = zoom;
     currentMenulist = menulist;
+
     populateChildren(menulist, items, selectedIndex, zoom);
   },
 
   open: function(browser, menulist, rect, isOpenedViaTouch) {
     menulist.hidden = false;
     currentBrowser = browser;
     closedWithEnter = false;
+    //let selectedItem = menulist.selectedItem;
+    //if(selectedItem){
+    //    selectedItem.style.color = lastColor;
+    //    selectedItem.style.backgroundColor = lastBackground;
+    //}
     this._registerListeners(browser, menulist.menupopup);
 
     let win = browser.ownerDocument.defaultView;
 
     // Set the maximum height to show exactly MAX_ROWS items.
     let menupopup = menulist.menupopup;
     let firstItem = menupopup.firstChild;
     while (firstItem && firstItem.hidden) {
@@ -52,43 +67,46 @@ this.SelectParentHelper = {
 
     menupopup.classList.toggle("isOpenedViaTouch", isOpenedViaTouch);
 
     let constraintRect = browser.getBoundingClientRect();
     constraintRect = new win.DOMRect(constraintRect.left + win.mozInnerScreenX,
                                      constraintRect.top + win.mozInnerScreenY,
                                      constraintRect.width, constraintRect.height);
     menupopup.setConstraintRect(constraintRect);
-    menupopup.openPopupAtScreenRect("after_start", rect.left, rect.top, rect.width, rect.height, false, false);
+    menupopup.openPopupAtScreenRect(AppConstants.platform == "macosx" ? "selection" : "after_start", rect.left, rect.top, rect.width, rect.height, false, false);
   },
 
   hide: function(menulist, browser) {
     if (currentBrowser == browser) {
       menulist.menupopup.hidePopup();
     }
   },
 
   handleEvent: function(event) {
     switch (event.type) {
       case "mouseover":
         currentBrowser.messageManager.sendAsyncMessage("Forms:MouseOver", {});
+      
         break;
 
       case "mouseout":
         currentBrowser.messageManager.sendAsyncMessage("Forms:MouseOut", {});
         break;
 
       case "keydown":
         if (event.keyCode == event.DOM_VK_RETURN) {
           closedWithEnter = true;
         }
         break;
 
       case "command":
         if (event.target.hasAttribute("value")) {
+          //event.target.style.color = "";
+          //event.target.style.backgroundColor = "";
           let win = currentBrowser.ownerDocument.defaultView;
 
           currentBrowser.messageManager.sendAsyncMessage("Forms:SelectDropDownItem", {
             value: event.target.value,
             closedWithEnter: closedWithEnter
           });
         }
         break;
@@ -142,71 +160,62 @@ this.SelectParentHelper = {
     popup.removeEventListener("mouseout", this);
     browser.ownerDocument.defaultView.removeEventListener("keydown", this, true);
     browser.ownerDocument.defaultView.removeEventListener("fullscreen", this, true);
     browser.messageManager.removeMessageListener("Forms:UpdateDropDown", this);
   },
 
 };
 
-// code for working on the scaling of the padding to text size
-
 function populateChildren(menulist, options, selectedIndex, zoom,
-                          parentElement = null, isGroupDisabled = false, adjustedTextSize = -1) {
-
+                          parentElement = null, isGroupDisabled = false, adjustedTextSize = -1, addSearch = true) {
   let element = menulist.menupopup;
-  let adjustedTextRatio; 
-  let win = element.ownerDocument.defaultView;
 
   // -1 just means we haven't calculated it yet. When we recurse through this function
   // we will pass in adjustedTextSize to save on recalculations.
   if (adjustedTextSize == -1) {
+    let win = element.ownerDocument.defaultView;
 
     // Grab the computed text size and multiply it by the remote browser's fullZoom to ensure
     // the popup's text size is matched with the content's. We can't just apply a CSS transform
     // here as the popup's preferred size is calculated pre-transform.
     let textSize = win.getComputedStyle(element).getPropertyValue("font-size");
-    adjustedTextRatio = (zoom * parseFloat(textSize, 10));
-    adjustedTextSize = adjustedTextRatio + "px";
+    adjustedTextSize = (zoom * parseFloat(textSize, 10)) + "px";
+
   }
 
   for (let option of options) {
     let isOptGroup = (option.tagName == 'OPTGROUP');
     let item = element.ownerDocument.createElement(isOptGroup ? "menucaption" : "menuitem");
 
     item.setAttribute("label", option.textContent);
     item.style.direction = option.textDirection;
     item.style.fontSize = adjustedTextSize;
+    
+    if(!isOptGroup && option.color != ""){
+      item.style.color = option.color;
+    }
+    if(!isOptGroup && option.backgroundColor != ""){
+      item.style.backgroundColor = option.backgroundColor;
+    }
+
     item.hidden = option.display == "none" || (parentElement && parentElement.hidden);
     item.setAttribute("tooltiptext", option.tooltip);
 
     element.appendChild(item);
-    
-    // element.ownerDocument.getAnonymousElementByAttribute(element, attribute, value);
-
-    //let menuCaption = element.ownerDocument.getAnonymousElementByAttribute(item, "class", "menu-iconic-text");
-
-    //let shadowChildren = element.ownerDocument.getAnonymousNodes(element);
-
-    //let textSizeValue = parseFloat(win.getComputedStyle(item).getPropertyValue("font-size"),10);
-    //let padding = parseFloat(win.getComputedStyle(item).getPropertyValue("padding-top"),10);
-    //let paddingRatio = parseFloat((padding / adjustedTextRatio),10);
-
-    //item.style.paddingTop = (textSizeValue * paddingRatio) + "px";
-    //item.style.paddingBottom = (textSizeValue * paddingRatio) + "px";
 
     // A disabled optgroup disables all of its child options.
     let isDisabled = isGroupDisabled || option.disabled;
     if (isDisabled) {
       item.setAttribute("disabled", "true");
     }
 
     if (isOptGroup) {
       populateChildren(menulist, option.children, selectedIndex, zoom,
-                       item, isDisabled, adjustedTextSize);
+                       item, isDisabled, adjustedTextSize, false);
     } else {
       if (option.index == selectedIndex) {
         // We expect the parent element of the popup to be a <xul:menulist> that
         // has the popuponly attribute set to "true". This is necessary in order
         // for a <xul:menupopup> to act like a proper <html:select> dropdown, as
         // the <xul:menulist> does things like remember state and set the
         // _moz-menuactive attribute on the selected <xul:menuitem>.
         menulist.selectedItem = item;
@@ -221,9 +230,82 @@ function populateChildren(menulist, opti
 
       item.setAttribute("value", option.index);
 
       if (parentElement) {
         item.classList.add("contentSelectDropdown-ingroup")
       }
     }
   }
+
+  // Check if this is the first time iterating through the dropdown and if list is
+  //  long enough for a search element to be added.
+  if(addSearch && element.childElementCount > 40){
+
+    // Add a search text field as the first element of the dropdown
+    let searchbox = element.ownerDocument.createElement("textbox");
+    searchbox.setAttribute("type", "search");
+    searchbox.addEventListener("input", onSearchInput);
+    element.insertBefore(searchbox, element.childNodes[0]);
+  }
+
 }
+
+function onSearchInput(){
+
+  let searchObj = this;
+
+  // Get input from search field, set to all lower case for comparison
+  let input = searchObj.value.toLowerCase();
+
+  // Get all items in dropdown (could be options or optgroups)
+  let menupopup = searchObj.parentElement;
+  let menuItems = menupopup.querySelectorAll("menuitem, menucaption");
+
+  // Flag used to detect any group headers with no visible options.
+  //  These group headers should be hidden.
+  let allHidden = true;
+
+  // Keep a reference to the previous group header (menucaption) to go back
+  //  and set to hidden if all options within are hidden.
+  let prevCaption = null;
+
+  for (let currentItem of menuItems) {
+    // Get label and tooltip (title) from option and change to
+    //  lower case for comparison
+    let itemLabel = currentItem.getAttribute("label").toLowerCase();
+    let itemTooltip = currentItem.getAttribute("title").toLowerCase();
+
+    // If search input is empty, all options should be shown
+    if(input==""){
+      currentItem.setAttribute("hidden", "false");
+
+    } else if(currentItem.localName=="menucaption"){
+      if(prevCaption!=null){
+          prevCaption.setAttribute("hidden", (allHidden ? "true" : "false"));
+      }
+      prevCaption = currentItem;
+      allHidden = true;
+
+    } else{
+      if(!currentItem.classList.contains("contentSelectDropdown-ingroup") && currentItem.previousSibling.classList.contains("contentSelectDropdown-ingroup")){
+        if(prevCaption!=null){
+            prevCaption.setAttribute("hidden", (allHidden ? "true" : "false"));
+        }
+        prevCaption = null;
+        allHidden = true;
+      }
+
+      if(itemLabel.includes(input) || itemTooltip.includes(input)){
+        currentItem.setAttribute("hidden", "false");
+
+        allHidden = false;
+      } else{
+        currentItem.setAttribute("hidden", "true");
+      }
+    }
+
+  }
+  if(prevCaption!=null && allHidden){
+    prevCaption.setAttribute("hidden", (allHidden ? "true" : "false"));
+  }
+
+}