aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--[-rwxr-xr-x]components/script/dom/htmlformelement.rs222
-rw-r--r--components/script/dom/webidls/HTMLFormElement.webidl2
-rw-r--r--tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-2.html.ini2
-rw-r--r--tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit.html.ini2
-rw-r--r--tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini3
-rw-r--r--tests/wpt/metadata/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html.ini3
-rw-r--r--tests/wpt/metadata/html/semantics/forms/the-form-element/form-nameditem.html.ini15
8 files changed, 231 insertions, 22 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 9dad0da9cd3..8f019f9a37f 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -145,13 +145,15 @@ use style::values::specified::Length;
use tendril::fmt::UTF8;
use tendril::stream::LossyDecoder;
use tendril::{StrTendril, TendrilSink};
-use time::{Duration, Timespec};
+use time::{Duration, Timespec, Tm};
use uuid::Uuid;
use webgpu::{WebGPU, WebGPUAdapter, WebGPUDevice};
use webrender_api::{DocumentId, ImageKey};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
use webxr_api::SwapChainId as WebXRSwapChainId;
+unsafe_no_jsmanaged_fields!(Tm);
+
/// A trait to allow tracing (only) DOM objects.
pub unsafe trait JSTraceable {
/// Trace `self`.
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index f4135ac3e0a..959bca74b54 100755..100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -64,6 +64,13 @@ use std::cell::Cell;
use style::attr::AttrValue;
use style::str::split_html_space_chars;
+use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement;
+use crate::dom::radionodelist::RadioNodeList;
+use std::collections::HashMap;
+use time::{now, Duration, Tm};
+
+use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
+
#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
pub struct GenerationId(u32);
@@ -76,6 +83,7 @@ pub struct HTMLFormElement {
elements: DomOnceCell<HTMLFormControlsCollection>,
generation_id: Cell<GenerationId>,
controls: DomRefCell<Vec<Dom<Element>>>,
+ past_names_map: DomRefCell<HashMap<DOMString, (Dom<Element>, Tm)>>,
}
impl HTMLFormElement {
@@ -91,6 +99,7 @@ impl HTMLFormElement {
elements: Default::default(),
generation_id: Cell::new(GenerationId(0)),
controls: DomRefCell::new(Vec::new()),
+ past_names_map: DomRefCell::new(HashMap::new()),
}
}
@@ -253,6 +262,219 @@ impl HTMLFormElementMethods for HTMLFormElement {
let elements = self.Elements();
elements.IndexedGetter(index)
}
+
+ // https://html.spec.whatwg.org/multipage/#the-form-element%3Adetermine-the-value-of-a-named-property
+ fn NamedGetter(&self, name: DOMString) -> Option<RadioNodeListOrElement> {
+ let mut candidates: Vec<DomRoot<Node>> = Vec::new();
+
+ let controls = self.controls.borrow();
+ // Step 1
+ for child in controls.iter() {
+ if child
+ .downcast::<HTMLElement>()
+ .map_or(false, |c| c.is_listed_element())
+ {
+ if (child.has_attribute(&local_name!("id")) &&
+ child.get_string_attribute(&local_name!("id")) == name) ||
+ (child.has_attribute(&local_name!("name")) &&
+ child.get_string_attribute(&local_name!("name")) == name)
+ {
+ candidates.push(DomRoot::from_ref(&*child.upcast::<Node>()));
+ }
+ }
+ }
+ // Step 2
+ if candidates.len() == 0 {
+ for child in controls.iter() {
+ if child.is::<HTMLImageElement>() {
+ if (child.has_attribute(&local_name!("id")) &&
+ child.get_string_attribute(&local_name!("id")) == name) ||
+ (child.has_attribute(&local_name!("name")) &&
+ child.get_string_attribute(&local_name!("name")) == name)
+ {
+ candidates.push(DomRoot::from_ref(&*child.upcast::<Node>()));
+ }
+ }
+ }
+ }
+
+ let mut past_names_map = self.past_names_map.borrow_mut();
+
+ // Step 3
+ if candidates.len() == 0 {
+ if past_names_map.contains_key(&name) {
+ return Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
+ &*past_names_map.get(&name).unwrap().0,
+ )));
+ }
+ return None;
+ }
+
+ // Step 4
+ if candidates.len() > 1 {
+ let window = window_from_node(self);
+
+ return Some(RadioNodeListOrElement::RadioNodeList(
+ RadioNodeList::new_simple_list(&window, candidates.into_iter()),
+ ));
+ }
+
+ // Step 5
+ let element_node = &candidates[0];
+ past_names_map.insert(
+ name,
+ (
+ Dom::from_ref(&*element_node.downcast::<Element>().unwrap()),
+ now(),
+ ),
+ );
+
+ // Step 6
+ return Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
+ &*element_node.downcast::<Element>().unwrap(),
+ )));
+ }
+
+ // https://html.spec.whatwg.org/multipage/#the-form-element:supported-property-names
+ fn SupportedPropertyNames(&self) -> Vec<DOMString> {
+ // Step 1
+ #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
+ enum SourcedNameSource {
+ Id,
+ Name,
+ Past(Duration),
+ }
+
+ impl SourcedNameSource {
+ fn is_past(&self) -> bool {
+ match self {
+ SourcedNameSource::Past(..) => true,
+ _ => false,
+ }
+ }
+ }
+
+ struct SourcedName {
+ name: DOMString,
+ element: DomRoot<Element>,
+ source: SourcedNameSource,
+ }
+
+ let mut sourcedNamesVec: Vec<SourcedName> = Vec::new();
+
+ let controls = self.controls.borrow();
+
+ // Step 2
+ for child in controls.iter() {
+ if child
+ .downcast::<HTMLElement>()
+ .map_or(false, |c| c.is_listed_element())
+ {
+ if child.has_attribute(&local_name!("id")) {
+ let entry = SourcedName {
+ name: child.get_string_attribute(&local_name!("id")),
+ element: DomRoot::from_ref(&*child),
+ source: SourcedNameSource::Id,
+ };
+ sourcedNamesVec.push(entry);
+ }
+ if child.has_attribute(&local_name!("name")) {
+ let entry = SourcedName {
+ name: child.get_string_attribute(&local_name!("name")),
+ element: DomRoot::from_ref(&*child),
+ source: SourcedNameSource::Name,
+ };
+ sourcedNamesVec.push(entry);
+ }
+ }
+ }
+
+ // Step 3
+ for child in controls.iter() {
+ if child.is::<HTMLImageElement>() {
+ if child.has_attribute(&local_name!("id")) {
+ let entry = SourcedName {
+ name: child.get_string_attribute(&local_name!("id")),
+ element: DomRoot::from_ref(&*child),
+ source: SourcedNameSource::Id,
+ };
+ sourcedNamesVec.push(entry);
+ }
+ if child.has_attribute(&local_name!("name")) {
+ let entry = SourcedName {
+ name: child.get_string_attribute(&local_name!("name")),
+ element: DomRoot::from_ref(&*child),
+ source: SourcedNameSource::Name,
+ };
+ sourcedNamesVec.push(entry);
+ }
+ }
+ }
+
+ // Step 4
+ let past_names_map = self.past_names_map.borrow();
+ for (key, val) in past_names_map.iter() {
+ let entry = SourcedName {
+ name: key.clone(),
+ element: DomRoot::from_ref(&*val.0),
+ source: SourcedNameSource::Past(now() - val.1), // calculate difference now()-val.1 to find age
+ };
+ sourcedNamesVec.push(entry);
+ }
+
+ // Step 5
+ // TODO need to sort as per spec.
+ // if a.CompareDocumentPosition(b) returns 0 that means a=b in which case
+ // the remaining part where sorting is to be done by putting entries whose source is id first,
+ // then entries whose source is name, and finally entries whose source is past,
+ // and sorting entries with the same element and source by their age, oldest first.
+
+ // if a.CompareDocumentPosition(b) has set NodeConstants::DOCUMENT_POSITION_FOLLOWING
+ // (this can be checked by bitwise operations) then b would follow a in tree order and
+ // Ordering::Less should be returned in the closure else Ordering::Greater
+
+ sourcedNamesVec.sort_by(|a, b| {
+ if a.element
+ .upcast::<Node>()
+ .CompareDocumentPosition(b.element.upcast::<Node>()) ==
+ 0
+ {
+ if a.source.is_past() && b.source.is_past() {
+ b.source.cmp(&a.source)
+ } else {
+ a.source.cmp(&b.source)
+ }
+ } else {
+ if a.element
+ .upcast::<Node>()
+ .CompareDocumentPosition(b.element.upcast::<Node>()) &
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING ==
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING
+ {
+ std::cmp::Ordering::Less
+ } else {
+ std::cmp::Ordering::Greater
+ }
+ }
+ });
+
+ // Step 6
+ sourcedNamesVec.retain(|sn| !sn.name.to_string().is_empty());
+
+ // Step 7-8
+ let mut namesVec: Vec<DOMString> = Vec::new();
+ for elem in sourcedNamesVec.iter() {
+ if namesVec
+ .iter()
+ .find(|name| name.to_string() == elem.name.to_string())
+ .is_none()
+ {
+ namesVec.push(elem.name.clone());
+ }
+ }
+
+ return namesVec;
+ }
}
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
diff --git a/components/script/dom/webidls/HTMLFormElement.webidl b/components/script/dom/webidls/HTMLFormElement.webidl
index f48726c343a..13ffbf4dfe8 100644
--- a/components/script/dom/webidls/HTMLFormElement.webidl
+++ b/components/script/dom/webidls/HTMLFormElement.webidl
@@ -29,7 +29,7 @@ interface HTMLFormElement : HTMLElement {
[SameObject] readonly attribute HTMLFormControlsCollection elements;
readonly attribute unsigned long length;
getter Element? (unsigned long index);
- //getter (RadioNodeList or Element) (DOMString name);
+ getter (RadioNodeList or Element) (DOMString name);
void submit();
[CEReactions]
diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-2.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-2.html.ini
index 61799e4c935..6d6e5b37510 100644
--- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-2.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-2.html.ini
@@ -1,5 +1,5 @@
[form-double-submit-2.html]
- expected: ERROR
+ expected: TIMEOUT
[preventDefault should allow onclick submit() to succeed]
expected: FAIL
diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit.html.ini
index dce74c6dd71..a951013eb1e 100644
--- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit.html.ini
@@ -1,5 +1,5 @@
[form-double-submit.html]
- expected: ERROR
+ expected: TIMEOUT
[default submit action should supersede onclick submit()]
expected: FAIL
diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini
index 52cb32ee092..1183ab27af2 100644
--- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini
@@ -14,3 +14,6 @@
[firing an event named submit; form.requestSubmit()]
expected: FAIL
+ [Cannot navigate (after constructing the entry list)]
+ expected: FAIL
+
diff --git a/tests/wpt/metadata/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html.ini b/tests/wpt/metadata/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html.ini
index 57d01129025..9bdcfee9aeb 100644
--- a/tests/wpt/metadata/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html.ini
@@ -6,6 +6,3 @@
[The elements must return an HTMLFormControlsCollection object]
expected: FAIL
- [The controls must root at the fieldset element]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/semantics/forms/the-form-element/form-nameditem.html.ini b/tests/wpt/metadata/html/semantics/forms/the-form-element/form-nameditem.html.ini
index 3ebc134b386..2a540f30348 100644
--- a/tests/wpt/metadata/html/semantics/forms/the-form-element/form-nameditem.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/the-form-element/form-nameditem.html.ini
@@ -3,33 +3,18 @@
[Name for a single element should work]
expected: FAIL
- [Calling item() on the NodeList returned from the named getter should work]
- expected: FAIL
-
- [Indexed getter on the NodeList returned from the named getter should work]
- expected: FAIL
-
[All listed elements except input type=image should be present in the form]
expected: FAIL
[Named elements should override builtins]
expected: FAIL
- [The form attribute should be taken into account for named getters (single element)]
- expected: FAIL
-
[The form attribute should be taken into account for named getters (multiple elements)]
expected: FAIL
- [Input should only be a named property on the innermost form that contains it]
- expected: FAIL
-
[Trying to set an expando that would shadow an already-existing named property]
expected: FAIL
- [Trying to set an expando that shadows a named property that gets added later]
- expected: FAIL
-
[Past names map should work correctly]
expected: FAIL