Commit 34bc581e authored by Kent Tamura's avatar Kent Tamura Committed by Commit Bot

Form-associated custom elements: Add more state checks

 - HTMLElement.prototype.attachInternals() should throw InvalidStateErrors
   for customized built-in elements.
 - Form-related operations and attributes of ElementInternals should throw
   InvalidStateErrors for non-form-associated custom elements.

Bug: 905922
Bug: https://github.com/w3c/webcomponents/issues/187
Change-Id: I09e574a843296b236fe4c63eb2cd0912c7a673b1
Reviewed-on: https://chromium-review.googlesource.com/c/1353035Reviewed-by: default avatarHayato Ito <hayato@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611960}
parent 1bcdee9c
......@@ -25,12 +25,20 @@ void ElementInternals::Trace(Visitor* visitor) {
ScriptWrappable::Trace(visitor);
}
void ElementInternals::setFormValue(const FileOrUSVString& value) {
setFormValue(value, nullptr);
void ElementInternals::setFormValue(const FileOrUSVString& value,
ExceptionState& exception_state) {
setFormValue(value, nullptr, exception_state);
}
void ElementInternals::setFormValue(const FileOrUSVString& value,
FormData* entry_source) {
FormData* entry_source,
ExceptionState& exception_state) {
if (!Target().IsFormAssociatedCustomElement()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"The target element is not a form-associated custom element.");
return;
}
if (!entry_source || entry_source->size() == 0u) {
value_ = value;
entry_source_ = nullptr;
......@@ -40,7 +48,13 @@ void ElementInternals::setFormValue(const FileOrUSVString& value,
entry_source_ = MakeGarbageCollected<FormData>(*entry_source);
}
HTMLFormElement* ElementInternals::form() const {
HTMLFormElement* ElementInternals::form(ExceptionState& exception_state) const {
if (!Target().IsFormAssociatedCustomElement()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"The target element is not a form-associated custom element.");
return nullptr;
}
return ListedElement::Form();
}
......@@ -49,7 +63,7 @@ void ElementInternals::DidUpgrade() {
if (!parent)
return;
InsertedInto(*parent);
if (auto* owner_form = form()) {
if (auto* owner_form = Form()) {
if (auto* lists = owner_form->NodeLists())
lists->InvalidateCaches(nullptr);
}
......@@ -97,7 +111,7 @@ void ElementInternals::AppendToFormData(FormData& form_data) {
void ElementInternals::DidChangeForm() {
ListedElement::DidChangeForm();
CustomElement::EnqueueFormAssociatedCallback(Target(), form());
CustomElement::EnqueueFormAssociatedCallback(Target(), Form());
}
} // namespace blink
......@@ -26,9 +26,12 @@ class ElementInternals : public ScriptWrappable, public ListedElement {
void DidUpgrade();
// IDL attributes/operations
void setFormValue(const FileOrUSVString& value);
void setFormValue(const FileOrUSVString& value, FormData* entry_source);
HTMLFormElement* form() const;
void setFormValue(const FileOrUSVString& value,
ExceptionState& exception_state);
void setFormValue(const FileOrUSVString& value,
FormData* entry_source,
ExceptionState& exception_state);
HTMLFormElement* form(ExceptionState& exception_state) const;
private:
// ListedElement overrides:
......
......@@ -10,7 +10,7 @@
]
interface ElementInternals {
// Attributes and operations for form-associated custom elements.
void setFormValue(FormDataEntryValue value, optional FormData entrySource);
readonly attribute HTMLFormElement? form;
[RaisesException] void setFormValue(FormDataEntryValue value, optional FormData entrySource);
[RaisesException] readonly attribute HTMLFormElement? form;
};
......@@ -1436,6 +1436,12 @@ ElementInternals* HTMLElement::attachInternals(
"Unable to attach ElementInternals to non-custom elements.");
return nullptr;
}
if (!definition->Descriptor().IsAutonomous()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Unable to attach ElementInternals to a customized built-in element.");
return nullptr;
}
if (definition->DisableInternals()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
......
<!DOCTYPE html>
<body>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
test(() => {
class MyElement1 extends HTMLElement {}
customElements.define('my-element1', MyElement1);
const element = new MyElement1();
const internals = element.attachInternals();
assert_throws('InvalidStateError', () => { internals.setFormValue(''); });
assert_throws('InvalidStateError', () => { internals.form; });
}, 'Form-related operations and attributes should throw InvalidStateErrors for' +
'non-form-associated custom elements.');
</script>
</body>
......@@ -30,6 +30,14 @@ test(() => {
}, 'Parser - 2nd call');
}, 'Successful attachInternals() and the second call.');
test(() => {
class MyDiv extends HTMLDivElement {}
customElements.define('my-div', MyDiv, { extends: 'div' });
const customizedBuiltin = document.createElement('div', { is: 'my-div'});
assert_throws('InvalidStateError', () => { customizedBuiltin.attachInternals() });
}, 'attachInternals() throws an InvalidStateError if it is called for ' +
'a customized built-in element');
test(() => {
const builtin = document.createElement('div');
assert_throws('InvalidStateError', () => { builtin.attachInternals() });
......
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