Auto merge of #22743 - ferjm:shadowdom, r=emilio

Partial ShadowDOM support

This is just an early WIP, not to take it very seriously yet.

- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [x] These changes fix #22719
- [x] There are tests for these changes

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22743)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-04-29 08:38:50 -04:00 committed by GitHub
commit 799490a02e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 2081 additions and 702 deletions

View file

@ -72,8 +72,8 @@ use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::root::DomRoot;
use crate::dom::create::create_native_html_element;
use crate::dom::customelementregistry::ConstructionStackEntry;
use crate::dom::element::{CustomElementState, Element, ElementCreator};
use crate::dom::customelementregistry::{ConstructionStackEntry, CustomElementState};
use crate::dom::element::{Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::window::Window;
use crate::script_thread::ScriptThread;

View file

@ -118,6 +118,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{Arc, Mutex};
use std::time::{Instant, SystemTime};
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
use style::author_styles::AuthorStyles;
use style::context::QuirksMode;
use style::dom::OpaqueNode;
use style::element_state::*;
@ -125,10 +126,11 @@ use style::media_queries::MediaList;
use style::properties::PropertyDeclarationBlock;
use style::selector_parser::{PseudoElement, Snapshot};
use style::shared_lock::{Locked as StyleLocked, SharedRwLock as StyleSharedRwLock};
use style::stylesheet_set::DocumentStylesheetSet;
use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet};
use style::stylesheets::keyframes_rule::Keyframe;
use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule, Stylesheet};
use style::stylesheets::{ImportRule, NamespaceRule, StyleRule, SupportsRule, ViewportRule};
use style::stylist::CascadeData;
use style::values::specified::Length;
use tendril::fmt::UTF8;
use tendril::stream::LossyDecoder;
@ -497,6 +499,7 @@ unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
unsafe_no_jsmanaged_fields!(Rotation3D<f64>, Transform2D<f32>, Transform3D<f64>);
unsafe_no_jsmanaged_fields!(Point2D<f32>, Vector2D<f32>, Rect<Au>);
unsafe_no_jsmanaged_fields!(Rect<f32>, RigidTransform3D<f64>);
unsafe_no_jsmanaged_fields!(CascadeData);
unsafe impl<'a> JSTraceable for &'a str {
#[inline]
@ -715,6 +718,26 @@ where
}
}
unsafe impl<S> JSTraceable for AuthorStylesheetSet<S>
where
S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
{
unsafe fn trace(&self, tracer: *mut JSTracer) {
for s in self.iter() {
s.trace(tracer)
}
}
}
unsafe impl<S> JSTraceable for AuthorStyles<S>
where
S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
{
unsafe fn trace(&self, tracer: *mut JSTracer) {
self.stylesheets.trace(tracer)
}
}
unsafe impl<Sink> JSTraceable for LossyDecoder<Sink>
where
Sink: JSTraceable + TendrilSink<UTF8>,

View file

@ -5,9 +5,11 @@
use crate::dom::bindings::error::{report_pending_exception, throw_dom_exception};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::customelementregistry::{is_valid_custom_element_name, upgrade_element};
use crate::dom::customelementregistry::{
is_valid_custom_element_name, upgrade_element, CustomElementState,
};
use crate::dom::document::Document;
use crate::dom::element::{CustomElementCreationMode, CustomElementState, Element, ElementCreator};
use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlanchorelement::HTMLAnchorElement;
use crate::dom::htmlareaelement::HTMLAreaElement;

View file

@ -13,7 +13,7 @@ use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssrule::CSSRule;
use crate::dom::element::Element;
use crate::dom::node::{document_from_node, window_from_node, Node};
use crate::dom::node::{document_from_node, stylesheets_owner_from_node, window_from_node, Node};
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_arc::Arc;
@ -115,9 +115,7 @@ impl CSSStyleOwner {
if changed {
// If this is changed, see also
// CSSStyleRule::SetSelectorText, which does the same thing.
rule.global()
.as_window()
.Document()
stylesheets_owner_from_node(rule.parent_stylesheet().owner().upcast::<Node>())
.invalidate_stylesheets();
}
result
@ -246,7 +244,7 @@ impl CSSStyleDeclaration {
},
CSSStyleOwner::Element(ref el) => {
let node = el.upcast::<Node>();
if !node.is_in_doc() {
if !node.is_connected() {
// TODO: Node should be matched against the style rules of this window.
// Firefox is currently the only browser to implement this.
return DOMString::new();

View file

@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::{self, CSSStyleRuleMethods};
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
@ -11,6 +10,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::cssrule::{CSSRule, SpecificCSSRule};
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::node::{stylesheets_owner_from_node, Node};
use crate::dom::window::Window;
use cssparser::ToCss;
use cssparser::{Parser as CssParser, ParserInput as CssParserInput};
@ -118,11 +118,7 @@ impl CSSStyleRuleMethods for CSSStyleRule {
let mut guard = self.cssrule.shared_lock().write();
let stylerule = self.stylerule.write_with(&mut guard);
mem::swap(&mut stylerule.selectors, &mut s);
// It seems like we will want to avoid having to invalidate all
// stylesheets eventually!
self.global()
.as_window()
.Document()
stylesheets_owner_from_node(self.cssrule.parent_stylesheet().owner().upcast::<Node>())
.invalidate_stylesheets();
}
}

View file

@ -4,13 +4,14 @@
use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding;
use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssrulelist::{CSSRuleList, RulesSource};
use crate::dom::element::Element;
use crate::dom::node::{stylesheets_owner_from_node, Node};
use crate::dom::stylesheet::StyleSheet;
use crate::dom::window::Window;
use dom_struct::dom_struct;
@ -64,6 +65,10 @@ impl CSSStyleSheet {
)
}
pub fn owner(&self) -> DomRoot<Element> {
DomRoot::from_ref(&*self.owner)
}
fn rulelist(&self) -> DomRoot<CSSRuleList> {
self.rulelist.or_init(|| {
let rules = self.style_stylesheet.contents.rules.clone();
@ -81,10 +86,7 @@ impl CSSStyleSheet {
pub fn set_disabled(&self, disabled: bool) {
if self.style_stylesheet.set_disabled(disabled) {
self.global()
.as_window()
.Document()
.invalidate_stylesheets();
stylesheets_owner_from_node(self.owner().upcast::<Node>()).invalidate_stylesheets();
}
}

View file

@ -23,10 +23,10 @@ use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::domexception::{DOMErrorName, DOMException};
use crate::dom::element::{CustomElementState, Element};
use crate::dom::element::Element;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, window_from_node, Node};
use crate::dom::node::{document_from_node, window_from_node, Node, ShadowIncluding};
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::microtask::Microtask;
@ -47,6 +47,21 @@ use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
/// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
pub enum CustomElementState {
Undefined,
Failed,
Uncustomized,
Custom,
}
impl Default for CustomElementState {
fn default() -> CustomElementState {
CustomElementState::Uncustomized
}
}
/// <https://html.spec.whatwg.org/multipage/#customelementregistry>
#[dom_struct]
pub struct CustomElementRegistry {
@ -364,7 +379,7 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
// Steps 14-15
for candidate in document
.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<Element>)
{
let is = candidate.get_is();

View file

@ -43,6 +43,7 @@ use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::customelementregistry::CustomElementDefinition;
use crate::dom::customevent::CustomEvent;
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::documentorshadowroot::{DocumentOrShadowRoot, StyleSheetInDocument};
use crate::dom::documenttype::DocumentType;
use crate::dom::domimplementation::DOMImplementation;
use crate::dom::element::CustomElementCreationMode;
@ -67,16 +68,14 @@ use crate::dom::htmlheadelement::HTMLHeadElement;
use crate::dom::htmlhtmlelement::HTMLHtmlElement;
use crate::dom::htmliframeelement::HTMLIFrameElement;
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlmetaelement::HTMLMetaElement;
use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult};
use crate::dom::htmltitleelement::HTMLTitleElement;
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::location::Location;
use crate::dom::messageevent::MessageEvent;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::VecPreOrderInsertionHelper;
use crate::dom::node::{self, document_from_node, window_from_node, CloneChildrenFlag};
use crate::dom::node::{LayoutNodeHelpers, Node, NodeDamage, NodeFlags};
use crate::dom::node::{LayoutNodeHelpers, Node, NodeDamage, NodeFlags, ShadowIncluding};
use crate::dom::nodeiterator::NodeIterator;
use crate::dom::nodelist::NodeList;
use crate::dom::pagetransitionevent::PageTransitionEvent;
@ -86,8 +85,9 @@ use crate::dom::progressevent::ProgressEvent;
use crate::dom::promise::Promise;
use crate::dom::range::Range;
use crate::dom::servoparser::ServoParser;
use crate::dom::shadowroot::ShadowRoot;
use crate::dom::storageevent::StorageEvent;
use crate::dom::stylesheetlist::StyleSheetList;
use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::text::Text;
use crate::dom::touch::Touch;
use crate::dom::touchevent::TouchEvent;
@ -101,6 +101,7 @@ use crate::dom::windowproxy::WindowProxy;
use crate::fetch::FetchCanceller;
use crate::script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
use crate::script_thread::{MainThreadScriptMsg, ScriptThread};
use crate::stylesheet_set::StylesheetSetRef;
use crate::task::TaskBox;
use crate::task_source::{TaskSource, TaskSourceName};
use crate::timers::OneshotTimerCallback;
@ -113,7 +114,6 @@ use euclid::Point2D;
use html5ever::{LocalName, Namespace, QualName};
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::JS_GetRuntime;
use js::jsapi::{JSContext, JSObject, JSRuntime};
use keyboard_types::{Key, KeyState, Modifiers};
use metrics::{
@ -132,7 +132,7 @@ use num_traits::ToPrimitive;
use profile_traits::ipc as profile_ipc;
use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType};
use ref_slice::ref_slice;
use script_layout_interface::message::{Msg, NodesFromPointQueryType, QueryMsg, ReflowGoal};
use script_layout_interface::message::{Msg, ReflowGoal};
use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType};
use script_traits::{MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress};
use servo_arc::Arc;
@ -144,7 +144,6 @@ use std::cell::{Cell, Ref, RefMut};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{HashMap, HashSet, VecDeque};
use std::default::Default;
use std::fmt;
use std::mem;
use std::ptr::NonNull;
use std::rc::Rc;
@ -152,12 +151,12 @@ use std::time::{Duration, Instant};
use style::attr::AttrValue;
use style::context::QuirksMode;
use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::{Device, MediaList, MediaType};
use style::media_queries::{Device, MediaType};
use style::selector_parser::{RestyleDamage, Snapshot};
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::str::{split_html_space_chars, str_join};
use style::stylesheet_set::DocumentStylesheetSet;
use style::stylesheets::{CssRule, Origin, OriginSet, Stylesheet};
use style::stylesheets::{Origin, OriginSet, Stylesheet};
use url::percent_encoding::percent_decode;
use url::Host;
@ -220,52 +219,11 @@ impl PendingRestyle {
}
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[must_root]
struct StyleSheetInDocument {
#[ignore_malloc_size_of = "Arc"]
sheet: Arc<Stylesheet>,
owner: Dom<Element>,
}
impl fmt::Debug for StyleSheetInDocument {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.sheet.fmt(formatter)
}
}
impl PartialEq for StyleSheetInDocument {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.sheet, &other.sheet)
}
}
impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
self.sheet.origin(guard)
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.sheet.quirks_mode(guard)
}
fn enabled(&self) -> bool {
self.sheet.enabled()
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.sheet.media(guard)
}
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.sheet.rules(guard)
}
}
/// <https://dom.spec.whatwg.org/#document>
#[dom_struct]
pub struct Document {
node: Node,
document_or_shadow_root: DocumentOrShadowRoot,
window: Dom<Window>,
implementation: MutNullableDom<DOMImplementation>,
#[ignore_malloc_size_of = "type from external crate"]
@ -419,6 +377,10 @@ pub struct Document {
delayed_tasks: DomRefCell<Vec<Box<dyn TaskBox>>>,
/// https://html.spec.whatwg.org/multipage/#completely-loaded
completely_loaded: Cell<bool>,
/// Set of shadow roots connected to the document tree.
shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
/// Whether any of the shadow roots need the stylesheets flushed.
shadow_roots_styles_changed: Cell<bool>,
}
#[derive(JSTraceable, MallocSizeOf)]
@ -634,7 +596,7 @@ impl Document {
pub fn refresh_base_element(&self) {
let base = self
.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLBaseElement>)
.find(|element| {
element
@ -684,7 +646,7 @@ impl Document {
}
pub fn content_and_heritage_changed(&self, node: &Node) {
if node.is_in_doc() {
if node.is_connected() {
node.note_dirty_descendants();
}
@ -720,55 +682,23 @@ impl Document {
/// Remove any existing association between the provided id and any elements in this document.
pub fn unregister_named_element(&self, to_unregister: &Element, id: Atom) {
debug!(
"Removing named element from document {:p}: {:p} id={}",
self, to_unregister, id
);
// Limit the scope of the borrow because id_map might be borrowed again by
// GetElementById through the following sequence of calls
// reset_form_owner_for_listeners -> reset_form_owner -> GetElementById
{
let mut id_map = self.id_map.borrow_mut();
let is_empty = match id_map.get_mut(&id) {
None => false,
Some(elements) => {
let position = elements
.iter()
.position(|element| &**element == to_unregister)
.expect("This element should be in registered.");
elements.remove(position);
elements.is_empty()
},
};
if is_empty {
id_map.remove(&id);
}
}
self.document_or_shadow_root
.unregister_named_element(&self.id_map, to_unregister, &id);
self.reset_form_owner_for_listeners(&id);
}
/// Associate an element present in this document with the provided id.
pub fn register_named_element(&self, element: &Element, id: Atom) {
debug!(
"Adding named element to document {:p}: {:p} id={}",
self, element, id
);
assert!(element.upcast::<Node>().is_in_doc());
assert!(!id.is_empty());
let root = self.GetDocumentElement().expect(
"The element is in the document, so there must be a document \
element.",
);
// Limit the scope of the borrow because id_map might be borrowed again by
// GetElementById through the following sequence of calls
// reset_form_owner_for_listeners -> reset_form_owner -> GetElementById
{
let mut id_map = self.id_map.borrow_mut();
let elements = id_map.entry(id.clone()).or_insert(Vec::new());
elements.insert_pre_order(element, root.upcast::<Node>());
}
self.document_or_shadow_root.register_named_element(
&self.id_map,
element,
&id,
DomRoot::from_ref(root.upcast::<Node>()),
);
self.reset_form_owner_for_listeners(&id);
}
@ -872,7 +802,7 @@ impl Document {
};
let doc_node = self.upcast::<Node>();
doc_node
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast)
.find(|node| check_anchor(&node))
.map(DomRoot::upcast)
@ -981,7 +911,7 @@ impl Document {
pub fn dirty_all_nodes(&self) {
let root = self.upcast::<Node>();
for node in root.traverse_preorder() {
for node in root.traverse_preorder(ShadowIncluding::Yes) {
node.dirty(NodeDamage::OtherNodeDamage)
}
}
@ -1005,7 +935,7 @@ impl Document {
let el = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
node.inclusive_ancestors()
node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
@ -1189,7 +1119,7 @@ impl Document {
let maybe_new_target = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
node.inclusive_ancestors()
node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
@ -1225,7 +1155,7 @@ impl Document {
if !old_target_is_ancestor_of_new_target {
for element in old_target
.upcast::<Node>()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
{
element.set_hover_state(false);
@ -1243,7 +1173,7 @@ impl Document {
if let Some(ref new_target) = maybe_new_target {
for element in new_target
.upcast::<Node>()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
{
if element.hover_state() {
@ -1285,7 +1215,7 @@ impl Document {
let el = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
node.inclusive_ancestors()
node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
@ -2276,7 +2206,7 @@ impl Document {
/// Iterate over all iframes in the document.
pub fn iter_iframes(&self) -> impl Iterator<Item = DomRoot<HTMLIFrameElement>> {
self.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<HTMLIFrameElement>)
}
@ -2395,21 +2325,6 @@ impl Document {
!self.has_browsing_context || !url_has_network_scheme(&self.url())
}
pub fn nodes_from_point(
&self,
client_point: &Point2D<f32>,
reflow_goal: NodesFromPointQueryType,
) -> Vec<UntrustedNodeAddress> {
if !self
.window
.layout_reflow(QueryMsg::NodesFromPointQuery(*client_point, reflow_goal))
{
return vec![];
};
self.window.layout().nodes_from_point_response()
}
/// <https://html.spec.whatwg.org/multipage/#look-up-a-custom-element-definition>
pub fn lookup_custom_element_definition(
&self,
@ -2485,6 +2400,9 @@ pub trait LayoutDocumentHelpers {
unsafe fn will_paint(&self);
unsafe fn quirks_mode(&self) -> QuirksMode;
unsafe fn style_shared_lock(&self) -> &StyleSharedRwLock;
unsafe fn shadow_roots(&self) -> Vec<LayoutDom<ShadowRoot>>;
unsafe fn shadow_roots_styles_changed(&self) -> bool;
unsafe fn flush_shadow_roots_stylesheets(&self);
}
#[allow(unsafe_code)]
@ -2500,12 +2418,12 @@ impl LayoutDocumentHelpers for LayoutDom<Document> {
let mut elements = (*self.unsafe_get())
.pending_restyles
.borrow_mut_for_layout();
// Elements were in a document when they were adding to this list, but that
// Elements were in a document when they were added to this list, but that
// may no longer be true when the next layout occurs.
let result = elements
.drain()
.map(|(k, v)| (k.to_layout(), v))
.filter(|&(ref k, _)| k.upcast::<Node>().get_flag(NodeFlags::IS_IN_DOC))
.filter(|&(ref k, _)| k.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED))
.collect();
result
}
@ -2529,6 +2447,26 @@ impl LayoutDocumentHelpers for LayoutDom<Document> {
unsafe fn style_shared_lock(&self) -> &StyleSharedRwLock {
(*self.unsafe_get()).style_shared_lock()
}
#[inline]
unsafe fn shadow_roots(&self) -> Vec<LayoutDom<ShadowRoot>> {
(*self.unsafe_get())
.shadow_roots
.borrow_for_layout()
.iter()
.map(|sr| sr.to_layout())
.collect()
}
#[inline]
unsafe fn shadow_roots_styles_changed(&self) -> bool {
(*self.unsafe_get()).shadow_roots_styles_changed()
}
#[inline]
unsafe fn flush_shadow_roots_stylesheets(&self) {
(*self.unsafe_get()).flush_shadow_roots_stylesheets()
}
}
// https://html.spec.whatwg.org/multipage/#is-a-registrable-domain-suffix-of-or-is-equal-to
@ -2637,21 +2575,23 @@ impl Document {
.and_then(|charset| Encoding::for_label(charset.as_str().as_bytes()))
.unwrap_or(UTF_8);
let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
Document {
node: Node::new_document_node(),
document_or_shadow_root: DocumentOrShadowRoot::new(window),
window: Dom::from_ref(window),
has_browsing_context: has_browsing_context == HasBrowsingContext::Yes,
has_browsing_context,
implementation: Default::default(),
content_type,
last_modified: last_modified,
url: DomRefCell::new(url),
// https://dom.spec.whatwg.org/#concept-document-quirks
quirks_mode: Cell::new(QuirksMode::NoQuirks),
id_map: DomRefCell::new(HashMap::new()),
// https://dom.spec.whatwg.org/#concept-document-encoding
encoding: Cell::new(encoding),
is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
activity: Cell::new(activity),
id_map: DomRefCell::new(HashMap::new()),
tag_map: DomRefCell::new(HashMap::new()),
tagns_map: DomRefCell::new(HashMap::new()),
classes_map: DomRefCell::new(HashMap::new()),
@ -2689,7 +2629,7 @@ impl Document {
deferred_scripts: Default::default(),
asap_in_order_scripts_list: Default::default(),
asap_scripts_set: Default::default(),
scripting_enabled: has_browsing_context == HasBrowsingContext::Yes,
scripting_enabled: has_browsing_context,
animation_frame_ident: Cell::new(0),
animation_frame_list: DomRefCell::new(vec![]),
running_animation_callbacks: Cell::new(false),
@ -2735,6 +2675,8 @@ impl Document {
completely_loaded: Cell::new(false),
script_and_layout_blockers: Cell::new(0),
delayed_tasks: Default::default(),
shadow_roots: DomRefCell::new(HashSet::new()),
shadow_roots_styles_changed: Cell::new(false),
}
}
@ -2853,7 +2795,7 @@ impl Document {
let maybe_node = doc.deref().map(Castable::upcast::<Node>);
let iter = maybe_node
.iter()
.flat_map(|node| node.traverse_preorder())
.flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
.filter(|node| callback(&node));
NodeList::new_simple_list(&self.window, iter)
}
@ -2893,93 +2835,10 @@ impl Document {
Device::new(MediaType::screen(), viewport_size, device_pixel_ratio)
}
/// Remove a stylesheet owned by `owner` from the list of document sheets.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
self.window()
.layout_chan()
.send(Msg::RemoveStylesheet(s.clone()))
.unwrap();
let guard = s.shared_lock.read();
// FIXME(emilio): Would be nice to remove the clone, etc.
self.stylesheets.borrow_mut().remove_stylesheet(
None,
StyleSheetInDocument {
sheet: s.clone(),
owner: Dom::from_ref(owner),
},
&guard,
);
}
/// Add a stylesheet owned by `owner` to the list of document sheets, in the
/// correct tree position.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
// FIXME(emilio): It'd be nice to unify more code between the elements
// that own stylesheets, but StylesheetOwner is more about loading
// them...
debug_assert!(
owner.as_stylesheet_owner().is_some() || owner.is::<HTMLMetaElement>(),
"Wat"
);
let mut stylesheets = self.stylesheets.borrow_mut();
let insertion_point = stylesheets
.iter()
.map(|(sheet, _origin)| sheet)
.find(|sheet_in_doc| {
owner
.upcast::<Node>()
.is_before(sheet_in_doc.owner.upcast())
})
.cloned();
self.window()
.layout_chan()
.send(Msg::AddStylesheet(
sheet.clone(),
insertion_point.as_ref().map(|s| s.sheet.clone()),
))
.unwrap();
let sheet = StyleSheetInDocument {
sheet,
owner: Dom::from_ref(owner),
};
let lock = self.style_shared_lock();
let guard = lock.read();
match insertion_point {
Some(ip) => {
stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
},
None => {
stylesheets.append_stylesheet(None, sheet, &guard);
},
}
}
/// Returns the number of document stylesheets.
pub fn stylesheet_count(&self) -> usize {
self.stylesheets.borrow().len()
}
pub fn salvageable(&self) -> bool {
self.salvageable.get()
}
pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
let stylesheets = self.stylesheets.borrow();
stylesheets
.get(Origin::Author, index)
.and_then(|s| s.owner.upcast::<Node>().get_cssom_stylesheet())
}
/// <https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document>
pub fn appropriate_template_contents_owner_document(&self) -> DomRoot<Document> {
self.appropriate_template_contents_owner_document
@ -3270,6 +3129,92 @@ impl Document {
}
}
}
pub fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
self.shadow_roots
.borrow_mut()
.insert(Dom::from_ref(shadow_root));
self.invalidate_shadow_roots_stylesheets();
}
pub fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
let mut shadow_roots = self.shadow_roots.borrow_mut();
shadow_roots.remove(&Dom::from_ref(shadow_root));
}
pub fn invalidate_shadow_roots_stylesheets(&self) {
self.shadow_roots_styles_changed.set(true);
}
pub fn shadow_roots_styles_changed(&self) -> bool {
self.shadow_roots_styles_changed.get()
}
pub fn flush_shadow_roots_stylesheets(&self) {
if !self.shadow_roots_styles_changed.get() {
return;
}
self.shadow_roots_styles_changed.set(false);
}
pub fn stylesheet_count(&self) -> usize {
self.stylesheets.borrow().len()
}
pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
let stylesheets = self.stylesheets.borrow();
stylesheets
.get(Origin::Author, index)
.and_then(|s| s.owner.upcast::<Node>().get_cssom_stylesheet())
}
/// Add a stylesheet owned by `owner` to the list of document sheets, in the
/// correct tree position.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
let stylesheets = &mut *self.stylesheets.borrow_mut();
let insertion_point = stylesheets
.iter()
.map(|(sheet, _origin)| sheet)
.find(|sheet_in_doc| {
owner
.upcast::<Node>()
.is_before(sheet_in_doc.owner.upcast())
})
.cloned();
self.window
.layout_chan()
.send(Msg::AddStylesheet(
sheet.clone(),
insertion_point.as_ref().map(|s| s.sheet.clone()),
))
.unwrap();
DocumentOrShadowRoot::add_stylesheet(
owner,
StylesheetSetRef::Document(stylesheets),
sheet,
insertion_point,
self.style_shared_lock(),
);
}
/// Remove a stylesheet owned by `owner` from the list of document sheets.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
self.window
.layout_chan()
.send(Msg::RemoveStylesheet(s.clone()))
.unwrap();
DocumentOrShadowRoot::remove_stylesheet(
owner,
s,
StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
)
}
}
impl Element {
@ -3301,8 +3246,12 @@ impl ProfilerMetadataFactory for Document {
impl DocumentMethods for Document {
// https://drafts.csswg.org/cssom/#dom-document-stylesheets
fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
self.stylesheet_list
.or_init(|| StyleSheetList::new(&self.window, Dom::from_ref(&self)))
self.stylesheet_list.or_init(|| {
StyleSheetList::new(
&self.window,
StyleSheetListOwner::Document(Dom::from_ref(self)),
)
})
}
// https://dom.spec.whatwg.org/#dom-document-implementation
@ -3317,16 +3266,11 @@ impl DocumentMethods for Document {
// https://html.spec.whatwg.org/multipage/#dom-document-activeelement
fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
// TODO: Step 2.
match self.get_focused_element() {
Some(element) => Some(element), // Step 3. and 4.
None => match self.GetBody() {
// Step 5.
Some(body) => Some(DomRoot::upcast(body)),
None => self.GetDocumentElement(),
},
}
self.document_or_shadow_root.get_active_element(
self.get_focused_element(),
self.GetBody(),
self.GetDocumentElement(),
)
}
// https://html.spec.whatwg.org/multipage/#dom-document-hasfocus
@ -3643,7 +3587,7 @@ impl DocumentMethods for Document {
// https://dom.spec.whatwg.org/#dom-document-importnode
fn ImportNode(&self, node: &Node, deep: bool) -> Fallible<DomRoot<Node>> {
// Step 1.
if node.is::<Document>() {
if node.is::<Document>() || node.is::<ShadowRoot>() {
return Err(Error::NotSupported);
}
@ -3665,9 +3609,14 @@ impl DocumentMethods for Document {
}
// Step 2.
Node::adopt(node, self);
if node.is::<ShadowRoot>() {
return Err(Error::HierarchyRequest);
}
// Step 3.
Node::adopt(node, self);
// Step 4.
Ok(DomRoot::from_ref(node))
}
@ -3805,7 +3754,7 @@ impl DocumentMethods for Document {
} else {
// Step 2.
root.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.find(|node| node.is::<HTMLTitleElement>())
}
});
@ -3852,7 +3801,7 @@ impl DocumentMethods for Document {
} else if root.namespace() == &ns!(html) {
let elem = root
.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.find(|node| node.is::<HTMLTitleElement>());
match elem {
Some(elem) => elem,
@ -4219,7 +4168,7 @@ impl DocumentMethods for Document {
{
// Step 1.
let mut elements = root
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter(|node| filter_by_name(&name, &node))
.peekable();
if let Some(first) = elements.next() {
@ -4273,82 +4222,24 @@ impl DocumentMethods for Document {
SetOnreadystatechange
);
#[allow(unsafe_code)]
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let window = window_from_node(self);
let viewport = window.window_size().initial_viewport;
if self.browsing_context().is_none() {
return None;
}
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return None;
}
match self
.nodes_from_point(point, NodesFromPointQueryType::Topmost)
.first()
{
Some(address) => {
let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
let node = unsafe { node::from_untrusted_node_address(js_runtime, *address) };
let parent_node = node.GetParentNode().unwrap();
let element_ref = node
.downcast::<Element>()
.unwrap_or_else(|| parent_node.downcast::<Element>().unwrap());
Some(DomRoot::from_ref(element_ref))
},
None => self.GetDocumentElement(),
}
self.document_or_shadow_root.element_from_point(
x,
y,
self.GetDocumentElement(),
self.has_browsing_context,
)
}
#[allow(unsafe_code)]
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let window = window_from_node(self);
let viewport = window.window_size().initial_viewport;
if self.browsing_context().is_none() {
return vec![];
}
// Step 2
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return vec![];
}
let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
// Step 1 and Step 3
let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
let mut elements: Vec<DomRoot<Element>> = nodes
.iter()
.flat_map(|&untrusted_node_address| {
let node = unsafe {
node::from_untrusted_node_address(js_runtime, untrusted_node_address)
};
DomRoot::downcast::<Element>(node)
})
.collect();
// Step 4
if let Some(root_element) = self.GetDocumentElement() {
if elements.last() != Some(&root_element) {
elements.push(root_element);
}
}
// Step 5
elements
self.document_or_shadow_root.elements_from_point(
x,
y,
self.GetDocumentElement(),
self.has_browsing_context,
)
}
// https://html.spec.whatwg.org/multipage/#dom-document-open
@ -4403,7 +4294,10 @@ impl DocumentMethods for Document {
}
// Step 8
for node in self.upcast::<Node>().traverse_preorder() {
for node in self
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::Yes)
{
node.upcast::<EventTarget>().remove_all_listeners();
}

View file

@ -2,13 +2,14 @@
* 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 crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DocumentFragmentBinding;
use crate::dom::bindings::codegen::Bindings::DocumentFragmentBinding::DocumentFragmentMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
use crate::dom::bindings::error::{ErrorResult, 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::document::Document;
use crate::dom::element::Element;
@ -18,18 +19,22 @@ use crate::dom::nodelist::NodeList;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_atoms::Atom;
use std::collections::HashMap;
// https://dom.spec.whatwg.org/#documentfragment
#[dom_struct]
pub struct DocumentFragment {
node: Node,
/// Caches for the getElement methods
id_map: DomRefCell<HashMap<Atom, Vec<Dom<Element>>>>,
}
impl DocumentFragment {
/// Creates a new DocumentFragment.
fn new_inherited(document: &Document) -> DocumentFragment {
pub fn new_inherited(document: &Document) -> DocumentFragment {
DocumentFragment {
node: Node::new_inherited(document),
id_map: DomRefCell::new(HashMap::new()),
}
}
@ -46,6 +51,10 @@ impl DocumentFragment {
Ok(DocumentFragment::new(&document))
}
pub fn id_map(&self) -> &DomRefCell<HashMap<Atom, Vec<Dom<Element>>>> {
&self.id_map
}
}
impl DocumentFragmentMethods for DocumentFragment {
@ -57,16 +66,11 @@ impl DocumentFragmentMethods for DocumentFragment {
// https://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid
fn GetElementById(&self, id: DOMString) -> Option<DomRoot<Element>> {
let node = self.upcast::<Node>();
let id = Atom::from(id);
node.traverse_preorder()
.filter_map(DomRoot::downcast::<Element>)
.find(
|descendant| match descendant.get_attribute(&ns!(), &local_name!("id")) {
None => false,
Some(attr) => *attr.value().as_atom() == id,
},
)
self.id_map
.borrow()
.get(&id)
.map(|ref elements| DomRoot::from_ref(&*(*elements)[0]))
}
// https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild

View file

@ -0,0 +1,312 @@
/* 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 crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::element::Element;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlmetaelement::HTMLMetaElement;
use crate::dom::node::{self, Node, VecPreOrderInsertionHelper};
use crate::dom::window::Window;
use crate::stylesheet_set::StylesheetSetRef;
use euclid::Point2D;
use js::jsapi::JS_GetRuntime;
use script_layout_interface::message::{NodesFromPointQueryType, QueryMsg};
use script_traits::UntrustedNodeAddress;
use servo_arc::Arc;
use servo_atoms::Atom;
use std::collections::HashMap;
use std::fmt;
use style::context::QuirksMode;
use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use style::media_queries::MediaList;
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
use style::stylesheets::{CssRule, Origin, Stylesheet};
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[must_root]
pub struct StyleSheetInDocument {
#[ignore_malloc_size_of = "Arc"]
pub sheet: Arc<Stylesheet>,
pub owner: Dom<Element>,
}
impl fmt::Debug for StyleSheetInDocument {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.sheet.fmt(formatter)
}
}
impl PartialEq for StyleSheetInDocument {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.sheet, &other.sheet)
}
}
impl ToMediaListKey for StyleSheetInDocument {
fn to_media_list_key(&self) -> MediaListKey {
self.sheet.to_media_list_key()
}
}
impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
self.sheet.origin(guard)
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.sheet.quirks_mode(guard)
}
fn enabled(&self) -> bool {
self.sheet.enabled()
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.sheet.media(guard)
}
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.sheet.rules(guard)
}
}
// https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
#[must_root]
#[derive(JSTraceable, MallocSizeOf)]
pub struct DocumentOrShadowRoot {
window: Dom<Window>,
}
impl DocumentOrShadowRoot {
pub fn new(window: &Window) -> Self {
Self {
window: Dom::from_ref(window),
}
}
pub fn nodes_from_point(
&self,
client_point: &Point2D<f32>,
reflow_goal: NodesFromPointQueryType,
) -> Vec<UntrustedNodeAddress> {
if !self
.window
.layout_reflow(QueryMsg::NodesFromPointQuery(*client_point, reflow_goal))
{
return vec![];
};
self.window.layout().nodes_from_point_response()
}
#[allow(unsafe_code)]
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
pub fn element_from_point(
&self,
x: Finite<f64>,
y: Finite<f64>,
document_element: Option<DomRoot<Element>>,
has_browsing_context: bool,
) -> Option<DomRoot<Element>> {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let viewport = self.window.window_size().initial_viewport;
if !has_browsing_context {
return None;
}
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return None;
}
match self
.nodes_from_point(point, NodesFromPointQueryType::Topmost)
.first()
{
Some(address) => {
let js_runtime = unsafe { JS_GetRuntime(self.window.get_cx()) };
let node = unsafe { node::from_untrusted_node_address(js_runtime, *address) };
let parent_node = node.GetParentNode().unwrap();
let element_ref = node
.downcast::<Element>()
.unwrap_or_else(|| parent_node.downcast::<Element>().unwrap());
Some(DomRoot::from_ref(element_ref))
},
None => document_element,
}
}
#[allow(unsafe_code)]
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
pub fn elements_from_point(
&self,
x: Finite<f64>,
y: Finite<f64>,
document_element: Option<DomRoot<Element>>,
has_browsing_context: bool,
) -> Vec<DomRoot<Element>> {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let viewport = self.window.window_size().initial_viewport;
if !has_browsing_context {
return vec![];
}
// Step 2
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return vec![];
}
let js_runtime = unsafe { JS_GetRuntime(self.window.get_cx()) };
// Step 1 and Step 3
let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
let mut elements: Vec<DomRoot<Element>> = nodes
.iter()
.flat_map(|&untrusted_node_address| {
let node = unsafe {
node::from_untrusted_node_address(js_runtime, untrusted_node_address)
};
DomRoot::downcast::<Element>(node)
})
.collect();
// Step 4
if let Some(root_element) = document_element {
if elements.last() != Some(&root_element) {
elements.push(root_element);
}
}
// Step 5
elements
}
// https://html.spec.whatwg.org/multipage/#dom-document-activeelement
pub fn get_active_element(
&self,
focused_element: Option<DomRoot<Element>>,
body: Option<DomRoot<HTMLElement>>,
document_element: Option<DomRoot<Element>>,
) -> Option<DomRoot<Element>> {
// TODO: Step 2.
match focused_element {
Some(element) => Some(element), // Step 3. and 4.
None => match body {
// Step 5.
Some(body) => Some(DomRoot::upcast(body)),
None => document_element,
},
}
}
/// Remove a stylesheet owned by `owner` from the list of document sheets.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn remove_stylesheet(
owner: &Element,
s: &Arc<Stylesheet>,
mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
) {
let guard = s.shared_lock.read();
// FIXME(emilio): Would be nice to remove the clone, etc.
stylesheets.remove_stylesheet(
None,
StyleSheetInDocument {
sheet: s.clone(),
owner: Dom::from_ref(owner),
},
&guard,
);
}
/// Add a stylesheet owned by `owner` to the list of document sheets, in the
/// correct tree position.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn add_stylesheet(
owner: &Element,
mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
sheet: Arc<Stylesheet>,
insertion_point: Option<StyleSheetInDocument>,
style_shared_lock: &StyleSharedRwLock,
) {
// FIXME(emilio): It'd be nice to unify more code between the elements
// that own stylesheets, but StylesheetOwner is more about loading
// them...
debug_assert!(
owner.as_stylesheet_owner().is_some() || owner.is::<HTMLMetaElement>(),
"Wat"
);
let sheet = StyleSheetInDocument {
sheet,
owner: Dom::from_ref(owner),
};
let guard = style_shared_lock.read();
match insertion_point {
Some(ip) => {
stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
},
None => {
stylesheets.append_stylesheet(None, sheet, &guard);
},
}
}
/// Remove any existing association between the provided id and any elements in this document.
pub fn unregister_named_element(
&self,
id_map: &DomRefCell<HashMap<Atom, Vec<Dom<Element>>>>,
to_unregister: &Element,
id: &Atom,
) {
debug!(
"Removing named element {:p}: {:p} id={}",
self, to_unregister, id
);
let mut id_map = id_map.borrow_mut();
let is_empty = match id_map.get_mut(&id) {
None => false,
Some(elements) => {
let position = elements
.iter()
.position(|element| &**element == to_unregister)
.expect("This element should be in registered.");
elements.remove(position);
elements.is_empty()
},
};
if is_empty {
id_map.remove(&id);
}
}
/// Associate an element present in this document with the provided id.
pub fn register_named_element(
&self,
id_map: &DomRefCell<HashMap<Atom, Vec<Dom<Element>>>>,
element: &Element,
id: &Atom,
root: DomRoot<Node>,
) {
debug!("Adding named element {:p}: {:p} id={}", self, element, id);
assert!(element.upcast::<Node>().is_connected());
assert!(!id.is_empty());
let mut id_map = id_map.borrow_mut();
let elements = id_map.entry(id.clone()).or_insert(Vec::new());
elements.insert_pre_order(element, &root);
}
}

View file

@ -15,6 +15,7 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
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::ShadowRootBinding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
@ -32,7 +33,7 @@ use crate::dom::bindings::xmlname::{
use crate::dom::characterdata::CharacterData;
use crate::dom::create::create_element;
use crate::dom::customelementregistry::{
CallbackReaction, CustomElementDefinition, CustomElementReaction,
CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState,
};
use crate::dom::document::{Document, LayoutDocumentHelpers};
use crate::dom::documentfragment::DocumentFragment;
@ -71,11 +72,13 @@ use crate::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaEle
use crate::dom::mutationobserver::{Mutation, MutationObserver};
use crate::dom::namednodemap::NamedNodeMap;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{ChildrenMutation, LayoutNodeHelpers, Node};
use crate::dom::node::{NodeDamage, NodeFlags, UnbindContext};
use crate::dom::node::{BindContext, NodeDamage, NodeFlags, UnbindContext};
use crate::dom::node::{ChildrenMutation, LayoutNodeHelpers, Node, ShadowIncluding};
use crate::dom::nodelist::NodeList;
use crate::dom::promise::Promise;
use crate::dom::raredata::ElementRareData;
use crate::dom::servoparser::ServoParser;
use crate::dom::shadowroot::ShadowRoot;
use crate::dom::text::Text;
use crate::dom::validation::Validatable;
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
@ -103,7 +106,7 @@ use selectors::Element as SelectorsElement;
use servo_arc::Arc;
use servo_atoms::Atom;
use std::borrow::Cow;
use std::cell::{Cell, Ref};
use std::cell::{Cell, Ref, RefMut};
use std::default::Default;
use std::fmt;
use std::mem;
@ -163,13 +166,7 @@ pub struct Element {
/// when it has exclusive access to the element.
#[ignore_malloc_size_of = "bitflags defined in rust-selectors"]
selector_flags: Cell<ElementSelectorFlags>,
/// <https://html.spec.whatwg.org/multipage/#custom-element-reaction-queue>
custom_element_reaction_queue: DomRefCell<Vec<CustomElementReaction>>,
/// <https://dom.spec.whatwg.org/#concept-element-custom-element-definition>
#[ignore_malloc_size_of = "Rc"]
custom_element_definition: DomRefCell<Option<Rc<CustomElementDefinition>>>,
/// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
custom_element_state: Cell<CustomElementState>,
rare_data: DomRefCell<Option<Box<ElementRareData>>>,
}
impl fmt::Debug for Element {
@ -199,15 +196,6 @@ pub enum CustomElementCreationMode {
Asynchronous,
}
/// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
pub enum CustomElementState {
Undefined,
Failed,
Uncustomized,
Custom,
}
impl ElementCreator {
pub fn is_parser_created(&self) -> bool {
match *self {
@ -244,6 +232,13 @@ impl FromStr for AdjacentPosition {
}
}
/// Whether a shadow root hosts an User Agent widget.
#[derive(PartialEq)]
pub enum IsUserAgentWidget {
No,
Yes,
}
//
// Element methods
//
@ -294,9 +289,7 @@ impl Element {
class_list: Default::default(),
state: Cell::new(state),
selector_flags: Cell::new(ElementSelectorFlags::empty()),
custom_element_reaction_queue: Default::default(),
custom_element_definition: Default::default(),
custom_element_state: Cell::new(CustomElementState::Uncustomized),
rare_data: Default::default(),
}
}
@ -315,6 +308,8 @@ impl Element {
)
}
impl_rare_data!(ElementRareData);
pub fn restyle(&self, damage: NodeDamage) {
let doc = self.node.owner_doc();
let mut restyle = doc.ensure_pending_restyle(self);
@ -337,49 +332,60 @@ impl Element {
}
pub fn set_custom_element_state(&self, state: CustomElementState) {
self.custom_element_state.set(state);
self.ensure_rare_data().custom_element_state = state;
}
pub fn get_custom_element_state(&self) -> CustomElementState {
self.custom_element_state.get()
if let Some(rare_data) = self.rare_data().as_ref() {
return rare_data.custom_element_state;
}
CustomElementState::Undefined
}
pub fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
*self.custom_element_definition.borrow_mut() = Some(definition);
self.ensure_rare_data().custom_element_definition = Some(definition);
}
pub fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
(*self.custom_element_definition.borrow()).clone()
self.rare_data().as_ref()?.custom_element_definition.clone()
}
pub fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
self.custom_element_reaction_queue
.borrow_mut()
self.ensure_rare_data()
.custom_element_reaction_queue
.push(CustomElementReaction::Callback(function, args));
}
pub fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
self.custom_element_reaction_queue
.borrow_mut()
self.ensure_rare_data()
.custom_element_reaction_queue
.push(CustomElementReaction::Upgrade(definition));
}
pub fn clear_reaction_queue(&self) {
self.custom_element_reaction_queue.borrow_mut().clear();
if let Some(ref mut rare_data) = *self.rare_data_mut() {
rare_data.custom_element_reaction_queue.clear();
}
}
pub fn invoke_reactions(&self) {
// TODO: This is not spec compliant, as this will allow some reactions to be processed
// after clear_reaction_queue has been called.
rooted_vec!(let mut reactions);
while !self.custom_element_reaction_queue.borrow().is_empty() {
mem::swap(
&mut *reactions,
&mut *self.custom_element_reaction_queue.borrow_mut(),
);
loop {
rooted_vec!(let mut reactions);
match *self.rare_data_mut() {
Some(ref mut data) => {
mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
},
None => break,
};
if reactions.is_empty() {
break;
}
for reaction in reactions.iter() {
reaction.invoke(self);
}
reactions.clear();
}
}
@ -436,6 +442,71 @@ impl Element {
box_.clone_overflow_y() == overflow_y::computed_value::T::Hidden
})
}
fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
self.rare_data()
.as_ref()?
.shadow_root
.as_ref()
.map(|sr| DomRoot::from_ref(&**sr))
}
pub fn is_shadow_host(&self) -> bool {
self.shadow_root().is_some()
}
/// https://dom.spec.whatwg.org/#dom-element-attachshadow
/// XXX This is not exposed to web content yet. It is meant to be used
/// for UA widgets only.
pub fn attach_shadow(&self, is_ua_widget: IsUserAgentWidget) -> Fallible<DomRoot<ShadowRoot>> {
// Step 1.
if self.namespace != ns!(html) {
return Err(Error::NotSupported);
}
// Step 2.
match self.local_name() {
&local_name!("article") |
&local_name!("aside") |
&local_name!("blockquote") |
&local_name!("body") |
&local_name!("div") |
&local_name!("footer") |
&local_name!("h1") |
&local_name!("h2") |
&local_name!("h3") |
&local_name!("h4") |
&local_name!("h5") |
&local_name!("h6") |
&local_name!("header") |
&local_name!("main") |
&local_name!("nav") |
&local_name!("p") |
&local_name!("section") |
&local_name!("span") => {},
&local_name!("video") | &local_name!("audio")
if is_ua_widget == IsUserAgentWidget::Yes => {},
_ => return Err(Error::NotSupported),
};
// Step 3.
if self.is_shadow_host() {
return Err(Error::InvalidState);
}
// Steps 4, 5 and 6.
let shadow_root = ShadowRoot::new(self, &*self.node.owner_doc());
self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
shadow_root
.upcast::<Node>()
.set_containing_shadow_root(&shadow_root);
if self.is_connected() {
self.node.owner_doc().register_shadow_root(&*shadow_root);
}
Ok(shadow_root)
}
}
#[allow(unsafe_code)]
@ -534,6 +605,9 @@ pub trait LayoutElementHelpers {
fn get_state_for_layout(&self) -> ElementState;
fn insert_selector_flags(&self, flags: ElementSelectorFlags);
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
/// The shadow root this element is a host of.
#[allow(unsafe_code)]
unsafe fn get_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>>;
}
impl LayoutElementHelpers for LayoutDom<Element> {
@ -932,7 +1006,7 @@ impl LayoutElementHelpers for LayoutDom<Element> {
unsafe {
let mut current_node = Some(self.upcast::<Node>());
while let Some(node) = current_node {
current_node = node.parent_node_ref();
current_node = node.composed_parent_node_ref();
match node.downcast::<Element>().map(|el| el.unsafe_get()) {
Some(elem) => {
if let Some(attr) =
@ -996,6 +1070,17 @@ impl LayoutElementHelpers for LayoutDom<Element> {
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
unsafe { (*self.unsafe_get()).selector_flags.get().contains(flags) }
}
#[inline]
#[allow(unsafe_code)]
unsafe fn get_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>> {
(*self.unsafe_get())
.rare_data_for_layout()
.as_ref()?
.shadow_root
.as_ref()
.map(|sr| sr.to_layout())
}
}
impl Element {
@ -1040,7 +1125,7 @@ impl Element {
let inclusive_ancestor_elements = self
.upcast::<Node>()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Self>);
// Steps 3-4.
@ -1156,7 +1241,7 @@ impl Element {
.unwrap()
} else {
self.upcast::<Node>()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast)
.last()
.expect("We know inclusive_ancestors will return `self` which is an element")
@ -1165,7 +1250,10 @@ impl Element {
// https://dom.spec.whatwg.org/#locate-a-namespace-prefix
pub fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
for node in self.upcast::<Node>().inclusive_ancestors() {
for node in self
.upcast::<Node>()
.inclusive_ancestors(ShadowIncluding::No)
{
let element = node.downcast::<Element>()?;
// Step 1.
if *element.namespace() == namespace {
@ -2372,7 +2460,7 @@ impl ElementMethods for Element {
NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed),
// Step 4.
NodeTypeId::DocumentFragment => {
NodeTypeId::DocumentFragment(_) => {
let body_elem = Element::create(
QualName::new(None, ns!(html), local_name!("body")),
None,
@ -2586,6 +2674,13 @@ impl ElementMethods for Element {
let doc = document_from_node(self);
doc.enter_fullscreen(self)
}
// XXX Hidden under dom.shadowdom.enabled pref. Only exposed to be able
// to test partial Shadow DOM support for UA widgets.
// https://dom.spec.whatwg.org/#dom-element-attachshadow
fn AttachShadow(&self) -> Fallible<DomRoot<ShadowRoot>> {
self.attach_shadow(IsUserAgentWidget::No)
}
}
impl VirtualMethods for Element {
@ -2658,21 +2753,34 @@ impl VirtualMethods for Element {
None
}
});
if node.is_in_doc() {
let containing_shadow_root = self.upcast::<Node>().containing_shadow_root();
if node.is_connected() {
let value = attr.value().as_atom().clone();
match mutation {
AttributeMutation::Set(old_value) => {
if let Some(old_value) = old_value {
let old_value = old_value.as_atom().clone();
doc.unregister_named_element(self, old_value);
if let Some(ref shadow_root) = containing_shadow_root {
shadow_root.unregister_named_element(self, old_value);
} else {
doc.unregister_named_element(self, old_value);
}
}
if value != atom!("") {
doc.register_named_element(self, value);
if let Some(ref shadow_root) = containing_shadow_root {
shadow_root.register_named_element(self, value);
} else {
doc.register_named_element(self, value);
}
}
},
AttributeMutation::Removed => {
if value != atom!("") {
doc.unregister_named_element(self, value);
if let Some(ref shadow_root) = containing_shadow_root {
shadow_root.unregister_named_element(self, value);
} else {
doc.unregister_named_element(self, value);
}
}
},
}
@ -2704,22 +2812,37 @@ impl VirtualMethods for Element {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
if let Some(f) = self.as_maybe_form_control() {
f.bind_form_control_to_tree();
}
if !tree_in_doc {
let doc = document_from_node(self);
if let Some(ref shadow_root) = self.shadow_root() {
doc.register_shadow_root(&shadow_root);
let shadow_root = shadow_root.upcast::<Node>();
shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
for node in shadow_root.children() {
node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
node.bind_to_tree(context);
}
}
if !context.tree_connected {
return;
}
let doc = document_from_node(self);
if let Some(ref value) = *self.id_attribute.borrow() {
doc.register_named_element(self, value.clone());
if let Some(shadow_root) = self.upcast::<Node>().containing_shadow_root() {
shadow_root.register_named_element(self, value.clone());
} else {
doc.register_named_element(self, value.clone());
}
}
// This is used for layout optimization.
doc.increment_dom_count();
@ -2732,11 +2855,22 @@ impl VirtualMethods for Element {
f.unbind_form_control_from_tree();
}
if !context.tree_in_doc {
if !context.tree_connected {
return;
}
let doc = document_from_node(self);
if let Some(ref shadow_root) = self.shadow_root() {
doc.unregister_shadow_root(&shadow_root);
let shadow_root = shadow_root.upcast::<Node>();
shadow_root.set_flag(NodeFlags::IS_CONNECTED, false);
for node in shadow_root.children() {
node.set_flag(NodeFlags::IS_CONNECTED, false);
node.unbind_from_tree(context);
}
}
let fullscreen = doc.GetFullscreenElement();
if fullscreen.deref() == Some(self) {
doc.exit_fullscreen();
@ -2797,11 +2931,18 @@ impl<'a> SelectorsElement for DomRoot<Element> {
}
fn parent_node_is_shadow_root(&self) -> bool {
false
match self.upcast::<Node>().GetParentNode() {
None => false,
Some(node) => node.is::<ShadowRoot>(),
}
}
fn containing_shadow_host(&self) -> Option<Self> {
None
if let Some(shadow_root) = self.upcast::<Node>().containing_shadow_root() {
Some(shadow_root.Host())
} else {
None
}
}
fn match_pseudo_element(
@ -3111,7 +3252,7 @@ impl Element {
// https://html.spec.whatwg.org/multipage/#language
pub fn get_lang(&self) -> String {
self.upcast::<Node>()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.filter_map(|node| {
node.downcast::<Element>().and_then(|el| {
el.get_attribute(&ns!(xml), &local_name!("lang"))
@ -3229,9 +3370,7 @@ impl Element {
/// <https://dom.spec.whatwg.org/#connected>
pub fn is_connected(&self) -> bool {
let node = self.upcast::<Node>();
let root = node.GetRootNode();
root.is::<Document>()
self.upcast::<Node>().is_connected()
}
// https://html.spec.whatwg.org/multipage/#cannot-navigate

View file

@ -11,7 +11,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, Node, UnbindContext};
use crate::dom::node::{document_from_node, BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -64,7 +64,7 @@ impl HTMLBaseElement {
/// Update the cached base element in response to binding or unbinding from
/// a tree.
pub fn bind_unbind(&self, tree_in_doc: bool) {
if !tree_in_doc {
if !tree_in_doc || self.upcast::<Node>().containing_shadow_root().is_some() {
return;
}
@ -119,9 +119,9 @@ impl VirtualMethods for HTMLBaseElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().bind_to_tree(tree_in_doc);
self.bind_unbind(tree_in_doc);
fn bind_to_tree(&self, context: &BindContext) {
self.super_type().unwrap().bind_to_tree(context);
self.bind_unbind(context.tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {

View file

@ -14,7 +14,7 @@ use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use crate::dom::eventtarget::EventTarget;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, window_from_node, Node};
use crate::dom::node::{document_from_node, window_from_node, BindContext, Node};
use crate::dom::virtualmethods::VirtualMethods;
use cssparser::RGBA;
use dom_struct::dom_struct;
@ -149,12 +149,12 @@ impl VirtualMethods for HTMLBodyElement {
.attribute_affects_presentational_hints(attr)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
if !tree_in_doc {
if !context.tree_in_doc {
return;
}

View file

@ -18,7 +18,7 @@ use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmlformelement::{FormControl, FormDatum, FormDatumValue};
use crate::dom::htmlformelement::{FormSubmitter, ResetFrom, SubmittedFrom};
use crate::dom::node::{document_from_node, window_from_node, Node, UnbindContext};
use crate::dom::node::{document_from_node, window_from_node, BindContext, Node, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::validation::Validatable;
use crate::dom::validitystate::{ValidationFlags, ValidityState};
@ -232,9 +232,9 @@ impl VirtualMethods for HTMLButtonElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.upcast::<Element>()

View file

@ -28,7 +28,7 @@ use crate::dom::htmlhtmlelement::HTMLHtmlElement;
use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
use crate::dom::htmllabelelement::HTMLLabelElement;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{Node, NodeFlags};
use crate::dom::node::{BindContext, Node, NodeFlags, ShadowIncluding};
use crate::dom::nodelist::NodeList;
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
@ -459,7 +459,7 @@ impl HTMLElementMethods for HTMLElement {
let element = self.upcast::<Element>();
// Step 1.
let element_not_rendered = !node.is_in_doc() || !element.has_css_layout_box();
let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
if element_not_rendered {
return node.GetTextContent().unwrap();
}
@ -707,7 +707,7 @@ impl HTMLElement {
let root_element = element.root_element();
let root_node = root_element.upcast::<Node>();
let children = root_node
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.filter(|elem| elem.is::<HTMLLabelElement>())
.filter(|elem| elem.get_string_attribute(&local_name!("for")) == id)
@ -740,9 +740,9 @@ impl VirtualMethods for HTMLElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.update_sequentially_focusable_status();
}

View file

@ -13,7 +13,7 @@ use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
use crate::dom::htmllegendelement::HTMLLegendElement;
use crate::dom::node::{window_from_node, Node};
use crate::dom::node::{window_from_node, Node, ShadowIncluding};
use crate::dom::validitystate::ValidityState;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@ -128,7 +128,7 @@ impl VirtualMethods for HTMLFieldSetElement {
});
let fields = children.flat_map(|child| {
child
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter(|descendant| match descendant.type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(
HTMLElementTypeId::HTMLButtonElement,

View file

@ -41,7 +41,8 @@ use crate::dom::htmloutputelement::HTMLOutputElement;
use crate::dom::htmlselectelement::HTMLSelectElement;
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{Node, NodeFlags, UnbindContext, VecPreOrderInsertionHelper};
use crate::dom::node::{Node, NodeFlags, ShadowIncluding};
use crate::dom::node::{UnbindContext, VecPreOrderInsertionHelper};
use crate::dom::validitystate::ValidationFlags;
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window;
@ -582,7 +583,7 @@ impl HTMLFormElement {
// form, refactor this when html5ever's form owner PR lands
// Step 1-3
let invalid_controls = node
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(|field| {
if let Some(el) = field.downcast::<Element>() {
if el.disabled_state() {
@ -1100,7 +1101,7 @@ pub trait FormControl: DomObject {
let form_id = elem.get_string_attribute(&local_name!("form"));
let node = elem.upcast::<Node>();
if self.is_listed() && !form_id.is_empty() && node.is_in_doc() {
if self.is_listed() && !form_id.is_empty() && node.is_connected() {
let doc = document_from_node(node);
doc.register_form_id_listener(form_id, self);
}

View file

@ -10,7 +10,7 @@ use crate::dom::document::{determine_policy_for_token, Document};
use crate::dom::element::Element;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlmetaelement::HTMLMetaElement;
use crate::dom::node::{document_from_node, Node};
use crate::dom::node::{document_from_node, BindContext, Node, ShadowIncluding};
use crate::dom::userscripts::load_script;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@ -55,7 +55,7 @@ impl HTMLHeadElement {
let node = self.upcast::<Node>();
let candidates = node
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.filter(|elem| elem.is::<HTMLMetaElement>())
.filter(|elem| elem.get_string_attribute(&local_name!("name")) == "referrer")
@ -81,9 +81,9 @@ impl VirtualMethods for HTMLHeadElement {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
load_script(self);
}

View file

@ -19,7 +19,9 @@ use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext};
use crate::dom::node::{
document_from_node, window_from_node, BindContext, Node, NodeDamage, UnbindContext,
};
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::ReflowReason;
use crate::dom::windowproxy::WindowProxy;
@ -584,7 +586,7 @@ impl VirtualMethods for HTMLIFrameElement {
// may be in a different script thread. Instread, we check to see if the parent
// is in a document tree and has a browsing context, which is what causes
// the child browsing context to be created.
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
if self.upcast::<Node>().is_connected_with_browsing_context() {
debug!("iframe src set while in browsing context.");
self.process_the_iframe_attributes(ProcessingMode::NotFirstTime);
}
@ -610,11 +612,12 @@ impl VirtualMethods for HTMLIFrameElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
let tree_connected = context.tree_connected;
let iframe = Trusted::new(self);
document_from_node(self).add_delayed_task(task!(IFrameDelayedInitialize: move || {
let this = iframe.root();
@ -624,9 +627,9 @@ impl VirtualMethods for HTMLIFrameElement {
// browsing context, set the element's nested browsing context
// to the newly-created browsing context, and then process the
// iframe attributes for the "first time"."
if this.upcast::<Node>().is_in_doc_with_browsing_context() {
if this.upcast::<Node>().is_connected_with_browsing_context() {
debug!("iframe bound to browsing context.");
debug_assert!(tree_in_doc, "is_in_doc_with_bc, but not tree_in_doc");
debug_assert!(tree_connected, "is_connected_with_bc, but not tree_connected");
this.create_nested_browsing_context();
this.process_the_iframe_attributes(ProcessingMode::FirstTime);
}

View file

@ -32,7 +32,10 @@ use crate::dom::htmlmapelement::HTMLMapElement;
use crate::dom::htmlpictureelement::HTMLPictureElement;
use crate::dom::htmlsourceelement::HTMLSourceElement;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext};
use crate::dom::node::UnbindContext;
use crate::dom::node::{
document_from_node, window_from_node, BindContext, Node, NodeDamage, ShadowIncluding,
};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::progressevent::ProgressEvent;
use crate::dom::values::UNSIGNED_LONG_MAX;
@ -1259,7 +1262,7 @@ impl HTMLImageElement {
let useMapElements = document_from_node(self)
.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLMapElement>)
.find(|n| {
n.upcast::<Element>()
@ -1645,12 +1648,12 @@ impl VirtualMethods for HTMLImageElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
let document = document_from_node(self);
if tree_in_doc {
if context.tree_connected {
document.register_responsive_image(self);
}

View file

@ -35,7 +35,7 @@ use crate::dom::htmlformelement::{ResetFrom, SubmittedFrom};
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{Node, NodeDamage, UnbindContext};
use crate::dom::node::{BindContext, Node, NodeDamage, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
use crate::dom::validation::Validatable;
@ -1422,9 +1422,9 @@ impl VirtualMethods for HTMLInputElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.upcast::<Element>()
.check_ancestors_disabled_state_for_form_control();

View file

@ -15,7 +15,7 @@ use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
use crate::dom::node::{document_from_node, Node};
use crate::dom::node::{document_from_node, Node, ShadowIncluding};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -162,7 +162,7 @@ impl VirtualMethods for HTMLLabelElement {
impl HTMLLabelElement {
pub fn first_labelable_descendant(&self) -> Option<DomRoot<HTMLElement>> {
self.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLElement>)
.filter(|elem| elem.is_labelable_element())
.next()

View file

@ -12,7 +12,7 @@ use crate::dom::element::Element;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
use crate::dom::node::{Node, UnbindContext};
use crate::dom::node::{BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -56,9 +56,9 @@ impl VirtualMethods for HTMLLegendElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.upcast::<Element>()

View file

@ -18,7 +18,10 @@ use crate::dom::element::{
};
use crate::dom::element::{AttributeMutation, Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, window_from_node, Node, UnbindContext};
use crate::dom::node::{
document_from_node, stylesheets_owner_from_node, window_from_node, BindContext, Node,
UnbindContext,
};
use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
use crate::dom::virtualmethods::VirtualMethods;
use crate::stylesheet_loader::{StylesheetContextSource, StylesheetLoader, StylesheetOwner};
@ -107,14 +110,15 @@ impl HTMLLinkElement {
// FIXME(emilio): These methods are duplicated with
// HTMLStyleElement::set_stylesheet.
#[allow(unrooted_must_root)]
pub fn set_stylesheet(&self, s: Arc<Stylesheet>) {
let doc = document_from_node(self);
let stylesheets_owner = stylesheets_owner_from_node(self);
if let Some(ref s) = *self.stylesheet.borrow() {
doc.remove_stylesheet(self.upcast(), s)
stylesheets_owner.remove_stylesheet(self.upcast(), s)
}
*self.stylesheet.borrow_mut() = Some(s.clone());
self.cssom_stylesheet.set(None);
doc.add_stylesheet(self.upcast(), s);
stylesheets_owner.add_stylesheet(self.upcast(), s);
}
pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
@ -183,7 +187,7 @@ impl VirtualMethods for HTMLLinkElement {
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
if !self.upcast::<Node>().is_in_doc() || mutation.is_removal() {
if !self.upcast::<Node>().is_connected() || mutation.is_removal() {
return;
}
@ -222,12 +226,12 @@ impl VirtualMethods for HTMLLinkElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
if tree_in_doc {
if context.tree_connected {
let element = self.upcast();
let rel = get_attr(element, &local_name!("rel"));
@ -252,7 +256,7 @@ impl VirtualMethods for HTMLLinkElement {
}
if let Some(s) = self.stylesheet.borrow_mut().take() {
document_from_node(self).remove_stylesheet(self.upcast(), &s);
stylesheets_owner_from_node(self).remove_stylesheet(self.upcast(), &s);
}
}
}

View file

@ -8,7 +8,7 @@ use crate::dom::bindings::root::DomRoot;
use crate::dom::document::Document;
use crate::dom::htmlareaelement::HTMLAreaElement;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::Node;
use crate::dom::node::{Node, ShadowIncluding};
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -43,7 +43,7 @@ impl HTMLMapElement {
pub fn get_area_elements(&self) -> Vec<DomRoot<HTMLAreaElement>> {
self.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLAreaElement>)
.collect()
}

View file

@ -2009,7 +2009,7 @@ impl VirtualMethods for HTMLMediaElement {
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
if context.tree_in_doc {
if context.tree_connected {
let task = MediaElementMicrotask::PauseIfNotInDocumentTask {
elem: DomRoot::from_ref(self),
};
@ -2061,7 +2061,7 @@ impl MicrotaskRunnable for MediaElementMicrotask {
}
},
&MediaElementMicrotask::PauseIfNotInDocumentTask { ref elem } => {
if !elem.upcast::<Node>().is_in_doc() {
if !elem.upcast::<Node>().is_connected() {
elem.internal_pause_steps();
}
},

View file

@ -15,7 +15,10 @@ use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlheadelement::HTMLHeadElement;
use crate::dom::node::{document_from_node, window_from_node, Node, UnbindContext};
use crate::dom::node::{
document_from_node, stylesheets_owner_from_node, window_from_node, BindContext, Node,
UnbindContext,
};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -97,6 +100,7 @@ impl HTMLMetaElement {
}
}
#[allow(unrooted_must_root)]
fn apply_viewport(&self) {
if !pref!(layout.viewport.enabled) {
return;
@ -106,6 +110,7 @@ impl HTMLMetaElement {
let content = content.value();
if !content.is_empty() {
if let Some(translated_rule) = ViewportRule::from_meta(&**content) {
let stylesheets_owner = stylesheets_owner_from_node(self);
let document = document_from_node(self);
let shared_lock = document.style_shared_lock();
let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule)));
@ -124,7 +129,7 @@ impl HTMLMetaElement {
disabled: AtomicBool::new(false),
});
*self.stylesheet.borrow_mut() = Some(sheet.clone());
document.add_stylesheet(self.upcast(), sheet);
stylesheets_owner.add_stylesheet(self.upcast(), sheet);
}
}
}
@ -171,12 +176,12 @@ impl VirtualMethods for HTMLMetaElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
if tree_in_doc {
if context.tree_connected {
self.process_attributes();
}
}
@ -204,11 +209,11 @@ impl VirtualMethods for HTMLMetaElement {
s.unbind_from_tree(context);
}
if context.tree_in_doc {
if context.tree_connected {
self.process_referrer_attribute();
if let Some(s) = self.stylesheet.borrow_mut().take() {
document_from_node(self).remove_stylesheet(self.upcast(), &s);
stylesheets_owner_from_node(self).remove_stylesheet(self.upcast(), &s);
}
}
}

View file

@ -19,7 +19,7 @@ use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
use crate::dom::htmlscriptelement::HTMLScriptElement;
use crate::dom::htmlselectelement::HTMLSelectElement;
use crate::dom::node::{Node, UnbindContext};
use crate::dom::node::{BindContext, Node, ShadowIncluding, UnbindContext};
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@ -235,9 +235,9 @@ impl VirtualMethods for HTMLOptionElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.upcast::<Element>()
@ -251,7 +251,7 @@ impl VirtualMethods for HTMLOptionElement {
if let Some(select) = context
.parent
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLSelectElement>)
.next()
{

View file

@ -22,7 +22,7 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node};
use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
@ -372,7 +372,7 @@ impl HTMLScriptElement {
}
// Step 5.
if !self.upcast::<Node>().is_in_doc() {
if !self.upcast::<Node>().is_connected() {
return;
}
@ -760,7 +760,7 @@ impl VirtualMethods for HTMLScriptElement {
match *attr.local_name() {
local_name!("src") => {
if let AttributeMutation::Set(_) = mutation {
if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
@ -773,17 +773,17 @@ impl VirtualMethods for HTMLScriptElement {
if let Some(ref s) = self.super_type() {
s.children_changed(mutation);
}
if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
if tree_in_doc && !self.parser_inserted.get() {
if context.tree_connected && !self.parser_inserted.get() {
let script = Trusted::new(self);
document_from_node(self).add_delayed_task(task!(ScriptDelayedInitialize: move || {
script.root().prepare();

View file

@ -12,7 +12,6 @@ use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelec
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
use crate::dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
//use dom::bindings::error::ErrorResult;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
@ -25,7 +24,7 @@ use crate::dom::htmlformelement::{FormControl, FormDatum, FormDatumValue, HTMLFo
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
use crate::dom::htmloptionelement::HTMLOptionElement;
use crate::dom::htmloptionscollection::HTMLOptionsCollection;
use crate::dom::node::{window_from_node, Node, UnbindContext};
use crate::dom::node::{window_from_node, BindContext, Node, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::validation::Validatable;
use crate::dom::validitystate::{ValidationFlags, ValidityState};
@ -382,9 +381,9 @@ impl VirtualMethods for HTMLSelectElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.upcast::<Element>()

View file

@ -15,7 +15,7 @@ use crate::dom::element::AttributeMutation;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlmediaelement::HTMLMediaElement;
use crate::dom::node::{Node, UnbindContext};
use crate::dom::node::{BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -82,8 +82,8 @@ impl VirtualMethods for HTMLSourceElement {
}
/// <https://html.spec.whatwg.org/multipage/#the-source-element:nodes-are-inserted>
fn bind_to_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().bind_to_tree(tree_in_doc);
fn bind_to_tree(&self, context: &BindContext) {
self.super_type().unwrap().bind_to_tree(context);
let parent = self.upcast::<Node>().GetParentNode().unwrap();
if let Some(media) = parent.downcast::<HTMLMediaElement>() {
media.handle_source_child_insertion();

View file

@ -13,7 +13,8 @@ use crate::dom::document::Document;
use crate::dom::element::{Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{
document_from_node, window_from_node, ChildrenMutation, Node, UnbindContext,
document_from_node, stylesheets_owner_from_node, window_from_node, BindContext,
ChildrenMutation, Node, UnbindContext,
};
use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
use crate::dom::virtualmethods::VirtualMethods;
@ -81,7 +82,7 @@ impl HTMLStyleElement {
pub fn parse_own_css(&self) {
let node = self.upcast::<Node>();
let element = self.upcast::<Element>();
assert!(node.is_in_doc());
assert!(node.is_connected());
let window = window_from_node(node);
let doc = document_from_node(self);
@ -137,14 +138,15 @@ impl HTMLStyleElement {
}
// FIXME(emilio): This is duplicated with HTMLLinkElement::set_stylesheet.
#[allow(unrooted_must_root)]
pub fn set_stylesheet(&self, s: Arc<Stylesheet>) {
let doc = document_from_node(self);
let stylesheets_owner = stylesheets_owner_from_node(self);
if let Some(ref s) = *self.stylesheet.borrow() {
doc.remove_stylesheet(self.upcast(), s)
stylesheets_owner.remove_stylesheet(self.upcast(), s)
}
*self.stylesheet.borrow_mut() = Some(s.clone());
self.cssom_stylesheet.set(None);
doc.add_stylesheet(self.upcast(), s);
stylesheets_owner.add_stylesheet(self.upcast(), s);
}
pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
@ -185,14 +187,14 @@ impl VirtualMethods for HTMLStyleElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().bind_to_tree(tree_in_doc);
fn bind_to_tree(&self, context: &BindContext) {
self.super_type().unwrap().bind_to_tree(context);
// https://html.spec.whatwg.org/multipage/#update-a-style-block
// Handles the case when:
// "The element is not on the stack of open elements of an HTML parser or XML parser,
// and it becomes connected or disconnected."
if tree_in_doc && !self.in_stack_of_open_elements.get() {
if context.tree_connected && !self.in_stack_of_open_elements.get() {
self.parse_own_css();
}
}
@ -214,9 +216,9 @@ impl VirtualMethods for HTMLStyleElement {
s.unbind_from_tree(context);
}
if context.tree_in_doc {
if context.tree_connected {
if let Some(s) = self.stylesheet.borrow_mut().take() {
document_from_node(self).remove_stylesheet(self.upcast(), &s)
stylesheets_owner_from_node(self).remove_stylesheet(self.upcast(), &s)
}
}
}

View file

@ -24,7 +24,9 @@ use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext};
use crate::dom::node::{
BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext,
};
use crate::dom::nodelist::NodeList;
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
use crate::dom::validation::Validatable;
@ -466,9 +468,9 @@ impl VirtualMethods for HTMLTextAreaElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
self.upcast::<Element>()

View file

@ -10,7 +10,7 @@ use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{ChildrenMutation, Node};
use crate::dom::node::{BindContext, ChildrenMutation, Node};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@ -74,12 +74,12 @@ impl VirtualMethods for HTMLTitleElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
let node = self.upcast::<Node>();
if tree_in_doc {
if context.tree_in_doc {
node.owner_doc().title_changed();
}
}

View file

@ -632,3 +632,31 @@ macro_rules! handle_potential_webgl_error {
handle_potential_webgl_error!($context, $call, ());
};
}
macro_rules! impl_rare_data (
($type:ty) => (
fn rare_data(&self) -> Ref<Option<Box<$type>>> {
self.rare_data.borrow()
}
#[allow(dead_code)]
fn rare_data_mut(&self) -> RefMut<Option<Box<$type>>> {
self.rare_data.borrow_mut()
}
fn ensure_rare_data(&self) -> RefMut<Box<$type>> {
let mut rare_data = self.rare_data.borrow_mut();
if rare_data.is_none() {
*rare_data = Some(Default::default());
}
RefMut::map(rare_data, |rare_data| {
rare_data.as_mut().unwrap()
})
}
#[allow(unsafe_code)]
fn rare_data_for_layout(&self) -> &Option<Box<$type>> {
unsafe { self.rare_data.borrow_for_layout() }
}
);
);

View file

@ -280,6 +280,7 @@ pub mod dissimilaroriginlocation;
pub mod dissimilaroriginwindow;
pub mod document;
pub mod documentfragment;
pub mod documentorshadowroot;
pub mod documenttype;
pub mod domexception;
pub mod domimplementation;
@ -442,6 +443,7 @@ pub mod promisenativehandler;
pub mod promiserejectionevent;
pub mod radionodelist;
pub mod range;
pub mod raredata;
pub mod request;
pub mod response;
pub mod rtcicecandidate;
@ -454,6 +456,7 @@ pub mod serviceworkercontainer;
pub mod serviceworkerglobalscope;
pub mod serviceworkerregistration;
pub mod servoparser;
pub mod shadowroot;
pub mod storage;
pub mod storageevent;
pub mod stylepropertymapreadonly;

View file

@ -13,7 +13,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::mutationrecord::MutationRecord;
use crate::dom::node::Node;
use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::window::Window;
use crate::microtask::Microtask;
use crate::script_thread::ScriptThread;
@ -131,7 +131,7 @@ impl MutationObserver {
let mut interested_observers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];
// Step 2 & 3
for node in target.inclusive_ancestors() {
for node in target.inclusive_ancestors(ShadowIncluding::No) {
for registered in &*node.registered_mutation_observers() {
if &*node != target && !registered.options.subtree {
continue;
@ -318,20 +318,18 @@ impl MutationObserverMethods for MutationObserver {
// Step 8
if add_new_observer {
target
.registered_mutation_observers()
.push(RegisteredObserver {
observer: DomRoot::from_ref(self),
options: ObserverOptions {
attributes,
attribute_old_value,
character_data,
character_data_old_value,
subtree,
attribute_filter,
child_list,
},
});
target.add_mutation_observer(RegisteredObserver {
observer: DomRoot::from_ref(self),
options: ObserverOptions {
attributes,
attribute_old_value,
character_data,
character_data_old_value,
subtree,
attribute_filter,
child_list,
},
});
self.node_list.borrow_mut().push(DomRoot::from_ref(target));
}

View file

@ -10,10 +10,14 @@ use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterData
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
use crate::dom::bindings::codegen::Bindings::NodeBinding::{
GetRootNodeOptions, NodeConstants, NodeMethods,
};
use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootBinding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::InheritTypes::DocumentFragmentTypeId;
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
use crate::dom::bindings::conversions::{self, DerivedFrom};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
@ -49,6 +53,9 @@ use crate::dom::mutationobserver::{Mutation, MutationObserver, RegisteredObserve
use crate::dom::nodelist::NodeList;
use crate::dom::processinginstruction::ProcessingInstruction;
use crate::dom::range::WeakRangeVec;
use crate::dom::raredata::NodeRareData;
use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot};
use crate::dom::stylesheetlist::StyleSheetListOwner;
use crate::dom::svgsvgelement::{LayoutSVGSVGElementHelpers, SVGSVGElement};
use crate::dom::text::Text;
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
@ -76,7 +83,7 @@ use servo_arc::Arc;
use servo_url::ServoUrl;
use smallvec::SmallVec;
use std::borrow::ToOwned;
use std::cell::{Cell, RefMut, UnsafeCell};
use std::cell::{Cell, Ref, RefMut, UnsafeCell};
use std::cmp;
use std::default::Default;
use std::iter;
@ -118,6 +125,9 @@ pub struct Node {
/// The document that this node belongs to.
owner_doc: MutNullableDom<Document>,
/// Rare node data.
rare_data: DomRefCell<Option<Box<NodeRareData>>>,
/// The live list of children return by .childNodes.
child_list: MutNullableDom<NodeList>,
@ -142,9 +152,6 @@ pub struct Node {
/// node is finalized.
style_and_layout_data: Cell<Option<OpaqueStyleAndLayoutData>>,
/// Registered observers for this node.
mutation_observers: DomRefCell<Vec<RegisteredObserver>>,
unique_id: UniqueId,
}
@ -181,6 +188,12 @@ bitflags! {
/// Whether this element has already handled the stored snapshot.
const HANDLED_SNAPSHOT = 1 << 8;
/// Whether this node participates in a shadow tree.
const IS_IN_SHADOW_TREE = 1 << 9;
/// Specifies whether this node's shadow-including root is a document.
const IS_CONNECTED = 1 << 10;
}
}
@ -264,11 +277,25 @@ impl Node {
self.children_count.set(self.children_count.get() + 1);
let parent_in_doc = self.is_in_doc();
for node in new_child.traverse_preorder() {
let parent_in_shadow_tree = self.is_in_shadow_tree();
let parent_is_connected = self.is_connected();
for node in new_child.traverse_preorder(ShadowIncluding::No) {
if parent_in_shadow_tree {
if let Some(shadow_root) = self.containing_shadow_root() {
node.set_containing_shadow_root(&*shadow_root);
}
debug_assert!(node.containing_shadow_root().is_some());
}
node.set_flag(NodeFlags::IS_IN_DOC, parent_in_doc);
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, parent_in_shadow_tree);
node.set_flag(NodeFlags::IS_CONNECTED, parent_is_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(parent_in_doc);
vtable_for(&&*node).bind_to_tree(&BindContext {
tree_connected: parent_is_connected,
tree_in_doc: parent_in_doc,
});
}
}
@ -312,17 +339,18 @@ impl Node {
child.parent_node.set(None);
self.children_count.set(self.children_count.get() - 1);
for node in child.traverse_preorder() {
for node in child.traverse_preorder(ShadowIncluding::Yes) {
// Out-of-document elements never have the descendants flag set.
node.set_flag(
NodeFlags::IS_IN_DOC |
NodeFlags::IS_CONNECTED |
NodeFlags::HAS_DIRTY_DESCENDANTS |
NodeFlags::HAS_SNAPSHOT |
NodeFlags::HANDLED_SNAPSHOT,
false,
);
}
for node in child.traverse_preorder() {
for node in child.traverse_preorder(ShadowIncluding::Yes) {
// This needs to be in its own loop, because unbind_from_tree may
// rely on the state of IS_IN_DOC of the context node's descendants,
// e.g. when removing a <form>.
@ -403,6 +431,8 @@ impl<'a> Iterator for QuerySelectorIterator {
}
impl Node {
impl_rare_data!(NodeRareData);
pub fn teardown(&self) {
self.style_and_layout_data.get().map(|d| self.dispose(d));
for kid in self.children() {
@ -422,14 +452,26 @@ impl Node {
}
/// Return all registered mutation observers for this node.
/// XXX(ferjm) This should probably be split into two functions,
/// `registered_mutation_observers`, which returns an Option or
/// an empty slice or something, and doesn't create the rare data,
/// and `registered_mutation_observers_mut`, which does lazily
/// initialize the raredata.
pub fn registered_mutation_observers(&self) -> RefMut<Vec<RegisteredObserver>> {
self.mutation_observers.borrow_mut()
RefMut::map(self.ensure_rare_data(), |rare_data| {
&mut rare_data.mutation_observers
})
}
/// Add a new mutation observer for a given node.
pub fn add_mutation_observer(&self, observer: RegisteredObserver) {
self.ensure_rare_data().mutation_observers.push(observer);
}
/// Removes the mutation observer for a given node.
pub fn remove_mutation_observer(&self, observer: &MutationObserver) {
self.mutation_observers
.borrow_mut()
self.ensure_rare_data()
.mutation_observers
.retain(|reg_obs| &*reg_obs.observer != observer)
}
@ -463,6 +505,14 @@ impl Node {
self.flags.get().contains(NodeFlags::IS_IN_DOC)
}
pub fn is_in_shadow_tree(&self) -> bool {
self.flags.get().contains(NodeFlags::IS_IN_SHADOW_TREE)
}
pub fn is_connected(&self) -> bool {
self.flags.get().contains(NodeFlags::IS_CONNECTED)
}
/// Returns the type ID of this node.
pub fn type_id(&self) -> NodeTypeId {
match *self.eventtarget.type_id() {
@ -521,14 +571,16 @@ impl Node {
// FIXME(emilio): This and the function below should move to Element.
pub fn note_dirty_descendants(&self) {
debug_assert!(self.is_in_doc());
debug_assert!(self.is_connected());
for ancestor in self.inclusive_ancestors() {
for ancestor in self.inclusive_ancestors(ShadowIncluding::Yes) {
if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
return;
}
ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
if ancestor.is::<Element>() {
ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
}
}
}
@ -546,7 +598,7 @@ impl Node {
self.inclusive_descendants_version(),
doc.inclusive_descendants_version(),
) + 1;
for ancestor in self.inclusive_ancestors() {
for ancestor in self.inclusive_ancestors(ShadowIncluding::No) {
ancestor.inclusive_descendants_version.set(version);
}
doc.inclusive_descendants_version.set(version);
@ -554,19 +606,21 @@ impl Node {
pub fn dirty(&self, damage: NodeDamage) {
self.rev_version();
if !self.is_in_doc() {
if !self.is_connected() {
return;
}
match self.type_id() {
NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => self
.parent_node
.get()
.unwrap()
.downcast::<Element>()
.unwrap()
.restyle(damage),
NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
self.parent_node.get().unwrap().dirty(damage)
},
NodeTypeId::Element(_) => self.downcast::<Element>().unwrap().restyle(damage),
NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) => self
.downcast::<ShadowRoot>()
.unwrap()
.Host()
.upcast::<Element>()
.restyle(damage),
_ => {},
};
}
@ -577,8 +631,8 @@ impl Node {
}
/// Iterates over this node and all its descendants, in preorder.
pub fn traverse_preorder(&self) -> TreeIterator {
TreeIterator::new(self)
pub fn traverse_preorder(&self, shadow_including: ShadowIncluding) -> TreeIterator {
TreeIterator::new(self, shadow_including)
}
pub fn inclusively_following_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
@ -603,6 +657,11 @@ impl Node {
parent.ancestors().any(|ancestor| &*ancestor == self)
}
fn is_shadow_including_inclusive_ancestor_of(&self, node: &Node) -> bool {
node.inclusive_ancestors(ShadowIncluding::Yes)
.any(|ancestor| &*ancestor == self)
}
pub fn following_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
SimpleNodeIterator {
current: self.GetNextSibling(),
@ -820,7 +879,7 @@ impl Node {
self.owner_doc().quirks_mode(),
);
Ok(self
.traverse_preorder()
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast)
.find(|element| matches_selector_list(&selectors, element, &mut ctx)))
},
@ -838,7 +897,7 @@ impl Node {
Err(_) => Err(Error::Syntax),
// Step 3.
Ok(selectors) => {
let mut descendants = self.traverse_preorder();
let mut descendants = self.traverse_preorder(ShadowIncluding::No);
// Skip the root of the tree.
assert!(&*descendants.next().unwrap() == self);
Ok(QuerySelectorIterator::new(descendants, selectors))
@ -861,10 +920,21 @@ impl Node {
}
}
pub fn inclusive_ancestors(&self) -> impl Iterator<Item = DomRoot<Node>> {
/// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
pub fn inclusive_ancestors(
&self,
shadow_including: ShadowIncluding,
) -> impl Iterator<Item = DomRoot<Node>> {
SimpleNodeIterator {
current: Some(DomRoot::from_ref(self)),
next_node: |n| n.GetParentNode(),
next_node: move |n| {
if shadow_including == ShadowIncluding::Yes {
if let Some(shadow_root) = n.downcast::<ShadowRoot>() {
return Some(DomRoot::from_ref(shadow_root.Host().upcast::<Node>()));
}
}
n.GetParentNode()
},
}
}
@ -876,12 +946,24 @@ impl Node {
self.owner_doc.set(Some(document));
}
pub fn containing_shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
self.rare_data()
.as_ref()?
.containing_shadow_root
.as_ref()
.map(|sr| DomRoot::from_ref(&**sr))
}
pub fn set_containing_shadow_root(&self, shadow_root: &ShadowRoot) {
self.ensure_rare_data().containing_shadow_root = Some(Dom::from_ref(shadow_root));
}
pub fn is_in_html_doc(&self) -> bool {
self.owner_doc().is_html_document()
}
pub fn is_in_doc_with_browsing_context(&self) -> bool {
self.is_in_doc() && self.owner_doc().browsing_context().is_some()
pub fn is_connected_with_browsing_context(&self) -> bool {
self.is_connected() && self.owner_doc().browsing_context().is_some()
}
pub fn children(&self) -> impl Iterator<Item = DomRoot<Node>> {
@ -1042,6 +1124,27 @@ impl Node {
None
}
}
/// https://dom.spec.whatwg.org/#retarget
pub fn retarget(&self, b: &Node) -> DomRoot<Node> {
let mut a = DomRoot::from_ref(&*self);
loop {
// Step 1.
let a_root = a.GetRootNode(&GetRootNodeOptions::empty());
if !a_root.is::<ShadowRoot>() || a_root.is_shadow_including_inclusive_ancestor_of(b) {
return DomRoot::from_ref(&a);
}
// Step 2.
a = DomRoot::from_ref(
a_root
.downcast::<ShadowRoot>()
.unwrap()
.Host()
.upcast::<Node>(),
);
}
}
}
/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
@ -1080,13 +1183,14 @@ pub unsafe fn from_untrusted_node_address(
pub trait LayoutNodeHelpers {
unsafe fn type_id_for_layout(&self) -> NodeTypeId;
unsafe fn parent_node_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn composed_parent_node_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn first_child_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn last_child_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn prev_sibling_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn next_sibling_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn owner_doc_for_layout(&self) -> LayoutDom<Document>;
unsafe fn containing_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>>;
unsafe fn is_element_for_layout(&self) -> bool;
unsafe fn get_flag(&self, flag: NodeFlags) -> bool;
@ -1126,8 +1230,14 @@ impl LayoutNodeHelpers for LayoutDom<Node> {
#[inline]
#[allow(unsafe_code)]
unsafe fn parent_node_ref(&self) -> Option<LayoutDom<Node>> {
(*self.unsafe_get()).parent_node.get_inner_as_layout()
unsafe fn composed_parent_node_ref(&self) -> Option<LayoutDom<Node>> {
let parent = (*self.unsafe_get()).parent_node.get_inner_as_layout();
if let Some(ref parent) = parent {
if let Some(shadow_root) = parent.downcast::<ShadowRoot>() {
return Some(shadow_root.get_host_for_layout().upcast());
}
}
parent
}
#[inline]
@ -1163,6 +1273,17 @@ impl LayoutNodeHelpers for LayoutDom<Node> {
.unwrap()
}
#[inline]
#[allow(unsafe_code)]
unsafe fn containing_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>> {
(*self.unsafe_get())
.rare_data_for_layout()
.as_ref()?
.containing_shadow_root
.as_ref()
.map(|sr| sr.to_layout())
}
#[inline]
#[allow(unsafe_code)]
unsafe fn get_flag(&self, flag: NodeFlags) -> bool {
@ -1325,7 +1446,7 @@ impl FollowingNodeIterator {
return current.GetNextSibling();
}
for ancestor in current.inclusive_ancestors() {
for ancestor in current.inclusive_ancestors(ShadowIncluding::No) {
if self.root == ancestor {
break;
}
@ -1405,16 +1526,25 @@ where
}
}
/// Whether a tree traversal should pass shadow tree boundaries.
#[derive(PartialEq)]
pub enum ShadowIncluding {
No,
Yes,
}
pub struct TreeIterator {
current: Option<DomRoot<Node>>,
depth: usize,
shadow_including: bool,
}
impl TreeIterator {
fn new(root: &Node) -> TreeIterator {
fn new(root: &Node, shadow_including: ShadowIncluding) -> TreeIterator {
TreeIterator {
current: Some(DomRoot::from_ref(root)),
depth: 0,
shadow_including: shadow_including == ShadowIncluding::Yes,
}
}
@ -1425,7 +1555,13 @@ impl TreeIterator {
}
fn next_skipping_children_impl(&mut self, current: DomRoot<Node>) -> Option<DomRoot<Node>> {
for ancestor in current.inclusive_ancestors() {
let iter = current.inclusive_ancestors(if self.shadow_including {
ShadowIncluding::Yes
} else {
ShadowIncluding::No
});
for ancestor in iter {
if self.depth == 0 {
break;
}
@ -1445,8 +1581,18 @@ impl Iterator for TreeIterator {
type Item = DomRoot<Node>;
// https://dom.spec.whatwg.org/#concept-tree-order
// https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
fn next(&mut self) -> Option<DomRoot<Node>> {
let current = self.current.take()?;
if !self.shadow_including {
if let Some(element) = current.downcast::<Element>() {
if element.is_shadow_host() {
return self.next_skipping_children_impl(current);
}
}
}
if let Some(first_child) = current.GetFirstChild() {
self.current = Some(first_child);
self.depth += 1;
@ -1487,7 +1633,10 @@ impl Node {
#[allow(unrooted_must_root)]
pub fn new_document_node() -> Node {
Node::new_(NodeFlags::new() | NodeFlags::IS_IN_DOC, None)
Node::new_(
NodeFlags::new() | NodeFlags::IS_IN_DOC | NodeFlags::IS_CONNECTED,
None,
)
}
#[allow(unrooted_must_root)]
@ -1501,6 +1650,7 @@ impl Node {
next_sibling: Default::default(),
prev_sibling: Default::default(),
owner_doc: MutNullableDom::new(doc),
rare_data: Default::default(),
child_list: Default::default(),
children_count: Cell::new(0u32),
flags: Cell::new(flags),
@ -1509,8 +1659,6 @@ impl Node {
style_and_layout_data: Cell::new(None),
mutation_observers: Default::default(),
unique_id: UniqueId::new(),
}
}
@ -1527,11 +1675,11 @@ impl Node {
// Step 3.
if &*old_doc != document {
// Step 3.1.
for descendant in node.traverse_preorder() {
for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
descendant.set_owner_doc(document);
}
for descendant in node
.traverse_preorder()
.traverse_preorder(ShadowIncluding::Yes)
.filter_map(|d| d.as_custom_element())
{
// Step 3.2.
@ -1541,7 +1689,7 @@ impl Node {
None,
);
}
for descendant in node.traverse_preorder() {
for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
// Step 3.3.
vtable_for(&descendant).adopting_steps(&old_doc);
}
@ -1559,7 +1707,9 @@ impl Node {
) -> ErrorResult {
// Step 1.
match parent.type_id() {
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => (),
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
()
},
_ => return Err(Error::HierarchyRequest),
}
@ -1587,7 +1737,7 @@ impl Node {
return Err(Error::HierarchyRequest);
}
},
NodeTypeId::DocumentFragment |
NodeTypeId::DocumentFragment(_) |
NodeTypeId::Element(_) |
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) |
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => (),
@ -1598,7 +1748,7 @@ impl Node {
if parent.is::<Document>() {
match node.type_id() {
// Step 6.1
NodeTypeId::DocumentFragment => {
NodeTypeId::DocumentFragment(_) => {
// Step 6.1.1(b)
if node.children().any(|c| c.is::<Text>()) {
return Err(Error::HierarchyRequest);
@ -1723,7 +1873,7 @@ impl Node {
}
}
rooted_vec!(let mut new_nodes);
let new_nodes = if let NodeTypeId::DocumentFragment = node.type_id() {
let new_nodes = if let NodeTypeId::DocumentFragment(_) = node.type_id() {
// Step 3.
new_nodes.extend(node.children().map(|kid| Dom::from_ref(&*kid)));
// Step 4.
@ -1760,7 +1910,7 @@ impl Node {
parent.add_child(*kid, child);
// Step 7.7.
for descendant in kid
.traverse_preorder()
.traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<Element>)
{
// Step 7.7.2.
@ -1809,7 +1959,7 @@ impl Node {
// Step 3.
rooted_vec!(let mut added_nodes);
let added_nodes = if let Some(node) = node.as_ref() {
if let NodeTypeId::DocumentFragment = node.type_id() {
if let NodeTypeId::DocumentFragment(_) = node.type_id() {
added_nodes.extend(node.children().map(|child| Dom::from_ref(&*child)));
added_nodes.r()
} else {
@ -1935,7 +2085,7 @@ impl Node {
);
DomRoot::upcast::<Node>(doctype)
},
NodeTypeId::DocumentFragment => {
NodeTypeId::DocumentFragment(_) => {
let doc_fragment = DocumentFragment::new(&document);
DomRoot::upcast::<Node>(doc_fragment)
},
@ -2068,7 +2218,7 @@ impl Node {
.GetDocumentElement()
.as_ref()
.map_or(ns!(), |elem| elem.locate_namespace(prefix)),
NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => ns!(),
NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => ns!(),
_ => node
.GetParentElement()
.as_ref()
@ -2093,7 +2243,7 @@ impl NodeMethods for Node {
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => NodeConstants::COMMENT_NODE,
NodeTypeId::Document(_) => NodeConstants::DOCUMENT_NODE,
NodeTypeId::DocumentType => NodeConstants::DOCUMENT_TYPE_NODE,
NodeTypeId::DocumentFragment => NodeConstants::DOCUMENT_FRAGMENT_NODE,
NodeTypeId::DocumentFragment(_) => NodeConstants::DOCUMENT_FRAGMENT_NODE,
NodeTypeId::Element(_) => NodeConstants::ELEMENT_NODE,
}
}
@ -2113,7 +2263,7 @@ impl NodeMethods for Node {
},
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => DOMString::from("#comment"),
NodeTypeId::DocumentType => self.downcast::<DocumentType>().unwrap().name().clone(),
NodeTypeId::DocumentFragment => DOMString::from("#document-fragment"),
NodeTypeId::DocumentFragment(_) => DOMString::from("#document-fragment"),
NodeTypeId::Document(_) => DOMString::from("#document"),
}
}
@ -2129,14 +2279,29 @@ impl NodeMethods for Node {
NodeTypeId::CharacterData(..) |
NodeTypeId::Element(..) |
NodeTypeId::DocumentType |
NodeTypeId::DocumentFragment => Some(self.owner_doc()),
NodeTypeId::DocumentFragment(_) => Some(self.owner_doc()),
NodeTypeId::Document(_) => None,
}
}
// https://dom.spec.whatwg.org/#dom-node-getrootnode
fn GetRootNode(&self) -> DomRoot<Node> {
self.inclusive_ancestors().last().unwrap()
fn GetRootNode(&self, options: &GetRootNodeOptions) -> DomRoot<Node> {
if let Some(shadow_root) = self.containing_shadow_root() {
return if options.composed {
// shadow-including root.
shadow_root.Host().upcast::<Node>().GetRootNode(options)
} else {
DomRoot::from_ref(shadow_root.upcast::<Node>())
};
}
if self.is_in_doc() {
DomRoot::from_ref(self.owner_doc().upcast::<Node>())
} else {
self.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap()
}
}
// https://dom.spec.whatwg.org/#dom-node-parentnode
@ -2198,8 +2363,9 @@ impl NodeMethods for Node {
// https://dom.spec.whatwg.org/#dom-node-textcontent
fn GetTextContent(&self) -> Option<DOMString> {
match self.type_id() {
NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => {
let content = Node::collect_text_contents(self.traverse_preorder());
NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
let content =
Node::collect_text_contents(self.traverse_preorder(ShadowIncluding::No));
Some(content)
},
NodeTypeId::CharacterData(..) => {
@ -2214,7 +2380,7 @@ impl NodeMethods for Node {
fn SetTextContent(&self, value: Option<DOMString>) {
let value = value.unwrap_or_default();
match self.type_id() {
NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => {
NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
// Step 1-2.
let node = if value.is_empty() {
None
@ -2247,7 +2413,9 @@ impl NodeMethods for Node {
fn ReplaceChild(&self, node: &Node, child: &Node) -> Fallible<DomRoot<Node>> {
// Step 1.
match self.type_id() {
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => (),
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
()
},
_ => return Err(Error::HierarchyRequest),
}
@ -2277,7 +2445,7 @@ impl NodeMethods for Node {
if self.is::<Document>() {
match node.type_id() {
// Step 6.1
NodeTypeId::DocumentFragment => {
NodeTypeId::DocumentFragment(_) => {
// Step 6.1.1(b)
if node.children().any(|c| c.is::<Text>()) {
return Err(Error::HierarchyRequest);
@ -2350,7 +2518,10 @@ impl NodeMethods for Node {
// Step 12.
rooted_vec!(let mut nodes);
let nodes = if node.type_id() == NodeTypeId::DocumentFragment {
let nodes = if node.type_id() ==
NodeTypeId::DocumentFragment(DocumentFragmentTypeId::DocumentFragment) ||
node.type_id() == NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
{
nodes.extend(node.children().map(|node| Dom::from_ref(&*node)));
nodes.r()
} else {
@ -2418,8 +2589,11 @@ impl NodeMethods for Node {
}
// https://dom.spec.whatwg.org/#dom-node-clonenode
fn CloneNode(&self, deep: bool) -> DomRoot<Node> {
Node::clone(
fn CloneNode(&self, deep: bool) -> Fallible<DomRoot<Node>> {
if deep && self.is::<ShadowRoot>() {
return Err(Error::NotSupported);
}
Ok(Node::clone(
self,
None,
if deep {
@ -2427,7 +2601,7 @@ impl NodeMethods for Node {
} else {
CloneChildrenFlag::DoNotCloneChildren
},
)
))
}
// https://dom.spec.whatwg.org/#dom-node-isequalnode
@ -2532,8 +2706,12 @@ impl NodeMethods for Node {
// FIXME(emilio): This will eventually need to handle attribute nodes.
let mut self_and_ancestors = self.inclusive_ancestors().collect::<SmallVec<[_; 20]>>();
let mut other_and_ancestors = other.inclusive_ancestors().collect::<SmallVec<[_; 20]>>();
let mut self_and_ancestors = self
.inclusive_ancestors(ShadowIncluding::No)
.collect::<SmallVec<[_; 20]>>();
let mut other_and_ancestors = other
.inclusive_ancestors(ShadowIncluding::No)
.collect::<SmallVec<[_; 20]>>();
if self_and_ancestors.last() != other_and_ancestors.last() {
let random = as_uintptr(self_and_ancestors.last().unwrap()) <
@ -2612,7 +2790,7 @@ impl NodeMethods for Node {
.unwrap()
.GetDocumentElement()
.and_then(|element| element.lookup_prefix(namespace)),
NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => None,
NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => None,
_ => self
.GetParentElement()
.and_then(|element| element.lookup_prefix(namespace)),
@ -2644,6 +2822,23 @@ pub fn document_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> DomR
derived.upcast().owner_doc()
}
pub fn containing_shadow_root<T: DerivedFrom<Node> + DomObject>(
derived: &T,
) -> Option<DomRoot<ShadowRoot>> {
derived.upcast().containing_shadow_root()
}
#[allow(unrooted_must_root)]
pub fn stylesheets_owner_from_node<T: DerivedFrom<Node> + DomObject>(
derived: &T,
) -> StyleSheetListOwner {
if let Some(shadow_root) = containing_shadow_root(derived) {
StyleSheetListOwner::ShadowRoot(Dom::from_ref(&*shadow_root))
} else {
StyleSheetListOwner::Document(Dom::from_ref(&*document_from_node(derived)))
}
}
pub fn window_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> DomRoot<Window> {
let document = document_from_node(derived);
DomRoot::from_ref(document.window())
@ -2853,6 +3048,14 @@ impl<'a> ChildrenMutation<'a> {
}
}
/// The context of the binding to tree of a node.
pub struct BindContext {
/// Whether the tree is connected.
pub tree_connected: bool,
/// Whether the tree is in the document.
pub tree_in_doc: bool,
}
/// The context of the unbinding from a tree of a node when one of its
/// inclusive ancestors is removed.
pub struct UnbindContext<'a> {
@ -2864,7 +3067,9 @@ pub struct UnbindContext<'a> {
prev_sibling: Option<&'a Node>,
/// The next sibling of the inclusive ancestor that was removed.
pub next_sibling: Option<&'a Node>,
/// Whether the tree is in a document.
/// Whether the tree is connected.
pub tree_connected: bool,
/// Whether the tree is in doc.
pub tree_in_doc: bool,
}
@ -2881,6 +3086,7 @@ impl<'a> UnbindContext<'a> {
parent: parent,
prev_sibling: prev_sibling,
next_sibling: next_sibling,
tree_connected: parent.is_connected(),
tree_in_doc: parent.is_in_doc(),
}
}
@ -3028,7 +3234,7 @@ where
let elem_node = elem.upcast::<Node>();
let mut head: usize = 0;
for node in tree_root.traverse_preorder() {
for node in tree_root.traverse_preorder(ShadowIncluding::No) {
let head_node = DomRoot::upcast::<Node>(DomRoot::from_ref(&*self[head]));
if head_node == node {
head += 1;

View file

@ -23,7 +23,7 @@ use crate::dom::document::Document;
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::element::Element;
use crate::dom::htmlscriptelement::HTMLScriptElement;
use crate::dom::node::{Node, UnbindContext};
use crate::dom::node::{Node, ShadowIncluding, UnbindContext};
use crate::dom::text::Text;
use crate::dom::window::Window;
use dom_struct::dom_struct;
@ -102,10 +102,10 @@ impl Range {
// https://dom.spec.whatwg.org/#partially-contained
fn partially_contains(&self, node: &Node) -> bool {
self.StartContainer()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.any(|n| &*n == node) !=
self.EndContainer()
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.any(|n| &*n == node)
}
@ -193,8 +193,14 @@ impl Range {
// https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset
fn compare_point(&self, node: &Node, offset: u32) -> Fallible<Ordering> {
let start_node = self.StartContainer();
let start_node_root = start_node.inclusive_ancestors().last().unwrap();
let node_root = node.inclusive_ancestors().last().unwrap();
let start_node_root = start_node
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
let node_root = node
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
if start_node_root != node_root {
// Step 1.
return Err(Error::WrongDocument);
@ -253,7 +259,10 @@ impl RangeMethods for Range {
fn CommonAncestorContainer(&self) -> DomRoot<Node> {
let end_container = self.EndContainer();
// Step 1.
for container in self.StartContainer().inclusive_ancestors() {
for container in self
.StartContainer()
.inclusive_ancestors(ShadowIncluding::No)
{
// Step 2.
if container.is_inclusive_ancestor_of(&end_container) {
// Step 3.
@ -368,8 +377,16 @@ impl RangeMethods for Range {
// Step 1.
return Err(Error::NotSupported);
}
let this_root = self.StartContainer().inclusive_ancestors().last().unwrap();
let other_root = other.StartContainer().inclusive_ancestors().last().unwrap();
let this_root = self
.StartContainer()
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
let other_root = other
.StartContainer()
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
if this_root != other_root {
// Step 2.
return Err(Error::WrongDocument);
@ -429,8 +446,15 @@ impl RangeMethods for Range {
// https://dom.spec.whatwg.org/#dom-range-intersectsnode
fn IntersectsNode(&self, node: &Node) -> bool {
let start_node = self.StartContainer();
let start_node_root = self.StartContainer().inclusive_ancestors().last().unwrap();
let node_root = node.inclusive_ancestors().last().unwrap();
let start_node_root = self
.StartContainer()
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
let node_root = node
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
if start_node_root != node_root {
// Step 1.
return false;
@ -499,7 +523,7 @@ impl RangeMethods for Range {
fragment.upcast::<Node>().AppendChild(&clone)?;
} else {
// Step 14.1.
let clone = child.CloneNode(false);
let clone = child.CloneNode(/* deep */ false)?;
// Step 14.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 14.3.
@ -520,7 +544,7 @@ impl RangeMethods for Range {
// Step 15.
for child in contained_children {
// Step 15.1.
let clone = child.CloneNode(true);
let clone = child.CloneNode(/* deep */ true)?;
// Step 15.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
}
@ -536,7 +560,7 @@ impl RangeMethods for Range {
fragment.upcast::<Node>().AppendChild(&clone)?;
} else {
// Step 17.1.
let clone = child.CloneNode(false);
let clone = child.CloneNode(/* deep */ false)?;
// Step 17.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 17.3.
@ -572,7 +596,7 @@ impl RangeMethods for Range {
if end_node == start_node {
if let Some(end_data) = end_node.downcast::<CharacterData>() {
// Step 4.1.
let clone = end_node.CloneNode(true);
let clone = end_node.CloneNode(/* deep */ true)?;
// Step 4.2.
let text = end_data.SubstringData(start_offset, end_offset - start_offset);
clone
@ -613,7 +637,7 @@ impl RangeMethods for Range {
if let Some(start_data) = child.downcast::<CharacterData>() {
assert!(child == start_node);
// Step 15.1.
let clone = start_node.CloneNode(true);
let clone = start_node.CloneNode(/* deep */ true)?;
// Step 15.2.
let text = start_data.SubstringData(start_offset, start_node.len() - start_offset);
clone
@ -630,7 +654,7 @@ impl RangeMethods for Range {
)?;
} else {
// Step 16.1.
let clone = child.CloneNode(false);
let clone = child.CloneNode(/* deep */ false)?;
// Step 16.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 16.3.
@ -657,7 +681,7 @@ impl RangeMethods for Range {
if let Some(end_data) = child.downcast::<CharacterData>() {
assert!(child == end_node);
// Step 18.1.
let clone = end_node.CloneNode(true);
let clone = end_node.CloneNode(/* deep */ true)?;
// Step 18.2.
let text = end_data.SubstringData(0, end_offset);
clone
@ -670,7 +694,7 @@ impl RangeMethods for Range {
end_data.ReplaceData(0, end_offset, DOMString::new())?;
} else {
// Step 19.1.
let clone = child.CloneNode(false);
let clone = child.CloneNode(/* deep */ false)?;
// Step 19.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 19.3.
@ -763,7 +787,7 @@ impl RangeMethods for Range {
// Step 11
let new_offset = new_offset +
if node.type_id() == NodeTypeId::DocumentFragment {
if let NodeTypeId::DocumentFragment(_) = node.type_id() {
node.len()
} else {
1
@ -868,9 +892,9 @@ impl RangeMethods for Range {
let end = self.EndContainer();
if start
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.any(|n| !n.is_inclusive_ancestor_of(&end) && !n.is::<Text>()) ||
end.inclusive_ancestors()
end.inclusive_ancestors(ShadowIncluding::No)
.any(|n| !n.is_inclusive_ancestor_of(&start) && !n.is::<Text>())
{
return Err(Error::InvalidState);
@ -878,7 +902,9 @@ impl RangeMethods for Range {
// Step 2.
match new_parent.type_id() {
NodeTypeId::Document(_) | NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => {
NodeTypeId::Document(_) |
NodeTypeId::DocumentType |
NodeTypeId::DocumentFragment(_) => {
return Err(Error::InvalidNodeType);
},
_ => (),
@ -954,7 +980,7 @@ impl RangeMethods for Range {
let node = self.StartContainer();
let owner_doc = node.owner_doc();
let element = match node.type_id() {
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment => None,
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) => None,
NodeTypeId::Element(_) => Some(DomRoot::downcast::<Element>(node).unwrap()),
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) |
NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => node.GetParentElement(),
@ -969,7 +995,10 @@ impl RangeMethods for Range {
let fragment_node = element.parse_fragment(fragment)?;
// Step 4.
for node in fragment_node.upcast::<Node>().traverse_preorder() {
for node in fragment_node
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::No)
{
if let Some(script) = node.downcast::<HTMLScriptElement>() {
script.set_already_started(false);
script.set_parser_inserted(false);
@ -1046,7 +1075,7 @@ fn bp_position(a_node: &Node, a_offset: u32, b_node: &Node, b_offset: u32) -> Op
}
} else if position & NodeConstants::DOCUMENT_POSITION_CONTAINS != 0 {
// Step 3-1, 3-2.
let mut b_ancestors = b_node.inclusive_ancestors();
let mut b_ancestors = b_node.inclusive_ancestors(ShadowIncluding::No);
let child = b_ancestors
.find(|child| &*child.GetParentNode().unwrap() == a_node)
.unwrap();

View file

@ -0,0 +1,42 @@
/* 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 crate::dom::bindings::root::Dom;
use crate::dom::customelementregistry::{
CustomElementDefinition, CustomElementReaction, CustomElementState,
};
use crate::dom::mutationobserver::RegisteredObserver;
use crate::dom::shadowroot::ShadowRoot;
use std::rc::Rc;
//XXX(ferjm) Ideally merge NodeRareData and ElementRareData so they share
// storage.
#[derive(Default, JSTraceable, MallocSizeOf)]
#[must_root]
pub struct NodeRareData {
/// The shadow root the node belongs to.
/// This is None if the node is not in a shadow tree or
/// if it is a ShadowRoot.
pub containing_shadow_root: Option<Dom<ShadowRoot>>,
/// Registered observers for this node.
pub mutation_observers: Vec<RegisteredObserver>,
}
#[derive(Default, JSTraceable, MallocSizeOf)]
#[must_root]
pub struct ElementRareData {
/// https://dom.spec.whatwg.org/#dom-element-shadowroot
/// The ShadowRoot this element is host of.
/// XXX This is currently not exposed to web content. Only for
/// internal use.
pub shadow_root: Option<Dom<ShadowRoot>>,
/// <https://html.spec.whatwg.org/multipage/#custom-element-reaction-queue>
pub custom_element_reaction_queue: Vec<CustomElementReaction>,
/// <https://dom.spec.whatwg.org/#concept-element-custom-element-definition>
#[ignore_malloc_size_of = "Rc"]
pub custom_element_definition: Option<Rc<CustomElementDefinition>>,
/// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
pub custom_element_state: CustomElementState,
}

View file

@ -249,7 +249,7 @@ impl<'a> Serialize for &'a Node {
serializer.write_processing_instruction(&pi.target(), &data)?;
},
NodeTypeId::DocumentFragment => {},
NodeTypeId::DocumentFragment(_) => {},
NodeTypeId::Document(_) => panic!("Can't serialize Document node itself"),
NodeTypeId::Element(_) => panic!("Element shouldn't appear here"),

View file

@ -27,7 +27,7 @@ use crate::dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult};
use crate::dom::htmltemplateelement::HTMLTemplateElement;
use crate::dom::node::Node;
use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::performanceentry::PerformanceEntry;
use crate::dom::performancenavigationtiming::PerformanceNavigationTiming;
use crate::dom::processinginstruction::ProcessingInstruction;
@ -194,7 +194,7 @@ impl ServoParser {
// Step 11.
let form = context_node
.inclusive_ancestors()
.inclusive_ancestors(ShadowIncluding::No)
.find(|element| element.is::<HTMLFormElement>());
let fragment_context = FragmentContext {

View file

@ -0,0 +1,268 @@
/* 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 crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootBinding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{self, ShadowRootMode};
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::cssstylesheet::CSSStyleSheet;
use crate::dom::document::Document;
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::documentorshadowroot::{DocumentOrShadowRoot, StyleSheetInDocument};
use crate::dom::element::Element;
use crate::dom::node::{Node, NodeDamage, NodeFlags, ShadowIncluding};
use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::window::Window;
use crate::stylesheet_set::StylesheetSetRef;
use dom_struct::dom_struct;
use selectors::context::QuirksMode;
use servo_arc::Arc;
use servo_atoms::Atom;
use style::author_styles::AuthorStyles;
use style::dom::TElement;
use style::media_queries::Device;
use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::Stylesheet;
// https://dom.spec.whatwg.org/#interface-shadowroot
#[dom_struct]
pub struct ShadowRoot {
document_fragment: DocumentFragment,
document_or_shadow_root: DocumentOrShadowRoot,
document: Dom<Document>,
host: Dom<Element>,
/// List of author styles associated with nodes in this shadow tree.
author_styles: DomRefCell<AuthorStyles<StyleSheetInDocument>>,
stylesheet_list: MutNullableDom<StyleSheetList>,
window: Dom<Window>,
}
impl ShadowRoot {
#[allow(unrooted_must_root)]
fn new_inherited(host: &Element, document: &Document) -> 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: Dom::from_ref(host),
author_styles: DomRefCell::new(AuthorStyles::new()),
stylesheet_list: MutNullableDom::new(None),
window: Dom::from_ref(document.window()),
}
}
pub fn new(host: &Element, document: &Document) -> DomRoot<ShadowRoot> {
reflect_dom_object(
Box::new(ShadowRoot::new_inherited(host, document)),
document.window(),
ShadowRootBinding::Wrap,
)
}
pub fn get_focused_element(&self) -> Option<DomRoot<Element>> {
//XXX get retargeted focused element
None
}
pub fn stylesheet_count(&self) -> usize {
self.author_styles.borrow().stylesheets.len()
}
pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
let stylesheets = &self.author_styles.borrow().stylesheets;
stylesheets
.get(index)
.and_then(|s| s.owner.upcast::<Node>().get_cssom_stylesheet())
}
/// Add a stylesheet owned by `owner` to the list of shadow root sheets, in the
/// correct tree position.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
let insertion_point = stylesheets
.iter()
.find(|sheet_in_shadow| {
owner
.upcast::<Node>()
.is_before(sheet_in_shadow.owner.upcast())
})
.cloned();
DocumentOrShadowRoot::add_stylesheet(
owner,
StylesheetSetRef::Author(stylesheets),
sheet,
insertion_point,
self.document.style_shared_lock(),
);
}
/// Remove a stylesheet owned by `owner` from the list of shadow root sheets.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
DocumentOrShadowRoot::remove_stylesheet(
owner,
s,
StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
)
}
pub 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.
self.host
.upcast::<Node>()
.dirty(NodeDamage::NodeStyleDamaged);
}
/// Remove any existing association between the provided id and any elements
/// in this shadow tree.
pub fn unregister_named_element(&self, to_unregister: &Element, id: Atom) {
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 fn register_named_element(&self, element: &Element, id: Atom) {
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,
);
}
}
impl ShadowRootMethods 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::<Node>().retarget(e.upcast::<Node>());
retargeted_node
.downcast::<Element>()
.map(|n| DomRoot::from_ref(n))
},
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::<Node>().retarget(e.upcast::<Node>());
if let Some(element) = retargeted_node
.downcast::<Element>()
.map(|n| DomRoot::from_ref(n))
{
elements.push(element);
}
}
elements
}
/// https://dom.spec.whatwg.org/#dom-shadowroot-mode
fn Mode(&self) -> ShadowRootMode {
ShadowRootMode::Closed
}
/// https://dom.spec.whatwg.org/#dom-shadowroot-host
fn Host(&self) -> DomRoot<Element> {
DomRoot::from_ref(&self.host)
}
// 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)),
)
})
}
}
#[allow(unsafe_code)]
pub trait LayoutShadowRootHelpers {
unsafe fn get_host_for_layout(&self) -> LayoutDom<Element>;
unsafe fn get_style_data_for_layout<'a, E: TElement>(
&self,
) -> &'a AuthorStyles<StyleSheetInDocument>;
unsafe fn flush_stylesheets<E: TElement>(
&self,
device: &Device,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard,
);
}
impl LayoutShadowRootHelpers for LayoutDom<ShadowRoot> {
#[inline]
#[allow(unsafe_code)]
unsafe fn get_host_for_layout(&self) -> LayoutDom<Element> {
(*self.unsafe_get()).host.to_layout()
}
#[inline]
#[allow(unsafe_code)]
unsafe fn get_style_data_for_layout<'a, E: TElement>(
&self,
) -> &'a AuthorStyles<StyleSheetInDocument> {
(*self.unsafe_get()).author_styles.borrow_for_layout()
}
#[inline]
#[allow(unsafe_code)]
unsafe fn flush_stylesheets<E: TElement>(
&self,
device: &Device,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard,
) {
let mut author_styles = (*self.unsafe_get()).author_styles.borrow_mut_for_layout();
if author_styles.stylesheets.dirty() {
author_styles.flush::<E>(device, quirks_mode, guard);
}
}
}

View file

@ -6,30 +6,85 @@ use crate::dom::bindings::codegen::Bindings::StyleSheetListBinding;
use crate::dom::bindings::codegen::Bindings::StyleSheetListBinding::StyleSheetListMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::document::Document;
use crate::dom::element::Element;
use crate::dom::shadowroot::ShadowRoot;
use crate::dom::stylesheet::StyleSheet;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_arc::Arc;
use style::stylesheets::Stylesheet;
#[must_root]
#[derive(JSTraceable, MallocSizeOf)]
pub enum StyleSheetListOwner {
Document(Dom<Document>),
ShadowRoot(Dom<ShadowRoot>),
}
impl StyleSheetListOwner {
pub fn stylesheet_count(&self) -> usize {
match *self {
StyleSheetListOwner::Document(ref doc) => doc.stylesheet_count(),
StyleSheetListOwner::ShadowRoot(ref shadow_root) => shadow_root.stylesheet_count(),
}
}
pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
match *self {
StyleSheetListOwner::Document(ref doc) => doc.stylesheet_at(index),
StyleSheetListOwner::ShadowRoot(ref shadow_root) => shadow_root.stylesheet_at(index),
}
}
pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
match *self {
StyleSheetListOwner::Document(ref doc) => doc.add_stylesheet(owner, sheet),
StyleSheetListOwner::ShadowRoot(ref shadow_root) => {
shadow_root.add_stylesheet(owner, sheet)
},
}
}
pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
match *self {
StyleSheetListOwner::Document(ref doc) => doc.remove_stylesheet(owner, s),
StyleSheetListOwner::ShadowRoot(ref shadow_root) => {
shadow_root.remove_stylesheet(owner, s)
},
}
}
pub fn invalidate_stylesheets(&self) {
match *self {
StyleSheetListOwner::Document(ref doc) => doc.invalidate_stylesheets(),
StyleSheetListOwner::ShadowRoot(ref shadow_root) => {
shadow_root.invalidate_stylesheets()
},
}
}
}
#[dom_struct]
pub struct StyleSheetList {
reflector_: Reflector,
document: Dom<Document>,
document_or_shadow_root: StyleSheetListOwner,
}
impl StyleSheetList {
#[allow(unrooted_must_root)]
fn new_inherited(doc: Dom<Document>) -> StyleSheetList {
fn new_inherited(doc_or_sr: StyleSheetListOwner) -> StyleSheetList {
StyleSheetList {
reflector_: Reflector::new(),
document: doc,
document_or_shadow_root: doc_or_sr,
}
}
#[allow(unrooted_must_root)]
pub fn new(window: &Window, document: Dom<Document>) -> DomRoot<StyleSheetList> {
pub fn new(window: &Window, doc_or_sr: StyleSheetListOwner) -> DomRoot<StyleSheetList> {
reflect_dom_object(
Box::new(StyleSheetList::new_inherited(document)),
Box::new(StyleSheetList::new_inherited(doc_or_sr)),
window,
StyleSheetListBinding::Wrap,
)
@ -39,14 +94,14 @@ impl StyleSheetList {
impl StyleSheetListMethods for StyleSheetList {
// https://drafts.csswg.org/cssom/#dom-stylesheetlist-length
fn Length(&self) -> u32 {
self.document.stylesheet_count() as u32
self.document_or_shadow_root.stylesheet_count() as u32
}
// https://drafts.csswg.org/cssom/#dom-stylesheetlist-item
fn Item(&self, index: u32) -> Option<DomRoot<StyleSheet>> {
// XXXManishearth this doesn't handle the origin clean flag and is a
// cors vulnerability
self.document
self.document_or_shadow_root
.stylesheet_at(index as usize)
.map(DomRoot::upcast)
}

View file

@ -51,7 +51,7 @@ use crate::dom::htmltemplateelement::HTMLTemplateElement;
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
use crate::dom::htmltitleelement::HTMLTitleElement;
use crate::dom::htmlvideoelement::HTMLVideoElement;
use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
use crate::dom::svgsvgelement::SVGSVGElement;
use html5ever::LocalName;
use style::attr::AttrValue;
@ -90,15 +90,15 @@ pub trait VirtualMethods {
}
}
/// Called when a Node is appended to a tree, where 'tree_in_doc' indicates
/// Called when a Node is appended to a tree, where 'tree_connected' indicates
/// whether the tree is part of a Document.
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(context);
}
}
/// Called when a Node is removed from a tree, where 'tree_in_doc'
/// Called when a Node is removed from a tree, where 'tree_connected'
/// indicates whether the tree is part of a Document.
/// Implements removing steps:
/// <https://dom.spec.whatwg.org/#concept-node-remove-ext>

View file

@ -132,7 +132,6 @@ partial /*sealed*/ interface Document {
// user interaction
readonly attribute Window?/*Proxy?*/ defaultView;
readonly attribute Element? activeElement;
boolean hasFocus();
// [CEReactions]
// attribute DOMString designMode;
@ -199,17 +198,6 @@ partial interface Document {
TouchList createTouchList(Touch... touches);
};
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
partial interface Document {
Element? elementFromPoint(double x, double y);
sequence<Element> elementsFromPoint(double x, double y);
};
// https://drafts.csswg.org/cssom/#extensions-to-the-document-interface
partial interface Document {
[SameObject] readonly attribute StyleSheetList styleSheets;
};
// https://fullscreen.spec.whatwg.org/#api
partial interface Document {
[LenientSetter] readonly attribute boolean fullscreenEnabled;
@ -221,3 +209,5 @@ partial interface Document {
attribute EventHandler onfullscreenchange;
attribute EventHandler onfullscreenerror;
};
Document implements DocumentOrShadowRoot;

View file

@ -0,0 +1,18 @@
/* 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/. */
/*
* The origin of this IDL file is
* https://dom.spec.whatwg.org/#documentorshadowroot
* https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
*/
[NoInterfaceObject]
interface DocumentOrShadowRoot {
// Selection? getSelection();
Element? elementFromPoint (double x, double y);
sequence<Element> elementsFromPoint (double x, double y);
// CaretPosition? caretPositionFromPoint (double x, double y);
readonly attribute Element? activeElement;
readonly attribute StyleSheetList styleSheets;
};

View file

@ -81,6 +81,8 @@ interface Element : Node {
void insertAdjacentText(DOMString where_, DOMString data);
[CEReactions, Throws]
void insertAdjacentHTML(DOMString position, DOMString html);
[Throws, Pref="dom.shadowdom.enabled"] ShadowRoot attachShadow();
};
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-element-interface

View file

@ -32,7 +32,7 @@ interface Node : EventTarget {
readonly attribute Document? ownerDocument;
[Pure]
Node getRootNode();
Node getRootNode(optional GetRootNodeOptions options);
[Pure]
readonly attribute Node? parentNode;
@ -58,7 +58,7 @@ interface Node : EventTarget {
[CEReactions]
void normalize();
[CEReactions]
[CEReactions, Throws]
Node cloneNode(optional boolean deep = false);
[Pure]
boolean isEqualNode(Node? node);
@ -92,3 +92,7 @@ interface Node : EventTarget {
[CEReactions, Throws]
Node removeChild(Node child);
};
dictionary GetRootNodeOptions {
boolean composed = false;
};

View file

@ -0,0 +1,17 @@
/* 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/. */
/*
* The origin of this IDL file is:
* https://dom.spec.whatwg.org/#interface-shadowroot
*/
[Exposed=Window]
interface ShadowRoot : DocumentFragment {
readonly attribute ShadowRootMode mode;
readonly attribute Element host;
};
enum ShadowRootMode { "open", "closed"};
ShadowRoot implements DocumentOrShadowRoot;