| /* 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 std::collections::HashMap; |
| use std::collections::hash_map::Entry; |
| |
| use dom_struct::dom_struct; |
| use html5ever::serialize::TraversalScope; |
| use js::rust::{HandleValue, MutableHandleValue}; |
| use script_bindings::error::{ErrorResult, Fallible}; |
| use script_bindings::script_runtime::JSContext; |
| use servo_arc::Arc; |
| use style::author_styles::AuthorStyles; |
| use style::dom::TElement; |
| use style::invalidation::element::restyle_hints::RestyleHint; |
| use style::shared_lock::SharedRwLockReadGuard; |
| use style::stylesheets::Stylesheet; |
| use style::stylist::{CascadeData, Stylist}; |
| use stylo_atoms::Atom; |
| |
| use crate::conversions::Convert; |
| use crate::dom::bindings::cell::DomRefCell; |
| use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions; |
| use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods; |
| 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::{ |
| TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString, |
| }; |
| use crate::dom::bindings::frozenarray::CachedFrozenArray; |
| use crate::dom::bindings::inheritance::Castable; |
| use crate::dom::bindings::num::Finite; |
| use crate::dom::bindings::reflector::reflect_dom_object; |
| use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom}; |
| use crate::dom::bindings::str::DOMString; |
| use crate::dom::cssstylesheet::CSSStyleSheet; |
| use crate::dom::document::Document; |
| use crate::dom::documentfragment::DocumentFragment; |
| use crate::dom::documentorshadowroot::{ |
| DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource, |
| }; |
| use crate::dom::element::Element; |
| use crate::dom::html::htmlslotelement::HTMLSlotElement; |
| use crate::dom::node::{ |
| BindContext, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, UnbindContext, |
| VecPreOrderInsertionHelper, |
| }; |
| use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner}; |
| use crate::dom::trustedhtml::TrustedHTML; |
| use crate::dom::types::EventTarget; |
| use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; |
| use crate::dom::window::Window; |
| use crate::script_runtime::CanGc; |
| use crate::stylesheet_set::StylesheetSetRef; |
| |
| /// Whether a shadow root hosts an User Agent widget. |
| #[derive(JSTraceable, MallocSizeOf, PartialEq)] |
| pub(crate) enum IsUserAgentWidget { |
| No, |
| Yes, |
| } |
| |
| /// <https://dom.spec.whatwg.org/#interface-shadowroot> |
| #[dom_struct] |
| pub(crate) struct ShadowRoot { |
| document_fragment: DocumentFragment, |
| document_or_shadow_root: DocumentOrShadowRoot, |
| document: Dom<Document>, |
| host: MutNullableDom<Element>, |
| /// List of author styles associated with nodes in this shadow tree. |
| #[custom_trace] |
| author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>, |
| stylesheet_list: MutNullableDom<StyleSheetList>, |
| window: Dom<Window>, |
| |
| /// <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, |
| |
| /// <https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals> |
| available_to_element_internals: Cell<bool>, |
| |
| slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>, |
| |
| is_user_agent_widget: bool, |
| |
| /// <https://dom.spec.whatwg.org/#shadowroot-declarative> |
| declarative: Cell<bool>, |
| |
| /// <https://dom.spec.whatwg.org/#shadowroot-serializable> |
| serializable: Cell<bool>, |
| |
| /// <https://dom.spec.whatwg.org/#shadowroot-delegates-focus> |
| delegates_focus: Cell<bool>, |
| |
| /// The constructed stylesheet that is adopted by this [ShadowRoot]. |
| /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets> |
| adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>, |
| |
| /// Cached frozen array of [`Self::adopted_stylesheets`] |
| #[ignore_malloc_size_of = "mozjs"] |
| adopted_stylesheets_frozen_types: CachedFrozenArray, |
| } |
| |
| impl ShadowRoot { |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| fn new_inherited( |
| host: &Element, |
| document: &Document, |
| mode: ShadowRootMode, |
| slot_assignment_mode: SlotAssignmentMode, |
| clonable: bool, |
| is_user_agent_widget: IsUserAgentWidget, |
| ) -> ShadowRoot { |
| let document_fragment = DocumentFragment::new_inherited(document); |
| let node = document_fragment.upcast::<Node>(); |
| node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true); |
| node.set_flag( |
| NodeFlags::IS_CONNECTED, |
| host.upcast::<Node>().is_connected(), |
| ); |
| |
| ShadowRoot { |
| document_fragment, |
| document_or_shadow_root: DocumentOrShadowRoot::new(document.window()), |
| document: Dom::from_ref(document), |
| host: MutNullableDom::new(Some(host)), |
| author_styles: DomRefCell::new(AuthorStyles::new()), |
| stylesheet_list: MutNullableDom::new(None), |
| window: Dom::from_ref(document.window()), |
| mode, |
| slot_assignment_mode, |
| clonable, |
| available_to_element_internals: Cell::new(false), |
| slots: Default::default(), |
| is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes, |
| declarative: Cell::new(false), |
| serializable: Cell::new(false), |
| delegates_focus: Cell::new(false), |
| adopted_stylesheets: Default::default(), |
| adopted_stylesheets_frozen_types: CachedFrozenArray::new(), |
| } |
| } |
| |
| pub(crate) fn new( |
| host: &Element, |
| document: &Document, |
| mode: ShadowRootMode, |
| slot_assignment_mode: SlotAssignmentMode, |
| clonable: bool, |
| is_user_agent_widget: IsUserAgentWidget, |
| can_gc: CanGc, |
| ) -> DomRoot<ShadowRoot> { |
| reflect_dom_object( |
| Box::new(ShadowRoot::new_inherited( |
| host, |
| document, |
| mode, |
| slot_assignment_mode, |
| clonable, |
| is_user_agent_widget, |
| )), |
| document.window(), |
| can_gc, |
| ) |
| } |
| |
| pub(crate) fn detach(&self, can_gc: CanGc) { |
| self.document.unregister_shadow_root(self); |
| let node = self.upcast::<Node>(); |
| node.set_containing_shadow_root(None); |
| Node::complete_remove_subtree(node, &UnbindContext::new(node, None, None, None), can_gc); |
| self.host.set(None); |
| } |
| |
| pub(crate) fn owner_doc(&self) -> &Document { |
| &self.document |
| } |
| |
| pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> { |
| // XXX get retargeted focused element |
| None |
| } |
| |
| pub(crate) fn stylesheet_count(&self) -> usize { |
| self.author_styles.borrow().stylesheets.len() |
| } |
| |
| pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> { |
| let stylesheets = &self.author_styles.borrow().stylesheets; |
| |
| stylesheets |
| .get(index) |
| .and_then(|s| s.owner.get_cssom_object()) |
| } |
| |
| /// Add a stylesheet owned by `owner_node` to the list of shadow root sheets, in the |
| /// correct tree position. Additionally, ensure that owned stylesheet is inserted before |
| /// any constructed stylesheet. |
| /// |
| /// <https://drafts.csswg.org/cssom/#documentorshadowroot-final-css-style-sheets> |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily. |
| pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) { |
| let stylesheets = &mut self.author_styles.borrow_mut().stylesheets; |
| |
| // FIXME(stevennovaryo): This is almost identical with the one in Document::add_stylesheet. |
| let insertion_point = stylesheets |
| .iter() |
| .find(|sheet_in_shadow| { |
| match &sheet_in_shadow.owner { |
| StylesheetSource::Element(other_node) => { |
| owner_node.upcast::<Node>().is_before(other_node.upcast()) |
| }, |
| // Non-constructed stylesheet should be ordered before the |
| // constructed ones. |
| StylesheetSource::Constructed(_) => true, |
| } |
| }) |
| .cloned(); |
| |
| DocumentOrShadowRoot::add_stylesheet( |
| StylesheetSource::Element(Dom::from_ref(owner_node)), |
| StylesheetSetRef::Author(stylesheets), |
| sheet, |
| insertion_point, |
| self.document.style_shared_lock(), |
| ); |
| } |
| |
| /// Append a constructed stylesheet to the back of shadow root stylesheet set. |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) { |
| debug_assert!(cssom_stylesheet.is_constructed()); |
| |
| let stylesheets = &mut self.author_styles.borrow_mut().stylesheets; |
| let sheet = cssom_stylesheet.style_stylesheet().clone(); |
| |
| let insertion_point = stylesheets.iter().last().cloned(); |
| |
| DocumentOrShadowRoot::add_stylesheet( |
| StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)), |
| StylesheetSetRef::Author(stylesheets), |
| sheet, |
| insertion_point, |
| self.document.style_shared_lock(), |
| ); |
| } |
| |
| /// Remove a stylesheet owned by `owner` from the list of shadow root sheets. |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily. |
| pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) { |
| DocumentOrShadowRoot::remove_stylesheet( |
| owner, |
| s, |
| StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets), |
| ) |
| } |
| |
| pub(crate) fn invalidate_stylesheets(&self) { |
| self.document.invalidate_shadow_roots_stylesheets(); |
| self.author_styles.borrow_mut().stylesheets.force_dirty(); |
| // Mark the host element dirty so a reflow will be performed. |
| if let Some(host) = self.host.get() { |
| host.upcast::<Node>().dirty(NodeDamage::Style); |
| |
| // Also mark the host element with `RestyleHint::restyle_subtree` so a reflow |
| // can traverse into the shadow tree. |
| let mut restyle = self.document.ensure_pending_restyle(&host); |
| restyle.hint.insert(RestyleHint::restyle_subtree()); |
| } |
| } |
| |
| /// Remove any existing association between the provided id and any elements |
| /// in this shadow tree. |
| pub(crate) fn unregister_element_id(&self, to_unregister: &Element, id: Atom, _can_gc: CanGc) { |
| self.document_or_shadow_root.unregister_named_element( |
| self.document_fragment.id_map(), |
| to_unregister, |
| &id, |
| ); |
| } |
| |
| /// Associate an element present in this shadow tree with the provided id. |
| pub(crate) fn register_element_id(&self, element: &Element, id: Atom, _can_gc: CanGc) { |
| let root = self |
| .upcast::<Node>() |
| .inclusive_ancestors(ShadowIncluding::No) |
| .last() |
| .unwrap(); |
| self.document_or_shadow_root.register_named_element( |
| self.document_fragment.id_map(), |
| element, |
| &id, |
| root, |
| ); |
| } |
| |
| pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) { |
| debug!("Registering slot with name={:?}", slot.Name().str()); |
| |
| let mut slots = self.slots.borrow_mut(); |
| |
| let slots_with_the_same_name = slots.entry(slot.Name()).or_default(); |
| |
| // Insert the slot before the first element that comes after it in tree order |
| slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>()); |
| } |
| |
| pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) { |
| debug!("Unregistering slot with name={:?}", name.str()); |
| |
| let mut slots = self.slots.borrow_mut(); |
| let Entry::Occupied(mut entry) = slots.entry(name) else { |
| panic!("slot is not registered"); |
| }; |
| entry.get_mut().retain(|s| slot != &**s); |
| } |
| |
| /// Find the first slot with the given name among this root's descendants in tree order |
| pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> { |
| self.slots |
| .borrow() |
| .get(name) |
| .and_then(|slots| slots.first()) |
| .map(|slot| slot.as_rooted()) |
| } |
| |
| pub(crate) fn has_slot_descendants(&self) -> bool { |
| !self.slots.borrow().is_empty() |
| } |
| |
| pub(crate) fn set_available_to_element_internals(&self, value: bool) { |
| self.available_to_element_internals.set(value); |
| } |
| |
| /// <https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals> |
| pub(crate) fn is_available_to_element_internals(&self) -> bool { |
| self.available_to_element_internals.get() |
| } |
| |
| pub(crate) fn is_user_agent_widget(&self) -> bool { |
| self.is_user_agent_widget |
| } |
| |
| pub(crate) fn set_declarative(&self, declarative: bool) { |
| self.declarative.set(declarative); |
| } |
| |
| pub(crate) fn is_declarative(&self) -> bool { |
| self.declarative.get() |
| } |
| |
| pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode { |
| self.mode |
| } |
| |
| pub(crate) fn set_serializable(&self, serializable: bool) { |
| self.serializable.set(serializable); |
| } |
| |
| pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) { |
| self.delegates_focus.set(delegates_focus); |
| } |
| } |
| |
| impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot { |
| // https://html.spec.whatwg.org/multipage/#dom-document-activeelement |
| fn GetActiveElement(&self) -> Option<DomRoot<Element>> { |
| self.document_or_shadow_root |
| .get_active_element(self.get_focused_element(), None, None) |
| } |
| |
| // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint |
| fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> { |
| // Return the result of running the retargeting algorithm with context object |
| // and the original result as input. |
| match self.document_or_shadow_root.element_from_point( |
| x, |
| y, |
| None, |
| self.document.has_browsing_context(), |
| ) { |
| Some(e) => { |
| let retargeted_node = self |
| .upcast::<EventTarget>() |
| .retarget(e.upcast::<EventTarget>()); |
| retargeted_node.downcast::<Element>().map(DomRoot::from_ref) |
| }, |
| None => None, |
| } |
| } |
| |
| // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint |
| fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> { |
| // Return the result of running the retargeting algorithm with context object |
| // and the original result as input |
| let mut elements = Vec::new(); |
| for e in self |
| .document_or_shadow_root |
| .elements_from_point(x, y, None, self.document.has_browsing_context()) |
| .iter() |
| { |
| let retargeted_node = self |
| .upcast::<EventTarget>() |
| .retarget(e.upcast::<EventTarget>()); |
| if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) { |
| elements.push(element); |
| } |
| } |
| elements |
| } |
| |
| /// <https://dom.spec.whatwg.org/#dom-shadowroot-mode> |
| fn Mode(&self) -> ShadowRootMode { |
| self.mode |
| } |
| |
| /// <https://dom.spec.whatwg.org/#dom-delegates-focus> |
| fn DelegatesFocus(&self) -> bool { |
| self.delegates_focus.get() |
| } |
| |
| /// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable> |
| fn Clonable(&self) -> bool { |
| self.clonable |
| } |
| |
| /// <https://dom.spec.whatwg.org/#dom-serializable> |
| fn Serializable(&self) -> bool { |
| self.serializable.get() |
| } |
| |
| /// <https://dom.spec.whatwg.org/#dom-shadowroot-host> |
| fn Host(&self) -> DomRoot<Element> { |
| let host = self.host.get(); |
| host.expect("Trying to get host from a detached shadow root") |
| } |
| |
| // https://drafts.csswg.org/cssom/#dom-document-stylesheets |
| fn StyleSheets(&self) -> DomRoot<StyleSheetList> { |
| self.stylesheet_list.or_init(|| { |
| StyleSheetList::new( |
| &self.window, |
| StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)), |
| CanGc::note(), |
| ) |
| }) |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-gethtml> |
| fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString { |
| // > ShadowRoot's getHTML(options) method steps are to return the result of HTML fragment serialization |
| // > algorithm with this, options["serializableShadowRoots"], and options["shadowRoots"]. |
| self.upcast::<Node>().html_serialize( |
| TraversalScope::ChildrenOnly(None), |
| options.serializableShadowRoots, |
| options.shadowRoots.clone(), |
| can_gc, |
| ) |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml> |
| fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> { |
| // ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing |
| // algorithm steps with this and true. |
| self.upcast::<Node>() |
| .fragment_serialization_algorithm(true, can_gc) |
| .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString) |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml> |
| fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult { |
| // Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm |
| // with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script". |
| let value = TrustedHTML::get_trusted_script_compliant_string( |
| &self.owner_global(), |
| value.convert(), |
| "ShadowRoot innerHTML", |
| can_gc, |
| )?; |
| |
| // Step 2. Let context be this's host. |
| let context = self.Host(); |
| |
| // Step 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and |
| // compliantString. |
| // |
| // NOTE: The spec doesn't strictly tell us to bail out here, but |
| // we can't continue if parsing failed |
| let frag = context.parse_fragment(value, can_gc)?; |
| |
| // Step 4. Replace all with fragment within this. |
| Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc); |
| Ok(()) |
| } |
| |
| /// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment> |
| fn SlotAssignment(&self) -> SlotAssignmentMode { |
| self.slot_assignment_mode |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe> |
| fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult { |
| // Step 1. Let compliantHTML be the result of invoking the |
| // Get Trusted Type compliant string algorithm with TrustedHTML, |
| // this's relevant global object, html, "ShadowRoot setHTMLUnsafe", and "script". |
| let value = TrustedHTML::get_trusted_script_compliant_string( |
| &self.owner_global(), |
| value, |
| "ShadowRoot setHTMLUnsafe", |
| can_gc, |
| )?; |
| // Step 2. Unsafely set HTMl given this, this's shadow host, and complaintHTML |
| let target = self.upcast::<Node>(); |
| let context_element = self.Host(); |
| |
| Node::unsafely_set_html(target, &context_element, value, can_gc); |
| Ok(()) |
| } |
| |
| // https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange |
| event_handler!(onslotchange, GetOnslotchange, SetOnslotchange); |
| |
| /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets> |
| fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) { |
| self.adopted_stylesheets_frozen_types.get_or_init( |
| || { |
| self.adopted_stylesheets |
| .borrow() |
| .clone() |
| .iter() |
| .map(|sheet| sheet.as_rooted()) |
| .collect() |
| }, |
| context, |
| retval, |
| can_gc, |
| ); |
| } |
| |
| /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets> |
| fn SetAdoptedStyleSheets(&self, context: JSContext, val: HandleValue) -> ErrorResult { |
| let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval( |
| context, |
| self.adopted_stylesheets.borrow_mut().as_mut(), |
| val, |
| &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)), |
| ); |
| |
| // If update is successful, clear the FrozenArray cache. |
| if result.is_ok() { |
| self.adopted_stylesheets_frozen_types.clear(); |
| } |
| |
| result |
| } |
| } |
| |
| impl VirtualMethods for ShadowRoot { |
| fn super_type(&self) -> Option<&dyn VirtualMethods> { |
| Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods) |
| } |
| |
| fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) { |
| if let Some(s) = self.super_type() { |
| s.bind_to_tree(context, can_gc); |
| } |
| |
| // TODO(stevennovaryo): Handle adoptedStylesheet to deal with different |
| // constructor document. |
| if context.tree_connected { |
| let document = self.owner_document(); |
| document.register_shadow_root(self); |
| } |
| |
| let shadow_root = self.upcast::<Node>(); |
| |
| shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); |
| |
| // avoid iterate over the shadow root itself |
| for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) { |
| node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); |
| |
| // Out-of-document elements never have the descendants flag set |
| debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)); |
| vtable_for(&node).bind_to_tree( |
| &BindContext { |
| tree_connected: context.tree_connected, |
| tree_is_in_a_document_tree: false, |
| tree_is_in_a_shadow_tree: true, |
| }, |
| can_gc, |
| ); |
| } |
| } |
| |
| fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) { |
| if let Some(s) = self.super_type() { |
| s.unbind_from_tree(context, can_gc); |
| } |
| |
| if context.tree_connected { |
| let document = self.owner_document(); |
| document.unregister_shadow_root(self); |
| } |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| pub(crate) trait LayoutShadowRootHelpers<'dom> { |
| fn get_host_for_layout(self) -> LayoutDom<'dom, Element>; |
| fn get_style_data_for_layout(self) -> &'dom CascadeData; |
| unsafe fn flush_stylesheets<E: TElement>( |
| self, |
| stylist: &mut Stylist, |
| guard: &SharedRwLockReadGuard, |
| ); |
| } |
| |
| impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> { |
| #[inline] |
| #[allow(unsafe_code)] |
| fn get_host_for_layout(self) -> LayoutDom<'dom, Element> { |
| unsafe { |
| self.unsafe_get() |
| .host |
| .get_inner_as_layout() |
| .expect("We should never do layout on a detached shadow root") |
| } |
| } |
| |
| #[inline] |
| #[allow(unsafe_code)] |
| fn get_style_data_for_layout(self) -> &'dom CascadeData { |
| fn is_sync<T: Sync>() {} |
| let _ = is_sync::<CascadeData>; |
| unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data } |
| } |
| |
| // FIXME(nox): This uses the dreaded borrow_mut_for_layout so this should |
| // probably be revisited. |
| #[inline] |
| #[allow(unsafe_code)] |
| unsafe fn flush_stylesheets<E: TElement>( |
| self, |
| stylist: &mut Stylist, |
| guard: &SharedRwLockReadGuard, |
| ) { |
| let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout(); |
| if author_styles.stylesheets.dirty() { |
| author_styles.flush::<E>(stylist, guard); |
| } |
| } |
| } |
| |
| impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode { |
| fn convert(self) -> devtools_traits::ShadowRootMode { |
| match self { |
| ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open, |
| ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed, |
| } |
| } |
| } |