Bug 717787 - Suggestions from <datalist> are not shown in Native Fennec. r=mbrubeck
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Tue, 06 Mar 2012 13:56:16 -0800
changeset 88409 6250fad749e28cb08ad0382186b0e880e4a7b025
parent 88408 f7d880c9fa25bfd922733380868fdd250075f1d9
child 88410 120c201a664478f70bdd444cad519534b06855fa
push id22194
push usermak77@bonardo.net
push dateWed, 07 Mar 2012 09:33:54 +0000
treeherdermozilla-central@8ef88a69f861 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck
bugs717787
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 717787 - Suggestions from <datalist> are not shown in Native Fennec. r=mbrubeck
mobile/android/base/FormAssistPopup.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/FormAssistPopup.java
+++ b/mobile/android/base/FormAssistPopup.java
@@ -39,17 +39,20 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.FloatSize;
 
 import android.content.Context;
 import android.util.Log;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.ArrayAdapter;
 import android.widget.AdapterView;
 import android.widget.RelativeLayout;
 import android.widget.ListView;
 import android.widget.TextView;
@@ -86,18 +89,20 @@ public class FormAssistPopup extends Lis
         mAnimation = AnimationUtils.loadAnimation(context, R.anim.grow_fade_in);
         mAnimation.setDuration(75);
 
         setFocusable(false);
 
         setOnItemClickListener(new OnItemClickListener() {
             public void onItemClick(AdapterView<?> parentView, View view, int position, long id) {
                 if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) {
+                    // Use the value stored with the autocomplete view, not the label text,
+                    // since they can be different.
                     TextView textView = (TextView) view;
-                    String value = textView.getText().toString();
+                    String value = (String) textView.getTag();
                     GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value));
                     hide();
                 }
             }
         });
 
         GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this);
         GeckoAppShell.registerGeckoEventListener("FormAssist:ValidationMessage", this);
@@ -144,23 +149,18 @@ public class FormAssistPopup extends Lis
         GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
             public void run() {
                 hide();
             }
         });
     }
 
     private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) {
-        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, R.layout.autocomplete_list_item);
-        try {
-            for (int i = 0; i < suggestions.length(); i++)
-                adapter.add(suggestions.get(i).toString());
-        } catch (JSONException e) {
-            Log.e(LOGTAG, "JSONException: " + e);
-        }
+        AutoCompleteListAdapter adapter = new AutoCompleteListAdapter(mContext, R.layout.autocomplete_list_item);
+        adapter.populateSuggestionsList(suggestions);
         setAdapter(adapter);
 
         if (positionAndShowPopup(rect, zoom))
             mTypeShowing = PopupType.AUTOCOMPLETE;
     }
 
     // TODO: style the validation message popup differently (bug 731654)
     private void showValidationMessage(String validationMessage, JSONArray rect, double zoom) {
@@ -261,9 +261,53 @@ public class FormAssistPopup extends Lis
 
     public void hide() {
         if (isShown()) {
             setVisibility(View.GONE);
             mTypeShowing = PopupType.NONE;
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null));
         }
     }
+
+    private class AutoCompleteListAdapter extends ArrayAdapter<Pair<String, String>> {
+        private LayoutInflater mInflater;
+        private int mTextViewResourceId;
+
+        public AutoCompleteListAdapter(Context context, int textViewResourceId) {
+            super(context, textViewResourceId);
+
+            mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            mTextViewResourceId = textViewResourceId;
+        }
+
+        // This method takes an array of autocomplete suggestions with label/value properties
+        // and adds label/value Pair objects to the array that backs the adapter.
+        public void populateSuggestionsList(JSONArray suggestions) {
+            try {
+                for (int i = 0; i < suggestions.length(); i++) {
+                    JSONObject suggestion = (JSONObject) suggestions.get(i);
+                    String label = suggestion.getString("label");
+                    String value = suggestion.getString("value");
+                    add(new Pair<String, String>(label, value));
+                }
+            } catch (JSONException e) {
+                Log.e(LOGTAG, "JSONException: " + e);
+            }
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null)
+                convertView = mInflater.inflate(mTextViewResourceId, null);
+
+            Pair<String, String> item = getItem(position);
+            TextView itemView = (TextView) convertView;
+
+            // Set the text with the suggestion label
+            itemView.setText(item.first);
+
+            // Set a tag with the suggestion value
+            itemView.setTag(item.second);
+
+            return convertView;
+        }
+    }
 }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2961,17 +2961,51 @@ var FormAssistant = {
     let suggestions = [];
     for (let i = 0; i < results.matchCount; i++) {
       let value = results.getValueAt(i);
 
       // Do not show the value if it is the current one in the input field
       if (value == aSearchString)
         continue;
 
-      suggestions.push(value);
+      // Supply a label and value, since they can differ for datalist suggestions
+      suggestions.push({ label: value, value: value });
+    }
+
+    return suggestions;
+  },
+
+  /**
+   * (Copied from mobile/xul/chrome/content/forms.js)
+   * This function is similar to getListSuggestions from
+   * components/satchel/src/nsInputListAutoComplete.js but sadly this one is
+   * used by the autocomplete.xml binding which is not in used in fennec
+   */
+  _getListSuggestions: function _getListSuggestions(aElement) {
+    if (!(aElement instanceof HTMLInputElement) || !aElement.list)
+      return [];
+
+    let suggestions = [];
+    let filter = !aElement.hasAttribute("mozNoFilter");
+    let lowerFieldValue = aElement.value.toLowerCase();
+
+    let options = aElement.list.options;
+    let length = options.length;
+    for (let i = 0; i < length; i++) {
+      let item = options.item(i);
+
+      let label = item.value;
+      if (item.label)
+        label = item.label;
+      else if (item.text)
+        label = item.text;
+
+      if (filter && label.toLowerCase().indexOf(lowerFieldValue) == -1)
+        continue;
+      suggestions.push({ label: label, value: item.value });
     }
 
     return suggestions;
   },
 
   // Gets the element position data necessary for the Java UI to position
   // the form assist popup.
   _getElementPositionData: function _getElementPositionData(aElement) {
@@ -2986,17 +3020,22 @@ var FormAssistant = {
 
   // Retrieves autocomplete suggestions for an element from the form autocomplete service
   // and sends the suggestions to the Java UI, along with element position data.
   // Returns true if there are suggestions to show, false otherwise.
   _showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement) {
     if (!this._isAutoComplete(aElement))
       return false;
 
-    let suggestions = this._getAutoCompleteSuggestions(aElement.value, aElement);
+    let autoCompleteSuggestions = this._getAutoCompleteSuggestions(aElement.value, aElement);
+    let listSuggestions = this._getListSuggestions(aElement);
+
+    // On desktop, we show datalist suggestions below autocomplete suggestions,
+    // without duplicates removed.
+    let suggestions = autoCompleteSuggestions.concat(listSuggestions);
 
     // Return false if there are no suggestions to show
     if (!suggestions.length)
       return false;
 
     let positionData = this._getElementPositionData(aElement);
     sendMessageToJava({
       gecko: {