Bug 779723 - Implement RadioNodeList collection r=bz
authorDirkjan Ochtman <dirkjan@ochtman.nl>
Wed, 16 Jul 2014 18:44:19 +0200
changeset 194562 100732a8c93e05a90094f8b890d8d6baea5602a2
parent 194561 727ebe8a8cad11e6998d56ad051645f0b2cee805
child 194563 b26a1d2a1b0c7ca27f09ddf3bf25ec6cc2855564
push id46392
push usercbook@mozilla.com
push dateThu, 17 Jul 2014 08:02:00 +0000
treeherdermozilla-inbound@40a5725d7f92 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs779723
milestone33.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 779723 - Implement RadioNodeList collection r=bz
content/html/content/src/HTMLFormControlsCollection.cpp
content/html/content/src/HTMLFormControlsCollection.h
content/html/content/src/HTMLFormElement.cpp
content/html/content/src/RadioNodeList.cpp
content/html/content/src/RadioNodeList.h
content/html/content/src/moz.build
content/html/content/test/forms/mochitest.ini
content/html/content/test/forms/test_radio_radionodelist.html
dom/imptests/failures/html/html/semantics/forms/the-form-element/mochitest.ini
dom/imptests/failures/html/html/semantics/forms/the-form-element/test_form-elements-nameditem-01.html.json
dom/imptests/moz.build
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/HTMLFormControlsCollection.webidl
dom/webidl/RadioNodeList.webidl
dom/webidl/moz.build
--- a/content/html/content/src/HTMLFormControlsCollection.cpp
+++ b/content/html/content/src/HTMLFormControlsCollection.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/HTMLFormControlsCollectionBinding.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "nsGenericHTMLElement.h" // nsGenericHTMLFormElement
 #include "nsIDocument.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIFormControl.h"
+#include "RadioNodeList.h"
 #include "jsfriendapi.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ bool
 HTMLFormControlsCollection::ShouldBeInElements(nsIFormControl* aFormControl)
 {
@@ -342,51 +343,51 @@ HTMLFormControlsCollection::GetElementAt
 HTMLFormControlsCollection::GetParentObject()
 {
   return mForm;
 }
 
 /* virtual */ Element*
 HTMLFormControlsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound)
 {
-  Nullable<OwningNodeListOrElement> maybeResult;
+  Nullable<OwningRadioNodeListOrElement> maybeResult;
   NamedGetter(aName, aFound, maybeResult);
   if (!aFound) {
     return nullptr;
   }
   MOZ_ASSERT(!maybeResult.IsNull());
-  const OwningNodeListOrElement& result = maybeResult.Value();
+  const OwningRadioNodeListOrElement& result = maybeResult.Value();
   if (result.IsElement()) {
     return result.GetAsElement().get();
   }
-  if (result.IsNodeList()) {
-    nsINodeList& nodelist = result.GetAsNodeList();
+  if (result.IsRadioNodeList()) {
+    RadioNodeList& nodelist = result.GetAsRadioNodeList();
     return nodelist.Item(0)->AsElement();
   }
   MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
   return nullptr;
 }
 
 void
 HTMLFormControlsCollection::NamedGetter(const nsAString& aName,
                                         bool& aFound,
-                                        Nullable<OwningNodeListOrElement>& aResult)
+                                        Nullable<OwningRadioNodeListOrElement>& aResult)
 {
   nsISupports* item = NamedItemInternal(aName, true);
   if (!item) {
     aFound = false;
     return;
   }
   aFound = true;
   if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
     aResult.SetValue().SetAsElement() = element;
     return;
   }
-  if (nsCOMPtr<nsINodeList> nodelist = do_QueryInterface(item)) {
-    aResult.SetValue().SetAsNodeList() = nodelist;
+  if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
+    aResult.SetValue().SetAsRadioNodeList() = nodelist;
     return;
   }
   MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
 }
 
 static PLDHashOperator
 CollectNames(const nsAString& aName,
              nsISupports* /* unused */,
--- a/content/html/content/src/HTMLFormControlsCollection.h
+++ b/content/html/content/src/HTMLFormControlsCollection.h
@@ -15,17 +15,17 @@
 
 class nsGenericHTMLFormElement;
 class nsIFormControl;
 
 namespace mozilla {
 namespace dom {
 class HTMLFormElement;
 class HTMLImageElement;
-class OwningNodeListOrElement;
+class OwningRadioNodeListOrElement;
 template<typename> struct Nullable;
 
 class HTMLFormControlsCollection : public nsIHTMLCollection
                                  , public nsWrapperCache
 {
 public:
   HTMLFormControlsCollection(HTMLFormElement* aForm);
 
@@ -40,20 +40,20 @@ public:
   virtual nsINode* GetParentObject() MOZ_OVERRIDE;
 
   virtual Element*
   GetFirstNamedElement(const nsAString& aName, bool& aFound) MOZ_OVERRIDE;
 
   void
   NamedGetter(const nsAString& aName,
               bool& aFound,
-              Nullable<OwningNodeListOrElement>& aResult);
+              Nullable<OwningRadioNodeListOrElement>& aResult);
   void
   NamedItem(const nsAString& aName,
-            Nullable<OwningNodeListOrElement>& aResult)
+            Nullable<OwningRadioNodeListOrElement>& aResult)
   {
     bool dummy;
     NamedGetter(aName, dummy, aResult);
   }
   virtual void GetSupportedNames(unsigned aFlags,
                                  nsTArray<nsString>& aNames) MOZ_OVERRIDE;
 
   nsresult AddElementToTable(nsGenericHTMLFormElement* aChild,
--- a/content/html/content/src/HTMLFormElement.cpp
+++ b/content/html/content/src/HTMLFormElement.cpp
@@ -43,16 +43,17 @@
 #include "nsIWebProgress.h"
 #include "nsIDocShell.h"
 #include "nsFormData.h"
 #include "nsFormSubmissionConstants.h"
 
 // radio buttons
 #include "mozilla/dom/HTMLInputElement.h"
 #include "nsIRadioVisitor.h"
+#include "RadioNodeList.h"
 
 #include "nsLayoutUtils.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsIHTMLCollection.h"
 
 #include "nsIConstraintValidation.h"
 
@@ -2277,17 +2278,17 @@ HTMLFormElement::AddElementToTableIntern
       // happen if a form control has both a name and an id with the same
       // value
       if (content == aChild) {
         return NS_OK;
       }
 
       // Found an element, create a list, add the element to the list and put
       // the list in the hash
-      nsSimpleContentList *list = new nsSimpleContentList(this);
+      RadioNodeList *list = new RadioNodeList(this);
 
       // If an element has a @form, we can assume it *might* be able to not have
       // a parent and still be in the form.
       NS_ASSERTION(content->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
                    content->GetParent(), "Item in list without parent");
 
       // Determine the ordering between the new and old element.
       bool newFirst = nsContentUtils::PositionIsBefore(aChild, content);
@@ -2301,18 +2302,18 @@ HTMLFormElement::AddElementToTableIntern
       // Replace the element with the list.
       aTable.Put(aName, listSupports);
     } else {
       // There's already a list in the hash, add the child to the list
       nsCOMPtr<nsIDOMNodeList> nodeList = do_QueryInterface(supports);
       NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
 
       // Upcast, uggly, but it works!
-      nsSimpleContentList *list =
-        static_cast<nsSimpleContentList*>(nodeList.get());
+      RadioNodeList *list =
+        static_cast<RadioNodeList*>(nodeList.get());
 
       NS_ASSERTION(list->Length() > 1,
                    "List should have been converted back to a single element");
 
       // Fast-path appends; this check is ok even if the child is
       // already in the list, since if it tests true the child would
       // have come at the end of the list, and the PositionIsBefore
       // will test false.
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/RadioNodeList.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/RadioNodeList.h"
+
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/RadioNodeListBinding.h"
+#include "js/TypeDecls.h"
+
+#include "HTMLInputElement.h"
+
+namespace mozilla {
+namespace dom {
+
+/* virtual */ JSObject*
+RadioNodeList::WrapObject(JSContext* aCx)
+{
+  return RadioNodeListBinding::Wrap(aCx, this);
+}
+
+HTMLInputElement*
+GetAsRadio(nsIContent* node)
+{
+  HTMLInputElement* el = HTMLInputElement::FromContent(node);
+  if (el && el->GetType() == NS_FORM_INPUT_RADIO) {
+    return el;
+  }
+  return nullptr;
+}
+
+void
+RadioNodeList::GetValue(nsString& retval)
+{
+  for (uint32_t i = 0; i < Length(); i++) {
+    HTMLInputElement* maybeRadio = GetAsRadio(Item(i));
+    if (maybeRadio && maybeRadio->Checked()) {
+      maybeRadio->GetValue(retval);
+      return;
+    }
+  }
+  retval.Truncate();
+}
+
+void
+RadioNodeList::SetValue(const nsAString& value)
+{
+  for (uint32_t i = 0; i < Length(); i++) {
+
+    HTMLInputElement* maybeRadio = GetAsRadio(Item(i));
+    if (!maybeRadio) {
+      continue;
+    }
+
+    nsString curval = nsString();
+    maybeRadio->GetValue(curval);
+    if (curval.Equals(value)) {
+      maybeRadio->SetChecked(true);
+      return;
+    }
+
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(RadioNodeList, nsSimpleContentList, RadioNodeList)
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/RadioNodeList.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_RadioNodeList_h
+#define mozilla_dom_RadioNodeList_h
+
+#include "nsContentList.h"
+#include "nsCOMPtr.h"
+#include "HTMLFormElement.h"
+
+#define MOZILLA_DOM_RADIONODELIST_IMPLEMENTATION_IID \
+  { 0xbba7f3e8, 0xf3b5, 0x42e5, \
+  { 0x82, 0x08, 0xa6, 0x8b, 0xe0, 0xbc, 0x22, 0x19 } }
+
+namespace mozilla {
+namespace dom {
+
+class RadioNodeList : public nsSimpleContentList
+{
+public:
+  RadioNodeList(HTMLFormElement* aForm) : nsSimpleContentList(aForm) { }
+
+  virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
+  void GetValue(nsString& retval);
+  void SetValue(const nsAString& value);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_RADIONODELIST_IMPLEMENTATION_IID)
+private:
+  ~RadioNodeList() { }
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(RadioNodeList, MOZILLA_DOM_RADIONODELIST_IMPLEMENTATION_IID)
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RadioNodeList_h
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -67,16 +67,17 @@ EXPORTS.mozilla.dom += [
     'HTMLTableSectionElement.h',
     'HTMLTemplateElement.h',
     'HTMLTextAreaElement.h',
     'HTMLTimeElement.h',
     'HTMLTitleElement.h',
     'HTMLTrackElement.h',
     'HTMLUnknownElement.h',
     'MediaError.h',
+    'RadioNodeList.h',
     'TextTrackManager.h',
     'TimeRanges.h',
     'UndoManager.h',
     'ValidityState.h',
 ]
 
 UNIFIED_SOURCES += [
     'HTMLAnchorElement.cpp',
@@ -149,16 +150,17 @@ UNIFIED_SOURCES += [
     'nsDOMStringMap.cpp',
     'nsFormSubmission.cpp',
     'nsGenericHTMLElement.cpp',
     'nsGenericHTMLFrameElement.cpp',
     'nsHTMLDNSPrefetch.cpp',
     'nsIConstraintValidation.cpp',
     'nsRadioVisitor.cpp',
     'nsTextEditorState.cpp',
+    'RadioNodeList.cpp',
     'TextTrackManager.cpp',
     'TimeRanges.cpp',
     'UndoManager.cpp',
     'ValidityState.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
--- a/content/html/content/test/forms/mochitest.ini
+++ b/content/html/content/test/forms/mochitest.ini
@@ -67,16 +67,17 @@ skip-if = e10s
 [test_mozistextfield.html]
 [test_novalidate_attribute.html]
 [test_option_disabled.html]
 [test_option_index_attribute.html]
 [test_option_text.html]
 [test_output_element.html]
 [test_pattern_attribute.html]
 [test_progress_element.html]
+[test_radio_radionodelist.html]
 [test_required_attribute.html]
 skip-if = e10s
 [test_restore_form_elements.html]
 [test_save_restore_radio_groups.html]
 [test_select_selectedOptions.html]
 [test_select_validation.html]
 [test_set_range_text.html]
 [test_step_attribute.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/forms/test_radio_radionodelist.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=779723
+-->
+<head>
+  <title>Test for Bug 779723</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=779723">Mozilla Bug 779723</a>
+<p id="display"></p>
+<form>
+  <input type="checkbox" name="rdo" value="0" id="r0" checked="checked">
+  <input type="radio" name="rdo" id="r1">
+  <input type="radio" name="rdo" id="r2" value="2">
+</form>
+<script class="testbody" type="text/javascript">
+/** Test for Bug 779723 **/
+
+var rdoList = document.forms[0].elements.namedItem('rdo');
+ise(rdoList.value, "", "The value attribute should be empty");
+
+document.getElementById('r2').checked = true;
+ok(rdoList.value, "2", "The value attribute should be 2");
+
+document.getElementById('r1').checked = true;
+ok(rdoList.value, "on", "The value attribute should be on");
+
+document.getElementById('r1').value = 1;
+ok(rdoList.value, "1", "The value attribute should be 1");
+
+ise(rdoList.value, document.getElementById('r1').value,
+   "The value attribute should be equal to the first checked radio input element's value");
+ok(!document.getElementById('r2').checked,
+   "The second radio input element should not be checked");
+
+rdoList.value = '2';
+ise(rdoList.value, document.getElementById('r2').value,
+   "The value attribute should be equal to the second radio input element's value");
+ok(document.getElementById('r2').checked,
+   "The second radio input element should be checked");
+
+rdoList.value = '3';
+ise(rdoList.value, document.getElementById('r2').value,
+   "The value attribute should be the second radio input element's value");
+ok(document.getElementById('r2').checked,
+   "The second radio input element should be checked");
+
+</script>
+</pre>
+</body>
+</html>
+
deleted file mode 100644
--- a/dom/imptests/failures/html/html/semantics/forms/the-form-element/mochitest.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
-[DEFAULT]
-support-files =
-
-
-[test_form-elements-nameditem-01.html.json]
deleted file mode 100644
--- a/dom/imptests/failures/html/html/semantics/forms/the-form-element/test_form-elements-nameditem-01.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "RadioNodeList should exist": true
-}
--- a/dom/imptests/moz.build
+++ b/dom/imptests/moz.build
@@ -18,17 +18,16 @@ MOCHITEST_MANIFESTS += [
     'failures/html/dom/lists/mochitest.ini',
     'failures/html/dom/mochitest.ini',
     'failures/html/dom/nodes/mochitest.ini',
     'failures/html/html/browsers/the-window-object/mochitest.ini',
     'failures/html/html/browsers/the-window-object/named-access-on-the-window-object/mochitest.ini',
     'failures/html/html/dom/documents/dta/doc.gEBN/mochitest.ini',
     'failures/html/html/dom/documents/dta/mochitest.ini',
     'failures/html/html/obsolete/implreq/oeaaa/mochitest.ini',
-    'failures/html/html/semantics/forms/the-form-element/mochitest.ini',
     'failures/html/html/semantics/forms/the-option-element/mochitest.ini',
     'failures/html/html/semantics/forms/the-select-element/mochitest.ini',
     'failures/html/html/semantics/scripting-1/the-script-element/mochitest.ini',
     'failures/html/html/semantics/tabular-data/the-table-element/mochitest.ini',
     'failures/html/html/webappapis/atob/mochitest.ini',
     'failures/html/js/builtins/mochitest.ini',
     'failures/html/microdata/microdata-dom-api/mochitest.ini',
     'failures/html/typedarrays/mochitest.ini',
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -811,16 +811,18 @@ var interfaceNamesInGlobalScope =
     "ProcessingInstruction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProgressEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PropertyNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "RadioNodeList",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "Range",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RecordErrorEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Rect",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RGBColor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/HTMLFormControlsCollection.webidl
+++ b/dom/webidl/HTMLFormControlsCollection.webidl
@@ -6,14 +6,12 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#htmlformcontrolscollection
  *
  * © Copyright 2004-2013 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-typedef NodeList RadioNodeList;
-
 interface HTMLFormControlsCollection : HTMLCollection {
   // inherits length and item()
   /* legacycaller */ getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem()
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/RadioNodeList.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#htmlformcontrolscollection-0
+ *
+ * © Copyright 2004-2014 Apple Computer, Inc., Mozilla Foundation, and
+ * Opera Software ASA. You are granted a license to use, reproduce
+ * and create derivative works of this document.
+ */
+
+interface RadioNodeList : NodeList {
+  attribute DOMString value;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -294,16 +294,17 @@ WEBIDL_FILES = [
     'PluginArray.webidl',
     'PointerEvent.webidl',
     'Position.webidl',
     'PositionError.webidl',
     'ProcessingInstruction.webidl',
     'Promise.webidl',
     'PromiseDebugging.webidl',
     'PushManager.webidl',
+    'RadioNodeList.webidl',
     'Range.webidl',
     'Rect.webidl',
     'ResourceStats.webidl',
     'ResourceStatsManager.webidl',
     'RGBColor.webidl',
     'RTCConfiguration.webidl',
     'RTCIceCandidate.webidl',
     'RTCIdentityAssertion.webidl',