mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +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
|
@ -102,6 +102,7 @@ use crate::dom::document::{
|
|||
use crate::dom::documentfragment::DocumentFragment;
|
||||
use crate::dom::domrect::DOMRect;
|
||||
use crate::dom::domtokenlist::DOMTokenList;
|
||||
use crate::dom::elementinternals::ElementInternals;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::htmlanchorelement::HTMLAnchorElement;
|
||||
use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
|
||||
|
@ -145,6 +146,7 @@ use crate::dom::servoparser::ServoParser;
|
|||
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::validation::Validatable;
|
||||
use crate::dom::validitystate::ValidationFlags;
|
||||
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
|
||||
use crate::dom::window::ReflowReason;
|
||||
use crate::script_thread::ScriptThread;
|
||||
|
@ -1419,6 +1421,12 @@ impl Element {
|
|||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||
HTMLElementTypeId::HTMLOptionElement,
|
||||
)) => self.disabled_state(),
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
|
||||
self.downcast::<HTMLElement>()
|
||||
.unwrap()
|
||||
.is_form_associated_custom_element() &&
|
||||
self.disabled_state()
|
||||
},
|
||||
// TODO:
|
||||
// an optgroup 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
|
||||
pub fn parse_fragment(&self, markup: DOMString) -> Fallible<DomRoot<DocumentFragment>> {
|
||||
// Steps 1-2.
|
||||
let context_document = document_from_node(self);
|
||||
// TODO(#11995): XML case.
|
||||
let new_children = ServoParser::parse_html_fragment(self, markup);
|
||||
// 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);
|
||||
// Step 4.
|
||||
for child in new_children {
|
||||
|
@ -1973,6 +1988,24 @@ impl Element {
|
|||
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 {
|
||||
|
@ -3098,6 +3131,9 @@ impl VirtualMethods for Element {
|
|||
self.super_type().unwrap().unbind_from_tree(context);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -3543,6 +3579,38 @@ impl 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 {
|
||||
self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
|
||||
}
|
||||
|
@ -3743,6 +3811,11 @@ impl Element {
|
|||
self.set_disabled_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)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue