mirror of
https://github.com/servo/servo.git
synced 2025-09-30 00:29:14 +01:00
script: Move HTML DOM interfaces to script/dom/html/
(#39046)
See #38901. Testing: Refactor Fixes: Partially #38901 Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
parent
ec1b9b2480
commit
c92cd9e624
142 changed files with 546 additions and 533 deletions
257
components/script/dom/html/htmldetailselement.rs
Normal file
257
components/script/dom/html/htmldetailselement.rs
Normal file
|
@ -0,0 +1,257 @@
|
|||
/* 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, Ref};
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use html5ever::{LocalName, Prefix, local_name};
|
||||
use js::rust::HandleObject;
|
||||
|
||||
use crate::dom::attr::Attr;
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLDetailsElementBinding::HTMLDetailsElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::UnionTypes::ElementOrText;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::html::htmlelement::HTMLElement;
|
||||
use crate::dom::html::htmlslotelement::HTMLSlotElement;
|
||||
use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeDamage, NodeTraits};
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
/// The summary that should be presented if no `<summary>` element is present
|
||||
const DEFAULT_SUMMARY: &str = "Details";
|
||||
|
||||
/// Holds handles to all slots in the UA shadow tree
|
||||
///
|
||||
/// The composition of the tree is described in
|
||||
/// <https://html.spec.whatwg.org/multipage/#the-details-and-summary-elements>
|
||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||
struct ShadowTree {
|
||||
summary: Dom<HTMLSlotElement>,
|
||||
descendants: Dom<HTMLSlotElement>,
|
||||
/// The summary that is displayed if no other summary exists
|
||||
implicit_summary: Dom<HTMLElement>,
|
||||
}
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct HTMLDetailsElement {
|
||||
htmlelement: HTMLElement,
|
||||
toggle_counter: Cell<u32>,
|
||||
|
||||
/// Represents the UA widget for the details element
|
||||
shadow_tree: DomRefCell<Option<ShadowTree>>,
|
||||
}
|
||||
|
||||
impl HTMLDetailsElement {
|
||||
fn new_inherited(
|
||||
local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document,
|
||||
) -> HTMLDetailsElement {
|
||||
HTMLDetailsElement {
|
||||
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
|
||||
toggle_counter: Cell::new(0),
|
||||
shadow_tree: 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<HTMLDetailsElement> {
|
||||
Node::reflect_node_with_proto(
|
||||
Box::new(HTMLDetailsElement::new_inherited(
|
||||
local_name, prefix, document,
|
||||
)),
|
||||
document,
|
||||
proto,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn toggle(&self) {
|
||||
self.SetOpen(!self.Open());
|
||||
}
|
||||
|
||||
fn shadow_tree(&self, can_gc: CanGc) -> Ref<'_, ShadowTree> {
|
||||
if !self.upcast::<Element>().is_shadow_host() {
|
||||
self.create_shadow_tree(can_gc);
|
||||
}
|
||||
|
||||
Ref::filter_map(self.shadow_tree.borrow(), Option::as_ref)
|
||||
.ok()
|
||||
.expect("UA shadow tree was not created")
|
||||
}
|
||||
|
||||
fn create_shadow_tree(&self, can_gc: CanGc) {
|
||||
let document = self.owner_document();
|
||||
// TODO(stevennovaryo): Reimplement details styling so that it would not
|
||||
// mess the cascading and require some reparsing.
|
||||
let root = self
|
||||
.upcast::<Element>()
|
||||
.attach_ua_shadow_root(false, can_gc);
|
||||
|
||||
let summary = HTMLSlotElement::new(local_name!("slot"), None, &document, None, can_gc);
|
||||
root.upcast::<Node>()
|
||||
.AppendChild(summary.upcast::<Node>(), can_gc)
|
||||
.unwrap();
|
||||
|
||||
let fallback_summary =
|
||||
HTMLElement::new(local_name!("summary"), None, &document, None, can_gc);
|
||||
fallback_summary
|
||||
.upcast::<Node>()
|
||||
.set_text_content_for_element(Some(DEFAULT_SUMMARY.into()), can_gc);
|
||||
summary
|
||||
.upcast::<Node>()
|
||||
.AppendChild(fallback_summary.upcast::<Node>(), can_gc)
|
||||
.unwrap();
|
||||
|
||||
let descendants = HTMLSlotElement::new(local_name!("slot"), None, &document, None, can_gc);
|
||||
root.upcast::<Node>()
|
||||
.AppendChild(descendants.upcast::<Node>(), can_gc)
|
||||
.unwrap();
|
||||
|
||||
let _ = self.shadow_tree.borrow_mut().insert(ShadowTree {
|
||||
summary: summary.as_traced(),
|
||||
descendants: descendants.as_traced(),
|
||||
implicit_summary: fallback_summary.as_traced(),
|
||||
});
|
||||
self.upcast::<Node>()
|
||||
.dirty(crate::dom::node::NodeDamage::Other);
|
||||
}
|
||||
|
||||
pub(crate) fn find_corresponding_summary_element(&self) -> Option<DomRoot<HTMLElement>> {
|
||||
self.upcast::<Node>()
|
||||
.children()
|
||||
.filter_map(DomRoot::downcast::<HTMLElement>)
|
||||
.find(|html_element| {
|
||||
html_element.upcast::<Element>().local_name() == &local_name!("summary")
|
||||
})
|
||||
}
|
||||
|
||||
fn update_shadow_tree_contents(&self, can_gc: CanGc) {
|
||||
let shadow_tree = self.shadow_tree(can_gc);
|
||||
|
||||
if let Some(summary) = self.find_corresponding_summary_element() {
|
||||
shadow_tree
|
||||
.summary
|
||||
.Assign(vec![ElementOrText::Element(DomRoot::upcast(summary))]);
|
||||
}
|
||||
|
||||
let mut slottable_children = vec![];
|
||||
for child in self.upcast::<Node>().children() {
|
||||
if let Some(element) = child.downcast::<Element>() {
|
||||
if element.local_name() == &local_name!("summary") {
|
||||
continue;
|
||||
}
|
||||
|
||||
slottable_children.push(ElementOrText::Element(DomRoot::from_ref(element)));
|
||||
}
|
||||
|
||||
if let Some(text) = child.downcast::<Text>() {
|
||||
slottable_children.push(ElementOrText::Text(DomRoot::from_ref(text)));
|
||||
}
|
||||
}
|
||||
shadow_tree.descendants.Assign(slottable_children);
|
||||
}
|
||||
|
||||
fn update_shadow_tree_styles(&self, can_gc: CanGc) {
|
||||
let shadow_tree = self.shadow_tree(can_gc);
|
||||
|
||||
let value = if self.Open() {
|
||||
"display: block;"
|
||||
} else {
|
||||
// TODO: This should be "display: block; content-visibility: hidden;",
|
||||
// but servo does not support content-visibility yet
|
||||
"display: none;"
|
||||
};
|
||||
shadow_tree
|
||||
.descendants
|
||||
.upcast::<Element>()
|
||||
.set_string_attribute(&local_name!("style"), value.into(), can_gc);
|
||||
|
||||
// Manually update the list item style of the implicit summary element.
|
||||
// Unlike the other summaries, this summary is in the shadow tree and
|
||||
// can't be styled with UA sheets
|
||||
let implicit_summary_list_item_style = if self.Open() {
|
||||
"disclosure-open"
|
||||
} else {
|
||||
"disclosure-closed"
|
||||
};
|
||||
let implicit_summary_style = format!(
|
||||
"display: list-item;
|
||||
counter-increment: list-item 0;
|
||||
list-style: {implicit_summary_list_item_style} inside;"
|
||||
);
|
||||
shadow_tree
|
||||
.implicit_summary
|
||||
.upcast::<Element>()
|
||||
.set_string_attribute(&local_name!("style"), implicit_summary_style.into(), can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
impl HTMLDetailsElementMethods<crate::DomTypeHolder> for HTMLDetailsElement {
|
||||
// https://html.spec.whatwg.org/multipage/#dom-details-open
|
||||
make_bool_getter!(Open, "open");
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-details-open
|
||||
make_bool_setter!(SetOpen, "open");
|
||||
}
|
||||
|
||||
impl VirtualMethods for HTMLDetailsElement {
|
||||
fn super_type(&self) -> Option<&dyn VirtualMethods> {
|
||||
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
|
||||
}
|
||||
|
||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
|
||||
self.super_type()
|
||||
.unwrap()
|
||||
.attribute_mutated(attr, mutation, can_gc);
|
||||
|
||||
if attr.local_name() == &local_name!("open") {
|
||||
self.update_shadow_tree_styles(can_gc);
|
||||
|
||||
let counter = self.toggle_counter.get() + 1;
|
||||
self.toggle_counter.set(counter);
|
||||
|
||||
let this = Trusted::new(self);
|
||||
self.owner_global()
|
||||
.task_manager()
|
||||
.dom_manipulation_task_source()
|
||||
.queue(task!(details_notification_task_steps: move || {
|
||||
let this = this.root();
|
||||
if counter == this.toggle_counter.get() {
|
||||
this.upcast::<EventTarget>().fire_event(atom!("toggle"), CanGc::note());
|
||||
}
|
||||
}));
|
||||
self.upcast::<Node>().dirty(NodeDamage::Other);
|
||||
}
|
||||
}
|
||||
|
||||
fn children_changed(&self, mutation: &ChildrenMutation) {
|
||||
self.super_type().unwrap().children_changed(mutation);
|
||||
|
||||
self.update_shadow_tree_contents(CanGc::note());
|
||||
}
|
||||
|
||||
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
|
||||
self.super_type().unwrap().bind_to_tree(context, can_gc);
|
||||
|
||||
self.update_shadow_tree_contents(CanGc::note());
|
||||
self.update_shadow_tree_styles(CanGc::note());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue