mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
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:
commit
799490a02e
75 changed files with 2081 additions and 702 deletions
|
@ -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;
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
312
components/script/dom/documentorshadowroot.rs
Normal file
312
components/script/dom/documentorshadowroot.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
}
|
||||
);
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
42
components/script/dom/raredata.rs
Normal file
42
components/script/dom/raredata.rs
Normal 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,
|
||||
}
|
|
@ -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"),
|
||||
|
|
|
@ -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 {
|
||||
|
|
268
components/script/dom/shadowroot.rs
Normal file
268
components/script/dom/shadowroot.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
18
components/script/dom/webidls/DocumentOrShadowRoot.webidl
Normal file
18
components/script/dom/webidls/DocumentOrShadowRoot.webidl
Normal 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;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
17
components/script/dom/webidls/ShadowRoot.webidl
Normal file
17
components/script/dom/webidls/ShadowRoot.webidl
Normal 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;
|
Loading…
Add table
Add a link
Reference in a new issue