Bug 1020697 - Implement @autocomplete for <select>. r=smaug
authorGiovanni Sferro <agi.novanta@gmail.com>
Thu, 24 Jul 2014 16:55:00 +0200
changeset 219179 57e293375c811547c4a3e579090ff7fd1d033157
parent 219178 3652cd1e6f8ab0ed8cac8eba8e4cdfa143bd71a9
child 219180 fc4786cdbbb8742612880df3a5c9b0cbc948a141
push id583
push userbhearsum@mozilla.com
push dateMon, 24 Nov 2014 19:04:58 +0000
treeherdermozilla-release@c107e74250f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1020697
milestone34.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 1020697 - Implement @autocomplete for <select>. r=smaug
content/html/content/src/HTMLSelectElement.cpp
content/html/content/src/HTMLSelectElement.h
content/html/content/test/forms/test_input_autocomplete.html
dom/webidl/HTMLSelectElement.webidl
--- a/content/html/content/src/HTMLSelectElement.cpp
+++ b/content/html/content/src/HTMLSelectElement.cpp
@@ -99,16 +99,17 @@ SafeOptionListMutation::~SafeOptionListM
 
 // construction, destruction
 
 
 HTMLSelectElement::HTMLSelectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                      FromParser aFromParser)
   : nsGenericHTMLFormElementWithState(aNodeInfo),
     mOptions(new HTMLOptionsCollection(MOZ_THIS_IN_INITIALIZER_LIST())),
+    mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
     mIsDoneAddingChildren(!aFromParser),
     mDisabledChanged(false),
     mMutating(false),
     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
     mSelectionHasChanged(false),
     mDefaultSelectionSet(false),
     mCanShowInvalidUI(true),
     mCanShowValidUI(true),
@@ -172,16 +173,26 @@ HTMLSelectElement::SetCustomValidity(con
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   UpdateState(true);
 
   return NS_OK;
 }
 
+void
+HTMLSelectElement::GetAutocomplete(DOMString& aValue)
+{
+  const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
+
+  mAutocompleteAttrState =
+    nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aValue,
+                                                   mAutocompleteAttrState);
+}
+
 NS_IMETHODIMP
 HTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   return nsGenericHTMLFormElementWithState::GetForm(aForm);
 }
 
 nsresult
 HTMLSelectElement::InsertChildAt(nsIContent* aKid,
@@ -1328,16 +1339,19 @@ nsresult
 HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::disabled) {
       UpdateBarredFromConstraintValidation();
     } else if (aName == nsGkAtoms::required) {
       UpdateValueMissingValidityState();
+    } else if (aName == nsGkAtoms::autocomplete) {
+      // Clear the cached @autocomplete attribute state
+      mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
     }
 
     UpdateState(aNotify);
   }
 
   return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
                                                          aValue, aNotify);
 }
@@ -1415,18 +1429,23 @@ HTMLSelectElement::DoneAddingChildren(bo
 }
 
 bool
 HTMLSelectElement::ParseAttribute(int32_t aNamespaceID,
                                   nsIAtom* aAttribute,
                                   const nsAString& aValue,
                                   nsAttrValue& aResult)
 {
-  if (aAttribute == nsGkAtoms::size && kNameSpaceID_None == aNamespaceID) {
-    return aResult.ParsePositiveIntValue(aValue);
+  if (kNameSpaceID_None == aNamespaceID) {
+    if (aAttribute == nsGkAtoms::size) {
+      return aResult.ParsePositiveIntValue(aValue);
+    } else if (aAttribute == nsGkAtoms::autocomplete) {
+      aResult.ParseAtomArray(aValue);
+      return true;
+    }
   }
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 void
 HTMLSelectElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                          nsRuleData* aData)
--- a/content/html/content/src/HTMLSelectElement.h
+++ b/content/html/content/src/HTMLSelectElement.h
@@ -12,16 +12,17 @@
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/HTMLOptionsCollection.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCheapSets.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "mozilla/dom/HTMLFormElement.h"
+#include "nsContentUtils.h"
 
 class nsContentList;
 class nsIDOMHTMLOptionElement;
 class nsIHTMLCollection;
 class nsISelectControlFrame;
 class nsPresState;
 
 namespace mozilla {
@@ -153,16 +154,21 @@ public:
   bool Autofocus() const
   {
     return GetBoolAttr(nsGkAtoms::autofocus);
   }
   void SetAutofocus(bool aVal, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::autofocus, aVal, aRv);
   }
+  void GetAutocomplete(DOMString& aValue);
+  void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv)
+  {
+    SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
+  }
   bool Disabled() const
   {
     return GetBoolAttr(nsGkAtoms::disabled);
   }
   void SetDisabled(bool aVal, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::disabled, aVal, aRv);
   }
@@ -601,16 +607,17 @@ protected:
       return true;
     }
 
     return mSelectionHasChanged;
   }
 
   /** The options[] array */
   nsRefPtr<HTMLOptionsCollection> mOptions;
+  nsContentUtils::AutocompleteAttrState mAutocompleteAttrState;
   /** false if the parser is in the middle of adding children. */
   bool            mIsDoneAddingChildren;
   /** true if our disabled state has changed from the default **/
   bool            mDisabledChanged;
   /** true if child nodes are being added or removed.
    *  Used by SafeOptionListMutation.
    */
   bool            mMutating;
--- a/content/html/content/test/forms/test_input_autocomplete.html
+++ b/content/html/content/test/forms/test_input_autocomplete.html
@@ -2,26 +2,16 @@
 <html>
 <!--
 Test @autocomplete on <input>
 -->
 <head>
   <title>Test for &lt;input autocomplete='…'&gt;</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
-</head>
-
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-  <form>
-    <input id="field" />
-  </form>
-</div>
-<pre id="test">
 <script>
 "use strict";
 
 var values = [
   // @autocomplete content attribute, expected IDL attribute value
 
   // Missing or empty attribute
   [undefined, ""],
@@ -64,40 +54,53 @@ var values = [
   // Four tokens (invalid)
   ["billing billing mobile tel", ""],
 
   // Five tokens (invalid)
   ["billing billing billing mobile tel", ""],
 ];
 
 var types = [undefined, "hidden", "text", "search"]; // Valid types for all non-multiline hints.
-var field = document.getElementById("field");
 
-function checkAutocompleteValues(type) {
+function checkAutocompleteValues(field, type) {
   for (var test of values) {
     if (typeof(test[0]) === "undefined")
       field.removeAttribute("autocomplete");
     else
       field.setAttribute("autocomplete", test[0]);
     ise(field.autocomplete, test[1], "Checking @autocomplete for @type=" + type + " of: " + test[0]);
     ise(field.autocomplete, test[1], "Checking cached @autocomplete for @type=" + type + " of: " + test[0]);
   }
 }
 
 function start() {
+  var inputField = document.getElementById("input-field");
   for (var type of types) {
     // Switch the input type
     if (typeof(type) === "undefined")
-      field.removeAttribute("type");
+      inputField.removeAttribute("type");
     else
-      field.type = type;
-    checkAutocompleteValues(type || "");
+      inputField.type = type;
+    checkAutocompleteValues(inputField, type || "");
   }
+
+  var selectField = document.getElementById("select-field");
+  checkAutocompleteValues(selectField, "select");
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["dom.forms.autocomplete.experimental", true]]}, start);
+</script>
+</head>
 
-</script>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <form>
+    <input id="input-field" />
+    <select id="select-field" />
+  </form>
+</div>
+<pre id="test">
 </pre>
 </body>
 </html>
--- a/dom/webidl/HTMLSelectElement.webidl
+++ b/dom/webidl/HTMLSelectElement.webidl
@@ -5,16 +5,18 @@
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/html/#the-select-element
  */
 
 interface HTMLSelectElement : HTMLElement {
   [SetterThrows, Pure]
            attribute boolean autofocus;
+  [Pref="dom.forms.autocomplete.experimental", SetterThrows, Pure]
+           attribute DOMString autocomplete;
   [SetterThrows, Pure]
            attribute boolean disabled;
   [Pure]
   readonly attribute HTMLFormElement? form;
   [SetterThrows, Pure]
            attribute boolean multiple;
   [SetterThrows, Pure]
            attribute DOMString name;