mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
Implement form-associated custom elements and their ElementInternals (#31980)
* FACEs work, setFormValue test is awful so now has _mozilla backup * 1. Impl Validatable in ElementInternals instead of HTMLElement. 2. Reuse the code in Validatable trait. 3. The form associated custom element is not a customized built-in element. * add some comments * support readonly attribute and complete barred from constraint validation * Addressed the code review comments * Updated the legacy-layout results * Fixed the WPT failures in ElementInternals-validation.html * Addressed the code review comments * Review suggestions * Fixed silly mistakes and update the test result outside elementinternals * update the test results --------- Co-authored-by: Patrick Shaughnessy <pshaughn@comcast.net> Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
2eb959a159
commit
4e4a4c0a28
67 changed files with 1641 additions and 619 deletions
|
@ -12,7 +12,7 @@ use html5ever::{namespace_url, ns, LocalName, Namespace, Prefix};
|
||||||
use js::conversions::ToJSValConvertible;
|
use js::conversions::ToJSValConvertible;
|
||||||
use js::glue::UnwrapObjectStatic;
|
use js::glue::UnwrapObjectStatic;
|
||||||
use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject};
|
use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject};
|
||||||
use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue};
|
use js::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue};
|
||||||
use js::rust::wrappers::{Construct1, JS_GetProperty, SameValue};
|
use js::rust::wrappers::{Construct1, JS_GetProperty, SameValue};
|
||||||
use js::rust::{HandleObject, HandleValue, MutableHandleValue};
|
use js::rust::{HandleObject, HandleValue, MutableHandleValue};
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ use crate::dom::domexception::{DOMErrorName, DOMException};
|
||||||
use crate::dom::element::Element;
|
use crate::dom::element::Element;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::htmlelement::HTMLElement;
|
use crate::dom::htmlelement::HTMLElement;
|
||||||
|
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
|
||||||
use crate::dom::node::{document_from_node, window_from_node, Node, ShadowIncluding};
|
use crate::dom::node::{document_from_node, window_from_node, Node, ShadowIncluding};
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
|
@ -164,7 +165,8 @@ impl CustomElementRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
||||||
/// Steps 10.3, 10.4
|
/// This function includes both steps 14.3 and 14.4 which add the callbacks to a map and
|
||||||
|
/// process them.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn get_callbacks(&self, prototype: HandleObject) -> Fallible<LifecycleCallbacks> {
|
unsafe fn get_callbacks(&self, prototype: HandleObject) -> Fallible<LifecycleCallbacks> {
|
||||||
let cx = GlobalScope::get_cx();
|
let cx = GlobalScope::get_cx();
|
||||||
|
@ -175,11 +177,34 @@ impl CustomElementRegistry {
|
||||||
disconnected_callback: get_callback(cx, prototype, b"disconnectedCallback\0")?,
|
disconnected_callback: get_callback(cx, prototype, b"disconnectedCallback\0")?,
|
||||||
adopted_callback: get_callback(cx, prototype, b"adoptedCallback\0")?,
|
adopted_callback: get_callback(cx, prototype, b"adoptedCallback\0")?,
|
||||||
attribute_changed_callback: get_callback(cx, prototype, b"attributeChangedCallback\0")?,
|
attribute_changed_callback: get_callback(cx, prototype, b"attributeChangedCallback\0")?,
|
||||||
|
|
||||||
|
form_associated_callback: None,
|
||||||
|
form_disabled_callback: None,
|
||||||
|
form_reset_callback: None,
|
||||||
|
form_state_restore_callback: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
||||||
/// Step 10.6
|
/// Step 14.13: Add form associated callbacks to LifecycleCallbacks
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn add_form_associated_callbacks(
|
||||||
|
&self,
|
||||||
|
prototype: HandleObject,
|
||||||
|
callbacks: &mut LifecycleCallbacks,
|
||||||
|
) -> ErrorResult {
|
||||||
|
let cx = self.window.get_cx();
|
||||||
|
|
||||||
|
callbacks.form_associated_callback =
|
||||||
|
get_callback(cx, prototype, b"formAssociatedCallback\0")?;
|
||||||
|
callbacks.form_reset_callback = get_callback(cx, prototype, b"formResetCallback\0")?;
|
||||||
|
callbacks.form_disabled_callback = get_callback(cx, prototype, b"formDisabledCallback\0")?;
|
||||||
|
callbacks.form_state_restore_callback =
|
||||||
|
get_callback(cx, prototype, b"formStateRestoreCallback\0")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn get_observed_attributes(&self, constructor: HandleObject) -> Fallible<Vec<DOMString>> {
|
fn get_observed_attributes(&self, constructor: HandleObject) -> Fallible<Vec<DOMString>> {
|
||||||
let cx = GlobalScope::get_cx();
|
let cx = GlobalScope::get_cx();
|
||||||
|
@ -212,10 +237,75 @@ impl CustomElementRegistry {
|
||||||
_ => Err(Error::JSFailed),
|
_ => Err(Error::JSFailed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
||||||
|
/// Step 14.11: Get the value of `formAssociated`.
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn get_form_associated_value(&self, constructor: HandleObject) -> Fallible<bool> {
|
||||||
|
let cx = self.window.get_cx();
|
||||||
|
rooted!(in(*cx) let mut form_associated_value = UndefinedValue());
|
||||||
|
if unsafe {
|
||||||
|
!JS_GetProperty(
|
||||||
|
*cx,
|
||||||
|
constructor,
|
||||||
|
b"formAssociated\0".as_ptr() as *const _,
|
||||||
|
form_associated_value.handle_mut(),
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
return Err(Error::JSFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if form_associated_value.is_undefined() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let conversion =
|
||||||
|
unsafe { FromJSValConvertible::from_jsval(*cx, form_associated_value.handle(), ()) };
|
||||||
|
match conversion {
|
||||||
|
Ok(ConversionResult::Success(flag)) => Ok(flag),
|
||||||
|
Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into())),
|
||||||
|
_ => Err(Error::JSFailed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
||||||
|
/// Step 14.7: Get `disabledFeatures` value
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn get_disabled_features(&self, constructor: HandleObject) -> Fallible<Vec<DOMString>> {
|
||||||
|
let cx = self.window.get_cx();
|
||||||
|
rooted!(in(*cx) let mut disabled_features = UndefinedValue());
|
||||||
|
if unsafe {
|
||||||
|
!JS_GetProperty(
|
||||||
|
*cx,
|
||||||
|
constructor,
|
||||||
|
b"disabledFeatures\0".as_ptr() as *const _,
|
||||||
|
disabled_features.handle_mut(),
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
return Err(Error::JSFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if disabled_features.is_undefined() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let conversion = unsafe {
|
||||||
|
FromJSValConvertible::from_jsval(
|
||||||
|
*cx,
|
||||||
|
disabled_features.handle(),
|
||||||
|
StringificationBehavior::Default,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match conversion {
|
||||||
|
Ok(ConversionResult::Success(attributes)) => Ok(attributes),
|
||||||
|
Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into())),
|
||||||
|
_ => Err(Error::JSFailed),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define>
|
||||||
/// Step 10.4
|
/// Step 14.4: Get `callbackValue` for all `callbackName` in `lifecycleCallbacks`.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn get_callback(
|
fn get_callback(
|
||||||
cx: JSContext,
|
cx: JSContext,
|
||||||
|
@ -323,7 +413,10 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
|
||||||
// Step 9
|
// Step 9
|
||||||
self.element_definition_is_running.set(true);
|
self.element_definition_is_running.set(true);
|
||||||
|
|
||||||
// Steps 10.1 - 10.2
|
// Steps 10-13: Initialize `formAssociated`, `disableInternals`, `disableShadow`, and
|
||||||
|
// `observedAttributes` with default values, but this is done later.
|
||||||
|
|
||||||
|
// Steps 14.1 - 14.2: Get the value of the prototype.
|
||||||
rooted!(in(*cx) let mut prototype = UndefinedValue());
|
rooted!(in(*cx) let mut prototype = UndefinedValue());
|
||||||
{
|
{
|
||||||
let _ac = JSAutoRealm::new(*cx, constructor.get());
|
let _ac = JSAutoRealm::new(*cx, constructor.get());
|
||||||
|
@ -334,8 +427,12 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Steps 10.3 - 10.4
|
// Steps 10.3 - 10.4
|
||||||
|
// It would be easier to get all the callbacks in one pass after
|
||||||
|
// we know whether this definition is going to be form-associated,
|
||||||
|
// but the order of operations is specified and it's observable
|
||||||
|
// if one of the callback getters throws an exception.
|
||||||
rooted!(in(*cx) let proto_object = prototype.to_object());
|
rooted!(in(*cx) let proto_object = prototype.to_object());
|
||||||
let callbacks = {
|
let mut callbacks = {
|
||||||
let _ac = JSAutoRealm::new(*cx, proto_object.get());
|
let _ac = JSAutoRealm::new(*cx, proto_object.get());
|
||||||
match unsafe { self.get_callbacks(proto_object.handle()) } {
|
match unsafe { self.get_callbacks(proto_object.handle()) } {
|
||||||
Ok(callbacks) => callbacks,
|
Ok(callbacks) => callbacks,
|
||||||
|
@ -346,7 +443,8 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 10.5 - 10.6
|
// Step 14.5: Handle the case where with `attributeChangedCallback` on `lifecycleCallbacks`
|
||||||
|
// is not null.
|
||||||
let observed_attributes = if callbacks.attribute_changed_callback.is_some() {
|
let observed_attributes = if callbacks.attribute_changed_callback.is_some() {
|
||||||
let _ac = JSAutoRealm::new(*cx, constructor.get());
|
let _ac = JSAutoRealm::new(*cx, constructor.get());
|
||||||
match self.get_observed_attributes(constructor.handle()) {
|
match self.get_observed_attributes(constructor.handle()) {
|
||||||
|
@ -360,26 +458,71 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Steps 14.6 - 14.10: Handle `disabledFeatures`.
|
||||||
|
let (disable_internals, disable_shadow) = {
|
||||||
|
let _ac = JSAutoRealm::new(*cx, constructor.get());
|
||||||
|
match self.get_disabled_features(constructor.handle()) {
|
||||||
|
Ok(sequence) => (
|
||||||
|
sequence.iter().any(|s| *s == "internals"),
|
||||||
|
sequence.iter().any(|s| *s == "shadow"),
|
||||||
|
),
|
||||||
|
Err(error) => {
|
||||||
|
self.element_definition_is_running.set(false);
|
||||||
|
return Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 14.11 - 14.12: Handle `formAssociated`.
|
||||||
|
let form_associated = {
|
||||||
|
let _ac = JSAutoRealm::new(*cx, constructor.get());
|
||||||
|
match self.get_form_associated_value(constructor.handle()) {
|
||||||
|
Ok(flag) => flag,
|
||||||
|
Err(error) => {
|
||||||
|
self.element_definition_is_running.set(false);
|
||||||
|
return Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Steps 14.13: Add the `formAssociated` callbacks.
|
||||||
|
if form_associated {
|
||||||
|
let _ac = JSAutoRealm::new(*cx, proto_object.get());
|
||||||
|
unsafe {
|
||||||
|
match self.add_form_associated_callbacks(proto_object.handle(), &mut callbacks) {
|
||||||
|
Err(error) => {
|
||||||
|
self.element_definition_is_running.set(false);
|
||||||
|
return Err(error);
|
||||||
|
},
|
||||||
|
Ok(()) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.element_definition_is_running.set(false);
|
self.element_definition_is_running.set(false);
|
||||||
|
|
||||||
// Step 11
|
// Step 15: Set up the new custom element definition.
|
||||||
let definition = Rc::new(CustomElementDefinition::new(
|
let definition = Rc::new(CustomElementDefinition::new(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
local_name.clone(),
|
local_name.clone(),
|
||||||
constructor_,
|
constructor_,
|
||||||
observed_attributes,
|
observed_attributes,
|
||||||
callbacks,
|
callbacks,
|
||||||
|
form_associated,
|
||||||
|
disable_internals,
|
||||||
|
disable_shadow,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Step 12
|
// Step 16: Add definition to this CustomElementRegistry.
|
||||||
self.definitions
|
self.definitions
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(name.clone(), definition.clone());
|
.insert(name.clone(), definition.clone());
|
||||||
|
|
||||||
// Step 13
|
// Step 17: Let document be this CustomElementRegistry's relevant global object's
|
||||||
|
// associated Document.
|
||||||
let document = self.window.Document();
|
let document = self.window.Document();
|
||||||
|
|
||||||
// Steps 14-15
|
// Steps 18-19: Enqueue custom elements upgrade reaction for upgrade candidates.
|
||||||
for candidate in document
|
for candidate in document
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
.traverse_preorder(ShadowIncluding::Yes)
|
.traverse_preorder(ShadowIncluding::Yes)
|
||||||
|
@ -489,6 +632,18 @@ pub struct LifecycleCallbacks {
|
||||||
|
|
||||||
#[ignore_malloc_size_of = "Rc"]
|
#[ignore_malloc_size_of = "Rc"]
|
||||||
attribute_changed_callback: Option<Rc<Function>>,
|
attribute_changed_callback: Option<Rc<Function>>,
|
||||||
|
|
||||||
|
#[ignore_malloc_size_of = "Rc"]
|
||||||
|
form_associated_callback: Option<Rc<Function>>,
|
||||||
|
|
||||||
|
#[ignore_malloc_size_of = "Rc"]
|
||||||
|
form_reset_callback: Option<Rc<Function>>,
|
||||||
|
|
||||||
|
#[ignore_malloc_size_of = "Rc"]
|
||||||
|
form_disabled_callback: Option<Rc<Function>>,
|
||||||
|
|
||||||
|
#[ignore_malloc_size_of = "Rc"]
|
||||||
|
form_state_restore_callback: Option<Rc<Function>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
|
@ -514,6 +669,12 @@ pub struct CustomElementDefinition {
|
||||||
pub callbacks: LifecycleCallbacks,
|
pub callbacks: LifecycleCallbacks,
|
||||||
|
|
||||||
pub construction_stack: DomRefCell<Vec<ConstructionStackEntry>>,
|
pub construction_stack: DomRefCell<Vec<ConstructionStackEntry>>,
|
||||||
|
|
||||||
|
pub form_associated: bool,
|
||||||
|
|
||||||
|
pub disable_internals: bool,
|
||||||
|
|
||||||
|
pub disable_shadow: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomElementDefinition {
|
impl CustomElementDefinition {
|
||||||
|
@ -523,6 +684,9 @@ impl CustomElementDefinition {
|
||||||
constructor: Rc<CustomElementConstructor>,
|
constructor: Rc<CustomElementConstructor>,
|
||||||
observed_attributes: Vec<DOMString>,
|
observed_attributes: Vec<DOMString>,
|
||||||
callbacks: LifecycleCallbacks,
|
callbacks: LifecycleCallbacks,
|
||||||
|
form_associated: bool,
|
||||||
|
disable_internals: bool,
|
||||||
|
disable_shadow: bool,
|
||||||
) -> CustomElementDefinition {
|
) -> CustomElementDefinition {
|
||||||
CustomElementDefinition {
|
CustomElementDefinition {
|
||||||
name,
|
name,
|
||||||
|
@ -531,6 +695,9 @@ impl CustomElementDefinition {
|
||||||
observed_attributes,
|
observed_attributes,
|
||||||
callbacks,
|
callbacks,
|
||||||
construction_stack: Default::default(),
|
construction_stack: Default::default(),
|
||||||
|
form_associated: form_associated,
|
||||||
|
disable_internals: disable_internals,
|
||||||
|
disable_shadow: disable_shadow,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +843,43 @@ pub fn upgrade_element(definition: Rc<CustomElementDefinition>, element: &Elemen
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Step 9: "If element is a form-associated custom element..."
|
// Step 9: handle with form-associated custom element
|
||||||
|
if let Some(html_element) = element.downcast::<HTMLElement>() {
|
||||||
|
if html_element.is_form_associated_custom_element() {
|
||||||
|
// We know this element is is form-associated, so we can use the implementation of
|
||||||
|
// `FormControl` for HTMLElement, which makes that assumption.
|
||||||
|
// Step 9.1: Reset the form owner of element
|
||||||
|
html_element.reset_form_owner();
|
||||||
|
if let Some(form) = html_element.form_owner() {
|
||||||
|
// Even though the tree hasn't structurally mutated,
|
||||||
|
// HTMLCollections need to be invalidated.
|
||||||
|
form.upcast::<Node>().rev_version();
|
||||||
|
// The spec tells us specifically to enqueue a formAssociated reaction
|
||||||
|
// here, but it also says to do that for resetting form owner in general,
|
||||||
|
// and we don't need two reactions.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either enabled_state or disabled_state needs to be set,
|
||||||
|
// and the possibility of a disabled fieldset ancestor needs
|
||||||
|
// to be accounted for. (In the spec, being disabled is
|
||||||
|
// a fact that's true or false about a node at a given time,
|
||||||
|
// not a flag that belongs to the node and is updated,
|
||||||
|
// so it doesn't describe this check as an action.)
|
||||||
|
element.check_disabled_attribute();
|
||||||
|
element.check_ancestors_disabled_state_for_form_control();
|
||||||
|
element.update_read_write_state_from_readonly_attribute();
|
||||||
|
|
||||||
|
// Step 9.2: If element is disabled, then enqueue a custom element callback reaction
|
||||||
|
// with element.
|
||||||
|
if element.disabled_state() {
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(true),
|
||||||
|
Some(definition.clone()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 10
|
// Step 10
|
||||||
element.set_custom_element_state(CustomElementState::Custom);
|
element.set_custom_element_state(CustomElementState::Custom);
|
||||||
|
@ -796,6 +999,9 @@ pub enum CallbackReaction {
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Adopted(DomRoot<Document>, DomRoot<Document>),
|
Adopted(DomRoot<Document>, DomRoot<Document>),
|
||||||
AttributeChanged(LocalName, Option<DOMString>, Option<DOMString>, Namespace),
|
AttributeChanged(LocalName, Option<DOMString>, Option<DOMString>, Namespace),
|
||||||
|
FormAssociated(Option<DomRoot<HTMLFormElement>>),
|
||||||
|
FormDisabled(bool),
|
||||||
|
FormReset,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage/#processing-the-backup-element-queue>
|
/// <https://html.spec.whatwg.org/multipage/#processing-the-backup-element-queue>
|
||||||
|
@ -963,6 +1169,25 @@ impl CustomElementReactionStack {
|
||||||
args,
|
args,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
CallbackReaction::FormAssociated(form) => {
|
||||||
|
let args = vec![Heap::default()];
|
||||||
|
if let Some(form) = form {
|
||||||
|
args[0].set(ObjectValue(form.reflector().get_jsobject().get()));
|
||||||
|
} else {
|
||||||
|
args[0].set(NullValue());
|
||||||
|
}
|
||||||
|
(definition.callbacks.form_associated_callback.clone(), args)
|
||||||
|
},
|
||||||
|
CallbackReaction::FormDisabled(disabled) => {
|
||||||
|
let cx = GlobalScope::get_cx();
|
||||||
|
rooted!(in(*cx) let mut disabled_value = BooleanValue(disabled));
|
||||||
|
let args = vec![Heap::default()];
|
||||||
|
args[0].set(disabled_value.get());
|
||||||
|
(definition.callbacks.form_disabled_callback.clone(), args)
|
||||||
|
},
|
||||||
|
CallbackReaction::FormReset => {
|
||||||
|
(definition.callbacks.form_reset_callback.clone(), Vec::new())
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 3
|
// Step 3
|
||||||
|
|
|
@ -1256,7 +1256,8 @@ impl Document {
|
||||||
debug!("{} on {:?}", mouse_event_type_string, node.debug_str());
|
debug!("{} on {:?}", mouse_event_type_string, node.debug_str());
|
||||||
// Prevent click event if form control element is disabled.
|
// Prevent click event if form control element is disabled.
|
||||||
if let MouseEventType::Click = mouse_event_type {
|
if let MouseEventType::Click = mouse_event_type {
|
||||||
if el.click_event_filter_by_disabled_state() {
|
// The click event is filtered by the disabled state.
|
||||||
|
if el.is_actually_disabled() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3975,27 +3976,6 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element {
|
|
||||||
fn click_event_filter_by_disabled_state(&self) -> bool {
|
|
||||||
let node = self.upcast::<Node>();
|
|
||||||
matches!(node.type_id(), NodeTypeId::Element(ElementTypeId::HTMLElement(
|
|
||||||
HTMLElementTypeId::HTMLButtonElement,
|
|
||||||
)) |
|
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
|
||||||
HTMLElementTypeId::HTMLInputElement,
|
|
||||||
)) |
|
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
|
||||||
HTMLElementTypeId::HTMLOptionElement,
|
|
||||||
)) |
|
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
|
||||||
HTMLElementTypeId::HTMLSelectElement,
|
|
||||||
)) |
|
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
|
||||||
HTMLElementTypeId::HTMLTextAreaElement,
|
|
||||||
)) if self.disabled_state())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProfilerMetadataFactory for Document {
|
impl ProfilerMetadataFactory for Document {
|
||||||
fn new_metadata(&self) -> Option<TimerMetadata> {
|
fn new_metadata(&self) -> Option<TimerMetadata> {
|
||||||
Some(TimerMetadata {
|
Some(TimerMetadata {
|
||||||
|
|
|
@ -102,6 +102,7 @@ use crate::dom::document::{
|
||||||
use crate::dom::documentfragment::DocumentFragment;
|
use crate::dom::documentfragment::DocumentFragment;
|
||||||
use crate::dom::domrect::DOMRect;
|
use crate::dom::domrect::DOMRect;
|
||||||
use crate::dom::domtokenlist::DOMTokenList;
|
use crate::dom::domtokenlist::DOMTokenList;
|
||||||
|
use crate::dom::elementinternals::ElementInternals;
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::htmlanchorelement::HTMLAnchorElement;
|
use crate::dom::htmlanchorelement::HTMLAnchorElement;
|
||||||
use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
|
use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
|
||||||
|
@ -145,6 +146,7 @@ use crate::dom::servoparser::ServoParser;
|
||||||
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
||||||
use crate::dom::text::Text;
|
use crate::dom::text::Text;
|
||||||
use crate::dom::validation::Validatable;
|
use crate::dom::validation::Validatable;
|
||||||
|
use crate::dom::validitystate::ValidationFlags;
|
||||||
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
|
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
|
||||||
use crate::dom::window::ReflowReason;
|
use crate::dom::window::ReflowReason;
|
||||||
use crate::script_thread::ScriptThread;
|
use crate::script_thread::ScriptThread;
|
||||||
|
@ -1419,6 +1421,12 @@ impl Element {
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||||
HTMLElementTypeId::HTMLOptionElement,
|
HTMLElementTypeId::HTMLOptionElement,
|
||||||
)) => self.disabled_state(),
|
)) => self.disabled_state(),
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
|
||||||
|
self.downcast::<HTMLElement>()
|
||||||
|
.unwrap()
|
||||||
|
.is_form_associated_custom_element() &&
|
||||||
|
self.disabled_state()
|
||||||
|
},
|
||||||
// TODO:
|
// TODO:
|
||||||
// an optgroup element that has a disabled attribute
|
// an optgroup element that has a disabled attribute
|
||||||
// a menuitem element that has a disabled attribute
|
// a menuitem element that has a disabled attribute
|
||||||
|
@ -1857,10 +1865,17 @@ impl Element {
|
||||||
// https://w3c.github.io/DOM-Parsing/#parsing
|
// https://w3c.github.io/DOM-Parsing/#parsing
|
||||||
pub fn parse_fragment(&self, markup: DOMString) -> Fallible<DomRoot<DocumentFragment>> {
|
pub fn parse_fragment(&self, markup: DOMString) -> Fallible<DomRoot<DocumentFragment>> {
|
||||||
// Steps 1-2.
|
// Steps 1-2.
|
||||||
let context_document = document_from_node(self);
|
|
||||||
// TODO(#11995): XML case.
|
// TODO(#11995): XML case.
|
||||||
let new_children = ServoParser::parse_html_fragment(self, markup);
|
let new_children = ServoParser::parse_html_fragment(self, markup);
|
||||||
// Step 3.
|
// Step 3.
|
||||||
|
// See https://github.com/w3c/DOM-Parsing/issues/61.
|
||||||
|
let context_document = {
|
||||||
|
if let Some(template) = self.downcast::<HTMLTemplateElement>() {
|
||||||
|
template.Content().upcast::<Node>().owner_doc()
|
||||||
|
} else {
|
||||||
|
document_from_node(self)
|
||||||
|
}
|
||||||
|
};
|
||||||
let fragment = DocumentFragment::new(&context_document);
|
let fragment = DocumentFragment::new(&context_document);
|
||||||
// Step 4.
|
// Step 4.
|
||||||
for child in new_children {
|
for child in new_children {
|
||||||
|
@ -1973,6 +1988,24 @@ impl Element {
|
||||||
document.perform_focus_fixup_rule(self);
|
document.perform_focus_fixup_rule(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
|
||||||
|
self.rare_data()
|
||||||
|
.as_ref()?
|
||||||
|
.element_internals
|
||||||
|
.as_ref()
|
||||||
|
.map(|sr| DomRoot::from_ref(&**sr))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_element_internals(&self) -> DomRoot<ElementInternals> {
|
||||||
|
let mut rare_data = self.ensure_rare_data();
|
||||||
|
DomRoot::from_ref(rare_data.element_internals.get_or_insert_with(|| {
|
||||||
|
let elem = self
|
||||||
|
.downcast::<HTMLElement>()
|
||||||
|
.expect("ensure_element_internals should only be called for an HTMLElement");
|
||||||
|
Dom::from_ref(&*ElementInternals::new(elem))
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementMethods for Element {
|
impl ElementMethods for Element {
|
||||||
|
@ -3098,6 +3131,9 @@ impl VirtualMethods for Element {
|
||||||
self.super_type().unwrap().unbind_from_tree(context);
|
self.super_type().unwrap().unbind_from_tree(context);
|
||||||
|
|
||||||
if let Some(f) = self.as_maybe_form_control() {
|
if let Some(f) = self.as_maybe_form_control() {
|
||||||
|
// TODO: The valid state of ancestors might be wrong if the form control element
|
||||||
|
// has a fieldset ancestor, for instance: `<form><fieldset><input>`,
|
||||||
|
// if `<input>` is unbound, `<form><fieldset>` should trigger a call to `update_validity()`.
|
||||||
f.unbind_form_control_from_tree();
|
f.unbind_form_control_from_tree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3543,6 +3579,38 @@ impl Element {
|
||||||
element
|
element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_invalid(&self, needs_update: bool) -> bool {
|
||||||
|
if let Some(validatable) = self.as_maybe_validatable() {
|
||||||
|
if needs_update {
|
||||||
|
validatable
|
||||||
|
.validity_state()
|
||||||
|
.perform_validation_and_update(ValidationFlags::all());
|
||||||
|
}
|
||||||
|
return validatable.is_instance_validatable() && !validatable.satisfies_constraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(internals) = self.get_element_internals() {
|
||||||
|
return internals.is_invalid();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_instance_validatable(&self) -> bool {
|
||||||
|
if let Some(validatable) = self.as_maybe_validatable() {
|
||||||
|
return validatable.is_instance_validatable();
|
||||||
|
}
|
||||||
|
if let Some(internals) = self.get_element_internals() {
|
||||||
|
return internals.is_instance_validatable();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_state_for_internals(&self) {
|
||||||
|
self.set_enabled_state(true);
|
||||||
|
self.set_state(ElementState::VALID, true);
|
||||||
|
self.set_state(ElementState::INVALID, false);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn click_in_progress(&self) -> bool {
|
pub fn click_in_progress(&self) -> bool {
|
||||||
self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
|
self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
|
||||||
}
|
}
|
||||||
|
@ -3743,6 +3811,11 @@ impl Element {
|
||||||
self.set_disabled_state(has_disabled_attrib);
|
self.set_disabled_state(has_disabled_attrib);
|
||||||
self.set_enabled_state(!has_disabled_attrib);
|
self.set_enabled_state(!has_disabled_attrib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_read_write_state_from_readonly_attribute(&self) {
|
||||||
|
let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
|
||||||
|
self.set_read_write_state(has_readonly_attribute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
366
components/script/dom/elementinternals.rs
Normal file
366
components/script/dom/elementinternals.rs
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use html5ever::local_name;
|
||||||
|
|
||||||
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::ElementInternalsBinding::{
|
||||||
|
ElementInternalsMethods, ValidityStateFlags,
|
||||||
|
};
|
||||||
|
use crate::dom::bindings::codegen::UnionTypes::FileOrUSVStringOrFormData;
|
||||||
|
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||||
|
use crate::dom::bindings::inheritance::Castable;
|
||||||
|
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
|
||||||
|
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||||
|
use crate::dom::bindings::str::{DOMString, USVString};
|
||||||
|
use crate::dom::element::Element;
|
||||||
|
use crate::dom::file::File;
|
||||||
|
use crate::dom::htmlelement::HTMLElement;
|
||||||
|
use crate::dom::htmlformelement::{FormDatum, FormDatumValue, HTMLFormElement};
|
||||||
|
use crate::dom::node::{window_from_node, Node};
|
||||||
|
use crate::dom::nodelist::NodeList;
|
||||||
|
use crate::dom::validation::{is_barred_by_datalist_ancestor, Validatable};
|
||||||
|
use crate::dom::validitystate::{ValidationFlags, ValidityState};
|
||||||
|
|
||||||
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
|
enum SubmissionValue {
|
||||||
|
File(DomRoot<File>),
|
||||||
|
FormData(Vec<FormDatum>),
|
||||||
|
USVString(USVString),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<&FileOrUSVStringOrFormData>> for SubmissionValue {
|
||||||
|
fn from(value: Option<&FileOrUSVStringOrFormData>) -> Self {
|
||||||
|
match value {
|
||||||
|
None => SubmissionValue::None,
|
||||||
|
Some(FileOrUSVStringOrFormData::File(file)) => {
|
||||||
|
SubmissionValue::File(DomRoot::from_ref(file))
|
||||||
|
},
|
||||||
|
Some(FileOrUSVStringOrFormData::USVString(usv_string)) => {
|
||||||
|
SubmissionValue::USVString(usv_string.clone())
|
||||||
|
},
|
||||||
|
Some(FileOrUSVStringOrFormData::FormData(form_data)) => {
|
||||||
|
SubmissionValue::FormData(form_data.datums())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct ElementInternals {
|
||||||
|
reflector_: Reflector,
|
||||||
|
/// If `attached` is false, we're using this to hold form-related state
|
||||||
|
/// on an element for which `attachInternals()` wasn't called yet; this is
|
||||||
|
/// necessary because it might have a form owner.
|
||||||
|
attached: Cell<bool>,
|
||||||
|
target_element: Dom<HTMLElement>,
|
||||||
|
validity_state: MutNullableDom<ValidityState>,
|
||||||
|
validation_message: DomRefCell<DOMString>,
|
||||||
|
custom_validity_error_message: DomRefCell<DOMString>,
|
||||||
|
validation_anchor: MutNullableDom<HTMLElement>,
|
||||||
|
submission_value: DomRefCell<SubmissionValue>,
|
||||||
|
state: DomRefCell<SubmissionValue>,
|
||||||
|
form_owner: MutNullableDom<HTMLFormElement>,
|
||||||
|
labels_node_list: MutNullableDom<NodeList>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementInternals {
|
||||||
|
fn new_inherited(target_element: &HTMLElement) -> ElementInternals {
|
||||||
|
ElementInternals {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
attached: Cell::new(false),
|
||||||
|
target_element: Dom::from_ref(target_element),
|
||||||
|
validity_state: Default::default(),
|
||||||
|
validation_message: DomRefCell::new(DOMString::new()),
|
||||||
|
custom_validity_error_message: DomRefCell::new(DOMString::new()),
|
||||||
|
validation_anchor: MutNullableDom::new(None),
|
||||||
|
submission_value: DomRefCell::new(SubmissionValue::None),
|
||||||
|
state: DomRefCell::new(SubmissionValue::None),
|
||||||
|
form_owner: MutNullableDom::new(None),
|
||||||
|
labels_node_list: MutNullableDom::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(element: &HTMLElement) -> DomRoot<ElementInternals> {
|
||||||
|
let global = window_from_node(element);
|
||||||
|
reflect_dom_object(Box::new(ElementInternals::new_inherited(element)), &*global)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_target_form_associated(&self) -> bool {
|
||||||
|
self.target_element.is_form_associated_custom_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_validation_message(&self, message: DOMString) {
|
||||||
|
*self.validation_message.borrow_mut() = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_custom_validity_error_message(&self, message: DOMString) {
|
||||||
|
*self.custom_validity_error_message.borrow_mut() = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_submission_value(&self, value: SubmissionValue) {
|
||||||
|
*self.submission_value.borrow_mut() = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_state(&self, value: SubmissionValue) {
|
||||||
|
*self.state.borrow_mut() = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
|
||||||
|
self.form_owner.set(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
|
||||||
|
self.form_owner.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_attached(&self) {
|
||||||
|
self.attached.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attached(&self) -> bool {
|
||||||
|
self.attached.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perform_entry_construction(&self, entry_list: &mut Vec<FormDatum>) {
|
||||||
|
if self
|
||||||
|
.target_element
|
||||||
|
.upcast::<Element>()
|
||||||
|
.has_attribute(&local_name!("disabled"))
|
||||||
|
{
|
||||||
|
warn!("We are in perform_entry_construction on an element with disabled attribute!");
|
||||||
|
}
|
||||||
|
if self.target_element.upcast::<Element>().disabled_state() {
|
||||||
|
warn!("We are in perform_entry_construction on an element with disabled bit!");
|
||||||
|
}
|
||||||
|
if !self.target_element.upcast::<Element>().enabled_state() {
|
||||||
|
warn!("We are in perform_entry_construction on an element without enabled bit!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SubmissionValue::FormData(datums) = &*self.submission_value.borrow() {
|
||||||
|
entry_list.extend(datums.iter().map(|d| d.clone()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let name = self
|
||||||
|
.target_element
|
||||||
|
.upcast::<Element>()
|
||||||
|
.get_string_attribute(&local_name!("name"));
|
||||||
|
if name.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match &*self.submission_value.borrow() {
|
||||||
|
SubmissionValue::FormData(_) => unreachable!(
|
||||||
|
"The FormData submission value has been handled before name empty checking"
|
||||||
|
),
|
||||||
|
SubmissionValue::None => {},
|
||||||
|
SubmissionValue::USVString(string) => {
|
||||||
|
entry_list.push(FormDatum {
|
||||||
|
ty: DOMString::from("string"),
|
||||||
|
name: name,
|
||||||
|
value: FormDatumValue::String(DOMString::from(string.to_string())),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
SubmissionValue::File(file) => {
|
||||||
|
entry_list.push(FormDatum {
|
||||||
|
ty: DOMString::from("file"),
|
||||||
|
name: name,
|
||||||
|
value: FormDatumValue::File(DomRoot::from_ref(&*file)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_invalid(&self) -> bool {
|
||||||
|
self.is_target_form_associated() &&
|
||||||
|
self.is_instance_validatable() &&
|
||||||
|
!self.satisfies_constraints()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementInternalsMethods for ElementInternals {
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-setformvalue>
|
||||||
|
fn SetFormValue(
|
||||||
|
&self,
|
||||||
|
value: Option<FileOrUSVStringOrFormData>,
|
||||||
|
maybe_state: Option<Option<FileOrUSVStringOrFormData>>,
|
||||||
|
) -> ErrorResult {
|
||||||
|
// Steps 1-2: If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Set target element's submission value
|
||||||
|
self.set_submission_value(value.as_ref().into());
|
||||||
|
|
||||||
|
match maybe_state {
|
||||||
|
// Step 4: If the state argument of the function is omitted, set element's state to its submission value
|
||||||
|
None => self.set_state(value.as_ref().into()),
|
||||||
|
// Steps 5-6: Otherwise, set element's state to state
|
||||||
|
Some(state) => self.set_state(state.as_ref().into()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-setvalidity>
|
||||||
|
fn SetValidity(
|
||||||
|
&self,
|
||||||
|
flags: &ValidityStateFlags,
|
||||||
|
message: Option<DOMString>,
|
||||||
|
anchor: Option<&HTMLElement>,
|
||||||
|
) -> ErrorResult {
|
||||||
|
// Steps 1-2: Check form-associated custom element
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: If flags contains one or more true values and message is not given or is the empty
|
||||||
|
// string, then throw a TypeError.
|
||||||
|
let bits: ValidationFlags = flags.into();
|
||||||
|
if !bits.is_empty() && !message.as_ref().map_or_else(|| false, |m| !m.is_empty()) {
|
||||||
|
return Err(Error::Type(
|
||||||
|
"Setting an element to invalid requires a message string as the second argument."
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: For each entry `flag` → `value` of `flags`, set element's validity flag with the name
|
||||||
|
// `flag` to `value`.
|
||||||
|
self.validity_state().update_invalid_flags(bits);
|
||||||
|
self.validity_state().update_pseudo_classes();
|
||||||
|
|
||||||
|
// Step 5: Set element's validation message to the empty string if message is not given
|
||||||
|
// or all of element's validity flags are false, or to message otherwise.
|
||||||
|
if bits.is_empty() {
|
||||||
|
self.set_validation_message(DOMString::new());
|
||||||
|
} else {
|
||||||
|
self.set_validation_message(message.unwrap_or_else(|| DOMString::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: If element's customError validity flag is true, then set element's custom validity error
|
||||||
|
// message to element's validation message. Otherwise, set element's custom validity error
|
||||||
|
// message to the empty string.
|
||||||
|
if bits.contains(ValidationFlags::CUSTOM_ERROR) {
|
||||||
|
self.set_custom_validity_error_message(self.validation_message.borrow().clone());
|
||||||
|
} else {
|
||||||
|
self.set_custom_validity_error_message(DOMString::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7: Set element's validation anchor to null if anchor is not given.
|
||||||
|
match anchor {
|
||||||
|
None => self.validation_anchor.set(None),
|
||||||
|
Some(a) => {
|
||||||
|
if a == &*self.target_element ||
|
||||||
|
!self
|
||||||
|
.target_element
|
||||||
|
.upcast::<Node>()
|
||||||
|
.is_shadow_including_inclusive_ancestor_of(a.upcast::<Node>())
|
||||||
|
{
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
}
|
||||||
|
self.validation_anchor.set(Some(a));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-validationmessage>
|
||||||
|
fn GetValidationMessage(&self) -> Fallible<DOMString> {
|
||||||
|
// This check isn't in the spec but it's in WPT tests and it maintains
|
||||||
|
// consistency with other methods that do specify it
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.validation_message.borrow().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-validity>
|
||||||
|
fn GetValidity(&self) -> Fallible<DomRoot<ValidityState>> {
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.validity_state())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-labels>
|
||||||
|
fn GetLabels(&self) -> Fallible<DomRoot<NodeList>> {
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.labels_node_list.or_init(|| {
|
||||||
|
NodeList::new_labels_list(
|
||||||
|
self.target_element.upcast::<Node>().owner_doc().window(),
|
||||||
|
&*self.target_element,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-willvalidate>
|
||||||
|
fn GetWillValidate(&self) -> Fallible<bool> {
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.is_instance_validatable())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-form>
|
||||||
|
fn GetForm(&self) -> Fallible<Option<DomRoot<HTMLFormElement>>> {
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.form_owner.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-checkvalidity>
|
||||||
|
fn CheckValidity(&self) -> Fallible<bool> {
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.check_validity())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-reportvalidity>
|
||||||
|
fn ReportValidity(&self) -> Fallible<bool> {
|
||||||
|
if !self.is_target_form_associated() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
Ok(self.report_validity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form-associated custom elements also need the Validatable trait.
|
||||||
|
impl Validatable for ElementInternals {
|
||||||
|
fn as_element(&self) -> &Element {
|
||||||
|
debug_assert!(self.is_target_form_associated());
|
||||||
|
self.target_element.upcast::<Element>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validity_state(&self) -> DomRoot<ValidityState> {
|
||||||
|
debug_assert!(self.is_target_form_associated());
|
||||||
|
self.validity_state.or_init(|| {
|
||||||
|
ValidityState::new(
|
||||||
|
&window_from_node(self.target_element.upcast::<Node>()),
|
||||||
|
self.target_element.upcast(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#candidate-for-constraint-validation>
|
||||||
|
fn is_instance_validatable(&self) -> bool {
|
||||||
|
debug_assert!(self.is_target_form_associated());
|
||||||
|
if !self.target_element.is_submittable_element() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The form-associated custom element is barred from constraint validation,
|
||||||
|
// if the readonly attribute is specified, the element is disabled,
|
||||||
|
// or the element has a datalist element ancestor.
|
||||||
|
!self.as_element().read_write_state() &&
|
||||||
|
!self.as_element().disabled_state() &&
|
||||||
|
!is_barred_by_datalist_ancestor(self.target_element.upcast::<Node>())
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,28 +22,34 @@ use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMeth
|
||||||
use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
|
use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use crate::dom::bindings::error::{Error, ErrorResult};
|
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||||
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
|
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
|
||||||
|
use crate::dom::customelementregistry::CallbackReaction;
|
||||||
use crate::dom::document::{Document, FocusType};
|
use crate::dom::document::{Document, FocusType};
|
||||||
use crate::dom::documentfragment::DocumentFragment;
|
use crate::dom::documentfragment::DocumentFragment;
|
||||||
use crate::dom::domstringmap::DOMStringMap;
|
use crate::dom::domstringmap::DOMStringMap;
|
||||||
use crate::dom::element::{AttributeMutation, Element};
|
use crate::dom::element::{AttributeMutation, Element};
|
||||||
|
use crate::dom::elementinternals::ElementInternals;
|
||||||
use crate::dom::event::Event;
|
use crate::dom::event::Event;
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::htmlbodyelement::HTMLBodyElement;
|
use crate::dom::htmlbodyelement::HTMLBodyElement;
|
||||||
use crate::dom::htmlbrelement::HTMLBRElement;
|
use crate::dom::htmlbrelement::HTMLBRElement;
|
||||||
use crate::dom::htmldetailselement::HTMLDetailsElement;
|
use crate::dom::htmldetailselement::HTMLDetailsElement;
|
||||||
|
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
|
||||||
use crate::dom::htmlframesetelement::HTMLFrameSetElement;
|
use crate::dom::htmlframesetelement::HTMLFrameSetElement;
|
||||||
use crate::dom::htmlhtmlelement::HTMLHtmlElement;
|
use crate::dom::htmlhtmlelement::HTMLHtmlElement;
|
||||||
use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
|
use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
|
||||||
use crate::dom::htmllabelelement::HTMLLabelElement;
|
use crate::dom::htmllabelelement::HTMLLabelElement;
|
||||||
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
|
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
|
||||||
use crate::dom::node::{document_from_node, window_from_node, Node, ShadowIncluding};
|
use crate::dom::node::{
|
||||||
|
document_from_node, window_from_node, BindContext, Node, ShadowIncluding, UnbindContext,
|
||||||
|
};
|
||||||
use crate::dom::text::Text;
|
use crate::dom::text::Text;
|
||||||
use crate::dom::virtualmethods::VirtualMethods;
|
use crate::dom::virtualmethods::VirtualMethods;
|
||||||
|
use crate::script_thread::ScriptThread;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct HTMLElement {
|
pub struct HTMLElement {
|
||||||
|
@ -352,7 +358,7 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-click
|
// https://html.spec.whatwg.org/multipage/#dom-click
|
||||||
fn Click(&self) {
|
fn Click(&self) {
|
||||||
let element = self.upcast::<Element>();
|
let element = self.as_element();
|
||||||
if element.disabled_state() {
|
if element.disabled_state() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -377,7 +383,7 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-blur
|
// https://html.spec.whatwg.org/multipage/#dom-blur
|
||||||
fn Blur(&self) {
|
fn Blur(&self) {
|
||||||
// TODO: Run the unfocusing steps.
|
// TODO: Run the unfocusing steps.
|
||||||
if !self.upcast::<Element>().focus_state() {
|
if !self.as_element().focus_state() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// https://html.spec.whatwg.org/multipage/#unfocusing-steps
|
// https://html.spec.whatwg.org/multipage/#unfocusing-steps
|
||||||
|
@ -446,7 +452,7 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
fn InnerText(&self) -> DOMString {
|
fn InnerText(&self) -> DOMString {
|
||||||
let node = self.upcast::<Node>();
|
let node = self.upcast::<Node>();
|
||||||
let window = window_from_node(node);
|
let window = window_from_node(node);
|
||||||
let element = self.upcast::<Element>();
|
let element = self.as_element();
|
||||||
|
|
||||||
// Step 1.
|
// Step 1.
|
||||||
let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
|
let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
|
||||||
|
@ -511,12 +517,12 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-translate
|
// https://html.spec.whatwg.org/multipage/#dom-translate
|
||||||
fn Translate(&self) -> bool {
|
fn Translate(&self) -> bool {
|
||||||
self.upcast::<Element>().is_translate_enabled()
|
self.as_element().is_translate_enabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-translate
|
// https://html.spec.whatwg.org/multipage/#dom-translate
|
||||||
fn SetTranslate(&self, yesno: bool) {
|
fn SetTranslate(&self, yesno: bool) {
|
||||||
self.upcast::<Element>().set_string_attribute(
|
self.as_element().set_string_attribute(
|
||||||
&html5ever::local_name!("translate"),
|
&html5ever::local_name!("translate"),
|
||||||
match yesno {
|
match yesno {
|
||||||
true => DOMString::from("yes"),
|
true => DOMString::from("yes"),
|
||||||
|
@ -528,7 +534,7 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-contenteditable
|
// https://html.spec.whatwg.org/multipage/#dom-contenteditable
|
||||||
fn ContentEditable(&self) -> DOMString {
|
fn ContentEditable(&self) -> DOMString {
|
||||||
// TODO: https://github.com/servo/servo/issues/12776
|
// TODO: https://github.com/servo/servo/issues/12776
|
||||||
self.upcast::<Element>()
|
self.as_element()
|
||||||
.get_attribute(&ns!(), &local_name!("contenteditable"))
|
.get_attribute(&ns!(), &local_name!("contenteditable"))
|
||||||
.map(|attr| DOMString::from(&**attr.value()))
|
.map(|attr| DOMString::from(&**attr.value()))
|
||||||
.unwrap_or_else(|| DOMString::from("inherit"))
|
.unwrap_or_else(|| DOMString::from("inherit"))
|
||||||
|
@ -545,6 +551,46 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
// TODO: https://github.com/servo/servo/issues/12776
|
// TODO: https://github.com/servo/servo/issues/12776
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#dom-attachinternals>
|
||||||
|
fn AttachInternals(&self) -> Fallible<DomRoot<ElementInternals>> {
|
||||||
|
let element = self.as_element();
|
||||||
|
// Step 1: If this's is value is not null, then throw a "NotSupportedError" DOMException
|
||||||
|
if element.get_is().is_some() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Let definition be the result of looking up a custom element definition
|
||||||
|
// Note: the element can pass this check without yet being a custom
|
||||||
|
// element, as long as there is a registered definition
|
||||||
|
// that could upgrade it to one later.
|
||||||
|
let registry = document_from_node(self).window().CustomElements();
|
||||||
|
let definition = registry.lookup_definition(self.as_element().local_name(), None);
|
||||||
|
|
||||||
|
// Step 3: If definition is null, then throw an "NotSupportedError" DOMException
|
||||||
|
let definition = match definition {
|
||||||
|
Some(definition) => definition,
|
||||||
|
None => return Err(Error::NotSupported),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 4: If definition's disable internals is true, then throw a "NotSupportedError" DOMException
|
||||||
|
if definition.disable_internals {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: If this's attached internals is non-null, then throw an "NotSupportedError" DOMException
|
||||||
|
let internals = element.ensure_element_internals();
|
||||||
|
if internals.attached() {
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_form_associated_custom_element() {
|
||||||
|
element.init_state_for_internals();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6-7: Set this's attached internals to a new ElementInternals instance
|
||||||
|
internals.set_attached();
|
||||||
|
Ok(internals)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_text_node_to_fragment(document: &Document, fragment: &DocumentFragment, text: String) {
|
fn append_text_node_to_fragment(document: &Document, fragment: &DocumentFragment, text: String) {
|
||||||
|
@ -620,14 +666,14 @@ impl HTMLElement {
|
||||||
{
|
{
|
||||||
return Err(Error::Syntax);
|
return Err(Error::Syntax);
|
||||||
}
|
}
|
||||||
self.upcast::<Element>()
|
self.as_element()
|
||||||
.set_custom_attribute(to_snake_case(name), value)
|
.set_custom_attribute(to_snake_case(name), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_custom_attr(&self, local_name: DOMString) -> Option<DOMString> {
|
pub fn get_custom_attr(&self, local_name: DOMString) -> Option<DOMString> {
|
||||||
// FIXME(ajeffrey): Convert directly from DOMString to LocalName
|
// FIXME(ajeffrey): Convert directly from DOMString to LocalName
|
||||||
let local_name = LocalName::from(to_snake_case(local_name));
|
let local_name = LocalName::from(to_snake_case(local_name));
|
||||||
self.upcast::<Element>()
|
self.as_element()
|
||||||
.get_attribute(&ns!(), &local_name)
|
.get_attribute(&ns!(), &local_name)
|
||||||
.map(|attr| {
|
.map(|attr| {
|
||||||
DOMString::from(&**attr.value()) // FIXME(ajeffrey): Convert directly from AttrValue to DOMString
|
DOMString::from(&**attr.value()) // FIXME(ajeffrey): Convert directly from AttrValue to DOMString
|
||||||
|
@ -637,11 +683,10 @@ impl HTMLElement {
|
||||||
pub fn delete_custom_attr(&self, local_name: DOMString) {
|
pub fn delete_custom_attr(&self, local_name: DOMString) {
|
||||||
// FIXME(ajeffrey): Convert directly from DOMString to LocalName
|
// FIXME(ajeffrey): Convert directly from DOMString to LocalName
|
||||||
let local_name = LocalName::from(to_snake_case(local_name));
|
let local_name = LocalName::from(to_snake_case(local_name));
|
||||||
self.upcast::<Element>()
|
self.as_element().remove_attribute(&ns!(), &local_name);
|
||||||
.remove_attribute(&ns!(), &local_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#category-label
|
/// <https://html.spec.whatwg.org/multipage/#category-label>
|
||||||
pub fn is_labelable_element(&self) -> bool {
|
pub fn is_labelable_element(&self) -> bool {
|
||||||
match self.upcast::<Node>().type_id() {
|
match self.upcast::<Node>().type_id() {
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
|
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
|
||||||
|
@ -654,31 +699,54 @@ impl HTMLElement {
|
||||||
HTMLElementTypeId::HTMLProgressElement |
|
HTMLElementTypeId::HTMLProgressElement |
|
||||||
HTMLElementTypeId::HTMLSelectElement |
|
HTMLElementTypeId::HTMLSelectElement |
|
||||||
HTMLElementTypeId::HTMLTextAreaElement => true,
|
HTMLElementTypeId::HTMLTextAreaElement => true,
|
||||||
_ => false,
|
_ => self.is_form_associated_custom_element(),
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#category-listed
|
/// <https://html.spec.whatwg.org/multipage/#form-associated-custom-element>
|
||||||
|
pub fn is_form_associated_custom_element(&self) -> bool {
|
||||||
|
if let Some(definition) = self.as_element().get_custom_element_definition() {
|
||||||
|
definition.is_autonomous() && definition.form_associated
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#category-listed>
|
||||||
pub fn is_listed_element(&self) -> bool {
|
pub fn is_listed_element(&self) -> bool {
|
||||||
match self.upcast::<Node>().type_id() {
|
match self.upcast::<Node>().type_id() {
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => matches!(
|
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
|
||||||
type_id,
|
|
||||||
HTMLElementTypeId::HTMLButtonElement |
|
HTMLElementTypeId::HTMLButtonElement |
|
||||||
HTMLElementTypeId::HTMLFieldSetElement |
|
HTMLElementTypeId::HTMLFieldSetElement |
|
||||||
HTMLElementTypeId::HTMLInputElement |
|
HTMLElementTypeId::HTMLInputElement |
|
||||||
HTMLElementTypeId::HTMLObjectElement |
|
HTMLElementTypeId::HTMLObjectElement |
|
||||||
HTMLElementTypeId::HTMLOutputElement |
|
HTMLElementTypeId::HTMLOutputElement |
|
||||||
HTMLElementTypeId::HTMLSelectElement |
|
HTMLElementTypeId::HTMLSelectElement |
|
||||||
HTMLElementTypeId::HTMLTextAreaElement
|
HTMLElementTypeId::HTMLTextAreaElement => true,
|
||||||
),
|
_ => self.is_form_associated_custom_element(),
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#category-submit>
|
||||||
|
pub fn is_submittable_element(&self) -> bool {
|
||||||
|
match self.upcast::<Node>().type_id() {
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
|
||||||
|
HTMLElementTypeId::HTMLButtonElement |
|
||||||
|
HTMLElementTypeId::HTMLInputElement |
|
||||||
|
HTMLElementTypeId::HTMLSelectElement |
|
||||||
|
HTMLElementTypeId::HTMLTextAreaElement => true,
|
||||||
|
_ => self.is_form_associated_custom_element(),
|
||||||
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supported_prop_names_custom_attr(&self) -> Vec<DOMString> {
|
pub fn supported_prop_names_custom_attr(&self) -> Vec<DOMString> {
|
||||||
let element = self.upcast::<Element>();
|
let element = self.as_element();
|
||||||
element
|
element
|
||||||
.attrs()
|
.attrs()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -692,7 +760,7 @@ impl HTMLElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
|
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
|
||||||
// This gets the nth label in tree order.
|
// This gets the nth label in tree order.
|
||||||
pub fn label_at(&self, index: u32) -> Option<DomRoot<Node>> {
|
pub fn label_at(&self, index: u32) -> Option<DomRoot<Node>> {
|
||||||
let element = self.upcast::<Element>();
|
let element = self.as_element();
|
||||||
|
|
||||||
// Traverse entire tree for <label> elements that have
|
// Traverse entire tree for <label> elements that have
|
||||||
// this as their control.
|
// this as their control.
|
||||||
|
@ -721,7 +789,7 @@ impl HTMLElement {
|
||||||
// This counts the labels of the element, to support NodeList::Length
|
// This counts the labels of the element, to support NodeList::Length
|
||||||
pub fn labels_count(&self) -> u32 {
|
pub fn labels_count(&self) -> u32 {
|
||||||
// see label_at comments about performance
|
// see label_at comments about performance
|
||||||
let element = self.upcast::<Element>();
|
let element = self.as_element();
|
||||||
let root_element = element.root_element();
|
let root_element = element.root_element();
|
||||||
let root_node = root_element.upcast::<Node>();
|
let root_node = root_element.upcast::<Node>();
|
||||||
root_node
|
root_node
|
||||||
|
@ -814,7 +882,7 @@ impl HTMLElement {
|
||||||
.child_elements()
|
.child_elements()
|
||||||
.find(|el| el.local_name() == &local_name!("summary"));
|
.find(|el| el.local_name() == &local_name!("summary"));
|
||||||
match first_summary_element {
|
match first_summary_element {
|
||||||
Some(first_summary) => &*first_summary == self.upcast::<Element>(),
|
Some(first_summary) => &*first_summary == self.as_element(),
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,11 +890,12 @@ impl HTMLElement {
|
||||||
|
|
||||||
impl VirtualMethods for HTMLElement {
|
impl VirtualMethods for HTMLElement {
|
||||||
fn super_type(&self) -> Option<&dyn VirtualMethods> {
|
fn super_type(&self) -> Option<&dyn VirtualMethods> {
|
||||||
Some(self.upcast::<Element>() as &dyn VirtualMethods)
|
Some(self.as_element() as &dyn VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
|
let element = self.as_element();
|
||||||
match (attr.local_name(), mutation) {
|
match (attr.local_name(), mutation) {
|
||||||
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
||||||
let evtarget = self.upcast::<EventTarget>();
|
let evtarget = self.upcast::<EventTarget>();
|
||||||
|
@ -839,10 +908,95 @@ impl VirtualMethods for HTMLElement {
|
||||||
DOMString::from(&**attr.value()),
|
DOMString::from(&**attr.value()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
(&local_name!("form"), mutation) if self.is_form_associated_custom_element() => {
|
||||||
|
self.form_attribute_mutated(mutation);
|
||||||
|
},
|
||||||
|
// Adding a "disabled" attribute disables an enabled form element.
|
||||||
|
(&local_name!("disabled"), AttributeMutation::Set(_))
|
||||||
|
if self.is_form_associated_custom_element() && element.enabled_state() =>
|
||||||
|
{
|
||||||
|
element.set_disabled_state(true);
|
||||||
|
element.set_enabled_state(false);
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(true),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// Removing the "disabled" attribute may enable a disabled
|
||||||
|
// form element, but a fieldset ancestor may keep it disabled.
|
||||||
|
(&local_name!("disabled"), AttributeMutation::Removed)
|
||||||
|
if self.is_form_associated_custom_element() && element.disabled_state() =>
|
||||||
|
{
|
||||||
|
element.set_disabled_state(false);
|
||||||
|
element.set_enabled_state(true);
|
||||||
|
element.check_ancestors_disabled_state_for_form_control();
|
||||||
|
if element.enabled_state() {
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(&local_name!("readonly"), mutation) if self.is_form_associated_custom_element() => {
|
||||||
|
match mutation {
|
||||||
|
AttributeMutation::Set(_) => {
|
||||||
|
element.set_read_write_state(true);
|
||||||
|
},
|
||||||
|
AttributeMutation::Removed => {
|
||||||
|
element.set_read_write_state(false);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bind_to_tree(&self, context: &BindContext) {
|
||||||
|
if let Some(ref super_type) = self.super_type() {
|
||||||
|
super_type.bind_to_tree(context);
|
||||||
|
}
|
||||||
|
let element = self.as_element();
|
||||||
|
element.update_sequentially_focusable_status();
|
||||||
|
|
||||||
|
// Binding to a tree can disable a form control if one of the new
|
||||||
|
// ancestors is a fieldset.
|
||||||
|
if self.is_form_associated_custom_element() && element.enabled_state() {
|
||||||
|
element.check_ancestors_disabled_state_for_form_control();
|
||||||
|
if element.disabled_state() {
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(true),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind_from_tree(&self, context: &UnbindContext) {
|
||||||
|
if let Some(ref super_type) = self.super_type() {
|
||||||
|
super_type.unbind_from_tree(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unbinding from a tree might enable a form control, if a
|
||||||
|
// fieldset ancestor is the only reason it was disabled.
|
||||||
|
// (The fact that it's enabled doesn't do much while it's
|
||||||
|
// disconnected, but it is an observable fact to keep track of.)
|
||||||
|
let element = self.as_element();
|
||||||
|
if self.is_form_associated_custom_element() && element.disabled_state() {
|
||||||
|
element.check_disabled_attribute();
|
||||||
|
element.check_ancestors_disabled_state_for_form_control();
|
||||||
|
if element.enabled_state() {
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
|
fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
|
||||||
match *name {
|
match *name {
|
||||||
local_name!("itemprop") => AttrValue::from_serialized_tokenlist(value.into()),
|
local_name!("itemprop") => AttrValue::from_serialized_tokenlist(value.into()),
|
||||||
|
@ -869,3 +1023,35 @@ impl Activatable for HTMLElement {
|
||||||
self.summary_activation_behavior();
|
self.summary_activation_behavior();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Form-associated custom elements are the same interface type as
|
||||||
|
// normal HTMLElements, so HTMLElement needs to have the FormControl trait
|
||||||
|
// even though it's usually more specific trait implementations, like the
|
||||||
|
// HTMLInputElement one, that we really want. (Alternately we could put
|
||||||
|
// the FormControl trait on ElementInternals, but that raises lifetime issues.)
|
||||||
|
impl FormControl for HTMLElement {
|
||||||
|
fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
|
||||||
|
debug_assert!(self.is_form_associated_custom_element());
|
||||||
|
self.as_element()
|
||||||
|
.get_element_internals()
|
||||||
|
.and_then(|e| e.form_owner())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
|
||||||
|
debug_assert!(self.is_form_associated_custom_element());
|
||||||
|
self.as_element()
|
||||||
|
.ensure_element_internals()
|
||||||
|
.set_form_owner(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_element<'a>(&'a self) -> &'a Element {
|
||||||
|
debug_assert!(self.is_form_associated_custom_element());
|
||||||
|
self.as_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_listed(&self) -> bool {
|
||||||
|
debug_assert!(self.is_form_associated_custom_element());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO candidate_for_validation, satisfies_constraints traits
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFie
|
||||||
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
||||||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
|
use crate::dom::customelementregistry::CallbackReaction;
|
||||||
use crate::dom::document::Document;
|
use crate::dom::document::Document;
|
||||||
use crate::dom::element::{AttributeMutation, Element};
|
use crate::dom::element::{AttributeMutation, Element};
|
||||||
use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection};
|
use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection};
|
||||||
|
@ -24,6 +25,7 @@ use crate::dom::node::{window_from_node, Node, ShadowIncluding};
|
||||||
use crate::dom::validation::Validatable;
|
use crate::dom::validation::Validatable;
|
||||||
use crate::dom::validitystate::ValidityState;
|
use crate::dom::validitystate::ValidityState;
|
||||||
use crate::dom::virtualmethods::VirtualMethods;
|
use crate::dom::virtualmethods::VirtualMethods;
|
||||||
|
use crate::script_thread::ScriptThread;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct HTMLFieldSetElement {
|
pub struct HTMLFieldSetElement {
|
||||||
|
@ -71,14 +73,7 @@ impl HTMLFieldSetElement {
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
.traverse_preorder(ShadowIncluding::No)
|
.traverse_preorder(ShadowIncluding::No)
|
||||||
.flat_map(DomRoot::downcast::<Element>)
|
.flat_map(DomRoot::downcast::<Element>)
|
||||||
.any(|element| {
|
.any(|element| element.is_invalid(false));
|
||||||
if let Some(validatable) = element.as_maybe_validatable() {
|
|
||||||
validatable.is_instance_validatable() &&
|
|
||||||
!validatable.validity_state().invalid_flags().is_empty()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.upcast::<Element>()
|
self.upcast::<Element>()
|
||||||
.set_state(ElementState::VALID, !has_invalid_child);
|
.set_state(ElementState::VALID, !has_invalid_child);
|
||||||
|
@ -169,9 +164,9 @@ impl VirtualMethods for HTMLFieldSetElement {
|
||||||
AttributeMutation::Removed => false,
|
AttributeMutation::Removed => false,
|
||||||
};
|
};
|
||||||
let node = self.upcast::<Node>();
|
let node = self.upcast::<Node>();
|
||||||
let el = self.upcast::<Element>();
|
let element = self.upcast::<Element>();
|
||||||
el.set_disabled_state(disabled_state);
|
element.set_disabled_state(disabled_state);
|
||||||
el.set_enabled_state(!disabled_state);
|
element.set_enabled_state(!disabled_state);
|
||||||
let mut found_legend = false;
|
let mut found_legend = false;
|
||||||
let children = node.children().filter(|node| {
|
let children = node.children().filter(|node| {
|
||||||
if found_legend {
|
if found_legend {
|
||||||
|
@ -186,37 +181,64 @@ impl VirtualMethods for HTMLFieldSetElement {
|
||||||
let fields = children.flat_map(|child| {
|
let fields = children.flat_map(|child| {
|
||||||
child
|
child
|
||||||
.traverse_preorder(ShadowIncluding::No)
|
.traverse_preorder(ShadowIncluding::No)
|
||||||
.filter(|descendant| {
|
.filter(|descendant| match descendant.type_id() {
|
||||||
matches!(
|
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||||
descendant.type_id(),
|
HTMLElementTypeId::HTMLButtonElement |
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
HTMLElementTypeId::HTMLInputElement |
|
||||||
HTMLElementTypeId::HTMLButtonElement,
|
HTMLElementTypeId::HTMLSelectElement |
|
||||||
)) | NodeTypeId::Element(ElementTypeId::HTMLElement(
|
HTMLElementTypeId::HTMLTextAreaElement,
|
||||||
HTMLElementTypeId::HTMLInputElement,
|
)) => true,
|
||||||
)) | NodeTypeId::Element(ElementTypeId::HTMLElement(
|
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||||
HTMLElementTypeId::HTMLSelectElement,
|
HTMLElementTypeId::HTMLElement,
|
||||||
)) | NodeTypeId::Element(ElementTypeId::HTMLElement(
|
)) => descendant
|
||||||
HTMLElementTypeId::HTMLTextAreaElement,
|
.downcast::<HTMLElement>()
|
||||||
))
|
.unwrap()
|
||||||
)
|
.is_form_associated_custom_element(),
|
||||||
|
_ => false,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
if disabled_state {
|
if disabled_state {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let el = field.downcast::<Element>().unwrap();
|
let element = field.downcast::<Element>().unwrap();
|
||||||
el.set_disabled_state(true);
|
if element.enabled_state() {
|
||||||
el.set_enabled_state(false);
|
element.set_disabled_state(true);
|
||||||
el.update_sequentially_focusable_status();
|
element.set_enabled_state(false);
|
||||||
|
if element
|
||||||
|
.downcast::<HTMLElement>()
|
||||||
|
.map_or(false, |h| h.is_form_associated_custom_element())
|
||||||
|
{
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(true),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.update_sequentially_focusable_status();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let el = field.downcast::<Element>().unwrap();
|
let element = field.downcast::<Element>().unwrap();
|
||||||
el.check_disabled_attribute();
|
if element.disabled_state() {
|
||||||
el.check_ancestors_disabled_state_for_form_control();
|
element.check_disabled_attribute();
|
||||||
el.update_sequentially_focusable_status();
|
element.check_ancestors_disabled_state_for_form_control();
|
||||||
|
// Fire callback only if this has actually enabled the custom element
|
||||||
|
if element.enabled_state() &&
|
||||||
|
element
|
||||||
|
.downcast::<HTMLElement>()
|
||||||
|
.map_or(false, |h| h.is_form_associated_custom_element())
|
||||||
|
{
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
element,
|
||||||
|
CallbackReaction::FormDisabled(false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.update_sequentially_focusable_status();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
el.update_sequentially_focusable_status();
|
element.update_sequentially_focusable_status();
|
||||||
},
|
},
|
||||||
local_name!("form") => {
|
local_name!("form") => {
|
||||||
self.form_attribute_mutated(mutation);
|
self.form_attribute_mutated(mutation);
|
||||||
|
|
|
@ -46,6 +46,7 @@ use crate::dom::bindings::reflector::DomObject;
|
||||||
use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, MutNullableDom};
|
use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, MutNullableDom};
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
use crate::dom::blob::Blob;
|
use crate::dom::blob::Blob;
|
||||||
|
use crate::dom::customelementregistry::CallbackReaction;
|
||||||
use crate::dom::document::Document;
|
use crate::dom::document::Document;
|
||||||
use crate::dom::domtokenlist::DOMTokenList;
|
use crate::dom::domtokenlist::DOMTokenList;
|
||||||
use crate::dom::element::{AttributeMutation, Element};
|
use crate::dom::element::{AttributeMutation, Element};
|
||||||
|
@ -77,9 +78,9 @@ use crate::dom::node::{
|
||||||
use crate::dom::nodelist::{NodeList, RadioListMode};
|
use crate::dom::nodelist::{NodeList, RadioListMode};
|
||||||
use crate::dom::radionodelist::RadioNodeList;
|
use crate::dom::radionodelist::RadioNodeList;
|
||||||
use crate::dom::submitevent::SubmitEvent;
|
use crate::dom::submitevent::SubmitEvent;
|
||||||
use crate::dom::validitystate::ValidationFlags;
|
|
||||||
use crate::dom::virtualmethods::VirtualMethods;
|
use crate::dom::virtualmethods::VirtualMethods;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
|
use crate::script_thread::ScriptThread;
|
||||||
use crate::task_source::TaskSource;
|
use crate::task_source::TaskSource;
|
||||||
|
|
||||||
#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
|
#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
|
||||||
|
@ -363,6 +364,14 @@ impl HTMLFormElementMethods for HTMLFormElement {
|
||||||
HTMLElementTypeId::HTMLTextAreaElement => {
|
HTMLElementTypeId::HTMLTextAreaElement => {
|
||||||
elem.downcast::<HTMLTextAreaElement>().unwrap().form_owner()
|
elem.downcast::<HTMLTextAreaElement>().unwrap().form_owner()
|
||||||
},
|
},
|
||||||
|
HTMLElementTypeId::HTMLElement => {
|
||||||
|
let html_element = elem.downcast::<HTMLElement>().unwrap();
|
||||||
|
if html_element.is_form_associated_custom_element() {
|
||||||
|
html_element.form_owner()
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!elem
|
debug_assert!(!elem
|
||||||
.downcast::<HTMLElement>()
|
.downcast::<HTMLElement>()
|
||||||
|
@ -673,14 +682,7 @@ impl HTMLFormElement {
|
||||||
|
|
||||||
pub fn update_validity(&self) {
|
pub fn update_validity(&self) {
|
||||||
let controls = self.controls.borrow();
|
let controls = self.controls.borrow();
|
||||||
|
let is_any_invalid = controls.iter().any(|control| control.is_invalid(false));
|
||||||
let is_any_invalid = controls
|
|
||||||
.iter()
|
|
||||||
.filter_map(|control| control.as_maybe_validatable())
|
|
||||||
.any(|validatable| {
|
|
||||||
validatable.is_instance_validatable() &&
|
|
||||||
!validatable.validity_state().invalid_flags().is_empty()
|
|
||||||
});
|
|
||||||
|
|
||||||
self.upcast::<Element>()
|
self.upcast::<Element>()
|
||||||
.set_state(ElementState::VALID, !is_any_invalid);
|
.set_state(ElementState::VALID, !is_any_invalid);
|
||||||
|
@ -1027,6 +1029,8 @@ impl HTMLFormElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's form-associated and has a validation anchor, point the
|
||||||
|
// user there instead of the element itself.
|
||||||
// Step 4
|
// Step 4
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
@ -1039,20 +1043,11 @@ impl HTMLFormElement {
|
||||||
let invalid_controls = controls
|
let invalid_controls = controls
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|field| {
|
.filter_map(|field| {
|
||||||
if let Some(el) = field.downcast::<Element>() {
|
if let Some(element) = field.downcast::<Element>() {
|
||||||
let validatable = match el.as_maybe_validatable() {
|
if element.is_invalid(true) {
|
||||||
Some(v) => v,
|
Some(DomRoot::from_ref(element))
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
validatable
|
|
||||||
.validity_state()
|
|
||||||
.perform_validation_and_update(ValidationFlags::all());
|
|
||||||
if !validatable.is_instance_validatable() ||
|
|
||||||
validatable.validity_state().invalid_flags().is_empty()
|
|
||||||
{
|
|
||||||
None
|
|
||||||
} else {
|
} else {
|
||||||
Some(DomRoot::from_ref(el))
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1135,6 +1130,15 @@ impl HTMLFormElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
HTMLElementTypeId::HTMLElement => {
|
||||||
|
let custom = child.downcast::<HTMLElement>().unwrap();
|
||||||
|
if custom.is_form_associated_custom_element() {
|
||||||
|
// https://html.spec.whatwg.org/multipage/#face-entry-construction
|
||||||
|
let internals = custom.upcast::<Element>().ensure_element_internals();
|
||||||
|
internals.perform_entry_construction(&mut data_set);
|
||||||
|
// Otherwise no form value has been set so there is nothing to do.
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1287,6 +1291,16 @@ impl HTMLFormElement {
|
||||||
)) => {
|
)) => {
|
||||||
child.downcast::<HTMLOutputElement>().unwrap().reset();
|
child.downcast::<HTMLOutputElement>().unwrap().reset();
|
||||||
},
|
},
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
|
||||||
|
let html_element = child.downcast::<HTMLElement>().unwrap();
|
||||||
|
if html_element.is_form_associated_custom_element() {
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
html_element.upcast::<Element>(),
|
||||||
|
CallbackReaction::FormReset,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1550,6 +1564,19 @@ pub trait FormControl: DomObject {
|
||||||
if let Some(ref new_owner) = new_owner {
|
if let Some(ref new_owner) = new_owner {
|
||||||
new_owner.add_control(self);
|
new_owner.add_control(self);
|
||||||
}
|
}
|
||||||
|
// https://html.spec.whatwg.org/multipage/#custom-element-reactions:reset-the-form-owner
|
||||||
|
if let Some(html_elem) = elem.downcast::<HTMLElement>() {
|
||||||
|
if html_elem.is_form_associated_custom_element() {
|
||||||
|
ScriptThread::enqueue_callback_reaction(
|
||||||
|
elem,
|
||||||
|
CallbackReaction::FormAssociated(match new_owner {
|
||||||
|
None => None,
|
||||||
|
Some(ref form) => Some(DomRoot::from_ref(&**form)),
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
self.set_form_owner(new_owner.as_deref());
|
self.set_form_owner(new_owner.as_deref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1745,7 +1772,13 @@ impl FormControlElementHelpers for Element {
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||||
HTMLElementTypeId::HTMLTextAreaElement,
|
HTMLElementTypeId::HTMLTextAreaElement,
|
||||||
)) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
|
)) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
|
||||||
_ => None,
|
_ => self.downcast::<HTMLElement>().and_then(|elem| {
|
||||||
|
if elem.is_form_associated_custom_element() {
|
||||||
|
Some(elem as &dyn FormControl)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,6 +299,7 @@ pub mod domstringmap;
|
||||||
pub mod domtokenlist;
|
pub mod domtokenlist;
|
||||||
pub mod dynamicmoduleowner;
|
pub mod dynamicmoduleowner;
|
||||||
pub mod element;
|
pub mod element;
|
||||||
|
pub mod elementinternals;
|
||||||
pub mod errorevent;
|
pub mod errorevent;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod eventsource;
|
pub mod eventsource;
|
||||||
|
|
|
@ -738,7 +738,7 @@ impl Node {
|
||||||
parent.ancestors().any(|ancestor| &*ancestor == self)
|
parent.ancestors().any(|ancestor| &*ancestor == self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_shadow_including_inclusive_ancestor_of(&self, node: &Node) -> bool {
|
pub fn is_shadow_including_inclusive_ancestor_of(&self, node: &Node) -> bool {
|
||||||
node.inclusive_ancestors(ShadowIncluding::Yes)
|
node.inclusive_ancestors(ShadowIncluding::Yes)
|
||||||
.any(|ancestor| &*ancestor == self)
|
.any(|ancestor| &*ancestor == self)
|
||||||
}
|
}
|
||||||
|
@ -2092,19 +2092,17 @@ impl Node {
|
||||||
.traverse_preorder(ShadowIncluding::Yes)
|
.traverse_preorder(ShadowIncluding::Yes)
|
||||||
.filter_map(DomRoot::downcast::<Element>)
|
.filter_map(DomRoot::downcast::<Element>)
|
||||||
{
|
{
|
||||||
// Step 7.7.2.
|
// Step 7.7.2, whatwg/dom#833
|
||||||
if descendant.is_connected() {
|
if descendant.get_custom_element_definition().is_some() {
|
||||||
if descendant.get_custom_element_definition().is_some() {
|
if descendant.is_connected() {
|
||||||
// Step 7.7.2.1.
|
|
||||||
ScriptThread::enqueue_callback_reaction(
|
ScriptThread::enqueue_callback_reaction(
|
||||||
&descendant,
|
&descendant,
|
||||||
CallbackReaction::Connected,
|
CallbackReaction::Connected,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// Step 7.7.2.2.
|
|
||||||
try_upgrade_element(&descendant);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
try_upgrade_element(&*descendant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::dom::bindings::root::Dom;
|
||||||
use crate::dom::customelementregistry::{
|
use crate::dom::customelementregistry::{
|
||||||
CustomElementDefinition, CustomElementReaction, CustomElementState,
|
CustomElementDefinition, CustomElementReaction, CustomElementState,
|
||||||
};
|
};
|
||||||
|
use crate::dom::elementinternals::ElementInternals;
|
||||||
use crate::dom::mutationobserver::RegisteredObserver;
|
use crate::dom::mutationobserver::RegisteredObserver;
|
||||||
use crate::dom::node::UniqueId;
|
use crate::dom::node::UniqueId;
|
||||||
use crate::dom::shadowroot::ShadowRoot;
|
use crate::dom::shadowroot::ShadowRoot;
|
||||||
|
@ -54,4 +55,6 @@ pub struct ElementRareData {
|
||||||
/// The client rect reported by layout.
|
/// The client rect reported by layout.
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
pub client_rect: Option<LayoutValue<Rect<i32>>>,
|
pub client_rect: Option<LayoutValue<Rect<i32>>>,
|
||||||
|
/// <https://html.spec.whatwg.org/multipage#elementinternals>
|
||||||
|
pub element_internals: Option<Dom<ElementInternals>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,10 @@ use crate::dom::validitystate::{ValidationFlags, ValidityState};
|
||||||
pub trait Validatable {
|
pub trait Validatable {
|
||||||
fn as_element(&self) -> ∈
|
fn as_element(&self) -> ∈
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-cva-validity
|
/// <https://html.spec.whatwg.org/multipage/#dom-cva-validity>
|
||||||
fn validity_state(&self) -> DomRoot<ValidityState>;
|
fn validity_state(&self) -> DomRoot<ValidityState>;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#candidate-for-constraint-validation
|
/// <https://html.spec.whatwg.org/multipage/#candidate-for-constraint-validation>
|
||||||
fn is_instance_validatable(&self) -> bool;
|
fn is_instance_validatable(&self) -> bool;
|
||||||
|
|
||||||
// Check if element satisfies its constraints, excluding custom errors
|
// Check if element satisfies its constraints, excluding custom errors
|
||||||
|
@ -28,9 +28,14 @@ pub trait Validatable {
|
||||||
ValidationFlags::empty()
|
ValidationFlags::empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#check-validity-steps
|
/// <https://html.spec.whatwg.org/multipage/#concept-fv-valid>
|
||||||
|
fn satisfies_constraints(&self) -> bool {
|
||||||
|
self.validity_state().invalid_flags().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#check-validity-steps>
|
||||||
fn check_validity(&self) -> bool {
|
fn check_validity(&self) -> bool {
|
||||||
if self.is_instance_validatable() && !self.validity_state().invalid_flags().is_empty() {
|
if self.is_instance_validatable() && !self.satisfies_constraints() {
|
||||||
self.as_element()
|
self.as_element()
|
||||||
.upcast::<EventTarget>()
|
.upcast::<EventTarget>()
|
||||||
.fire_cancelable_event(atom!("invalid"));
|
.fire_cancelable_event(atom!("invalid"));
|
||||||
|
@ -40,15 +45,14 @@ pub trait Validatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#report-validity-steps
|
/// <https://html.spec.whatwg.org/multipage/#report-validity-steps>
|
||||||
fn report_validity(&self) -> bool {
|
fn report_validity(&self) -> bool {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
if !self.is_instance_validatable() {
|
if !self.is_instance_validatable() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let flags = self.validity_state().invalid_flags();
|
if self.satisfies_constraints() {
|
||||||
if flags.is_empty() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +64,7 @@ pub trait Validatable {
|
||||||
|
|
||||||
// Step 1.2.
|
// Step 1.2.
|
||||||
if !event.DefaultPrevented() {
|
if !event.DefaultPrevented() {
|
||||||
|
let flags = self.validity_state().invalid_flags();
|
||||||
println!(
|
println!(
|
||||||
"Validation error: {}",
|
"Validation error: {}",
|
||||||
validation_message_for_flags(&self.validity_state(), flags)
|
validation_message_for_flags(&self.validity_state(), flags)
|
||||||
|
@ -73,7 +78,7 @@ pub trait Validatable {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-cva-validationmessage
|
/// <https://html.spec.whatwg.org/multipage/#dom-cva-validationmessage>
|
||||||
fn validation_message(&self) -> DOMString {
|
fn validation_message(&self) -> DOMString {
|
||||||
if self.is_instance_validatable() {
|
if self.is_instance_validatable() {
|
||||||
let flags = self.validity_state().invalid_flags();
|
let flags = self.validity_state().invalid_flags();
|
||||||
|
@ -84,7 +89,7 @@ pub trait Validatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#the-datalist-element%3Abarred-from-constraint-validation
|
/// <https://html.spec.whatwg.org/multipage/#the-datalist-element%3Abarred-from-constraint-validation>
|
||||||
pub fn is_barred_by_datalist_ancestor(elem: &Node) -> bool {
|
pub fn is_barred_by_datalist_ancestor(elem: &Node) -> bool {
|
||||||
elem.upcast::<Node>()
|
elem.upcast::<Node>()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
|
|
|
@ -10,6 +10,7 @@ use dom_struct::dom_struct;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use style_traits::dom::ElementState;
|
use style_traits::dom::ElementState;
|
||||||
|
|
||||||
|
use super::bindings::codegen::Bindings::ElementInternalsBinding::ValidityStateFlags;
|
||||||
use crate::dom::bindings::cell::{DomRefCell, Ref};
|
use crate::dom::bindings::cell::{DomRefCell, Ref};
|
||||||
use crate::dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods;
|
use crate::dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods;
|
||||||
use crate::dom::bindings::inheritance::Castable;
|
use crate::dom::bindings::inheritance::Castable;
|
||||||
|
@ -129,20 +130,22 @@ impl ValidityState {
|
||||||
self.update_pseudo_classes();
|
self.update_pseudo_classes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_invalid_flags(&self, update_flags: ValidationFlags) {
|
||||||
|
self.invalid_flags.set(update_flags);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn invalid_flags(&self) -> ValidationFlags {
|
pub fn invalid_flags(&self) -> ValidationFlags {
|
||||||
self.invalid_flags.get()
|
self.invalid_flags.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_pseudo_classes(&self) {
|
pub fn update_pseudo_classes(&self) {
|
||||||
if let Some(validatable) = self.element.as_maybe_validatable() {
|
if self.element.is_instance_validatable() {
|
||||||
if validatable.is_instance_validatable() {
|
let is_valid = self.invalid_flags.get().is_empty();
|
||||||
let is_valid = self.invalid_flags.get().is_empty();
|
self.element.set_state(ElementState::VALID, is_valid);
|
||||||
self.element.set_state(ElementState::VALID, is_valid);
|
self.element.set_state(ElementState::INVALID, !is_valid);
|
||||||
self.element.set_state(ElementState::INVALID, !is_valid);
|
} else {
|
||||||
} else {
|
self.element.set_state(ElementState::VALID, false);
|
||||||
self.element.set_state(ElementState::VALID, false);
|
self.element.set_state(ElementState::INVALID, false);
|
||||||
self.element.set_state(ElementState::INVALID, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(form_control) = self.element.as_maybe_form_control() {
|
if let Some(form_control) = self.element.as_maybe_form_control() {
|
||||||
|
@ -225,3 +228,40 @@ impl ValidityStateMethods for ValidityState {
|
||||||
self.invalid_flags().is_empty()
|
self.invalid_flags().is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&ValidityStateFlags> for ValidationFlags {
|
||||||
|
fn from(flags: &ValidityStateFlags) -> Self {
|
||||||
|
let mut bits = ValidationFlags::empty();
|
||||||
|
if flags.valueMissing {
|
||||||
|
bits |= ValidationFlags::VALUE_MISSING;
|
||||||
|
}
|
||||||
|
if flags.typeMismatch {
|
||||||
|
bits |= ValidationFlags::TYPE_MISMATCH;
|
||||||
|
}
|
||||||
|
if flags.patternMismatch {
|
||||||
|
bits |= ValidationFlags::PATTERN_MISMATCH;
|
||||||
|
}
|
||||||
|
if flags.tooLong {
|
||||||
|
bits |= ValidationFlags::TOO_LONG;
|
||||||
|
}
|
||||||
|
if flags.tooShort {
|
||||||
|
bits |= ValidationFlags::TOO_SHORT;
|
||||||
|
}
|
||||||
|
if flags.rangeUnderflow {
|
||||||
|
bits |= ValidationFlags::RANGE_UNDERFLOW;
|
||||||
|
}
|
||||||
|
if flags.rangeOverflow {
|
||||||
|
bits |= ValidationFlags::RANGE_OVERFLOW;
|
||||||
|
}
|
||||||
|
if flags.stepMismatch {
|
||||||
|
bits |= ValidationFlags::STEP_MISMATCH;
|
||||||
|
}
|
||||||
|
if flags.badInput {
|
||||||
|
bits |= ValidationFlags::BAD_INPUT;
|
||||||
|
}
|
||||||
|
if flags.customError {
|
||||||
|
bits |= ValidationFlags::CUSTOM_ERROR;
|
||||||
|
}
|
||||||
|
bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
41
components/script/dom/webidls/ElementInternals.webidl
Normal file
41
components/script/dom/webidls/ElementInternals.webidl
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#elementinternals
|
||||||
|
[Exposed=Window]
|
||||||
|
interface ElementInternals {
|
||||||
|
// Form-associated custom elements
|
||||||
|
|
||||||
|
[Throws] undefined setFormValue((File or USVString or FormData)? value,
|
||||||
|
optional (File or USVString or FormData)? state);
|
||||||
|
|
||||||
|
[Throws] readonly attribute HTMLFormElement? form;
|
||||||
|
|
||||||
|
// flags shouldn't be optional here, #25704
|
||||||
|
[Throws] undefined setValidity(optional ValidityStateFlags flags = {},
|
||||||
|
optional DOMString message,
|
||||||
|
optional HTMLElement anchor);
|
||||||
|
[Throws] readonly attribute boolean willValidate;
|
||||||
|
[Throws] readonly attribute ValidityState validity;
|
||||||
|
[Throws] readonly attribute DOMString validationMessage;
|
||||||
|
[Throws] boolean checkValidity();
|
||||||
|
[Throws] boolean reportValidity();
|
||||||
|
|
||||||
|
[Throws] readonly attribute NodeList labels;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#elementinternals
|
||||||
|
dictionary ValidityStateFlags {
|
||||||
|
boolean valueMissing = false;
|
||||||
|
boolean typeMismatch = false;
|
||||||
|
boolean patternMismatch = false;
|
||||||
|
boolean tooLong = false;
|
||||||
|
boolean tooShort = false;
|
||||||
|
boolean rangeUnderflow = false;
|
||||||
|
boolean rangeOverflow = false;
|
||||||
|
boolean stepMismatch = false;
|
||||||
|
boolean badInput = false;
|
||||||
|
boolean customError = false;
|
||||||
|
};
|
||||||
|
|
|
@ -50,6 +50,8 @@ interface HTMLElement : Element {
|
||||||
|
|
||||||
attribute [LegacyNullToEmptyString] DOMString innerText;
|
attribute [LegacyNullToEmptyString] DOMString innerText;
|
||||||
|
|
||||||
|
[Throws] ElementInternals attachInternals();
|
||||||
|
|
||||||
// command API
|
// command API
|
||||||
// readonly attribute DOMString? commandType;
|
// readonly attribute DOMString? commandType;
|
||||||
// readonly attribute DOMString? commandLabel;
|
// readonly attribute DOMString? commandLabel;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
[state-in-has.html]
|
[state-in-has.html]
|
||||||
expected: ERROR
|
|
||||||
[Test :has() invalidation with :state() pseudo-classes]
|
[Test :has() invalidation with :state() pseudo-classes]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -2,42 +2,3 @@
|
||||||
type: testharness
|
type: testharness
|
||||||
[customElements.define must upgrade elements in the shadow-including tree order]
|
[customElements.define must upgrade elements in the shadow-including tree order]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on disabledFeatures]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while getting disabledFeatures on the constructor prototype]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while iterating over disabledFeatures to sequence<DOMString>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while converting the value of disabledFeatures to sequence<DOMString>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while getting formAssociated on the constructor prototype]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while getting additional formAssociated callbacks on the constructor prototype]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must get four additional callbacks on the prototype if formAssociated is converted to true]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must get "prototype", "disabledFeatures", and "formAssociated" property of the constructor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype")]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.getName must return null when the registry does not contain an entry with the given constructor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.getName returns the name of the entry with the given constructor when there is a matching entry.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.getName returns the name of the entry with the given customized built in constructor when there is a matching entry.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +1,138 @@
|
||||||
[ElementInternals-accessibility.html]
|
[ElementInternals-accessibility.html]
|
||||||
expected: ERROR
|
[role is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaActiveDescendantElement is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaAtomic is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaAutoComplete is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaBusy is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaChecked is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaColCount is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaColIndex is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaColSpan is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaControlsElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaCurrent is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaDescribedByElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaDetailsElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaDisabled is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaErrorMessageElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaExpanded is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaFlowToElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaHasPopup is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaHidden is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaInvalid is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaKeyShortcuts is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLabel is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLabelledByElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLevel is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLive is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaModal is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaMultiLine is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaMultiSelectable is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaOrientation is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaOwnsElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaPlaceholder is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaPosInSet is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaPressed is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaReadOnly is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRelevant is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRequired is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRoleDescription is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRowCount is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRowIndex is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRowSpan is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaSelected is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaSort is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueMax is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueMin is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueNow is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueText is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
[HTMLElement-attachInternals.html]
|
|
||||||
[Successful attachInternals() and the second call.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[attachInternals() throws a NotSupportedError if it is called for a customized built-in element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[If a custom element definition for the local name of the element has disable internals flag, throw a NotSupportedError]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[If a custom element definition for the local name of the element doesn't exist, throw an InvalidStateError]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[If a custom element definition for the local name of the element doesn't exist, throw an NotSupportedError]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[element-internals-aria-element-reflection.html]
|
[element-internals-aria-element-reflection.html]
|
||||||
expected: ERROR
|
[Getting previously-unset ARIA element reflection properties on ElementInternals should return null.]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
[element-internals-shadowroot.html]
|
[element-internals-shadowroot.html]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
[ElementInternals cannot be called after constructor calls it, create case]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals cannot be called before constructor, upgrade case]
|
[ElementInternals cannot be called before constructor, upgrade case]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -15,8 +12,5 @@
|
||||||
[ElementInternals *can* be called after constructor, upgrade case]
|
[ElementInternals *can* be called after constructor, upgrade case]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals disabled by disabledFeatures]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals.shadowRoot doesn't reveal pre-attached closed shadowRoot]
|
[ElementInternals.shadowRoot doesn't reveal pre-attached closed shadowRoot]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[ElementInternals-NotSupportedError.html]
|
|
||||||
[Form-related operations and attributes should throw NotSupportedErrors for non-form-associated custom elements.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ElementInternals-form.html]
|
|
||||||
expected: ERROR
|
|
|
@ -1,10 +0,0 @@
|
||||||
[ElementInternals-labels.html]
|
|
||||||
expected: ERROR
|
|
||||||
[LABEL click]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[LABEL association]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals.labels should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ElementInternals-setFormValue-nullish-value.html]
|
|
||||||
expected: ERROR
|
|
|
@ -1,5 +1,4 @@
|
||||||
[ElementInternals-setFormValue.html]
|
[ElementInternals-setFormValue.html]
|
||||||
expected: ERROR
|
|
||||||
[Single value - Non-empty name exists]
|
[Single value - Non-empty name exists]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -219,8 +218,5 @@
|
||||||
[Newline normalization - \\n\\r in FormData filename (formdata)]
|
[Newline normalization - \\n\\r in FormData filename (formdata)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals.setFormValue() should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Single value - name is missing]
|
[Single value - name is missing]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,37 +1,6 @@
|
||||||
[ElementInternals-validation.html]
|
[ElementInternals-validation.html]
|
||||||
expected: ERROR
|
|
||||||
[validity and setValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[reportValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
["anchor" argument of setValidity()]
|
["anchor" argument of setValidity()]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[checkValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Custom control affects validation at the owner form]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[willValidate]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Custom control affects :valid :invalid for FORM and FIELDSET]
|
[Custom control affects :valid :invalid for FORM and FIELDSET]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[willValidate after upgrade]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[willValidate should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[checkValidity() should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[reportValidity() should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[willValidate after upgrade (document.createElement)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[fieldset-elements.html]
|
|
||||||
[Form associated custom elements should work with fieldset.elements]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[focusability.html]
|
|
||||||
[Focusability of form-associated custom elements]
|
|
||||||
expected: FAIL
|
|
|
@ -1,5 +1,3 @@
|
||||||
[form-associated-callback.html]
|
[form-associated-callback.html]
|
||||||
expected: ERROR
|
[Updating "id" attribute of form element]
|
||||||
[formAssociatedCallback, and form IDL attribute of ElementInternals]
|
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,3 @@
|
||||||
[form-disabled-callback.html]
|
[form-disabled-callback.html]
|
||||||
expected: ERROR
|
|
||||||
[Upgrading an element with disabled content attribute]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Disabled attribute affects focus-capability]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Relationship with FIELDSET]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A disabled form-associated custom element should not submit an entry for it]
|
[A disabled form-associated custom element should not submit an entry for it]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Adding/removing disabled content attribute]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Toggling "disabled" attribute on a custom element inside disabled <fieldset> does not trigger a callback]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Toggling "disabled" attribute on a <fieldset> does not trigger a callback on disabled custom element descendant]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Callback triggered during a clone/append operation, with disabled state provided by ancestor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Callback triggered during a clone operation, with disabled state provided by ancestor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
[form-elements-namedItem.html]
|
|
||||||
[Form associated custom elements should work with document.forms.elements.namedItem()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Form associated custom elements should work with document.forms.elements.namedItem() after upgrading]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Form associated custom elements should work with document.forms.elements.namedItem() after updating the name attribute]
|
|
||||||
expected: FAIL
|
|
|
@ -1,10 +0,0 @@
|
||||||
[form-reset-callback.html]
|
|
||||||
[form.reset(): formResetCallback is called after reset of the last built-in form control and before the next statement.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Clicking a reset button invokes formResetCallback in a microtask]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[form.reset() should trigger formResetCallback]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
[HTMLTableElement.html]
|
|
||||||
type: testharness
|
|
||||||
[caption on HTMLTableElement must enqueue connectedCallback when inserting a custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[tHead on HTMLTableElement must enqueue connectedCallback when inserting a custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[tFoot on HTMLTableElement must enqueue connectedCallback when inserting a custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Custom Elements: CEReactions on HTMLTableElement interface]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[Range.html]
|
|
||||||
type: testharness
|
|
||||||
[createContextualFragment on Range must construct a custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[custom-state-set-strong-ref.html]
|
[custom-state-set-strong-ref.html]
|
||||||
expected: ERROR
|
[customstateset doesn't crash after GC on detached node]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,2 +1,10 @@
|
||||||
[state-css-selector-nth-of.html]
|
[state-css-selector-nth-of.html]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
|
[state selector has influence on nth-of when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies on given ident]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
||||||
|
[style is invalided on clear()]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,2 +1,30 @@
|
||||||
[state-css-selector.html]
|
[state-css-selector.html]
|
||||||
expected: ERROR
|
[state selector has no influence when state is not applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector has no influence on sibling selectors when not applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector has influence when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector influences siblings when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector influences has() when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies on given ident]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies to siblings on given ident]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies to has() on given ident]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[states added multiple times counts as one]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[style is invalided on clear()]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -32,9 +32,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)]
|
[OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "imageSmoothingQuality" with the proper type]
|
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "imageSmoothingQuality" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -209,9 +206,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation beginPath()]
|
[OffscreenCanvasRenderingContext2D interface: operation beginPath()]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface object length]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute onchange]
|
[SVGElement interface: attribute onchange]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -221,9 +215,6 @@
|
||||||
[CanvasRenderingContext2D interface: operation strokeText(DOMString, unrestricted double, unrestricted double, unrestricted double)]
|
[CanvasRenderingContext2D interface: operation strokeText(DOMString, unrestricted double, unrestricted double, unrestricted double)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation reportValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SharedWorker interface object name]
|
[SharedWorker interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -239,9 +230,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: attribute shadowColor]
|
[OffscreenCanvasRenderingContext2D interface: attribute shadowColor]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation checkValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute tabIndex]
|
[SVGElement interface: attribute tabIndex]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -650,12 +638,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface object name]
|
[OffscreenCanvasRenderingContext2D interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface object name]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface: attribute labels]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGAElement interface: attribute host]
|
[SVGAElement interface: attribute host]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -689,9 +671,6 @@
|
||||||
[ApplicationCache interface: attribute oncached]
|
[ApplicationCache interface: attribute oncached]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface prototype object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation commit()]
|
[OffscreenCanvasRenderingContext2D interface: operation commit()]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -737,9 +716,6 @@
|
||||||
[SVGSVGElement interface: attribute onrejectionhandled]
|
[SVGSVGElement interface: attribute onrejectionhandled]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute form]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ApplicationCache interface: attribute onerror]
|
[ApplicationCache interface: attribute onerror]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -818,12 +794,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation putImageData(ImageData, long, long, long, long, long, long)]
|
[OffscreenCanvasRenderingContext2D interface: operation putImageData(ImageData, long, long, long, long, long, long)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface prototype object's @@unscopables property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface prototype object's "constructor" property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SharedWorker interface object length]
|
[SharedWorker interface object length]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -836,9 +806,6 @@
|
||||||
[SVGElement interface: attribute onmouseleave]
|
[SVGElement interface: attribute onmouseleave]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute willValidate]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation moveTo(unrestricted double, unrestricted double)]
|
[OffscreenCanvasRenderingContext2D interface: operation moveTo(unrestricted double, unrestricted double)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1187,9 +1154,6 @@
|
||||||
[OffscreenCanvas interface: operation convertToBlob(ImageEncodeOptions)]
|
[OffscreenCanvas interface: operation convertToBlob(ImageEncodeOptions)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute validationMessage]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute onformdata]
|
[SVGElement interface: attribute onformdata]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1220,9 +1184,6 @@
|
||||||
[SVGElement interface: attribute ondrag]
|
[SVGElement interface: attribute ondrag]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute validity]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute autofocus]
|
[SVGElement interface: attribute autofocus]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1334,9 +1295,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation setTransform(optional DOMMatrix2DInit)]
|
[OffscreenCanvasRenderingContext2D interface: operation setTransform(optional DOMMatrix2DInit)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation setFormValue((File or USVString or FormData)?, optional (File or USVString or FormData)?)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Navigator interface: operation registerProtocolHandler(DOMString, USVString)]
|
[Navigator interface: operation registerProtocolHandler(DOMString, USVString)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1346,9 +1304,6 @@
|
||||||
[Navigator interface: calling registerProtocolHandler(DOMString, USVString) on window.navigator with too few arguments must throw TypeError]
|
[Navigator interface: calling registerProtocolHandler(DOMString, USVString) on window.navigator with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation setValidity(optional ValidityStateFlags, optional DOMString, optional HTMLElement)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface: attribute shadowRoot]
|
[ElementInternals interface: attribute shadowRoot]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -2862,9 +2817,6 @@
|
||||||
[HTMLInputElement interface: createInput("image") must inherit property "useMap" with the proper type]
|
[HTMLInputElement interface: createInput("image") must inherit property "useMap" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[HTMLElement interface: operation attachInternals()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[HTMLVideoElement interface: attribute playsInline]
|
[HTMLVideoElement interface: attribute playsInline]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -4050,9 +4002,6 @@
|
||||||
[HTMLMarqueeElement interface: attribute direction]
|
[HTMLMarqueeElement interface: attribute direction]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[HTMLElement interface: document.createElement("noscript") must inherit property "attachInternals()" with the proper type]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[HTMLFormElement interface: document.createElement("form") must inherit property "requestSubmit(HTMLElement)" with the proper type]
|
[HTMLFormElement interface: document.createElement("form") must inherit property "requestSubmit(HTMLElement)" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -563171,7 +563171,7 @@
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"form-associated-callback.html": [
|
"form-associated-callback.html": [
|
||||||
"7feec50fef89103326e404efc2287767ee0981fb",
|
"329c4d75931896e7e6a2a8e4499b6ff67fc22cab",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
[state-in-has.html]
|
[state-in-has.html]
|
||||||
expected: ERROR
|
|
||||||
[Test :has() invalidation with :state() pseudo-classes]
|
[Test :has() invalidation with :state() pseudo-classes]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,33 +1,3 @@
|
||||||
[CustomElementRegistry.html]
|
[CustomElementRegistry.html]
|
||||||
[customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while getting formAssociated on the constructor prototype]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while getting additional formAssociated callbacks on the constructor prototype]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on disabledFeatures]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must upgrade elements in the shadow-including tree order]
|
[customElements.define must upgrade elements in the shadow-including tree order]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[customElements.define must get four additional callbacks on the prototype if formAssociated is converted to true]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while getting disabledFeatures on the constructor prototype]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while iterating over disabledFeatures to sequence<DOMString>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must get "prototype", "disabledFeatures", and "formAssociated" property of the constructor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must rethrow an exception thrown while converting the value of disabledFeatures to sequence<DOMString>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype")]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +1,144 @@
|
||||||
[ElementInternals-accessibility.html]
|
[ElementInternals-accessibility.html]
|
||||||
expected: ERROR
|
[ElementInternals-accessibility]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaDescribedByElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaFlowToElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[role is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaPosInSet is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueMax is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaDisabled is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRoleDescription is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueMin is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaOrientation is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLabel is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaMultiSelectable is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaExpanded is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaDetailsElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaColIndex is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRowCount is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaBusy is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaChecked is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaControlsElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaAutoComplete is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaHasPopup is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLevel is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaAtomic is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaErrorMessageElement is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaHidden is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaSort is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRowSpan is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRowIndex is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaPlaceholder is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaReadOnly is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaOwnsElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaActiveDescendantElement is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaColCount is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRelevant is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLive is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueNow is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaRequired is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaValueText is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaLabelledByElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaColSpan is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaModal is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaSelected is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaKeyShortcuts is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaCurrent is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaMultiLine is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaPressed is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaErrorMessageElements is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ariaInvalid is defined in ElementInternals]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
[HTMLElement-attachInternals.html]
|
|
||||||
[Successful attachInternals() and the second call.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[attachInternals() throws a NotSupportedError if it is called for a customized built-in element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[If a custom element definition for the local name of the element doesn't exist, throw an NotSupportedError]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[If a custom element definition for the local name of the element has disable internals flag, throw a NotSupportedError]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +1,3 @@
|
||||||
[element-internals-aria-element-reflection.html]
|
[element-internals-aria-element-reflection.html]
|
||||||
expected: ERROR
|
[Getting previously-unset ARIA element reflection properties on ElementInternals should return null.]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -12,11 +12,5 @@
|
||||||
[ElementInternals *can* be called after constructor, upgrade case]
|
[ElementInternals *can* be called after constructor, upgrade case]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals cannot be called after constructor calls it, create case]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals disabled by disabledFeatures]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals.shadowRoot doesn't reveal pre-attached closed shadowRoot]
|
[ElementInternals.shadowRoot doesn't reveal pre-attached closed shadowRoot]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[ElementInternals-NotSupportedError.html]
|
|
||||||
[Form-related operations and attributes should throw NotSupportedErrors for non-form-associated custom elements.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ElementInternals-form.html]
|
|
||||||
expected: ERROR
|
|
|
@ -1,10 +0,0 @@
|
||||||
[ElementInternals-labels.html]
|
|
||||||
expected: ERROR
|
|
||||||
[LABEL association]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[LABEL click]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals.labels should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ElementInternals-setFormValue-nullish-value.html]
|
|
||||||
expected: ERROR
|
|
|
@ -1,5 +1,4 @@
|
||||||
[ElementInternals-setFormValue.html]
|
[ElementInternals-setFormValue.html]
|
||||||
expected: ERROR
|
|
||||||
[Newline normalization - \\n\\r in value]
|
[Newline normalization - \\n\\r in value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -221,6 +220,3 @@
|
||||||
|
|
||||||
[Newline normalization - \\n\\r in FormData filename (formdata)]
|
[Newline normalization - \\n\\r in FormData filename (formdata)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals.setFormValue() should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,37 +1,6 @@
|
||||||
[ElementInternals-validation.html]
|
[ElementInternals-validation.html]
|
||||||
expected: ERROR
|
|
||||||
[willValidate]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[willValidate after upgrade]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[willValidate should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[validity and setValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
["anchor" argument of setValidity()]
|
["anchor" argument of setValidity()]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[checkValidity() should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[checkValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[reportValidity() should throw NotSupportedError if the target element is not a form-associated custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[reportValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Custom control affects validation at the owner form]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Custom control affects :valid :invalid for FORM and FIELDSET]
|
[Custom control affects :valid :invalid for FORM and FIELDSET]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[willValidate after upgrade (document.createElement)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[fieldset-elements.html]
|
|
||||||
[Form associated custom elements should work with fieldset.elements]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[focusability.html]
|
|
||||||
[Focusability of form-associated custom elements]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +1,3 @@
|
||||||
[form-associated-callback.html]
|
[form-associated-callback.html]
|
||||||
expected: ERROR
|
[Updating "id" attribute of form element]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,28 +1,3 @@
|
||||||
[form-disabled-callback.html]
|
[form-disabled-callback.html]
|
||||||
expected: ERROR
|
|
||||||
[Adding/removing disabled content attribute]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Relationship with FIELDSET]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A disabled form-associated custom element should not submit an entry for it]
|
[A disabled form-associated custom element should not submit an entry for it]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Disabled attribute affects focus-capability]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Upgrading an element with disabled content attribute]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Toggling "disabled" attribute on a custom element inside disabled <fieldset> does not trigger a callback]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Toggling "disabled" attribute on a <fieldset> does not trigger a callback on disabled custom element descendant]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Callback triggered during a clone/append operation, with disabled state provided by ancestor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Callback triggered during a clone operation, with disabled state provided by ancestor]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
[form-elements-namedItem.html]
|
|
||||||
[Form associated custom elements should work with document.forms.elements.namedItem()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Form associated custom elements should work with document.forms.elements.namedItem() after upgrading]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Form associated custom elements should work with document.forms.elements.namedItem() after updating the name attribute]
|
|
||||||
expected: FAIL
|
|
|
@ -1,9 +0,0 @@
|
||||||
[form-reset-callback.html]
|
|
||||||
[form.reset() should trigger formResetCallback]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[form.reset(): formResetCallback is called after reset of the last built-in form control and before the next statement.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Clicking a reset button invokes formResetCallback in a microtask]
|
|
||||||
expected: FAIL
|
|
|
@ -1,9 +0,0 @@
|
||||||
[HTMLTableElement.html]
|
|
||||||
[caption on HTMLTableElement must enqueue connectedCallback when inserting a custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[tHead on HTMLTableElement must enqueue connectedCallback when inserting a custom element]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[tFoot on HTMLTableElement must enqueue connectedCallback when inserting a custom element]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[Range.html]
|
|
||||||
[createContextualFragment on Range must construct a custom element]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +1,3 @@
|
||||||
[custom-state-set-strong-ref.html]
|
[custom-state-set-strong-ref.html]
|
||||||
expected: ERROR
|
[customstateset doesn't crash after GC on detached node]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,2 +1,10 @@
|
||||||
[state-css-selector-nth-of.html]
|
[state-css-selector-nth-of.html]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
|
[state selector has influence on nth-of when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies on given ident]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
||||||
|
[style is invalided on clear()]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,2 +1,30 @@
|
||||||
[state-css-selector.html]
|
[state-css-selector.html]
|
||||||
expected: ERROR
|
[state selector has no influence when state is not applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector has no influence on sibling selectors when not applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector has influence when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector influences siblings when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector influences has() when state is applied]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies on given ident]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies to siblings on given ident]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[state selector only applies to has() on given ident]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[states added multiple times counts as one]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[style is invalided on clear()]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -62,12 +62,6 @@
|
||||||
[SVGElement interface: attribute onended]
|
[SVGElement interface: attribute onended]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface object length]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface: operation reportValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGSVGElement interface: attribute onbeforeprint]
|
[SVGSVGElement interface: attribute onbeforeprint]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -92,9 +86,6 @@
|
||||||
[OffscreenCanvas interface: operation convertToBlob(optional ImageEncodeOptions)]
|
[OffscreenCanvas interface: operation convertToBlob(optional ImageEncodeOptions)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation setValidity(ValidityStateFlags, optional DOMString, optional HTMLElement)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[DOMStringList interface: calling item(unsigned long) on location.ancestorOrigins with too few arguments must throw TypeError]
|
[DOMStringList interface: calling item(unsigned long) on location.ancestorOrigins with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -167,9 +158,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: attribute strokeStyle]
|
[OffscreenCanvasRenderingContext2D interface: attribute strokeStyle]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute labels]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute oncopy]
|
[SVGElement interface: attribute oncopy]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -323,9 +311,6 @@
|
||||||
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "getLineDash()" with the proper type]
|
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "getLineDash()" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvasRenderingContext2D interface: attribute imageSmoothingEnabled]
|
[OffscreenCanvasRenderingContext2D interface: attribute imageSmoothingEnabled]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -389,9 +374,6 @@
|
||||||
[Navigator interface: calling unregisterProtocolHandler(DOMString, USVString) on window.navigator with too few arguments must throw TypeError]
|
[Navigator interface: calling unregisterProtocolHandler(DOMString, USVString) on window.navigator with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface prototype object's "constructor" property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute ondurationchange]
|
[SVGElement interface: attribute ondurationchange]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -461,9 +443,6 @@
|
||||||
[Path2D interface: operation rect(unrestricted double, unrestricted double, unrestricted double, unrestricted double)]
|
[Path2D interface: operation rect(unrestricted double, unrestricted double, unrestricted double, unrestricted double)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute validationMessage]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[DataTransfer interface: attribute dropEffect]
|
[DataTransfer interface: attribute dropEffect]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -806,15 +785,9 @@
|
||||||
[DragEvent interface: existence and properties of interface object]
|
[DragEvent interface: existence and properties of interface object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface prototype object's @@unscopables property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Path2D interface: operation arc(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional boolean)]
|
[Path2D interface: operation arc(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional boolean)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute willValidate]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvas interface object length]
|
[OffscreenCanvas interface object length]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -851,9 +824,6 @@
|
||||||
[Navigator interface: window.navigator must inherit property "registerProtocolHandler(DOMString, USVString)" with the proper type]
|
[Navigator interface: window.navigator must inherit property "registerProtocolHandler(DOMString, USVString)" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: existence and properties of interface prototype object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvas interface object name]
|
[OffscreenCanvas interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -959,9 +929,6 @@
|
||||||
[SVGSVGElement interface: attribute onmessage]
|
[SVGSVGElement interface: attribute onmessage]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface object name]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute onmouseup]
|
[SVGElement interface: attribute onmouseup]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -971,9 +938,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation isPointInPath(unrestricted double, unrestricted double, optional CanvasFillRule)]
|
[OffscreenCanvasRenderingContext2D interface: operation isPointInPath(unrestricted double, unrestricted double, optional CanvasFillRule)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute validity]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvasRenderingContext2D interface: operation restore()]
|
[OffscreenCanvasRenderingContext2D interface: operation restore()]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -983,9 +947,6 @@
|
||||||
[DataTransferItem interface: existence and properties of interface object]
|
[DataTransferItem interface: existence and properties of interface object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation checkValidity()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Location interface: stringifier]
|
[Location interface: stringifier]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1112,9 +1073,6 @@
|
||||||
[SVGSVGElement interface: attribute onrejectionhandled]
|
[SVGSVGElement interface: attribute onrejectionhandled]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute form]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute onratechange]
|
[SVGElement interface: attribute onratechange]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1205,15 +1163,6 @@
|
||||||
[ApplicationCache interface: attribute onobsolete]
|
[ApplicationCache interface: attribute onobsolete]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: operation setFormValue((File or USVString or FormData)?, optional (File or USVString or FormData)?)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface: operation setValidity(optional ValidityStateFlags, optional DOMString, optional HTMLElement)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface: attribute shadowRoot]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Worklet interface object length]
|
[Worklet interface object length]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1838,9 +1787,6 @@
|
||||||
[Element interface: operation setHTMLUnsafe(DOMString)]
|
[Element interface: operation setHTMLUnsafe(DOMString)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ElementInternals interface: attribute states]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CustomStateSet interface: existence and properties of interface object]
|
[CustomStateSet interface: existence and properties of interface object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -2021,6 +1967,12 @@
|
||||||
[Element interface: operation setHTMLUnsafe(HTMLString)]
|
[Element interface: operation setHTMLUnsafe(HTMLString)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[ElementInternals interface: attribute shadowRoot]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[ElementInternals interface: attribute states]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
||||||
[idlharness.https.html?include=(Document|Window)]
|
[idlharness.https.html?include=(Document|Window)]
|
||||||
[Document interface: documentWithHandlers must inherit property "queryCommandEnabled(DOMString)" with the proper type]
|
[Document interface: documentWithHandlers must inherit property "queryCommandEnabled(DOMString)" with the proper type]
|
||||||
|
@ -2718,9 +2670,6 @@
|
||||||
[HTMLInputElement interface: createInput("image") must inherit property "useMap" with the proper type]
|
[HTMLInputElement interface: createInput("image") must inherit property "useMap" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[HTMLElement interface: operation attachInternals()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[HTMLVideoElement interface: attribute playsInline]
|
[HTMLVideoElement interface: attribute playsInline]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -3879,9 +3828,6 @@
|
||||||
[HTMLMarqueeElement interface: attribute direction]
|
[HTMLMarqueeElement interface: attribute direction]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[HTMLElement interface: document.createElement("noscript") must inherit property "attachInternals()" with the proper type]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[HTMLInputElement interface: createInput("radio") must inherit property "width" with the proper type]
|
[HTMLInputElement interface: createInput("radio") must inherit property "width" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -12615,6 +12615,13 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"ElementInternals-setFormValue.html": [
|
||||||
|
"d553dd1a90262700323a714359df60906ac057cf",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"Event.html": [
|
"Event.html": [
|
||||||
"3947b286122ee47f2f874232763ceeff3c2b661e",
|
"3947b286122ee47f2f874232763ceeff3c2b661e",
|
||||||
[
|
[
|
||||||
|
@ -13410,7 +13417,7 @@
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"interfaces.html": [
|
"interfaces.html": [
|
||||||
"5461ff50d836f872fdf09defd98575d08fda0061",
|
"063495d90f464f161c52a8d099a298e914b9b082",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- Like custom-elements/form-associated/ElementInternals-setFormValue,
|
||||||
|
but without any ordering assumptions about iframe loads and promise
|
||||||
|
microtasks, to test the form submissions despite Servo's racey iframe.
|
||||||
|
Includes web-platform-tests/wpt#21747 change to initial values. -->
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<div id="container1"></div>
|
||||||
|
<div id="container2"></div>
|
||||||
|
<div id="container3"></div>
|
||||||
|
<div id="container4"></div>
|
||||||
|
<div id="container5"></div>
|
||||||
|
<div id="container6"></div>
|
||||||
|
<script>
|
||||||
|
class MyControl extends HTMLElement {
|
||||||
|
static get formAssociated() { return true; }
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.internals_ = this.attachInternals();
|
||||||
|
this.value_ = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.value_;
|
||||||
|
}
|
||||||
|
set value(v) {
|
||||||
|
this.internals_.setFormValue(v);
|
||||||
|
this.value_ = v;
|
||||||
|
}
|
||||||
|
setValues(nameValues) {
|
||||||
|
const formData = new FormData();
|
||||||
|
for (let p of nameValues) {
|
||||||
|
formData.append(p[0], p[1]);
|
||||||
|
}
|
||||||
|
this.internals_.setFormValue(formData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define('my-control', MyControl);
|
||||||
|
const $ = document.querySelector.bind(document);
|
||||||
|
|
||||||
|
function submitPromise(t, n) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const iframe = $('#container'+n+' iframe');
|
||||||
|
iframe.onload = () => {
|
||||||
|
if(iframe.contentWindow.location.href == "about:blank") { return; }
|
||||||
|
resolve(iframe.contentWindow.location.search);
|
||||||
|
}
|
||||||
|
iframe.onerror = () => reject(new Error('iframe onerror fired'));
|
||||||
|
$('#container'+n+' form').submit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
promise_test(t => {
|
||||||
|
$('#container1').innerHTML = '<form action="/common/blank.html" target="if1">' +
|
||||||
|
'<input name=name-pd1 value="value-pd1">' +
|
||||||
|
'<my-control></my-control>' +
|
||||||
|
'</form>' +
|
||||||
|
'<iframe name="if1"></iframe>';
|
||||||
|
return submitPromise(t,1).then(query => {
|
||||||
|
assert_equals(query, '?name-pd1=value-pd1');
|
||||||
|
});
|
||||||
|
}, 'Single value - name is missing');
|
||||||
|
|
||||||
|
promise_test(t => {
|
||||||
|
$('#container2').innerHTML = '<form action="/common/blank.html" target="if2">' +
|
||||||
|
'<input name=name-pd1 value="value-pd1">' +
|
||||||
|
'<my-control name=""></my-control>' +
|
||||||
|
'<input name=name-pd2 value="value-pd2">' +
|
||||||
|
'</form>' +
|
||||||
|
'<iframe name="if2"></iframe>';
|
||||||
|
$('#container2 my-control').value = 'value-ce1';
|
||||||
|
return submitPromise(t,2).then(query => {
|
||||||
|
assert_equals(query, '?name-pd1=value-pd1&name-pd2=value-pd2');
|
||||||
|
});
|
||||||
|
}, 'Single value - empty name exists');
|
||||||
|
|
||||||
|
promise_test(t => {
|
||||||
|
$('#container3').innerHTML = '<form action="/common/blank.html" target="if3" accept-charset=utf-8>' +
|
||||||
|
'<input name=name-pd1 value="value-pd1">' +
|
||||||
|
'<my-control name="name-ce1"></my-control>' +
|
||||||
|
'<my-control name="name-usv"></my-control>' +
|
||||||
|
'<my-control name="name-file"></my-control>' +
|
||||||
|
'</form>' +
|
||||||
|
'<iframe name="if3"></iframe>';
|
||||||
|
const USV_INPUT = 'abc\uDC00\uD800def';
|
||||||
|
const USV_OUTPUT = 'abc\uFFFD\uFFFDdef';
|
||||||
|
const FILE_NAME = 'test_file.txt';
|
||||||
|
$('#container3 [name=name-usv]').value = USV_INPUT;
|
||||||
|
$('#container3 [name=name-file]').value = new File(['file content'], FILE_NAME);
|
||||||
|
return submitPromise(t,3).then(query => {
|
||||||
|
assert_equals(query, `?name-pd1=value-pd1&name-usv=${encodeURIComponent(USV_OUTPUT)}&name-file=${FILE_NAME}`);
|
||||||
|
});
|
||||||
|
}, 'Single value - Non-empty name exists');
|
||||||
|
|
||||||
|
promise_test(t => {
|
||||||
|
$('#container4').innerHTML = '<form action="/common/blank.html" target="if4">' +
|
||||||
|
'<input name=name-pd1 value="value-pd1">' +
|
||||||
|
'<my-control name="name-ce1"></my-control>' +
|
||||||
|
'<my-control name="name-ce2"></my-control>' +
|
||||||
|
'</form>' +
|
||||||
|
'<iframe name="if4"></iframe>';
|
||||||
|
$('#container4 my-control').value = null;
|
||||||
|
return submitPromise(t,4).then(query => {
|
||||||
|
assert_equals(query, '?name-pd1=value-pd1');
|
||||||
|
});
|
||||||
|
}, 'Null value should submit nothing');
|
||||||
|
|
||||||
|
promise_test(t => {
|
||||||
|
$('#container5').innerHTML = '<form action="/common/blank.html" target="if5">' +
|
||||||
|
'<input name=name-pd1 value="value-pd1">' +
|
||||||
|
'<my-control name=name-ce1></my-control>' +
|
||||||
|
'</form>' +
|
||||||
|
'<iframe name="if5"></iframe>';
|
||||||
|
$('#container5 my-control').value = 'value-ce1';
|
||||||
|
$('#container5 my-control').setValues([]);
|
||||||
|
$('#container5 my-control').setValues([['sub1', 'subvalue1'],
|
||||||
|
['sub2', 'subvalue2'],
|
||||||
|
['sub2', 'subvalue3']]);
|
||||||
|
return submitPromise(t,5).then(query => {
|
||||||
|
assert_equals(query, '?name-pd1=value-pd1&sub1=subvalue1&sub2=subvalue2&sub2=subvalue3');
|
||||||
|
});
|
||||||
|
}, 'Multiple values - name content attribute is ignored');
|
||||||
|
|
||||||
|
promise_test(t => {
|
||||||
|
$('#container6').innerHTML = '<form action="/common/blank.html" target="if6">' +
|
||||||
|
'<input name=name-pd1 value="value-pd1">' +
|
||||||
|
'<my-control name=name-ce1></my-control>' +
|
||||||
|
'</form>' +
|
||||||
|
'<iframe name="if6"></iframe>';
|
||||||
|
$('#container6 my-control').value = 'value-ce1';
|
||||||
|
$('#container6 my-control').setValues([]);
|
||||||
|
return submitPromise(t,6).then(query => {
|
||||||
|
assert_equals(query, '?name-pd1=value-pd1');
|
||||||
|
});
|
||||||
|
}, 'setFormValue with an empty FormData should submit nothing');
|
||||||
|
</script>
|
|
@ -77,6 +77,7 @@ test_interfaces([
|
||||||
"DOMStringMap",
|
"DOMStringMap",
|
||||||
"DOMTokenList",
|
"DOMTokenList",
|
||||||
"Element",
|
"Element",
|
||||||
|
"ElementInternals",
|
||||||
"ErrorEvent",
|
"ErrorEvent",
|
||||||
"Event",
|
"Event",
|
||||||
"EventSource",
|
"EventSource",
|
||||||
|
|
|
@ -122,7 +122,7 @@ test(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define('will-be-defined', WillBeDefined);
|
customElements.define('will-be-defined', WillBeDefined);
|
||||||
customElements.upgrade(container);
|
customElements.upgrade($('#container'));
|
||||||
|
|
||||||
controls = $('#form1').elements;
|
controls = $('#form1').elements;
|
||||||
assert_equals(controls.length, 3, 'form.elements.length');
|
assert_equals(controls.length, 3, 'form.elements.length');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue