/
dom.rs
364 lines (308 loc) · 13.2 KB
/
dom.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Types and traits used to access the DOM from style calculation.
#![allow(unsafe_code)]
#![deny(missing_docs)]
use {Atom, Namespace, LocalName};
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use data::ElementData;
use element_state::ElementState;
use parking_lot::RwLock;
use properties::{ComputedValues, PropertyDeclarationBlock};
use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement};
use sink::Push;
use std::fmt;
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::Arc;
use stylist::ApplicableDeclarationBlock;
pub use style_traits::UnsafeNode;
/// An opaque handle to a node, which, unlike UnsafeNode, cannot be transformed
/// back into a non-opaque representation. The only safe operation that can be
/// performed on this node is to compare it to another opaque handle or to another
/// OpaqueNode.
///
/// Layout and Graphics use this to safely represent nodes for comparison purposes.
/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
/// locality reasons. Using `OpaqueNode` enforces this invariant.
#[derive(Clone, PartialEq, Copy, Debug, Hash, Eq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
pub struct OpaqueNode(pub usize);
impl OpaqueNode {
/// Returns the address of this node, for debugging purposes.
#[inline]
pub fn id(&self) -> usize {
self.0
}
}
/// Simple trait to provide basic information about the type of an element.
///
/// We avoid exposing the full type id, since computing it in the general case
/// would be difficult for Gecko nodes.
pub trait NodeInfo {
/// Whether this node is an element.
fn is_element(&self) -> bool;
/// Whether this node is a text node.
fn is_text_node(&self) -> bool;
/// Whether this node needs layout.
///
/// Comments, doctypes, etc are ignored by layout algorithms.
fn needs_layout(&self) -> bool { self.is_element() || self.is_text_node() }
}
/// A node iterator that only returns node that don't need layout.
pub struct LayoutIterator<T>(pub T);
impl<T, I> Iterator for LayoutIterator<T>
where T: Iterator<Item=I>,
I: NodeInfo,
{
type Item = I;
fn next(&mut self) -> Option<I> {
loop {
// Filter out nodes that layout should ignore.
let n = self.0.next();
if n.is_none() || n.as_ref().unwrap().needs_layout() {
return n
}
}
}
}
/// The `TNode` trait. This is the main generic trait over which the style
/// system can be implemented.
pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo {
/// The concrete `TElement` type.
type ConcreteElement: TElement<ConcreteNode = Self>;
/// A concrete children iterator type in order to iterate over the `Node`s.
///
/// TODO(emilio): We should eventually replace this with the `impl Trait`
/// syntax.
type ConcreteChildrenIterator: Iterator<Item = Self>;
/// Convert this node in an `UnsafeNode`.
fn to_unsafe(&self) -> UnsafeNode;
/// Get a node back from an `UnsafeNode`.
unsafe fn from_unsafe(n: &UnsafeNode) -> Self;
/// Returns an iterator over this node's children.
fn children(self) -> LayoutIterator<Self::ConcreteChildrenIterator>;
/// Converts self into an `OpaqueNode`.
fn opaque(&self) -> OpaqueNode;
/// Get this node's parent element if present.
fn parent_element(&self) -> Option<Self::ConcreteElement> {
self.parent_node().and_then(|n| n.as_element())
}
/// A debug id, only useful, mm... for debugging.
fn debug_id(self) -> usize;
/// Get this node as an element, if it's one.
fn as_element(&self) -> Option<Self::ConcreteElement>;
/// Whether this node needs to be laid out on viewport size change.
fn needs_dirty_on_viewport_size_changed(&self) -> bool;
/// Mark this node as needing layout on viewport size change.
unsafe fn set_dirty_on_viewport_size_changed(&self);
/// Whether this node can be fragmented. This is used for multicol, and only
/// for Servo.
fn can_be_fragmented(&self) -> bool;
/// Set whether this node can be fragmented.
unsafe fn set_can_be_fragmented(&self, value: bool);
/// Get this node's parent node.
fn parent_node(&self) -> Option<Self>;
/// Whether this node is in the document right now needed to clear the
/// restyle data appropriately on some forced restyles.
fn is_in_doc(&self) -> bool;
}
/// Wrapper to output the ElementData along with the node when formatting for
/// Debug.
pub struct ShowData<N: TNode>(pub N);
impl<N: TNode> Debug for ShowData<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_with_data(f, self.0)
}
}
/// Wrapper to output the primary computed values along with the node when
/// formatting for Debug. This is very verbose.
pub struct ShowDataAndPrimaryValues<N: TNode>(pub N);
impl<N: TNode> Debug for ShowDataAndPrimaryValues<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_with_data_and_primary_values(f, self.0)
}
}
/// Wrapper to output the subtree rather than the single node when formatting
/// for Debug.
pub struct ShowSubtree<N: TNode>(pub N);
impl<N: TNode> Debug for ShowSubtree<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(writeln!(f, "DOM Subtree:"));
fmt_subtree(f, &|f, n| write!(f, "{:?}", n), self.0, 1)
}
}
/// Wrapper to output the subtree along with the ElementData when formatting
/// for Debug.
pub struct ShowSubtreeData<N: TNode>(pub N);
impl<N: TNode> Debug for ShowSubtreeData<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(writeln!(f, "DOM Subtree:"));
fmt_subtree(f, &|f, n| fmt_with_data(f, n), self.0, 1)
}
}
/// Wrapper to output the subtree along with the ElementData and primary
/// ComputedValues when formatting for Debug. This is extremely verbose.
pub struct ShowSubtreeDataAndPrimaryValues<N: TNode>(pub N);
impl<N: TNode> Debug for ShowSubtreeDataAndPrimaryValues<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(writeln!(f, "DOM Subtree:"));
fmt_subtree(f, &|f, n| fmt_with_data_and_primary_values(f, n), self.0, 1)
}
}
fn fmt_with_data<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
if let Some(el) = n.as_element() {
write!(f, "{:?} dd={} data={:?}", el, el.has_dirty_descendants(), el.borrow_data())
} else {
write!(f, "{:?}", n)
}
}
fn fmt_with_data_and_primary_values<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
if let Some(el) = n.as_element() {
let dd = el.has_dirty_descendants();
let data = el.borrow_data();
let styles = data.as_ref().and_then(|d| d.get_styles());
let values = styles.map(|s| &s.primary.values);
write!(f, "{:?} dd={} data={:?} values={:?}", el, dd, &data, values)
} else {
write!(f, "{:?}", n)
}
}
fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32)
-> fmt::Result
where F: Fn(&mut fmt::Formatter, N) -> fmt::Result
{
for _ in 0..indent {
try!(write!(f, " "));
}
try!(stringify(f, n));
for kid in n.children() {
try!(writeln!(f, ""));
try!(fmt_subtree(f, stringify, kid, indent + 1));
}
Ok(())
}
/// A trait used to synthesize presentational hints for HTML element attributes.
pub trait PresentationalHintsSynthetizer {
/// Generate the proper applicable declarations due to presentational hints,
/// and insert them into `hints`.
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<ApplicableDeclarationBlock>;
}
/// The animation rules. The first one is for Animation cascade level, and the second one is for
/// Transition cascade level.
pub struct AnimationRules(pub Option<Arc<RwLock<PropertyDeclarationBlock>>>,
pub Option<Arc<RwLock<PropertyDeclarationBlock>>>);
/// The element trait, the main abstraction the style crate acts over.
pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer {
/// The concrete node type.
type ConcreteNode: TNode<ConcreteElement = Self>;
/// Get this element as a node.
fn as_node(&self) -> Self::ConcreteNode;
/// While doing a reflow, the element at the root has no parent, as far as we're
/// concerned. This method returns `None` at the reflow root.
fn layout_parent_element(self, reflow_root: OpaqueNode) -> Option<Self> {
if self.as_node().opaque() == reflow_root {
None
} else {
self.parent_element()
}
}
/// Get this element's style attribute.
fn style_attribute(&self) -> Option<&Arc<RwLock<PropertyDeclarationBlock>>>;
/// Get this element's animation rules.
fn get_animation_rules(&self, _pseudo: Option<&PseudoElement>) -> AnimationRules {
AnimationRules(None, None)
}
/// Get this element's state, for non-tree-structural pseudos.
fn get_state(&self) -> ElementState;
/// Whether this element has an attribute with a given namespace.
fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool;
/// Whether an attribute value equals `value`.
fn attr_equals(&self, namespace: &Namespace, attr: &LocalName, value: &Atom) -> bool;
/// Get the pre-existing style to calculate restyle damage (change hints).
///
/// This needs to be generic since it varies between Servo and Gecko.
///
/// XXX(emilio): It's a bit unfortunate we need to pass the current computed
/// values as an argument here, but otherwise Servo would crash due to
/// double borrows to return it.
fn existing_style_for_restyle_damage<'a>(&'a self,
current_computed_values: Option<&'a Arc<ComputedValues>>,
pseudo: Option<&PseudoElement>)
-> Option<&'a PreExistingComputedValues>;
/// Returns true if this element may have a descendant needing style processing.
///
/// Note that we cannot guarantee the existence of such an element, because
/// it may have been removed from the DOM between marking it for restyle and
/// the actual restyle traversal.
fn has_dirty_descendants(&self) -> bool;
/// Flag that this element has a descendant for style processing.
///
/// Only safe to call with exclusive access to the element.
unsafe fn set_dirty_descendants(&self);
/// Flag that this element has no descendant for style processing.
///
/// Only safe to call with exclusive access to the element.
unsafe fn unset_dirty_descendants(&self);
/// Atomically stores the number of children of this node that we will
/// need to process during bottom-up traversal.
fn store_children_to_process(&self, n: isize);
/// Atomically notes that a child has been processed during bottom-up
/// traversal. Returns the number of children left to process.
fn did_process_child(&self) -> isize;
/// Gets a reference to the ElementData container.
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>>;
/// Immutably borrows the ElementData.
fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
self.get_data().map(|x| x.borrow())
}
/// Mutably borrows the ElementData.
fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
self.get_data().map(|x| x.borrow_mut())
}
/// Whether we should skip any root- or item-based display property
/// blockification on this element. (This function exists so that Gecko
/// native anonymous content can opt out of this style fixup.)
fn skip_root_and_item_based_display_fixup(&self) -> bool;
}
/// TNode and TElement aren't Send because we want to be careful and explicit
/// about our parallel traversal. However, there are certain situations
/// (including but not limited to the traversal) where we need to send DOM
/// objects to other threads.
///
/// That's the reason why `SendNode` exists.
#[derive(Clone, Debug, PartialEq)]
pub struct SendNode<N: TNode>(N);
unsafe impl<N: TNode> Send for SendNode<N> {}
impl<N: TNode> SendNode<N> {
/// Unsafely construct a SendNode.
pub unsafe fn new(node: N) -> Self {
SendNode(node)
}
}
impl<N: TNode> Deref for SendNode<N> {
type Target = N;
fn deref(&self) -> &N {
&self.0
}
}
/// Same reason as for the existence of SendNode, SendElement does the proper
/// things for a given `TElement`.
#[derive(Debug, PartialEq)]
pub struct SendElement<E: TElement>(E);
unsafe impl<E: TElement> Send for SendElement<E> {}
impl<E: TElement> SendElement<E> {
/// Unsafely construct a SendElement.
pub unsafe fn new(el: E) -> Self {
SendElement(el)
}
}
impl<E: TElement> Deref for SendElement<E> {
type Target = E;
fn deref(&self) -> &E {
&self.0
}
}