Bug 732265 - (2/2) Support XUL menulist controls in native Fennec SelectHelper [r=margaret]
authorMatt Brubeck <mbrubeck@mozilla.com>
Thu, 01 Mar 2012 17:34:37 -0800
changeset 88323 51e8195da28db2d910b4874721484f54a428c9cd
parent 88322 0f085bd21bc75eb6453c6101086e1fe80c21d61d
child 88324 09799386e12d6d9d0f8737c3bf2764a573bed3eb
push id157
push userMs2ger@gmail.com
push dateWed, 07 Mar 2012 19:27:10 +0000
reviewersmargaret
bugs732265
milestone13.0a1
Bug 732265 - (2/2) Support XUL menulist controls in native Fennec SelectHelper [r=margaret]
mobile/android/chrome/content/SelectHelper.js
mobile/android/chrome/content/aboutAddons.js
mobile/android/chrome/content/bindings.xml
mobile/android/themes/core/content.css
--- a/mobile/android/chrome/content/SelectHelper.js
+++ b/mobile/android/chrome/content/SelectHelper.js
@@ -9,17 +9,17 @@ var SelectHelper = {
   handleClick: function(aTarget) {
     // if we're busy looking at a select we want to eat any clicks that
     // come to us, but not to process them
     if (this._uiBusy)
         return true;
 
     let target = aTarget;
     while (target) {
-      if (this._isSelectElement(target) && !target.disabled) {
+      if (this._isMenu(target) && !target.disabled) {
         this._uiBusy = true;
         target.focus();
         let list = this.getListForElement(target);
         this.show(list, target);
         target = null;
         this._uiBusy = false;
         return true;
       }
@@ -30,32 +30,37 @@ var SelectHelper = {
   },
 
   show: function(aList, aElement) {
     let data = JSON.parse(sendMessageToJava({ gecko: aList }));
     let selected = data.button;
     if (selected == -1)
         return;
 
-    if (!(selected instanceof Array)) {
-      let temp = [];
-      for (let i = 0; i < aList.listitems.length; i++) {
-        temp[i] = (i == selected);
+    if (aElement instanceof Ci.nsIDOMXULMenuListElement) {
+      aElement.selectedIndex = selected;
+    } else if (aElement instanceof HTMLSelectElement) {
+      if (!(selected instanceof Array)) {
+        let temp = [];
+        for (let i = 0; i < aList.listitems.length; i++) {
+          temp[i] = (i == selected);
+        }
+        selected = temp;
       }
-      selected = temp;
+      let i = 0;
+      this.forOptions(aElement, function(aNode) {
+        aNode.selected = selected[i++];
+      });
     }
-    let i = 0;
-    this.forOptions(aElement, function(aNode) {
-      aNode.selected = selected[i++];
-    });
     this.fireOnChange(aElement);
   },
 
-  _isSelectElement: function(aElement) {
-    return (aElement instanceof HTMLSelectElement);
+  _isMenu: function(aElement) {
+    return (aElement instanceof HTMLSelectElement ||
+            aElement instanceof Ci.nsIDOMXULMenuListElement);
   },
 
   getListForElement: function(aElement) {
     let result = {
       type: "Prompt:Show",
       multiple: aElement.multiple,
       selected: [],
       listitems: []
@@ -82,24 +87,30 @@ var SelectHelper = {
       result.listitems[index] = item;
       result.selected[index] = aNode.selected;
       index++;
     });
     return result;
   },
 
   forOptions: function(aElement, aFunction) {
-    let children = aElement.children;
+    let parent = aElement;
+    if (aElement instanceof Ci.nsIDOMXULMenuListElement)
+      parent = aElement.menupopup;
+    let children = parent.children;
     let numChildren = children.length;
+
     // if there are no children in this select, we add a dummy row so that at least something appears
     if (numChildren == 0)
       aFunction.call(this, { label: "" }, { isGroup: false, inGroup: false });
+
     for (let i = 0; i < numChildren; i++) {
       let child = children[i];
-      if (child instanceof HTMLOptionElement) {
+      if (child instanceof HTMLOptionElement ||
+          child instanceof Ci.nsIDOMXULSelectControlItemElement) {
         // This is a regular choice under no group.
         aFunction.call(this, child, {
           isGroup: false, inGroup: false
         });
       } else if (child instanceof HTMLOptGroupElement) {
         aFunction.call(this, child, {
           isGroup: true, inGroup: false
         });
--- a/mobile/android/chrome/content/aboutAddons.js
+++ b/mobile/android/chrome/content/aboutAddons.js
@@ -3,33 +3,30 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm")
 Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 let gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutAddons.properties");
 
-let gChromeWin = null;
-function getChromeWin() {
-  if (!gChromeWin) {
-    gChromeWin = window
-                .QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIWebNavigation)
-                .QueryInterface(Ci.nsIDocShellTreeItem)
-                .rootTreeItem
-                .QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIDOMWindow)
-                .QueryInterface(Ci.nsIDOMChromeWindow);
-  }
-  return gChromeWin;
-}
+XPCOMUtils.defineLazyGetter(window, "gChromeWin", function()
+  window.QueryInterface(Ci.nsIInterfaceRequestor)
+    .getInterface(Ci.nsIWebNavigation)
+    .QueryInterface(Ci.nsIDocShellTreeItem)
+    .rootTreeItem
+    .QueryInterface(Ci.nsIInterfaceRequestor)
+    .getInterface(Ci.nsIDOMWindow)
+    .QueryInterface(Ci.nsIDOMChromeWindow));
+
+XPCOMUtils.defineLazyGetter(window, "SelectHelper", function() gChromeWin.SelectHelper);
 
 function init() {
   window.addEventListener("popstate", onPopState, false);
   Services.obs.addObserver(Addons, "browser-search-engine-modified", false);
 
   AddonManager.addInstallListener(Addons);
   Addons.getAddons();
   showList();
@@ -39,17 +36,17 @@ function uninit() {
   Services.obs.removeObserver(Addons, "browser-search-engine-modified");
   AddonManager.removeInstallListener(Addons);
 }
 
 function openLink(aElement) {
   try {
     let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
     let url = formatter.formatURLPref(aElement.getAttribute("pref"));
-    let BrowserApp = getChromeWin().BrowserApp;
+    let BrowserApp = gChromeWin.BrowserApp;
     BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id });
   } catch (ex) {}
 }
 
 function onPopState(aEvent) {
   // Called when back/forward is used to change the state of the page
   if (aEvent.state) {
     // Show the detail page for an addon
--- a/mobile/android/chrome/content/bindings.xml
+++ b/mobile/android/chrome/content/bindings.xml
@@ -89,17 +89,17 @@
       </handler>
 
       <handler event="click" button="0">
         <![CDATA[
           if (this.disabled || this.itemCount == 0)
             return;
 
           this.focus();
-          MenuListHelperUI.show(this);
+          SelectHelper.handleClick(this);
         ]]>
       </handler>
 
       <handler event="command" phase="capturing">
         <![CDATA[
           // The dropmark (button) fires a command event too. Don't forward that.
           // Just forward the menuitem command events, which the toolkit version does.
           if (event.target.parentNode.parentNode != this)
--- a/mobile/android/themes/core/content.css
+++ b/mobile/android/themes/core/content.css
@@ -361,8 +361,12 @@ input > .anonymous-div:after {
 
 /*
  * Enforce nearest scaling for video in order not to lose too much performance
  * after fixing bug 598736 ("Use higher-quality imageinterpolation on mobile")
  */
 video {
     image-rendering: -moz-crisp-edges;
 }
+
+xul|menulist {
+  -moz-binding: url("chrome://browser/content/bindings.xml#menulist");
+}