1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 //! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
6 //! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
7 //! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
8 //! escaping.
9 //!
10 //! As a security wrapper is only as good as its whitelist, be careful when adding operations to
11 //! this list. The cardinal rules are:
12 //!
13 //! 1. Layout is not allowed to mutate the DOM.
14 //!
15 //! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang
16 //!    onto these objects and cause use-after-free.
17 //!
18 //! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
19 //! will race and cause spurious thread failure. (Note that I do not believe these races are
20 //! exploitable, but they'll result in brokenness nonetheless.)
21 //!
22 //! Rules of the road for this file:
23 //!
24 //! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
25 //!
26 //!   o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
27 //!
28 //!   o Instead of `html_element_in_html_document()`, use
29 //!     `html_element_in_html_document_for_layout()`.
30 
31 #![allow(unsafe_code)]
32 
33 use atomic_refcell::{AtomicRef, AtomicRefMut, AtomicRefCell};
34 use gfx_traits::ByteIndex;
35 use html5ever::{LocalName, Namespace};
36 use layout::data::StyleAndLayoutData;
37 use layout::wrapper::GetRawData;
38 use msg::constellation_msg::{BrowsingContextId, PipelineId};
39 use range::Range;
40 use script::layout_exports::{CharacterDataTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId};
41 use script::layout_exports::{Document, Element, Node, Text};
42 use script::layout_exports::{LayoutCharacterDataHelpers, LayoutDocumentHelpers};
43 use script::layout_exports::{LayoutElementHelpers, LayoutNodeHelpers, LayoutDom, RawLayoutElementHelpers};
44 use script::layout_exports::NodeFlags;
45 use script::layout_exports::PendingRestyle;
46 use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress};
47 use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData};
48 use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
49 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
50 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
51 use selectors::matching::{ElementSelectorFlags, MatchingContext, QuirksMode};
52 use selectors::matching::VisitedHandlingMode;
53 use selectors::sink::Push;
54 use servo_arc::{Arc, ArcBorrow};
55 use servo_atoms::Atom;
56 use servo_url::ServoUrl;
57 use std::fmt;
58 use std::fmt::Debug;
59 use std::hash::{Hash, Hasher};
60 use std::marker::PhantomData;
61 use std::ptr::NonNull;
62 use std::sync::atomic::Ordering;
63 use style::CaseSensitivityExt;
64 use style::applicable_declarations::ApplicableDeclarationBlock;
65 use style::attr::AttrValue;
66 use style::context::SharedStyleContext;
67 use style::data::ElementData;
68 use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode};
69 use style::dom::{TDocument, TElement, TNode, TShadowRoot};
70 use style::element_state::*;
71 use style::font_metrics::ServoMetricsProvider;
72 use style::properties::{ComputedValues, PropertyDeclarationBlock};
73 use style::selector_parser::{AttrValue as SelectorAttrValue, NonTSPseudoClass, PseudoClassStringArg};
74 use style::selector_parser::{PseudoElement, SelectorImpl, extended_filtering};
75 use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
76 use style::str::is_whitespace;
77 use style::stylist::CascadeData;
78 
drop_style_and_layout_data(data: OpaqueStyleAndLayoutData)79 pub unsafe fn drop_style_and_layout_data(data: OpaqueStyleAndLayoutData) {
80     let ptr = data.ptr.as_ptr() as *mut StyleData;
81     let non_opaque: *mut StyleAndLayoutData = ptr as *mut _;
82     let _ = Box::from_raw(non_opaque);
83 }
84 
85 #[derive(Clone, Copy)]
86 pub struct ServoLayoutNode<'a> {
87     /// The wrapped node.
88     node: LayoutDom<Node>,
89 
90     /// Being chained to a PhantomData prevents `LayoutNode`s from escaping.
91     chain: PhantomData<&'a ()>,
92 }
93 
94 impl<'ln> Debug for ServoLayoutNode<'ln> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result95     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96         if let Some(el) = self.as_element() {
97             el.fmt(f)
98         } else {
99             if self.is_text_node() {
100                 write!(f, "<text node> ({:#x})", self.opaque().0)
101             } else {
102                 write!(f, "<non-text node> ({:#x})", self.opaque().0)
103             }
104         }
105     }
106 }
107 
108 impl<'a> PartialEq for ServoLayoutNode<'a> {
109     #[inline]
eq(&self, other: &ServoLayoutNode) -> bool110     fn eq(&self, other: &ServoLayoutNode) -> bool {
111         self.node == other.node
112     }
113 }
114 
115 impl<'ln> ServoLayoutNode<'ln> {
from_layout_js(n: LayoutDom<Node>) -> ServoLayoutNode<'ln>116     fn from_layout_js(n: LayoutDom<Node>) -> ServoLayoutNode<'ln> {
117         ServoLayoutNode {
118             node: n,
119             chain: PhantomData,
120         }
121     }
122 
new(address: &TrustedNodeAddress) -> ServoLayoutNode123     pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode {
124         ServoLayoutNode::from_layout_js(LayoutDom::from_trusted_node_address(*address))
125     }
126 
127     /// Creates a new layout node with the same lifetime as this layout node.
new_with_this_lifetime(&self, node: &LayoutDom<Node>) -> ServoLayoutNode<'ln>128     pub unsafe fn new_with_this_lifetime(&self, node: &LayoutDom<Node>) -> ServoLayoutNode<'ln> {
129         ServoLayoutNode {
130             node: *node,
131             chain: self.chain,
132         }
133     }
134 
script_type_id(&self) -> NodeTypeId135     fn script_type_id(&self) -> NodeTypeId {
136         unsafe {
137             self.node.type_id_for_layout()
138         }
139     }
140 }
141 
142 impl<'ln> NodeInfo for ServoLayoutNode<'ln> {
is_element(&self) -> bool143     fn is_element(&self) -> bool {
144         unsafe {
145             self.node.is_element_for_layout()
146         }
147     }
148 
is_text_node(&self) -> bool149     fn is_text_node(&self) -> bool {
150         self.script_type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text)
151     }
152 }
153 
154 #[derive(Clone, Copy)]
155 enum Impossible { }
156 
157 #[derive(Clone, Copy)]
158 pub struct ShadowRoot<'lr>(Impossible, PhantomData<&'lr ()>);
159 
160 impl<'lr> TShadowRoot for ShadowRoot<'lr> {
161     type ConcreteNode = ServoLayoutNode<'lr>;
162 
as_node(&self) -> Self::ConcreteNode163     fn as_node(&self) -> Self::ConcreteNode {
164         match self.0 { }
165     }
166 
host(&self) -> ServoLayoutElement<'lr>167     fn host(&self) -> ServoLayoutElement<'lr> {
168         match self.0 { }
169     }
170 
style_data<'a>(&self) -> &'a CascadeData where Self: 'a,171     fn style_data<'a>(&self) -> &'a CascadeData
172     where
173         Self: 'a,
174     {
175         match self.0 { }
176     }
177 }
178 
179 impl<'ln> TNode for ServoLayoutNode<'ln> {
180     type ConcreteDocument = ServoLayoutDocument<'ln>;
181     type ConcreteElement = ServoLayoutElement<'ln>;
182     type ConcreteShadowRoot = ShadowRoot<'ln>;
183 
parent_node(&self) -> Option<Self>184     fn parent_node(&self) -> Option<Self> {
185         unsafe {
186             self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node))
187         }
188     }
189 
first_child(&self) -> Option<Self>190     fn first_child(&self) -> Option<Self> {
191         unsafe {
192             self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node))
193         }
194     }
195 
last_child(&self) -> Option<Self>196     fn last_child(&self) -> Option<Self> {
197         unsafe {
198             self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node))
199         }
200     }
201 
prev_sibling(&self) -> Option<Self>202     fn prev_sibling(&self) -> Option<Self> {
203         unsafe {
204             self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
205         }
206     }
207 
next_sibling(&self) -> Option<Self>208     fn next_sibling(&self) -> Option<Self> {
209         unsafe {
210             self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
211         }
212     }
213 
owner_doc(&self) -> Self::ConcreteDocument214     fn owner_doc(&self) -> Self::ConcreteDocument {
215         ServoLayoutDocument::from_layout_js(unsafe { self.node.owner_doc_for_layout() })
216     }
217 
traversal_parent(&self) -> Option<ServoLayoutElement<'ln>>218     fn traversal_parent(&self) -> Option<ServoLayoutElement<'ln>> {
219         self.parent_element()
220     }
221 
opaque(&self) -> OpaqueNode222     fn opaque(&self) -> OpaqueNode {
223         unsafe { self.get_jsmanaged().opaque() }
224     }
225 
debug_id(self) -> usize226     fn debug_id(self) -> usize {
227         self.opaque().0
228     }
229 
as_element(&self) -> Option<ServoLayoutElement<'ln>>230     fn as_element(&self) -> Option<ServoLayoutElement<'ln>> {
231         as_element(self.node)
232     }
233 
as_document(&self) -> Option<ServoLayoutDocument<'ln>>234     fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
235         self.node.downcast().map(ServoLayoutDocument::from_layout_js)
236     }
237 
as_shadow_root(&self) -> Option<ShadowRoot<'ln>>238     fn as_shadow_root(&self) -> Option<ShadowRoot<'ln>> {
239         None
240     }
241 
is_in_document(&self) -> bool242     fn is_in_document(&self) -> bool {
243         unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) }
244     }
245 }
246 
247 impl<'ln> LayoutNode for ServoLayoutNode<'ln> {
248     type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>;
249 
to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode250     fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode {
251         ServoThreadSafeLayoutNode::new(self)
252     }
253 
type_id(&self) -> LayoutNodeType254     fn type_id(&self) -> LayoutNodeType {
255         self.script_type_id().into()
256     }
257 
initialize_data(&self)258     unsafe fn initialize_data(&self) {
259         if self.get_raw_data().is_none() {
260             let ptr: *mut StyleAndLayoutData =
261                 Box::into_raw(Box::new(StyleAndLayoutData::new()));
262             let opaque = OpaqueStyleAndLayoutData {
263                 ptr: NonNull::new_unchecked(ptr as *mut StyleData),
264             };
265             self.init_style_and_layout_data(opaque);
266         };
267     }
268 
init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData)269     unsafe fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) {
270         self.get_jsmanaged().init_style_and_layout_data(data);
271     }
272 
take_style_and_layout_data(&self) -> OpaqueStyleAndLayoutData273     unsafe fn take_style_and_layout_data(&self) -> OpaqueStyleAndLayoutData {
274         self.get_jsmanaged().take_style_and_layout_data()
275     }
276 }
277 
278 impl<'ln> GetLayoutData for ServoLayoutNode<'ln> {
get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>279     fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
280         unsafe {
281             self.get_jsmanaged().get_style_and_layout_data()
282         }
283     }
284 }
285 
286 impl<'le> GetLayoutData for ServoLayoutElement<'le> {
get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>287     fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
288         self.as_node().get_style_and_layout_data()
289     }
290 }
291 
292 impl<'ln> GetLayoutData for ServoThreadSafeLayoutNode<'ln> {
get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>293     fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
294         self.node.get_style_and_layout_data()
295     }
296 }
297 
298 impl<'le> GetLayoutData for ServoThreadSafeLayoutElement<'le> {
get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>299     fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
300         self.element.as_node().get_style_and_layout_data()
301     }
302 }
303 
304 impl<'ln> ServoLayoutNode<'ln> {
305     /// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to
306     /// call and as such is marked `unsafe`.
get_jsmanaged(&self) -> &LayoutDom<Node>307     pub unsafe fn get_jsmanaged(&self) -> &LayoutDom<Node> {
308         &self.node
309     }
310 }
311 
312 // A wrapper around documents that ensures ayout can only ever access safe properties.
313 #[derive(Clone, Copy)]
314 pub struct ServoLayoutDocument<'ld> {
315     document: LayoutDom<Document>,
316     chain: PhantomData<&'ld ()>,
317 }
318 
319 impl<'ld> TDocument for ServoLayoutDocument<'ld> {
320     type ConcreteNode = ServoLayoutNode<'ld>;
321 
as_node(&self) -> Self::ConcreteNode322     fn as_node(&self) -> Self::ConcreteNode {
323         ServoLayoutNode::from_layout_js(self.document.upcast())
324     }
325 
quirks_mode(&self) -> QuirksMode326     fn quirks_mode(&self) -> QuirksMode {
327         unsafe { self.document.quirks_mode() }
328     }
329 
is_html_document(&self) -> bool330     fn is_html_document(&self) -> bool {
331         unsafe { self.document.is_html_document_for_layout() }
332     }
333 }
334 
335 impl<'ld> ServoLayoutDocument<'ld> {
root_element(&self) -> Option<ServoLayoutElement<'ld>>336     pub fn root_element(&self) -> Option<ServoLayoutElement<'ld>> {
337         self.as_node().dom_children().flat_map(|n| n.as_element()).next()
338     }
339 
drain_pending_restyles(&self) -> Vec<(ServoLayoutElement<'ld>, PendingRestyle)>340     pub fn drain_pending_restyles(&self) -> Vec<(ServoLayoutElement<'ld>, PendingRestyle)> {
341         let elements =  unsafe { self.document.drain_pending_restyles() };
342         elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
343     }
344 
needs_paint_from_layout(&self)345     pub fn needs_paint_from_layout(&self) {
346         unsafe { self.document.needs_paint_from_layout(); }
347     }
348 
will_paint(&self)349     pub fn will_paint(&self) {
350         unsafe { self.document.will_paint(); }
351     }
352 
style_shared_lock(&self) -> &StyleSharedRwLock353     pub fn style_shared_lock(&self) -> &StyleSharedRwLock {
354         unsafe { self.document.style_shared_lock() }
355     }
356 
from_layout_js(doc: LayoutDom<Document>) -> ServoLayoutDocument<'ld>357     pub fn from_layout_js(doc: LayoutDom<Document>) -> ServoLayoutDocument<'ld> {
358         ServoLayoutDocument {
359             document: doc,
360             chain: PhantomData,
361         }
362     }
363 }
364 
365 /// A wrapper around elements that ensures layout can only ever access safe properties.
366 #[derive(Clone, Copy)]
367 pub struct ServoLayoutElement<'le> {
368     element: LayoutDom<Element>,
369     chain: PhantomData<&'le ()>,
370 }
371 
372 impl<'le> fmt::Debug for ServoLayoutElement<'le> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result373     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374         write!(f, "<{}", self.element.local_name())?;
375         if let Some(id) = self.id() {
376             write!(f, " id={}", id)?;
377         }
378         write!(f, "> ({:#x})", self.as_node().opaque().0)
379     }
380 }
381 
382 impl<'le> TElement for ServoLayoutElement<'le> {
383     type ConcreteNode = ServoLayoutNode<'le>;
384     type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>;
385 
386     type FontMetricsProvider = ServoMetricsProvider;
387 
as_node(&self) -> ServoLayoutNode<'le>388     fn as_node(&self) -> ServoLayoutNode<'le> {
389         ServoLayoutNode::from_layout_js(self.element.upcast())
390     }
391 
traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator>392     fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> {
393         LayoutIterator(self.as_node().dom_children())
394     }
395 
is_html_element(&self) -> bool396     fn is_html_element(&self) -> bool {
397         unsafe { self.element.is_html_element() }
398     }
399 
style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>>400     fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
401         unsafe {
402             (*self.element.style_attribute()).as_ref().map(|x| x.borrow_arc())
403         }
404     }
405 
state(&self) -> ElementState406     fn state(&self) -> ElementState {
407         self.element.get_state_for_layout()
408     }
409 
410     #[inline]
has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool411     fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool {
412         self.get_attr(namespace, attr).is_some()
413     }
414 
415     #[inline]
id(&self) -> Option<&Atom>416     fn id(&self) -> Option<&Atom> {
417         unsafe {
418             (*self.element.id_attribute()).as_ref()
419         }
420     }
421 
422     #[inline(always)]
each_class<F>(&self, mut callback: F) where F: FnMut(&Atom)423     fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
424         unsafe {
425             if let Some(ref classes) = self.element.get_classes_for_layout() {
426                 for class in *classes {
427                     callback(class)
428                 }
429             }
430         }
431     }
432 
has_dirty_descendants(&self) -> bool433     fn has_dirty_descendants(&self) -> bool {
434         unsafe { self.as_node().node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) }
435     }
436 
has_snapshot(&self) -> bool437     fn has_snapshot(&self) -> bool {
438         unsafe { self.as_node().node.get_flag(NodeFlags::HAS_SNAPSHOT) }
439     }
440 
handled_snapshot(&self) -> bool441     fn handled_snapshot(&self) -> bool {
442         unsafe { self.as_node().node.get_flag(NodeFlags::HANDLED_SNAPSHOT) }
443     }
444 
set_handled_snapshot(&self)445     unsafe fn set_handled_snapshot(&self) {
446         self.as_node().node.set_flag(NodeFlags::HANDLED_SNAPSHOT, true);
447     }
448 
set_dirty_descendants(&self)449     unsafe fn set_dirty_descendants(&self) {
450         debug_assert!(self.as_node().is_in_document());
451         self.as_node().node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true)
452     }
453 
unset_dirty_descendants(&self)454     unsafe fn unset_dirty_descendants(&self) {
455         self.as_node().node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
456     }
457 
store_children_to_process(&self, n: isize)458     fn store_children_to_process(&self, n: isize) {
459         let data = self.get_style_data().unwrap();
460         data.parallel.children_to_process.store(n, Ordering::Relaxed);
461     }
462 
did_process_child(&self) -> isize463     fn did_process_child(&self) -> isize {
464         let data = self.get_style_data().unwrap();
465         let old_value = data.parallel.children_to_process.fetch_sub(1, Ordering::Relaxed);
466         debug_assert!(old_value >= 1);
467         old_value - 1
468     }
469 
clear_data(&self)470     unsafe fn clear_data(&self) {
471         if self.get_raw_data().is_some() {
472             drop_style_and_layout_data(self.as_node().take_style_and_layout_data());
473         }
474     }
475 
ensure_data(&self) -> AtomicRefMut<ElementData>476     unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
477         self.as_node().initialize_data();
478         self.mutate_data().unwrap()
479     }
480 
get_data(&self) -> Option<&AtomicRefCell<ElementData>>481     fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
482         unsafe {
483             self.get_style_and_layout_data().map(|d| {
484                 &(*(d.ptr.as_ptr() as *mut StyleData)).element_data
485             })
486         }
487     }
488 
skip_item_display_fixup(&self) -> bool489     fn skip_item_display_fixup(&self) -> bool {
490         false
491     }
492 
set_selector_flags(&self, flags: ElementSelectorFlags)493     unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
494         self.element.insert_selector_flags(flags);
495     }
496 
has_selector_flags(&self, flags: ElementSelectorFlags) -> bool497     fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
498         self.element.has_selector_flags(flags)
499     }
500 
has_animations(&self) -> bool501     fn has_animations(&self) -> bool {
502         // We use this function not only for Gecko but also for Servo to know if this element has
503         // animations, so we maybe try to get the important rules of this element. This is used for
504         // off-main thread animations, but we don't support it on Servo, so return false directly.
505         false
506     }
507 
has_css_animations(&self) -> bool508     fn has_css_animations(&self) -> bool {
509         unreachable!("this should be only called on gecko");
510     }
511 
has_css_transitions(&self) -> bool512     fn has_css_transitions(&self) -> bool {
513         unreachable!("this should be only called on gecko");
514     }
515 
516     #[inline]
lang_attr(&self) -> Option<SelectorAttrValue>517     fn lang_attr(&self) -> Option<SelectorAttrValue> {
518         self.get_attr(&ns!(xml), &local_name!("lang"))
519             .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
520             .map(|v| String::from(v as &str))
521     }
522 
match_element_lang( &self, override_lang: Option<Option<SelectorAttrValue>>, value: &PseudoClassStringArg, ) -> bool523     fn match_element_lang(
524         &self,
525         override_lang: Option<Option<SelectorAttrValue>>,
526         value: &PseudoClassStringArg,
527     ) -> bool {
528         // Servo supports :lang() from CSS Selectors 4, which can take a comma-
529         // separated list of language tags in the pseudo-class, and which
530         // performs RFC 4647 extended filtering matching on them.
531         //
532         // FIXME(heycam): This is wrong, since extended_filtering accepts
533         // a string containing commas (separating each language tag in
534         // a list) but the pseudo-class instead should be parsing and
535         // storing separate <ident> or <string>s for each language tag.
536         //
537         // FIXME(heycam): Look at `element`'s document's Content-Language
538         // HTTP header for language tags to match `value` against.  To
539         // do this, we should make `get_lang_for_layout` return an Option,
540         // so we can decide when to fall back to the Content-Language check.
541         let element_lang = match override_lang {
542             Some(Some(lang)) => lang,
543             Some(None) => String::new(),
544             None => self.element.get_lang_for_layout(),
545         };
546         extended_filtering(&element_lang, &*value)
547     }
548 
is_html_document_body_element(&self) -> bool549     fn is_html_document_body_element(&self) -> bool {
550         // This is only used for the "tables inherit from body" quirk, which we
551         // don't implement.
552         //
553         // FIXME(emilio): We should be able to give the right answer though!
554         false
555     }
556 
synthesize_presentational_hints_for_legacy_attributes<V>( &self, _visited_handling: VisitedHandlingMode, hints: &mut V, ) where V: Push<ApplicableDeclarationBlock>,557     fn synthesize_presentational_hints_for_legacy_attributes<V>(
558         &self,
559         _visited_handling: VisitedHandlingMode,
560         hints: &mut V,
561     )
562     where
563         V: Push<ApplicableDeclarationBlock>,
564     {
565         unsafe {
566             self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
567         }
568     }
569 
shadow_root(&self) -> Option<ShadowRoot<'le>>570     fn shadow_root(&self) -> Option<ShadowRoot<'le>> {
571         None
572     }
573 
containing_shadow(&self) -> Option<ShadowRoot<'le>>574     fn containing_shadow(&self) -> Option<ShadowRoot<'le>> {
575         None
576     }
577 }
578 
579 impl<'le> PartialEq for ServoLayoutElement<'le> {
eq(&self, other: &Self) -> bool580     fn eq(&self, other: &Self) -> bool {
581         self.as_node() == other.as_node()
582     }
583 }
584 
585 impl<'le> Hash for ServoLayoutElement<'le> {
hash<H: Hasher>(&self, state: &mut H)586     fn hash<H: Hasher>(&self, state: &mut H) {
587         self.element.hash(state);
588     }
589 }
590 
591 impl<'le> Eq for ServoLayoutElement<'le> {}
592 
593 impl<'le> ServoLayoutElement<'le> {
from_layout_js(el: LayoutDom<Element>) -> ServoLayoutElement<'le>594     fn from_layout_js(el: LayoutDom<Element>) -> ServoLayoutElement<'le> {
595         ServoLayoutElement {
596             element: el,
597             chain: PhantomData,
598         }
599     }
600 
601     #[inline]
get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>602     fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
603         unsafe {
604             (*self.element.unsafe_get()).get_attr_for_layout(namespace, name)
605         }
606     }
607 
608     #[inline]
get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>609     fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> {
610         unsafe {
611             (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name)
612         }
613     }
614 
get_style_data(&self) -> Option<&StyleData>615     fn get_style_data(&self) -> Option<&StyleData> {
616         unsafe {
617             self.get_style_and_layout_data().map(|d| &*(d.ptr.as_ptr() as *mut StyleData))
618         }
619     }
620 
unset_snapshot_flags(&self)621     pub unsafe fn unset_snapshot_flags(&self) {
622         self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false);
623     }
624 
set_has_snapshot(&self)625     pub unsafe fn set_has_snapshot(&self) {
626         self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true);
627     }
628 
note_dirty_descendant(&self)629     pub unsafe fn note_dirty_descendant(&self) {
630         use ::selectors::Element;
631 
632         let mut current = Some(*self);
633         while let Some(el) = current {
634             // FIXME(bholley): Ideally we'd have the invariant that any element
635             // with has_dirty_descendants also has the bit set on all its
636             // ancestors.  However, there are currently some corner-cases where
637             // we get that wrong.  I have in-flight patches to fix all this
638             // stuff up, so we just always propagate this bit for now.
639             el.set_dirty_descendants();
640             current = el.parent_element();
641         }
642     }
643 }
644 
as_element<'le>(node: LayoutDom<Node>) -> Option<ServoLayoutElement<'le>>645 fn as_element<'le>(node: LayoutDom<Node>) -> Option<ServoLayoutElement<'le>> {
646     node.downcast().map(ServoLayoutElement::from_layout_js)
647 }
648 
649 impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
650     type Impl = SelectorImpl;
651 
opaque(&self) -> ::selectors::OpaqueElement652     fn opaque(&self) -> ::selectors::OpaqueElement {
653         ::selectors::OpaqueElement::new(self.as_node().opaque().0 as *const ())
654     }
655 
parent_element(&self) -> Option<ServoLayoutElement<'le>>656     fn parent_element(&self) -> Option<ServoLayoutElement<'le>> {
657         unsafe {
658             self.element.upcast().parent_node_ref().and_then(as_element)
659         }
660     }
661 
first_child_element(&self) -> Option<ServoLayoutElement<'le>>662     fn first_child_element(&self) -> Option<ServoLayoutElement<'le>> {
663         self.as_node().dom_children().filter_map(|n| n.as_element()).next()
664     }
665 
last_child_element(&self) -> Option<ServoLayoutElement<'le>>666     fn last_child_element(&self) -> Option<ServoLayoutElement<'le>> {
667         self.as_node().rev_children().filter_map(|n| n.as_element()).next()
668     }
669 
prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>>670     fn prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
671         let mut node = self.as_node();
672         while let Some(sibling) = node.prev_sibling() {
673             if let Some(element) = sibling.as_element() {
674                 return Some(element)
675             }
676             node = sibling;
677         }
678         None
679     }
680 
next_sibling_element(&self) -> Option<ServoLayoutElement<'le>>681     fn next_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
682         let mut node = self.as_node();
683         while let Some(sibling) = node.next_sibling() {
684             if let Some(element) = sibling.as_element() {
685                 return Some(element)
686             }
687             node = sibling;
688         }
689         None
690     }
691 
attr_matches(&self, ns: &NamespaceConstraint<&Namespace>, local_name: &LocalName, operation: &AttrSelectorOperation<&String>) -> bool692     fn attr_matches(&self,
693                     ns: &NamespaceConstraint<&Namespace>,
694                     local_name: &LocalName,
695                     operation: &AttrSelectorOperation<&String>)
696                     -> bool {
697         match *ns {
698             NamespaceConstraint::Specific(ref ns) => {
699                 self.get_attr_enum(ns, local_name)
700                     .map_or(false, |value| value.eval_selector(operation))
701             }
702             NamespaceConstraint::Any => {
703                 let values = unsafe {
704                     (*self.element.unsafe_get()).get_attr_vals_for_layout(local_name)
705                 };
706                 values.iter().any(|value| value.eval_selector(operation))
707             }
708         }
709     }
710 
is_root(&self) -> bool711     fn is_root(&self) -> bool {
712         match self.as_node().parent_node() {
713             None => false,
714             Some(node) => {
715                 match node.script_type_id() {
716                     NodeTypeId::Document(_) => true,
717                     _ => false
718                 }
719             },
720         }
721     }
722 
is_empty(&self) -> bool723     fn is_empty(&self) -> bool {
724         self.as_node().dom_children().all(|node| match node.script_type_id() {
725             NodeTypeId::Element(..) => false,
726             NodeTypeId::CharacterData(CharacterDataTypeId::Text) => unsafe {
727                 node.node.downcast().unwrap().data_for_layout().is_empty()
728             },
729             _ => true
730         })
731     }
732 
733     #[inline]
local_name(&self) -> &LocalName734     fn local_name(&self) -> &LocalName {
735         self.element.local_name()
736     }
737 
738     #[inline]
namespace(&self) -> &Namespace739     fn namespace(&self) -> &Namespace {
740         self.element.namespace()
741     }
742 
match_pseudo_element( &self, _pseudo: &PseudoElement, _context: &mut MatchingContext<Self::Impl>, ) -> bool743     fn match_pseudo_element(
744         &self,
745         _pseudo: &PseudoElement,
746         _context: &mut MatchingContext<Self::Impl>,
747     ) -> bool {
748         false
749     }
750 
match_non_ts_pseudo_class<F>( &self, pseudo_class: &NonTSPseudoClass, _: &mut MatchingContext<Self::Impl>, _: &mut F, ) -> bool where F: FnMut(&Self, ElementSelectorFlags),751     fn match_non_ts_pseudo_class<F>(
752         &self,
753         pseudo_class: &NonTSPseudoClass,
754         _: &mut MatchingContext<Self::Impl>,
755         _: &mut F,
756     ) -> bool
757     where
758         F: FnMut(&Self, ElementSelectorFlags),
759     {
760         match *pseudo_class {
761             // https://github.com/servo/servo/issues/8718
762             NonTSPseudoClass::Link |
763             NonTSPseudoClass::AnyLink => self.is_link(),
764             NonTSPseudoClass::Visited => false,
765 
766             NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, &*lang),
767 
768             NonTSPseudoClass::ServoNonZeroBorder => unsafe {
769                 match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &local_name!("border")) {
770                     None | Some(&AttrValue::UInt(_, 0)) => false,
771                     _ => true,
772                 }
773             },
774             NonTSPseudoClass::ServoCaseSensitiveTypeAttr(ref expected_value) => {
775                 self.get_attr_enum(&ns!(), &local_name!("type"))
776                     .map_or(false, |attr| attr == expected_value)
777             }
778             NonTSPseudoClass::ReadOnly =>
779                 !self.element.get_state_for_layout().contains(pseudo_class.state_flag()),
780 
781             NonTSPseudoClass::Active |
782             NonTSPseudoClass::Focus |
783             NonTSPseudoClass::Fullscreen |
784             NonTSPseudoClass::Hover |
785             NonTSPseudoClass::Enabled |
786             NonTSPseudoClass::Disabled |
787             NonTSPseudoClass::Checked |
788             NonTSPseudoClass::Indeterminate |
789             NonTSPseudoClass::ReadWrite |
790             NonTSPseudoClass::PlaceholderShown |
791             NonTSPseudoClass::Target =>
792                 self.element.get_state_for_layout().contains(pseudo_class.state_flag())
793         }
794     }
795 
796     #[inline]
is_link(&self) -> bool797     fn is_link(&self) -> bool {
798         unsafe {
799             match self.as_node().script_type_id() {
800                 // https://html.spec.whatwg.org/multipage/#selector-link
801                 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
802                 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
803                 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) =>
804                     (*self.element.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("href")).is_some(),
805                 _ => false,
806             }
807         }
808     }
809 
810     #[inline]
has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool811     fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
812         unsafe {
813             (*self.element.id_attribute())
814                 .as_ref()
815                 .map_or(false, |atom| case_sensitivity.eq_atom(atom, id))
816         }
817     }
818 
819     #[inline]
has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool820     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
821         unsafe {
822             self.element.has_class_for_layout(name, case_sensitivity)
823         }
824     }
825 
is_html_slot_element(&self) -> bool826     fn is_html_slot_element(&self) -> bool {
827         unsafe {
828             self.element.is_html_element() &&
829             self.local_name() == &local_name!("slot")
830         }
831     }
832 
is_html_element_in_html_document(&self) -> bool833     fn is_html_element_in_html_document(&self) -> bool {
834         unsafe {
835             if !self.element.is_html_element() {
836                 return false;
837             }
838         }
839 
840         self.as_node().owner_doc().is_html_document()
841     }
842 }
843 
844 #[derive(Clone, Copy, Debug)]
845 pub struct ServoThreadSafeLayoutNode<'ln> {
846     /// The wrapped node.
847     node: ServoLayoutNode<'ln>,
848 
849     /// The pseudo-element type, with (optionally)
850     /// a specified display value to override the stylesheet.
851     pseudo: PseudoElementType,
852 }
853 
854 impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> {
855     #[inline]
eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool856     fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool {
857         self.node == other.node
858     }
859 }
860 
861 impl<'ln> DangerousThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
dangerous_first_child(&self) -> Option<Self>862     unsafe fn dangerous_first_child(&self) -> Option<Self> {
863             self.get_jsmanaged().first_child_ref()
864                 .map(|node| self.new_with_this_lifetime(&node))
865     }
dangerous_next_sibling(&self) -> Option<Self>866     unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
867             self.get_jsmanaged().next_sibling_ref()
868                 .map(|node| self.new_with_this_lifetime(&node))
869     }
870 }
871 
872 impl<'ln> ServoThreadSafeLayoutNode<'ln> {
873     /// Creates a new layout node with the same lifetime as this layout node.
new_with_this_lifetime(&self, node: &LayoutDom<Node>) -> ServoThreadSafeLayoutNode<'ln>874     pub unsafe fn new_with_this_lifetime(&self, node: &LayoutDom<Node>) -> ServoThreadSafeLayoutNode<'ln> {
875         ServoThreadSafeLayoutNode {
876             node: self.node.new_with_this_lifetime(node),
877             pseudo: PseudoElementType::Normal,
878         }
879     }
880 
881     /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a>882     pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> {
883         ServoThreadSafeLayoutNode {
884             node: node.clone(),
885             pseudo: PseudoElementType::Normal,
886         }
887     }
888 
889     /// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to
890     /// call and as such is marked `unsafe`.
get_jsmanaged(&self) -> &LayoutDom<Node>891     unsafe fn get_jsmanaged(&self) -> &LayoutDom<Node> {
892         self.node.get_jsmanaged()
893     }
894 }
895 
896 impl<'ln> NodeInfo for ServoThreadSafeLayoutNode<'ln> {
is_element(&self) -> bool897     fn is_element(&self) -> bool {
898         self.node.is_element()
899     }
900 
is_text_node(&self) -> bool901     fn is_text_node(&self) -> bool {
902         self.node.is_text_node()
903     }
904 }
905 
906 impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
907     type ConcreteNode = ServoLayoutNode<'ln>;
908     type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>;
909     type ConcreteElement = ServoLayoutElement<'ln>;
910     type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator<Self>;
911 
opaque(&self) -> OpaqueNode912     fn opaque(&self) -> OpaqueNode {
913         unsafe { self.get_jsmanaged().opaque() }
914     }
915 
type_id(&self) -> Option<LayoutNodeType>916     fn type_id(&self) -> Option<LayoutNodeType> {
917         if self.pseudo == PseudoElementType::Normal {
918             Some(self.node.type_id())
919         } else {
920             None
921         }
922     }
923 
parent_style(&self) -> Arc<ComputedValues>924     fn parent_style(&self) -> Arc<ComputedValues> {
925         let parent = self.node.parent_node().unwrap().as_element().unwrap();
926         let parent_data = parent.get_data().unwrap().borrow();
927         parent_data.styles.primary().clone()
928     }
929 
debug_id(self) -> usize930     fn debug_id(self) -> usize {
931         self.node.debug_id()
932     }
933 
children(&self) -> LayoutIterator<Self::ChildrenIterator>934     fn children(&self) -> LayoutIterator<Self::ChildrenIterator> {
935         LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new(*self))
936     }
937 
as_element(&self) -> Option<ServoThreadSafeLayoutElement<'ln>>938     fn as_element(&self) -> Option<ServoThreadSafeLayoutElement<'ln>> {
939         self.node.as_element().map(|el| ServoThreadSafeLayoutElement {
940             element: el,
941             pseudo: self.pseudo,
942         })
943     }
944 
get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>945     fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
946         self.node.get_style_and_layout_data()
947     }
948 
is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool949     fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool {
950         unsafe {
951             let text: LayoutDom<Text> = match self.get_jsmanaged().downcast() {
952                 Some(text) => text,
953                 None => return false
954             };
955 
956             if !is_whitespace(text.upcast().data_for_layout()) {
957                 return false
958             }
959 
960             // NB: See the rules for `white-space` here:
961             //
962             //    http://www.w3.org/TR/CSS21/text.html#propdef-white-space
963             //
964             // If you implement other values for this property, you will almost certainly
965             // want to update this check.
966             !self.style(context).get_inheritedtext().white_space.preserve_newlines()
967         }
968     }
969 
unsafe_get(self) -> Self::ConcreteNode970     unsafe fn unsafe_get(self) -> Self::ConcreteNode {
971         self.node
972     }
973 
node_text_content(&self) -> String974     fn node_text_content(&self) -> String {
975         let this = unsafe { self.get_jsmanaged() };
976         return this.text_content();
977     }
978 
selection(&self) -> Option<Range<ByteIndex>>979     fn selection(&self) -> Option<Range<ByteIndex>> {
980         let this = unsafe { self.get_jsmanaged() };
981 
982         this.selection().map(|range| {
983             Range::new(ByteIndex(range.start as isize),
984                        ByteIndex(range.len() as isize))
985         })
986     }
987 
image_url(&self) -> Option<ServoUrl>988     fn image_url(&self) -> Option<ServoUrl> {
989         let this = unsafe { self.get_jsmanaged() };
990         this.image_url()
991     }
992 
canvas_data(&self) -> Option<HTMLCanvasData>993     fn canvas_data(&self) -> Option<HTMLCanvasData> {
994         let this = unsafe { self.get_jsmanaged() };
995         this.canvas_data()
996     }
997 
svg_data(&self) -> Option<SVGSVGData>998     fn svg_data(&self) -> Option<SVGSVGData> {
999         let this = unsafe { self.get_jsmanaged() };
1000         this.svg_data()
1001     }
1002 
1003     // Can return None if the iframe has no nested browsing context
iframe_browsing_context_id(&self) -> Option<BrowsingContextId>1004     fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId> {
1005         let this = unsafe { self.get_jsmanaged() };
1006         this.iframe_browsing_context_id()
1007     }
1008 
1009     // Can return None if the iframe has no nested browsing context
iframe_pipeline_id(&self) -> Option<PipelineId>1010     fn iframe_pipeline_id(&self) -> Option<PipelineId> {
1011         let this = unsafe { self.get_jsmanaged() };
1012         this.iframe_pipeline_id()
1013     }
1014 
get_colspan(&self) -> u321015     fn get_colspan(&self) -> u32 {
1016         unsafe {
1017             self.get_jsmanaged().downcast::<Element>().unwrap().get_colspan()
1018         }
1019     }
1020 
get_rowspan(&self) -> u321021     fn get_rowspan(&self) -> u32 {
1022         unsafe {
1023             self.get_jsmanaged().downcast::<Element>().unwrap().get_rowspan()
1024         }
1025     }
1026 }
1027 
1028 pub struct ThreadSafeLayoutNodeChildrenIterator<ConcreteNode: ThreadSafeLayoutNode> {
1029     current_node: Option<ConcreteNode>,
1030     parent_node: ConcreteNode,
1031 }
1032 
1033 impl<ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
1034                    where ConcreteNode: DangerousThreadSafeLayoutNode {
new(parent: ConcreteNode) -> Self1035     pub fn new(parent: ConcreteNode) -> Self {
1036         let first_child: Option<ConcreteNode> = match parent.get_pseudo_element_type() {
1037             PseudoElementType::Normal => {
1038                 parent.get_before_pseudo().or_else(|| parent.get_details_summary_pseudo()).or_else(|| {
1039                     unsafe { parent.dangerous_first_child() }
1040                 })
1041             },
1042             PseudoElementType::DetailsContent | PseudoElementType::DetailsSummary => {
1043                 unsafe { parent.dangerous_first_child() }
1044             },
1045             _ => None,
1046         };
1047         ThreadSafeLayoutNodeChildrenIterator {
1048             current_node: first_child,
1049             parent_node: parent,
1050         }
1051     }
1052 }
1053 
1054 impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
1055                             where ConcreteNode: DangerousThreadSafeLayoutNode {
1056     type Item = ConcreteNode;
next(&mut self) -> Option<ConcreteNode>1057     fn next(&mut self) -> Option<ConcreteNode> {
1058         use ::selectors::Element;
1059         match self.parent_node.get_pseudo_element_type() {
1060             PseudoElementType::Before | PseudoElementType::After => None,
1061 
1062             PseudoElementType::DetailsSummary => {
1063                 let mut current_node = self.current_node.clone();
1064                 loop {
1065                     let next_node = if let Some(ref node) = current_node {
1066                         if let Some(element) = node.as_element() {
1067                             if element.local_name() == &local_name!("summary") &&
1068                                element.namespace() == &ns!(html) {
1069                                 self.current_node = None;
1070                                 return Some(node.clone());
1071                             }
1072                         }
1073                         unsafe { node.dangerous_next_sibling() }
1074                     } else {
1075                         self.current_node = None;
1076                         return None
1077                     };
1078                     current_node = next_node;
1079                 }
1080             }
1081 
1082             PseudoElementType::DetailsContent => {
1083                 let node = self.current_node.clone();
1084                 let node = node.and_then(|node| {
1085                     if node.is_element() &&
1086                        node.as_element().unwrap().local_name() == &local_name!("summary") &&
1087                        node.as_element().unwrap().namespace() == &ns!(html) {
1088                         unsafe { node.dangerous_next_sibling() }
1089                     } else {
1090                         Some(node)
1091                     }
1092                 });
1093                 self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() });
1094                 node
1095             }
1096 
1097             PseudoElementType::Normal => {
1098                 let node = self.current_node.clone();
1099                 if let Some(ref node) = node {
1100                     self.current_node = match node.get_pseudo_element_type() {
1101                         PseudoElementType::Before => {
1102                             self.parent_node.get_details_summary_pseudo()
1103                                 .or_else(|| unsafe { self.parent_node.dangerous_first_child() })
1104                                 .or_else(|| self.parent_node.get_after_pseudo())
1105                         },
1106                         PseudoElementType::Normal => {
1107                             unsafe { node.dangerous_next_sibling() }.or_else(|| self.parent_node.get_after_pseudo())
1108                         },
1109                         PseudoElementType::DetailsSummary => self.parent_node.get_details_content_pseudo(),
1110                         PseudoElementType::DetailsContent => self.parent_node.get_after_pseudo(),
1111                         PseudoElementType::After => None,
1112                     };
1113                 }
1114                 node
1115             }
1116 
1117         }
1118     }
1119 }
1120 
1121 /// A wrapper around elements that ensures layout can only
1122 /// ever access safe properties and cannot race on elements.
1123 #[derive(Clone, Copy, Debug)]
1124 pub struct ServoThreadSafeLayoutElement<'le> {
1125     element: ServoLayoutElement<'le>,
1126 
1127     /// The pseudo-element type, with (optionally)
1128     /// a specified display value to override the stylesheet.
1129     pseudo: PseudoElementType,
1130 }
1131 
1132 impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> {
1133     type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>;
1134     type ConcreteElement = ServoLayoutElement<'le>;
1135 
as_node(&self) -> ServoThreadSafeLayoutNode<'le>1136     fn as_node(&self) -> ServoThreadSafeLayoutNode<'le> {
1137         ServoThreadSafeLayoutNode {
1138             node: self.element.as_node(),
1139             pseudo: self.pseudo.clone(),
1140         }
1141     }
1142 
get_pseudo_element_type(&self) -> PseudoElementType1143     fn get_pseudo_element_type(&self) -> PseudoElementType {
1144         self.pseudo
1145     }
1146 
with_pseudo(&self, pseudo: PseudoElementType) -> Self1147     fn with_pseudo(&self, pseudo: PseudoElementType) -> Self {
1148         ServoThreadSafeLayoutElement {
1149             element: self.element.clone(),
1150             pseudo,
1151         }
1152     }
1153 
type_id(&self) -> Option<LayoutNodeType>1154     fn type_id(&self) -> Option<LayoutNodeType> {
1155         self.as_node().type_id()
1156     }
1157 
unsafe_get(self) -> ServoLayoutElement<'le>1158     unsafe fn unsafe_get(self) -> ServoLayoutElement<'le> {
1159         self.element
1160     }
1161 
get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>1162     fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
1163         self.element.get_attr_enum(namespace, name)
1164     }
1165 
get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str>1166     fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
1167         self.element.get_attr(namespace, name)
1168     }
1169 
style_data(&self) -> AtomicRef<ElementData>1170     fn style_data(&self) -> AtomicRef<ElementData> {
1171         self.element.get_data()
1172             .expect("Unstyled layout node?")
1173             .borrow()
1174     }
1175 }
1176 
1177 /// This implementation of `::selectors::Element` is used for implementing lazy
1178 /// pseudo-elements.
1179 ///
1180 /// Lazy pseudo-elements in Servo only allows selectors using safe properties,
1181 /// i.e., local_name, attributes, so they can only be used for **private**
1182 /// pseudo-elements (like `::-servo-details-content`).
1183 ///
1184 /// Probably a few more of this functions can be implemented (like `has_class`, etc.),
1185 /// but they have no use right now.
1186 ///
1187 /// Note that the element implementation is needed only for selector matching,
1188 /// not for inheritance (styles are inherited appropiately).
1189 impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
1190     type Impl = SelectorImpl;
1191 
opaque(&self) -> ::selectors::OpaqueElement1192     fn opaque(&self) -> ::selectors::OpaqueElement {
1193         ::selectors::OpaqueElement::new(self.as_node().opaque().0 as *const ())
1194     }
1195 
1196 
parent_element(&self) -> Option<Self>1197     fn parent_element(&self) -> Option<Self> {
1198         warn!("ServoThreadSafeLayoutElement::parent_element called");
1199         None
1200     }
1201 
first_child_element(&self) -> Option<Self>1202     fn first_child_element(&self) -> Option<Self> {
1203         warn!("ServoThreadSafeLayoutElement::first_child_element called");
1204         None
1205     }
1206 
1207     // Skips non-element nodes
last_child_element(&self) -> Option<Self>1208     fn last_child_element(&self) -> Option<Self> {
1209         warn!("ServoThreadSafeLayoutElement::last_child_element called");
1210         None
1211     }
1212 
1213     // Skips non-element nodes
prev_sibling_element(&self) -> Option<Self>1214     fn prev_sibling_element(&self) -> Option<Self> {
1215         warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
1216         None
1217     }
1218 
1219     // Skips non-element nodes
next_sibling_element(&self) -> Option<Self>1220     fn next_sibling_element(&self) -> Option<Self> {
1221         warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
1222         None
1223     }
1224 
is_html_slot_element(&self) -> bool1225     fn is_html_slot_element(&self) -> bool {
1226         self.element.is_html_slot_element()
1227     }
1228 
is_html_element_in_html_document(&self) -> bool1229     fn is_html_element_in_html_document(&self) -> bool {
1230         debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called");
1231         true
1232     }
1233 
1234     #[inline]
local_name(&self) -> &LocalName1235     fn local_name(&self) -> &LocalName {
1236         self.element.local_name()
1237     }
1238 
1239     #[inline]
namespace(&self) -> &Namespace1240     fn namespace(&self) -> &Namespace {
1241         self.element.namespace()
1242     }
1243 
match_pseudo_element( &self, _pseudo: &PseudoElement, _context: &mut MatchingContext<Self::Impl> ) -> bool1244     fn match_pseudo_element(
1245         &self,
1246         _pseudo: &PseudoElement,
1247         _context: &mut MatchingContext<Self::Impl>
1248     ) -> bool {
1249         false
1250     }
1251 
attr_matches(&self, ns: &NamespaceConstraint<&Namespace>, local_name: &LocalName, operation: &AttrSelectorOperation<&String>) -> bool1252     fn attr_matches(&self,
1253                     ns: &NamespaceConstraint<&Namespace>,
1254                     local_name: &LocalName,
1255                     operation: &AttrSelectorOperation<&String>)
1256                     -> bool {
1257         match *ns {
1258             NamespaceConstraint::Specific(ref ns) => {
1259                 self.get_attr_enum(ns, local_name)
1260                     .map_or(false, |value| value.eval_selector(operation))
1261             }
1262             NamespaceConstraint::Any => {
1263                 let values = unsafe {
1264                     (*self.element.element.unsafe_get()).get_attr_vals_for_layout(local_name)
1265                 };
1266                 values.iter().any(|v| v.eval_selector(operation))
1267             }
1268         }
1269     }
1270 
match_non_ts_pseudo_class<F>( &self, _: &NonTSPseudoClass, _: &mut MatchingContext<Self::Impl>, _: &mut F, ) -> bool where F: FnMut(&Self, ElementSelectorFlags),1271     fn match_non_ts_pseudo_class<F>(
1272         &self,
1273         _: &NonTSPseudoClass,
1274         _: &mut MatchingContext<Self::Impl>,
1275         _: &mut F,
1276     ) -> bool
1277     where
1278         F: FnMut(&Self, ElementSelectorFlags),
1279     {
1280         // NB: This could maybe be implemented
1281         warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
1282         false
1283     }
1284 
is_link(&self) -> bool1285     fn is_link(&self) -> bool {
1286         warn!("ServoThreadSafeLayoutElement::is_link called");
1287         false
1288     }
1289 
has_id(&self, _id: &Atom, _case_sensitivity: CaseSensitivity) -> bool1290     fn has_id(&self, _id: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
1291         debug!("ServoThreadSafeLayoutElement::has_id called");
1292         false
1293     }
1294 
has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool1295     fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
1296         debug!("ServoThreadSafeLayoutElement::has_class called");
1297         false
1298     }
1299 
is_empty(&self) -> bool1300     fn is_empty(&self) -> bool {
1301         warn!("ServoThreadSafeLayoutElement::is_empty called");
1302         false
1303     }
1304 
is_root(&self) -> bool1305     fn is_root(&self) -> bool {
1306         warn!("ServoThreadSafeLayoutElement::is_root called");
1307         false
1308     }
1309 }
1310