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