mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement shadow dom slots (#35013)
* Implement slot-related algorithms Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Hook up slot elements to DOM creation logic Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Set a slot assignment mode for servo-internal shadow roots Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Assign slots when a slottable's slot attribute changes Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Properly compute slot name Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * ./mach test-tidy Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update <slot> name when name attribute changes Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Implement fast path for Node::assign_slottables_for_a_tree assign_slottables_for_a_tree traverses all descendants of the node and is potentially very expensive. If the node is not a shadow root then assigning slottables to it won't have any effect, so we take a fast path out. Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Move slottable data into ElementRareData This shrinks all element descendants back to their original size. Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Address review comments Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
8bb50fa3c9
commit
dabe162d44
31 changed files with 758 additions and 355 deletions
|
@ -66,6 +66,7 @@ use crate::dom::htmlprogresselement::HTMLProgressElement;
|
|||
use crate::dom::htmlquoteelement::HTMLQuoteElement;
|
||||
use crate::dom::htmlscriptelement::HTMLScriptElement;
|
||||
use crate::dom::htmlselectelement::HTMLSelectElement;
|
||||
use crate::dom::htmlslotelement::HTMLSlotElement;
|
||||
use crate::dom::htmlsourceelement::HTMLSourceElement;
|
||||
use crate::dom::htmlspanelement::HTMLSpanElement;
|
||||
use crate::dom::htmlstyleelement::HTMLStyleElement;
|
||||
|
@ -357,6 +358,7 @@ pub(crate) fn create_native_html_element(
|
|||
local_name!("script") => make!(HTMLScriptElement, creator),
|
||||
local_name!("section") => make!(HTMLElement),
|
||||
local_name!("select") => make!(HTMLSelectElement),
|
||||
local_name!("slot") => make!(HTMLSlotElement),
|
||||
local_name!("small") => make!(HTMLElement),
|
||||
local_name!("source") => make!(HTMLSourceElement),
|
||||
// https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:spacer
|
||||
|
|
|
@ -74,7 +74,7 @@ use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
|||
use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMethods, ShadowRootMode,
|
||||
ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
||||
ScrollBehavior, ScrollToOptions, WindowMethods,
|
||||
|
@ -105,6 +105,7 @@ use crate::dom::domrectlist::DOMRectList;
|
|||
use crate::dom::domtokenlist::DOMTokenList;
|
||||
use crate::dom::elementinternals::ElementInternals;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::htmlanchorelement::HTMLAnchorElement;
|
||||
use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
|
||||
use crate::dom::htmlbuttonelement::HTMLButtonElement;
|
||||
|
@ -124,6 +125,7 @@ use crate::dom::htmlobjectelement::HTMLObjectElement;
|
|||
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
|
||||
use crate::dom::htmloutputelement::HTMLOutputElement;
|
||||
use crate::dom::htmlselectelement::HTMLSelectElement;
|
||||
use crate::dom::htmlslotelement::{HTMLSlotElement, Slottable};
|
||||
use crate::dom::htmlstyleelement::HTMLStyleElement;
|
||||
use crate::dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementLayoutHelpers};
|
||||
use crate::dom::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers};
|
||||
|
@ -510,6 +512,7 @@ impl Element {
|
|||
is_ua_widget: IsUserAgentWidget,
|
||||
mode: ShadowRootMode,
|
||||
clonable: bool,
|
||||
slot_assignment_mode: SlotAssignmentMode,
|
||||
) -> Fallible<DomRoot<ShadowRoot>> {
|
||||
// Step 1.
|
||||
// If element’s namespace is not the HTML namespace,
|
||||
|
@ -536,7 +539,13 @@ impl Element {
|
|||
}
|
||||
|
||||
// Steps 4, 5 and 6.
|
||||
let shadow_root = ShadowRoot::new(self, &self.node.owner_doc(), mode, clonable);
|
||||
let shadow_root = ShadowRoot::new(
|
||||
self,
|
||||
&self.node.owner_doc(),
|
||||
mode,
|
||||
slot_assignment_mode,
|
||||
clonable,
|
||||
);
|
||||
self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
|
||||
shadow_root
|
||||
.upcast::<Node>()
|
||||
|
@ -603,6 +612,43 @@ impl Element {
|
|||
Some(node) => node.is::<Document>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
let assigned_slot = self
|
||||
.rare_data
|
||||
.borrow()
|
||||
.as_ref()?
|
||||
.slottable_data
|
||||
.assigned_slot
|
||||
.as_ref()?
|
||||
.as_rooted();
|
||||
Some(assigned_slot)
|
||||
}
|
||||
|
||||
pub(crate) fn set_assigned_slot(&self, assigned_slot: DomRoot<HTMLSlotElement>) {
|
||||
self.ensure_rare_data().slottable_data.assigned_slot = Some(assigned_slot.as_traced());
|
||||
}
|
||||
|
||||
pub(crate) fn manual_slot_assignment(&self) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
let manually_assigned_slot = self
|
||||
.rare_data
|
||||
.borrow()
|
||||
.as_ref()?
|
||||
.slottable_data
|
||||
.manual_slot_assignment
|
||||
.as_ref()?
|
||||
.as_rooted();
|
||||
Some(manually_assigned_slot)
|
||||
}
|
||||
|
||||
pub(crate) fn set_manual_slot_assignment(
|
||||
&self,
|
||||
manually_assigned_slot: Option<&HTMLSlotElement>,
|
||||
) {
|
||||
self.ensure_rare_data()
|
||||
.slottable_data
|
||||
.manual_slot_assignment = manually_assigned_slot.map(Dom::from_ref);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
|
||||
|
@ -3084,7 +3130,12 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
|
|||
fn AttachShadow(&self, init: &ShadowRootInit) -> Fallible<DomRoot<ShadowRoot>> {
|
||||
// Step 1. Run attach a shadow root with this, init["mode"], init["clonable"], init["serializable"],
|
||||
// init["delegatesFocus"], and init["slotAssignment"].
|
||||
let shadow_root = self.attach_shadow(IsUserAgentWidget::No, init.mode, init.clonable)?;
|
||||
let shadow_root = self.attach_shadow(
|
||||
IsUserAgentWidget::No,
|
||||
init.mode,
|
||||
init.clonable,
|
||||
init.slotAssignment,
|
||||
)?;
|
||||
|
||||
// Step 2. Return this’s shadow root.
|
||||
Ok(shadow_root)
|
||||
|
@ -3460,6 +3511,16 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
|
|||
fn SetAriaValueText(&self, value: Option<DOMString>, can_gc: CanGc) {
|
||||
self.set_nullable_string_attribute(&local_name!("aria-valuetext"), value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-slotable-assignedslot>
|
||||
fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
let cx = GlobalScope::get_cx();
|
||||
|
||||
// > The assignedSlot getter steps are to return the result of
|
||||
// > find a slot given this and with the open flag set.
|
||||
rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(self)));
|
||||
slottable.find_a_slot(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualMethods for Element {
|
||||
|
@ -3600,6 +3661,12 @@ impl VirtualMethods for Element {
|
|||
}
|
||||
}
|
||||
},
|
||||
&local_name!("slot") => {
|
||||
// Update slottable data
|
||||
let cx = GlobalScope::get_cx();
|
||||
rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(self)));
|
||||
slottable.update_slot_name(attr, mutation, CanGc::note())
|
||||
},
|
||||
_ => {
|
||||
// FIXME(emilio): This is pretty dubious, and should be done in
|
||||
// the relevant super-classes.
|
||||
|
|
|
@ -54,7 +54,9 @@ use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConsta
|
|||
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMode;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
|
||||
use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
|
||||
|
@ -1888,7 +1890,12 @@ impl HTMLMediaElement {
|
|||
return;
|
||||
}
|
||||
let shadow_root = element
|
||||
.attach_shadow(IsUserAgentWidget::Yes, ShadowRootMode::Closed, false)
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
)
|
||||
.unwrap();
|
||||
let document = self.owner_document();
|
||||
let script = HTMLScriptElement::new(
|
||||
|
|
547
components/script/dom/htmlslotelement.rs
Normal file
547
components/script/dom/htmlslotelement.rs
Normal file
|
@ -0,0 +1,547 @@
|
|||
/* 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::RefCell;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
|
||||
use js::gc::{RootedGuard, RootedVec};
|
||||
use js::rust::HandleObject;
|
||||
use servo_atoms::Atom;
|
||||
use style::attr::AttrValue;
|
||||
|
||||
use crate::dom::attr::Attr;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::{
|
||||
AssignedNodesOptions, HTMLSlotElementMethods,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::codegen::UnionTypes::ElementOrText;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::htmlelement::HTMLElement;
|
||||
use crate::dom::node::{Node, ShadowIncluding};
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#the-slot-element>
|
||||
#[dom_struct]
|
||||
pub struct HTMLSlotElement {
|
||||
htmlelement: HTMLElement,
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#slot-assigned-nodes>
|
||||
assigned_nodes: RefCell<Vec<Slottable>>,
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#manually-assigned-nodes>
|
||||
manually_assigned_nodes: RefCell<Vec<Slottable>>,
|
||||
}
|
||||
|
||||
impl HTMLSlotElementMethods<crate::DomTypeHolder> for HTMLSlotElement {
|
||||
// https://html.spec.whatwg.org/multipage/#dom-slot-name
|
||||
make_getter!(Name, "name");
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-slot-name
|
||||
make_atomic_setter!(SetName, "name");
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-slot-assignednodes>
|
||||
fn AssignedNodes(&self, options: &AssignedNodesOptions) -> Vec<DomRoot<Node>> {
|
||||
// Step 1. If options["flatten"] is false, then return this's assigned nodes.
|
||||
if !options.flatten {
|
||||
return self
|
||||
.assigned_nodes
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|slottable| slottable.node())
|
||||
.map(DomRoot::from_ref)
|
||||
.collect();
|
||||
}
|
||||
|
||||
// Step 2. Return the result of finding flattened slottables with this.
|
||||
rooted_vec!(let mut flattened_slottables);
|
||||
self.find_flattened_slottables(&mut flattened_slottables);
|
||||
|
||||
flattened_slottables
|
||||
.iter()
|
||||
.map(|slottable| DomRoot::from_ref(slottable.node()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-slot-assignedelements>
|
||||
fn AssignedElements(&self, options: &AssignedNodesOptions) -> Vec<DomRoot<Element>> {
|
||||
self.AssignedNodes(options)
|
||||
.into_iter()
|
||||
.flat_map(|node| node.downcast::<Element>().map(DomRoot::from_ref))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-slot-assign>
|
||||
fn Assign(&self, nodes: Vec<ElementOrText>) {
|
||||
let cx = GlobalScope::get_cx();
|
||||
|
||||
// Step 1. For each node of this's manually assigned nodes, set node's manual slot assignment to null.
|
||||
for slottable in self.manually_assigned_nodes.borrow().iter() {
|
||||
slottable.set_manual_slot_assignment(None);
|
||||
}
|
||||
|
||||
// Step 2. Let nodesSet be a new ordered set.
|
||||
rooted_vec!(let mut nodes_set);
|
||||
|
||||
// Step 3. For each node of nodes:
|
||||
for element_or_text in nodes.into_iter() {
|
||||
rooted!(in(*cx) let node = match element_or_text {
|
||||
ElementOrText::Element(element) => Slottable::Element(Dom::from_ref(&element)),
|
||||
ElementOrText::Text(text) => Slottable::Text(Dom::from_ref(&text)),
|
||||
});
|
||||
|
||||
// Step 3.1 If node's manual slot assignment refers to a slot,
|
||||
// then remove node from that slot's manually assigned nodes.
|
||||
if let Some(slot) = node.manual_slot_assignment() {
|
||||
let mut manually_assigned_nodes = slot.manually_assigned_nodes.borrow_mut();
|
||||
if let Some(position) = manually_assigned_nodes
|
||||
.iter()
|
||||
.position(|value| *value == *node)
|
||||
{
|
||||
manually_assigned_nodes.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3.2 Set node's manual slot assignment to this.
|
||||
node.set_manual_slot_assignment(Some(self));
|
||||
|
||||
// Step 3.3 Append node to nodesSet.
|
||||
if !nodes_set.contains(&*node) {
|
||||
nodes_set.push(node.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4. Set this's manually assigned nodes to nodesSet.
|
||||
*self.manually_assigned_nodes.borrow_mut() = nodes_set.iter().cloned().collect();
|
||||
|
||||
// Step 5. Run assign slottables for a tree for this's root.
|
||||
self.upcast::<Node>()
|
||||
.GetRootNode(&GetRootNodeOptions::empty())
|
||||
.assign_slottables_for_a_tree();
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#concept-slotable>
|
||||
#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
|
||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||
pub(crate) enum Slottable {
|
||||
Element(Dom<Element>),
|
||||
Text(Dom<Text>),
|
||||
}
|
||||
|
||||
/// Data shared between all [slottables](https://dom.spec.whatwg.org/#concept-slotable)
|
||||
///
|
||||
/// Note that the [slottable name](https://dom.spec.whatwg.org/#slotable-name) is not
|
||||
/// part of this. While the spec says that all slottables have a name, only Element's
|
||||
/// can ever have a non-empty name, so they store it seperately
|
||||
#[derive(Default, JSTraceable, MallocSizeOf)]
|
||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||
pub struct SlottableData {
|
||||
/// <https://dom.spec.whatwg.org/#slotable-assigned-slot>
|
||||
pub(crate) assigned_slot: Option<Dom<HTMLSlotElement>>,
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#slottable-manual-slot-assignment>
|
||||
pub(crate) manual_slot_assignment: Option<Dom<HTMLSlotElement>>,
|
||||
}
|
||||
|
||||
impl HTMLSlotElement {
|
||||
fn new_inherited(
|
||||
local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document,
|
||||
) -> HTMLSlotElement {
|
||||
HTMLSlotElement {
|
||||
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
|
||||
assigned_nodes: Default::default(),
|
||||
manually_assigned_nodes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
pub(crate) fn new(
|
||||
local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document,
|
||||
proto: Option<HandleObject>,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<HTMLSlotElement> {
|
||||
Node::reflect_node_with_proto(
|
||||
Box::new(HTMLSlotElement::new_inherited(local_name, prefix, document)),
|
||||
document,
|
||||
proto,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#find-flattened-slotables>
|
||||
fn find_flattened_slottables(&self, result: &mut RootedVec<Slottable>) {
|
||||
// Step 1. Let result be an empty list.
|
||||
debug_assert!(result.is_empty());
|
||||
|
||||
// Step 2. If slot’s root is not a shadow root, then return result.
|
||||
if self.upcast::<Node>().containing_shadow_root().is_none() {
|
||||
return;
|
||||
};
|
||||
|
||||
// Step 3. Let slottables be the result of finding slottables given slot.
|
||||
rooted_vec!(let mut slottables);
|
||||
self.find_slottables(&mut slottables);
|
||||
|
||||
// Step 4. If slottables is the empty list, then append each slottable
|
||||
// child of slot, in tree order, to slottables.
|
||||
if slottables.is_empty() {
|
||||
for child in self.upcast::<Node>().children() {
|
||||
if let Some(element) = child.downcast::<Element>() {
|
||||
slottables.push(Slottable::Element(Dom::from_ref(element)));
|
||||
} else if let Some(text) = child.downcast::<Text>() {
|
||||
slottables.push(Slottable::Text(Dom::from_ref(text)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5. For each node in slottables:
|
||||
for slottable in slottables.iter() {
|
||||
// Step 5.1 If node is a slot whose root is a shadow root:
|
||||
// NOTE: Only elements can be slots
|
||||
let maybe_slot_element = match &slottable {
|
||||
Slottable::Element(element) => element.downcast::<HTMLSlotElement>(),
|
||||
Slottable::Text(_) => None,
|
||||
};
|
||||
match maybe_slot_element {
|
||||
Some(slot_element)
|
||||
if slot_element
|
||||
.upcast::<Node>()
|
||||
.containing_shadow_root()
|
||||
.is_some() =>
|
||||
{
|
||||
// Step 5.1.1 Let temporaryResult be the result of finding flattened slottables given node.
|
||||
rooted_vec!(let mut temporary_result);
|
||||
slot_element.find_flattened_slottables(&mut temporary_result);
|
||||
|
||||
// Step 5.1.2 Append each slottable in temporaryResult, in order, to result.
|
||||
result.extend_from_slice(&temporary_result);
|
||||
},
|
||||
// Step 5.2 Otherwise, append node to result.
|
||||
_ => {
|
||||
result.push(slottable.clone());
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Step 6. Return result.
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#find-slotables>
|
||||
///
|
||||
/// To avoid rooting shenanigans, this writes the returned slottables
|
||||
/// into the `result` argument
|
||||
fn find_slottables(&self, result: &mut RootedVec<Slottable>) {
|
||||
let cx = GlobalScope::get_cx();
|
||||
|
||||
// Step 1. Let result be an empty list.
|
||||
debug_assert!(result.is_empty());
|
||||
|
||||
// Step 2. Let root be slot’s root.
|
||||
// Step 3. If root is not a shadow root, then return result.
|
||||
let Some(root) = self.upcast::<Node>().containing_shadow_root() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Step 4. Let host be root’s host.
|
||||
let host = root.Host();
|
||||
|
||||
// Step 5. If root’s slot assignment is "manual":
|
||||
if root.SlotAssignment() == SlotAssignmentMode::Manual {
|
||||
// Step 5.1 Let result be « ».
|
||||
// NOTE: redundant.
|
||||
|
||||
// Step 5.2 For each slottable slottable of slot’s manually assigned nodes,
|
||||
// if slottable’s parent is host, append slottable to result.
|
||||
for slottable in self.manually_assigned_nodes.borrow().iter() {
|
||||
if slottable
|
||||
.node()
|
||||
.GetParentNode()
|
||||
.is_some_and(|node| &*node == host.upcast::<Node>())
|
||||
{
|
||||
result.push(slottable.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 6. Otherwise, for each slottable child slottable of host, in tree order:
|
||||
else {
|
||||
let mut for_slottable = |slottable: RootedGuard<Slottable>| {
|
||||
// Step 6.1 Let foundSlot be the result of finding a slot given slottable.
|
||||
let found_slot = slottable.find_a_slot(false);
|
||||
|
||||
// Step 6.2 If foundSlot is slot, then append slottable to result.
|
||||
if found_slot.is_some_and(|found_slot| &*found_slot == self) {
|
||||
result.push(slottable.clone());
|
||||
}
|
||||
};
|
||||
for child in host.upcast::<Node>().children() {
|
||||
if let Some(element) = child.downcast::<Element>() {
|
||||
rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(element)));
|
||||
for_slottable(slottable);
|
||||
continue;
|
||||
}
|
||||
if let Some(text) = child.downcast::<Text>() {
|
||||
rooted!(in(*cx) let slottable = Slottable::Text(Dom::from_ref(text)));
|
||||
for_slottable(slottable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7. Return result.
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#assign-slotables>
|
||||
pub(crate) fn assign_slottables(&self) {
|
||||
// Step 1. Let slottables be the result of finding slottables for slot.
|
||||
rooted_vec!(let mut slottables);
|
||||
self.find_slottables(&mut slottables);
|
||||
|
||||
// Step 2. TODO If slottables and slot’s assigned nodes are not identical,
|
||||
// then run signal a slot change for slot.
|
||||
|
||||
// Step 3. Set slot’s assigned nodes to slottables.
|
||||
*self.assigned_nodes.borrow_mut() = slottables.iter().cloned().collect();
|
||||
|
||||
// Step 4. For each slottable in slottables, set slottable’s assigned slot to slot.
|
||||
for slottable in slottables.iter() {
|
||||
slottable.set_assigned_slot(DomRoot::from_ref(self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Slottable {
|
||||
/// <https://dom.spec.whatwg.org/#find-a-slot>
|
||||
pub(crate) fn find_a_slot(&self, open_flag: bool) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
// Step 1. If slottable’s parent is null, then return null.
|
||||
let parent = self.node().GetParentNode()?;
|
||||
|
||||
// Step 2. Let shadow be slottable’s parent’s shadow root.
|
||||
// Step 3. If shadow is null, then return null.
|
||||
let shadow_root = parent
|
||||
.downcast::<Element>()
|
||||
.and_then(Element::shadow_root)?;
|
||||
|
||||
// Step 4. If the open flag is set and shadow’s mode is not "open", then return null.
|
||||
if open_flag && shadow_root.Mode() != ShadowRootMode::Open {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Step 5. If shadow’s slot assignment is "manual", then return the slot in shadow’s descendants whose
|
||||
// manually assigned nodes contains slottable, if any; otherwise null.
|
||||
if shadow_root.SlotAssignment() == SlotAssignmentMode::Manual {
|
||||
for node in shadow_root
|
||||
.upcast::<Node>()
|
||||
.traverse_preorder(ShadowIncluding::No)
|
||||
{
|
||||
if let Some(slot) = node.downcast::<HTMLSlotElement>() {
|
||||
if slot.manually_assigned_nodes.borrow().contains(self) {
|
||||
return Some(DomRoot::from_ref(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Step 6. Return the first slot in tree order in shadow’s descendants whose
|
||||
// name is slottable’s name, if any; otherwise null.
|
||||
for node in shadow_root
|
||||
.upcast::<Node>()
|
||||
.traverse_preorder(ShadowIncluding::No)
|
||||
{
|
||||
if let Some(slot) = node.downcast::<HTMLSlotElement>() {
|
||||
if slot.Name() == self.name() {
|
||||
return Some(DomRoot::from_ref(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Slottable name change steps from <https://dom.spec.whatwg.org/#light-tree-slotables>
|
||||
pub(crate) fn update_slot_name(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
|
||||
debug_assert!(matches!(self, Self::Element(_)));
|
||||
|
||||
// Step 1. If localName is slot and namespace is null:
|
||||
// NOTE: This is done by the caller
|
||||
let old_value = if let AttributeMutation::Set(old_name) = mutation {
|
||||
old_name.and_then(|attr| match attr {
|
||||
AttrValue::String(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let value = mutation.new_value(attr).and_then(|attr| match &*attr {
|
||||
AttrValue::String(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
// Step 1.1 If value is oldValue, then return.
|
||||
if value == old_value {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1.2 If value is null and oldValue is the empty string, then return.
|
||||
if value.is_none() && old_value.as_ref().is_some_and(|s| s.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1.3 If value is the empty string and oldValue is null, then return.
|
||||
if old_value.is_none() && value.as_ref().is_some_and(|s| s.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1.4 If value is null or the empty string, then set element’s name to the empty string.
|
||||
if value.as_ref().is_none_or(|s| s.is_empty()) {
|
||||
self.set_name(DOMString::new(), can_gc);
|
||||
}
|
||||
// Step 1.5 Otherwise, set element’s name to value.
|
||||
else {
|
||||
self.set_name(DOMString::from(value.unwrap_or_default()), can_gc);
|
||||
}
|
||||
|
||||
// Step 1.6 If element is assigned, then run assign slottables for element’s assigned slot.
|
||||
if let Some(assigned_slot) = self.assigned_slot() {
|
||||
assigned_slot.assign_slottables();
|
||||
}
|
||||
|
||||
// Step 1.7 Run assign a slot for element.
|
||||
self.assign_a_slot();
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#assign-a-slot>
|
||||
pub(crate) fn assign_a_slot(&self) {
|
||||
// Step 1. Let slot be the result of finding a slot with slottable.
|
||||
let slot = self.find_a_slot(false);
|
||||
|
||||
// Step 2. If slot is non-null, then run assign slottables for slot.
|
||||
if let Some(slot) = slot {
|
||||
slot.assign_slottables();
|
||||
}
|
||||
}
|
||||
|
||||
fn node(&self) -> &Node {
|
||||
match self {
|
||||
Self::Element(element) => element.upcast(),
|
||||
Self::Text(text) => text.upcast(),
|
||||
}
|
||||
}
|
||||
|
||||
fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
match self {
|
||||
Self::Element(element) => element.assigned_slot(),
|
||||
Self::Text(text) => {
|
||||
let assigned_slot = text
|
||||
.slottable_data()
|
||||
.borrow()
|
||||
.assigned_slot
|
||||
.as_ref()?
|
||||
.as_rooted();
|
||||
Some(assigned_slot)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_assigned_slot(&self, assigned_slot: DomRoot<HTMLSlotElement>) {
|
||||
match self {
|
||||
Self::Element(element) => element.set_assigned_slot(assigned_slot),
|
||||
Self::Text(text) => {
|
||||
text.slottable_data().borrow_mut().assigned_slot = Some(assigned_slot.as_traced())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_manual_slot_assignment(
|
||||
&self,
|
||||
manually_assigned_slot: Option<&HTMLSlotElement>,
|
||||
) {
|
||||
match self {
|
||||
Self::Element(element) => element.set_manual_slot_assignment(manually_assigned_slot),
|
||||
Self::Text(text) => {
|
||||
text.slottable_data().borrow_mut().manual_slot_assignment =
|
||||
manually_assigned_slot.map(Dom::from_ref)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn manual_slot_assignment(&self) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
match self {
|
||||
Self::Element(element) => element.manual_slot_assignment(),
|
||||
Self::Text(text) => text
|
||||
.slottable_data()
|
||||
.borrow()
|
||||
.manual_slot_assignment
|
||||
.as_ref()
|
||||
.map(Dom::as_rooted),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_name(&self, name: DOMString, can_gc: CanGc) {
|
||||
// NOTE: Only elements have non-empty names
|
||||
let Self::Element(element) = self else {
|
||||
return;
|
||||
};
|
||||
let element = element.as_rooted();
|
||||
element.set_attribute(
|
||||
&local_name!("name"),
|
||||
AttrValue::Atom(Atom::from(name)),
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
|
||||
fn name(&self) -> DOMString {
|
||||
// NOTE: Only elements have non-empty names
|
||||
let Self::Element(element) = self else {
|
||||
return DOMString::new();
|
||||
};
|
||||
|
||||
element
|
||||
.name_attribute()
|
||||
.map(|a| DOMString::from(a.as_ref()))
|
||||
.unwrap_or_default()
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualMethods for HTMLSlotElement {
|
||||
fn super_type(&self) -> Option<&dyn VirtualMethods> {
|
||||
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#shadow-tree-slots>
|
||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||
|
||||
if attr.local_name() == &local_name!("name") && attr.namespace() == &ns!() {
|
||||
self.upcast::<Node>()
|
||||
.GetRootNode(&GetRootNodeOptions::empty())
|
||||
.assign_slottables_for_a_tree()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl js::gc::Rootable for Slottable {}
|
||||
|
||||
impl js::gc::Initialize for Slottable {
|
||||
#[allow(unsafe_code)]
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
unsafe fn initial() -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -398,6 +398,7 @@ pub(crate) mod htmlquoteelement;
|
|||
#[allow(dead_code)]
|
||||
pub(crate) mod htmlscriptelement;
|
||||
pub(crate) mod htmlselectelement;
|
||||
pub(crate) mod htmlslotelement;
|
||||
pub(crate) mod htmlsourceelement;
|
||||
pub(crate) mod htmlspanelement;
|
||||
pub(crate) mod htmlstyleelement;
|
||||
|
|
|
@ -91,6 +91,7 @@ use crate::dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMe
|
|||
use crate::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
|
||||
use crate::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
|
||||
use crate::dom::htmllinkelement::HTMLLinkElement;
|
||||
use crate::dom::htmlslotelement::HTMLSlotElement;
|
||||
use crate::dom::htmlstyleelement::HTMLStyleElement;
|
||||
use crate::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
|
||||
use crate::dom::htmlvideoelement::{HTMLVideoElement, LayoutHTMLVideoElementHelpers};
|
||||
|
@ -1316,6 +1317,24 @@ impl Node {
|
|||
.as_ref()
|
||||
.map(|data| data.element_data.borrow().styles.primary().clone())
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#assign-slotables-for-a-tree>
|
||||
pub(crate) fn assign_slottables_for_a_tree(&self) {
|
||||
// NOTE: This method traverses all descendants of the node and is potentially very
|
||||
// expensive. If the node is not a shadow root then assigning slottables to it won't
|
||||
// have any effect, so we take a fast path out.
|
||||
if !self.is::<ShadowRoot>() {
|
||||
return;
|
||||
}
|
||||
|
||||
// > To assign slottables for a tree, given a node root, run assign slottables for each slot
|
||||
// > slot in root’s inclusive descendants, in tree order.
|
||||
for node in self.traverse_preorder(ShadowIncluding::No) {
|
||||
if let Some(slot) = node.downcast::<HTMLSlotElement>() {
|
||||
slot.assign_slottables();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
|
||||
|
@ -2113,6 +2132,11 @@ impl Node {
|
|||
for kid in new_nodes {
|
||||
// Step 7.1.
|
||||
parent.add_child(kid, child);
|
||||
|
||||
// Step 7.6 Run assign slottables for a tree with node’s root.
|
||||
kid.GetRootNode(&GetRootNodeOptions::empty())
|
||||
.assign_slottables_for_a_tree();
|
||||
|
||||
// Step 7.7.
|
||||
for descendant in kid
|
||||
.traverse_preorder(ShadowIncluding::Yes)
|
||||
|
@ -2464,7 +2488,12 @@ impl Node {
|
|||
// node’s shadow root’s serializable, node’s shadow root’s delegates focus,
|
||||
// and node’s shadow root’s slot assignment.
|
||||
let copy_shadow_root =
|
||||
copy_elem.attach_shadow(IsUserAgentWidget::No, shadow_root.Mode(), true)
|
||||
copy_elem.attach_shadow(
|
||||
IsUserAgentWidget::No,
|
||||
shadow_root.Mode(),
|
||||
true,
|
||||
shadow_root.SlotAssignment()
|
||||
)
|
||||
.expect("placement of attached shadow root must be valid, as this is a copy of an existing one");
|
||||
|
||||
// TODO: Step 7.3 Set copy’s shadow root’s declarative to node’s shadow root’s declarative.
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::dom::customelementregistry::{
|
|||
CustomElementDefinition, CustomElementReaction, CustomElementState,
|
||||
};
|
||||
use crate::dom::elementinternals::ElementInternals;
|
||||
use crate::dom::htmlslotelement::SlottableData;
|
||||
use crate::dom::mutationobserver::RegisteredObserver;
|
||||
use crate::dom::node::UniqueId;
|
||||
use crate::dom::shadowroot::ShadowRoot;
|
||||
|
@ -57,4 +58,6 @@ pub(crate) struct ElementRareData {
|
|||
pub(crate) client_rect: Option<LayoutValue<Rect<i32>>>,
|
||||
/// <https://html.spec.whatwg.org/multipage#elementinternals>
|
||||
pub(crate) element_internals: Option<Dom<ElementInternals>>,
|
||||
|
||||
pub(crate) slottable_data: SlottableData,
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ use style::stylesheets::Stylesheet;
|
|||
use style::stylist::{CascadeData, Stylist};
|
||||
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMode;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::reflector::reflect_dom_object;
|
||||
|
@ -57,6 +59,9 @@ pub(crate) struct ShadowRoot {
|
|||
/// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
|
||||
mode: ShadowRootMode,
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
|
||||
slot_assignment_mode: SlotAssignmentMode,
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
|
||||
clonable: bool,
|
||||
}
|
||||
|
@ -67,6 +72,7 @@ impl ShadowRoot {
|
|||
host: &Element,
|
||||
document: &Document,
|
||||
mode: ShadowRootMode,
|
||||
slot_assignment_mode: SlotAssignmentMode,
|
||||
clonable: bool,
|
||||
) -> ShadowRoot {
|
||||
let document_fragment = DocumentFragment::new_inherited(document);
|
||||
|
@ -86,6 +92,7 @@ impl ShadowRoot {
|
|||
stylesheet_list: MutNullableDom::new(None),
|
||||
window: Dom::from_ref(document.window()),
|
||||
mode,
|
||||
slot_assignment_mode,
|
||||
clonable,
|
||||
}
|
||||
}
|
||||
|
@ -94,10 +101,17 @@ impl ShadowRoot {
|
|||
host: &Element,
|
||||
document: &Document,
|
||||
mode: ShadowRootMode,
|
||||
slot_assignment_mode: SlotAssignmentMode,
|
||||
clonable: bool,
|
||||
) -> DomRoot<ShadowRoot> {
|
||||
reflect_dom_object(
|
||||
Box::new(ShadowRoot::new_inherited(host, document, mode, clonable)),
|
||||
Box::new(ShadowRoot::new_inherited(
|
||||
host,
|
||||
document,
|
||||
mode,
|
||||
slot_assignment_mode,
|
||||
clonable,
|
||||
)),
|
||||
document.window(),
|
||||
CanGc::note(),
|
||||
)
|
||||
|
@ -306,6 +320,11 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
|
|||
// Step 4. Replace all with fragment within this.
|
||||
Node::replace_all(Some(frag.upcast()), self.upcast());
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
|
||||
fn SlotAssignment(&self) -> SlotAssignmentMode {
|
||||
self.slot_assignment_mode
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualMethods for ShadowRoot {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* 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::RefCell;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use js::rust::HandleObject;
|
||||
|
||||
|
@ -12,10 +14,12 @@ use crate::dom::bindings::codegen::Bindings::TextBinding::TextMethods;
|
|||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::error::{Error, Fallible};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::characterdata::CharacterData;
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::htmlslotelement::{HTMLSlotElement, Slottable, SlottableData};
|
||||
use crate::dom::node::Node;
|
||||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
@ -24,12 +28,14 @@ use crate::script_runtime::CanGc;
|
|||
#[dom_struct]
|
||||
pub(crate) struct Text {
|
||||
characterdata: CharacterData,
|
||||
slottable_data: RefCell<SlottableData>,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
pub(crate) fn new_inherited(text: DOMString, document: &Document) -> Text {
|
||||
Text {
|
||||
characterdata: CharacterData::new_inherited(text, document),
|
||||
slottable_data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +56,10 @@ impl Text {
|
|||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn slottable_data(&self) -> &RefCell<SlottableData> {
|
||||
&self.slottable_data
|
||||
}
|
||||
}
|
||||
|
||||
impl TextMethods<crate::DomTypeHolder> for Text {
|
||||
|
@ -119,4 +129,14 @@ impl TextMethods<crate::DomTypeHolder> for Text {
|
|||
}
|
||||
DOMString::from(text)
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-slotable-assignedslot>
|
||||
fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
|
||||
let cx = GlobalScope::get_cx();
|
||||
|
||||
// > The assignedSlot getter steps are to return the result of
|
||||
// > find a slot given this and with the open flag set.
|
||||
rooted!(in(*cx) let slottable = Slottable::Text(Dom::from_ref(self)));
|
||||
slottable.find_a_slot(true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ use crate::dom::htmloutputelement::HTMLOutputElement;
|
|||
use crate::dom::htmlpreelement::HTMLPreElement;
|
||||
use crate::dom::htmlscriptelement::HTMLScriptElement;
|
||||
use crate::dom::htmlselectelement::HTMLSelectElement;
|
||||
use crate::dom::htmlslotelement::HTMLSlotElement;
|
||||
use crate::dom::htmlsourceelement::HTMLSourceElement;
|
||||
use crate::dom::htmlstyleelement::HTMLStyleElement;
|
||||
use crate::dom::htmltablecellelement::HTMLTableCellElement;
|
||||
|
@ -257,6 +258,9 @@ pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods {
|
|||
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSourceElement)) => {
|
||||
node.downcast::<HTMLSourceElement>().unwrap() as &dyn VirtualMethods
|
||||
},
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSlotElement)) => {
|
||||
node.downcast::<HTMLSlotElement>().unwrap() as &dyn VirtualMethods
|
||||
},
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLStyleElement)) => {
|
||||
node.downcast::<HTMLStyleElement>().unwrap() as &dyn VirtualMethods
|
||||
},
|
||||
|
|
|
@ -90,7 +90,7 @@ interface Element : Node {
|
|||
dictionary ShadowRootInit {
|
||||
required ShadowRootMode mode;
|
||||
// boolean delegatesFocus = false;
|
||||
// SlotAssignmentMode slotAssignment = "named";
|
||||
SlotAssignmentMode slotAssignment = "named";
|
||||
boolean clonable = false;
|
||||
// boolean serializable = false;
|
||||
};
|
||||
|
|
25
components/script/dom/webidls/HTMLSlotElement.webidl
Normal file
25
components/script/dom/webidls/HTMLSlotElement.webidl
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* 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/#the-slot-element
|
||||
[Exposed=Window]
|
||||
interface HTMLSlotElement : HTMLElement {
|
||||
[HTMLConstructor] constructor();
|
||||
|
||||
[CEReactions] attribute DOMString name;
|
||||
sequence<Node> assignedNodes(optional AssignedNodesOptions options = {});
|
||||
sequence<Element> assignedElements(optional AssignedNodesOptions options = {});
|
||||
undefined assign((Element or Text)... nodes);
|
||||
};
|
||||
|
||||
dictionary AssignedNodesOptions {
|
||||
boolean flatten = false;
|
||||
};
|
||||
|
||||
// https://dom.spec.whatwg.org/#mixin-slotable
|
||||
interface mixin Slottable {
|
||||
readonly attribute HTMLSlotElement? assignedSlot;
|
||||
};
|
||||
Element includes Slottable;
|
||||
Text includes Slottable;
|
|
@ -10,7 +10,7 @@
|
|||
interface ShadowRoot : DocumentFragment {
|
||||
readonly attribute ShadowRootMode mode;
|
||||
// readonly attribute boolean delegatesFocus;
|
||||
// readonly attribute SlotAssignmentMode slotAssignment;
|
||||
readonly attribute SlotAssignmentMode slotAssignment;
|
||||
readonly attribute boolean clonable;
|
||||
// readonly attribute boolean serializable;
|
||||
readonly attribute Element host;
|
||||
|
@ -19,7 +19,7 @@ interface ShadowRoot : DocumentFragment {
|
|||
|
||||
|
||||
enum ShadowRootMode { "open", "closed"};
|
||||
// enum SlotAssignmentMode { "manual", "named" };
|
||||
enum SlotAssignmentMode { "manual", "named" };
|
||||
|
||||
ShadowRoot includes DocumentOrShadowRoot;
|
||||
|
||||
|
|
|
@ -35,5 +35,5 @@ sizeof_checker!(size_element, Element, 376);
|
|||
sizeof_checker!(size_htmlelement, HTMLElement, 392);
|
||||
sizeof_checker!(size_div, HTMLDivElement, 392);
|
||||
sizeof_checker!(size_span, HTMLSpanElement, 392);
|
||||
sizeof_checker!(size_text, Text, 232);
|
||||
sizeof_checker!(size_text, Text, 256);
|
||||
sizeof_checker!(size_characterdata, CharacterData, 232);
|
||||
|
|
|
@ -328,3 +328,6 @@
|
|||
|
||||
[dialog: Operator 'new' should instantiate a customized built-in element]
|
||||
expected: FAIL
|
||||
|
||||
[slot: Define a customized built-in element]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
[HTMLSlotElement.html]
|
||||
expected: ERROR
|
||||
[name on HTMLSlotElement must enqueue an attributeChanged reaction when adding name content attribute]
|
||||
expected: FAIL
|
||||
|
||||
[name on HTMLSlotElement must enqueue an attributeChanged reaction when replacing an existing attribute]
|
||||
expected: FAIL
|
||||
|
|
15
tests/wpt/meta/dom/idlharness.window.js.ini
vendored
15
tests/wpt/meta/dom/idlharness.window.js.ini
vendored
|
@ -8,18 +8,9 @@
|
|||
[AbortSignal must be primary interface of new AbortController().signal]
|
||||
expected: FAIL
|
||||
|
||||
[Element interface: element must inherit property "assignedSlot" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Text interface: document.createTextNode("abc") must inherit property "assignedSlot" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Event interface: attribute composed]
|
||||
expected: FAIL
|
||||
|
||||
[Text interface: attribute assignedSlot]
|
||||
expected: FAIL
|
||||
|
||||
[AbortSignal interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -65,9 +56,6 @@
|
|||
[Element interface: operation after((Node or DOMString)...)]
|
||||
expected: FAIL
|
||||
|
||||
[Element interface: attribute assignedSlot]
|
||||
expected: FAIL
|
||||
|
||||
[CharacterData interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -197,9 +185,6 @@
|
|||
[ShadowRoot interface: attribute delegatesFocus]
|
||||
expected: FAIL
|
||||
|
||||
[ShadowRoot interface: attribute slotAssignment]
|
||||
expected: FAIL
|
||||
|
||||
[XSLTProcessor interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -13,6 +13,3 @@
|
|||
|
||||
[param element's name property reflects its content attribute]
|
||||
expected: FAIL
|
||||
|
||||
[slot element's name property reflects its content attribute]
|
||||
expected: FAIL
|
||||
|
|
|
@ -7858,63 +7858,6 @@
|
|||
[HTMLTemplateElement interface: document.createElement("template") must inherit property "shadowRootSerializable" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: attribute name]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: operation assignedNodes(optional AssignedNodesOptions)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: operation assignedElements(optional AssignedNodesOptions)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: operation assign((Element or Text)...)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement must be primary interface of document.createElement("slot")]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of document.createElement("slot")]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: document.createElement("slot") must inherit property "name" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: document.createElement("slot") must inherit property "assignedNodes(optional AssignedNodesOptions)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: calling assignedNodes(optional AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: document.createElement("slot") must inherit property "assignedElements(optional AssignedNodesOptions)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: calling assignedElements(optional AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: document.createElement("slot") must inherit property "assign((Element or Text)...)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLSlotElement interface: calling assign((Element or Text)...) on document.createElement("slot") with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLMarqueeElement interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
|
|
114
tests/wpt/meta/html/dom/reflection-misc.html.ini
vendored
114
tests/wpt/meta/html/dom/reflection-misc.html.ini
vendored
|
@ -1073,120 +1073,6 @@
|
|||
[slot.tabIndex: IDL set to -2147483648]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: typeof IDL attribute]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL get with DOM attribute unset]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to ""]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to 7]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to 1.5]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to "5%"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to "+100"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to ".5"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to true]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to false]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to object "[object Object\]"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to "\\0"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to null]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to object "test-toString"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: setAttribute() to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to ""]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to 7]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to 1.5]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to "5%"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to "+100"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to ".5"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to true]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to false]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to object "[object Object\]"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to "\\0"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to null]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to object "test-toString"]
|
||||
expected: FAIL
|
||||
|
||||
[slot.name: IDL set to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[ins.accessKey: typeof IDL attribute]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -53,15 +53,6 @@
|
|||
[Interfaces for RTC: createElement]
|
||||
expected: FAIL
|
||||
|
||||
[Interfaces for slot: useNS]
|
||||
expected: FAIL
|
||||
|
||||
[Interfaces for slot: useParser]
|
||||
expected: FAIL
|
||||
|
||||
[Interfaces for SLOT: createElement]
|
||||
expected: FAIL
|
||||
|
||||
[Interfaces for permission: useNS]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
[HTMLSlotElement-interface.html]
|
||||
[HTMLSlotElement must be defined on window]
|
||||
expected: FAIL
|
||||
|
||||
["name" attribute on HTMLSlotElement must reflect "name" attribute]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes() on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes({"flattened":false}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes({"flattened":true}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes() must return the list of assigned nodes when none of the assigned nodes themselves are slots]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -32,15 +17,6 @@
|
|||
[assignedNodes({"flattened":true}) must update when slot and name attributes are modified]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes() must update when a default slot is introduced dynamically by a slot rename]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes({"flattened":false}) must update when a default slot is introduced dynamically by a slot rename]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes({"flattened":true}) must update when a default slot is introduced dynamically by a slot rename]
|
||||
expected: FAIL
|
||||
|
||||
[assignedNodes() must update when slot elements are inserted or removed]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
[Slottable-mixin.html]
|
||||
[assignedSlot attribute must be defined on Element and Text interfaces]
|
||||
expected: FAIL
|
||||
|
||||
[assignedSlot must return null when the node does not have an assigned node]
|
||||
expected: FAIL
|
||||
|
||||
[assignedSlot must return the assigned slot]
|
||||
expected: FAIL
|
||||
|
||||
[assignedSlot must return null when the assigned slot element is inside a closed shadow tree]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,33 +1,31 @@
|
|||
[imperative-slot-api-slotchange.html]
|
||||
expected: TIMEOUT
|
||||
[slotchange event must not fire synchronously.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event should not fire when assignments do not change assignedNodes.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[slotchange event should not fire when same node is assigned.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Fire slotchange event when slot's assigned nodes changes.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Fire slotchange event on previous slot and new slot when node is reassigned.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Fire slotchange event on node assignment and when assigned node is removed.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Fire slotchange event when order of assigned nodes changes.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Fire slotchange event when assigned node is removed.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Fire slotchange event when removing a slot from Shadows Root that changes its assigned nodes.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[Fire slotchange event when assign node to nested slot, ensure event bubbles ups.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Signal a slot change should be done in tree order.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
[imperative-slot-api.html]
|
||||
[attachShadow can take slotAssignment parameter.]
|
||||
expected: FAIL
|
||||
|
||||
[slot.attach() should take variadic not sequence.]
|
||||
expected: FAIL
|
||||
|
||||
[Imperative slot API can assign nodes in manual slot assignment.]
|
||||
expected: FAIL
|
||||
|
||||
[Order of slottables is preserved in manual slot assignment.]
|
||||
expected: FAIL
|
||||
|
||||
[Previously assigned slottable is moved to new slot when it's reassigned.]
|
||||
expected: FAIL
|
||||
|
||||
[Order and assignment of nodes are preserved during multiple assignment in a row.]
|
||||
expected: FAIL
|
||||
|
||||
[Assigning invalid nodes should be allowed.]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -29,15 +11,6 @@
|
|||
[Appending slottable to different host, it loses slot assignment. It can be re-assigned within a new host.]
|
||||
expected: FAIL
|
||||
|
||||
[Previously assigned node should not be assigned if slot moved to a new shadow root. The node is re-assigned when moved back.]
|
||||
expected: FAIL
|
||||
|
||||
[Assignment with the same node in parameters should be ignored, first one wins.]
|
||||
expected: FAIL
|
||||
|
||||
[Removing a slot from DOM resets its slottable's slot assignment.]
|
||||
expected: FAIL
|
||||
|
||||
[Nodes can be assigned even if slots or nodes aren't in the same tree.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[imperative-slot-initial-fallback.html]
|
||||
[Unassigned imperative slot can render text node as the initial fallback]
|
||||
expected: FAIL
|
||||
|
||||
[Unassigned imperative slot can render element as the initial fallback]
|
||||
expected: FAIL
|
|
@ -1,22 +1,4 @@
|
|||
[slots-fallback.html]
|
||||
[Slots fallback: Basic.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots fallback: Basic, elements only.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots fallback: Slots in Slots.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots fallback: Slots in Slots, elements only.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots fallback: Fallback contents should not be used if a node is assigned.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots fallback: Slots in Slots: Assigned nodes should be used as fallback contents of another slot]
|
||||
expected: FAIL
|
||||
|
||||
[Slots fallback: Complex case.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[slots-outside-shadow-dom.html]
|
||||
[Light DOM slot element should be in flattened assignedNodes]
|
||||
expected: FAIL
|
42
tests/wpt/meta/shadow-dom/slots.html.ini
vendored
42
tests/wpt/meta/shadow-dom/slots.html.ini
vendored
|
@ -1,37 +1,4 @@
|
|||
[slots.html]
|
||||
[Slots: Basic.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Basic, elements only.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Slots in closed.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Slots in closed, elements only.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Slots not in a shadow tree.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Slots not in a shadow tree, elements only.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Distributed nodes for Slots not in a shadow tree.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Name matching]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: No direct host child.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Default Slot.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Slot in Slot does not matter in assignment.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Slot is assigned to another slot]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -50,21 +17,12 @@
|
|||
[Slots: Mutation: appendChild.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Mutation: Change slot= attribute 1.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Mutation: Change slot= attribute 2.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Mutation: Change slot= attribute 3.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Mutation: Remove a child.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Mutation: Add a slot: after.]
|
||||
expected: FAIL
|
||||
|
||||
[Slots: Mutation: Add a slot: before.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
2
tests/wpt/mozilla/meta/MANIFEST.json
vendored
2
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -13499,7 +13499,7 @@
|
|||
]
|
||||
],
|
||||
"interfaces.https.html": [
|
||||
"71d2291cb162143e4abf298f0a23e6a06fe5c1bc",
|
||||
"501caf7410bba6533e11c0d83acb2729da48a289",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
|
|
@ -160,6 +160,7 @@ test_interfaces([
|
|||
"HTMLQuoteElement",
|
||||
"HTMLScriptElement",
|
||||
"HTMLSelectElement",
|
||||
"HTMLSlotElement",
|
||||
"HTMLSourceElement",
|
||||
"HTMLSpanElement",
|
||||
"HTMLStyleElement",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue