Commit 8df10e67 authored by Mason Freed's avatar Mason Freed Committed by Commit Bot

Fix forms.elements.namedItem for Form Associated Custom Elements

Prior to this CL, if there were multiple form associated custom elements
with the same "name" attribute, the document.forms.elements.namedItem()
API would not contain the custom elements.

Fixed: 1124818
Change-Id: I5f97fbddc6ef9d03432edde7a643c3e01df70e56
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2398953
Commit-Queue: Kent Tamura <tkent@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Auto-Submit: Mason Freed <masonfreed@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806515}
parent 96d76ad2
...@@ -3384,6 +3384,10 @@ ElementInternals& Element::EnsureElementInternals() { ...@@ -3384,6 +3384,10 @@ ElementInternals& Element::EnsureElementInternals() {
return EnsureElementRareData().EnsureElementInternals(To<HTMLElement>(*this)); return EnsureElementRareData().EnsureElementInternals(To<HTMLElement>(*this));
} }
const ElementInternals* Element::GetElementInternals() const {
return HasRareData() ? GetElementRareData()->GetElementInternals() : nullptr;
}
ShadowRoot* Element::createShadowRoot(ExceptionState& exception_state) { ShadowRoot* Element::createShadowRoot(ExceptionState& exception_state) {
DCHECK(RuntimeEnabledFeatures::ShadowDOMV0Enabled(GetExecutionContext())); DCHECK(RuntimeEnabledFeatures::ShadowDOMV0Enabled(GetExecutionContext()));
if (ShadowRoot* root = GetShadowRoot()) { if (ShadowRoot* root = GetShadowRoot()) {
......
...@@ -828,6 +828,7 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable { ...@@ -828,6 +828,7 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {
void SetDidAttachInternals(); void SetDidAttachInternals();
bool DidAttachInternals() const; bool DidAttachInternals() const;
ElementInternals& EnsureElementInternals(); ElementInternals& EnsureElementInternals();
const ElementInternals* GetElementInternals() const;
bool ContainsFullScreenElement() const { bool ContainsFullScreenElement() const {
return HasElementFlag(ElementFlags::kContainsFullScreenElement); return HasElementFlag(ElementFlags::kContainsFullScreenElement);
......
...@@ -144,6 +144,9 @@ class ElementRareData : public NodeRareData { ...@@ -144,6 +144,9 @@ class ElementRareData : public NodeRareData {
void SetDidAttachInternals() { did_attach_internals_ = true; } void SetDidAttachInternals() { did_attach_internals_ = true; }
bool DidAttachInternals() const { return did_attach_internals_; } bool DidAttachInternals() const { return did_attach_internals_; }
ElementInternals& EnsureElementInternals(HTMLElement& target); ElementInternals& EnsureElementInternals(HTMLElement& target);
const ElementInternals* GetElementInternals() const {
return element_internals_;
}
void SetStyleShouldForceLegacyLayout(bool force) { void SetStyleShouldForceLegacyLayout(bool force) {
style_should_force_legacy_layout_ = force; style_should_force_legacy_layout_ = force;
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node_rare_data.h" #include "third_party/blink/renderer/core/dom/node_rare_data.h"
#include "third_party/blink/renderer/core/html/custom/element_internals.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h" #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/html_image_element.h" #include "third_party/blink/renderer/core/html/html_image_element.h"
...@@ -92,20 +93,6 @@ bool RadioNodeList::MatchesByIdOrName(const Element& test_element) const { ...@@ -92,20 +93,6 @@ bool RadioNodeList::MatchesByIdOrName(const Element& test_element) const {
test_element.GetNameAttribute() == name_; test_element.GetNameAttribute() == name_;
} }
bool RadioNodeList::CheckElementMatchesRadioNodeListFilter(
const Element& test_element) const {
DCHECK(!ShouldOnlyMatchImgElements());
DCHECK(IsA<HTMLObjectElement>(test_element) ||
test_element.IsFormControlElement());
if (IsA<HTMLFormElement>(ownerNode())) {
auto* form_element = To<HTMLElement>(test_element).formOwner();
if (!form_element || form_element != ownerNode())
return false;
}
return MatchesByIdOrName(test_element);
}
bool RadioNodeList::ElementMatches(const Element& element) const { bool RadioNodeList::ElementMatches(const Element& element) const {
if (ShouldOnlyMatchImgElements()) { if (ShouldOnlyMatchImgElements()) {
auto* html_image_element = DynamicTo<HTMLImageElement>(element); auto* html_image_element = DynamicTo<HTMLImageElement>(element);
...@@ -117,16 +104,27 @@ bool RadioNodeList::ElementMatches(const Element& element) const { ...@@ -117,16 +104,27 @@ bool RadioNodeList::ElementMatches(const Element& element) const {
return MatchesByIdOrName(element); return MatchesByIdOrName(element);
} }
auto* html_element = DynamicTo<HTMLElement>(element);
if (!IsA<HTMLObjectElement>(element) && !element.IsFormControlElement()) bool is_form_associated =
html_element && html_element->IsFormAssociatedCustomElement();
if (!IsA<HTMLObjectElement>(element) && !element.IsFormControlElement() &&
!is_form_associated) {
return false; return false;
}
auto* html_input_element = DynamicTo<HTMLInputElement>(&element); auto* html_input_element = DynamicTo<HTMLInputElement>(&element);
if (html_input_element && if (html_input_element &&
html_input_element->type() == input_type_names::kImage) html_input_element->type() == input_type_names::kImage) {
return false; return false;
}
if (IsA<HTMLFormElement>(ownerNode())) {
auto* form_element = html_element->formOwner();
if (!form_element || form_element != ownerNode())
return false;
}
return CheckElementMatchesRadioNodeListFilter(element); return MatchesByIdOrName(element);
} }
} // namespace blink } // namespace blink
...@@ -45,8 +45,6 @@ class RadioNodeList final : public LiveNodeList { ...@@ -45,8 +45,6 @@ class RadioNodeList final : public LiveNodeList {
void setValue(const String&); void setValue(const String&);
private: private:
bool CheckElementMatchesRadioNodeListFilter(const Element&) const;
bool MatchesByIdOrName(const Element&) const; bool MatchesByIdOrName(const Element&) const;
bool ShouldOnlyMatchImgElements() const { bool ShouldOnlyMatchImgElements() const {
return GetType() == kRadioImgNodeListType; return GetType() == kRadioImgNodeListType;
......
...@@ -1077,6 +1077,12 @@ void HTMLElement::setDir(const AtomicString& value) { ...@@ -1077,6 +1077,12 @@ void HTMLElement::setDir(const AtomicString& value) {
setAttribute(html_names::kDirAttr, value); setAttribute(html_names::kDirAttr, value);
} }
HTMLFormElement* HTMLElement::formOwner() const {
if (const auto* internals = GetElementInternals())
return internals->Form();
return nullptr;
}
HTMLFormElement* HTMLElement::FindFormAncestor() const { HTMLFormElement* HTMLElement::FindFormAncestor() const {
return Traversal<HTMLFormElement>::FirstAncestor(*this); return Traversal<HTMLFormElement>::FirstAncestor(*this);
} }
......
...@@ -100,7 +100,7 @@ class CORE_EXPORT HTMLElement : public Element { ...@@ -100,7 +100,7 @@ class CORE_EXPORT HTMLElement : public Element {
bool ShouldSerializeEndTag() const; bool ShouldSerializeEndTag() const;
virtual HTMLFormElement* formOwner() const { return nullptr; } virtual HTMLFormElement* formOwner() const;
HTMLFormElement* FindFormAncestor() const; HTMLFormElement* FindFormAncestor() const;
......
<!DOCTYPE html>
<body>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
customElements.define('custom-input', class extends HTMLElement {
static get formAssociated() {return true;}
});
</script>
<form>
<custom-input id="custom-1" name="alone"></custom-input>
<custom-input id="custom-2" name="group"></custom-input>
<custom-input id="custom-3" name="group"></custom-input>
</form>
<script>
test(() => {
const formElements = document.forms[0].elements;
assert_equals(formElements['invalid'],undefined);
assert_equals(formElements['alone'],document.getElementById('custom-1'),'Single input should be returned as-is');
assert_true(formElements['group'] instanceof RadioNodeList,'Repeated names should result in RadioNodeList');
const expected = [document.getElementById('custom-2'),
document.getElementById('custom-3')];
assert_array_equals(formElements['group'],expected,'Repeated names should be contained in RadioNodeList, in tree order');
}, 'Form associated custom elements should work with document.forms.elements.namedItem()');
</script>
</body>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment