2016-01-08 08:27:39 +00:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
//! Implements parallel traversal over the DOM tree.
|
|
|
|
//!
|
2016-12-22 20:02:38 +00:00
|
|
|
//! This traversal is based on Rayon, and therefore its safety is largely
|
|
|
|
//! verified by the type system.
|
|
|
|
//!
|
|
|
|
//! The primary trickiness and fine print for the above relates to the
|
|
|
|
//! thread safety of the DOM nodes themselves. Accessing a DOM element
|
|
|
|
//! concurrently on multiple threads is actually mostly "safe", since all
|
|
|
|
//! the mutable state is protected by an AtomicRefCell, and so we'll
|
|
|
|
//! generally panic if something goes wrong. Still, we try to to enforce our
|
|
|
|
//! thread invariants at compile time whenever possible. As such, TNode and
|
|
|
|
//! TElement are not Send, so ordinary style system code cannot accidentally
|
|
|
|
//! share them with other threads. In the parallel traversal, we explicitly
|
|
|
|
//! invoke |unsafe { SendNode::new(n) }| to put nodes in containers that may
|
|
|
|
//! be sent to other threads. This occurs in only a handful of places and is
|
|
|
|
//! easy to grep for. At the time of this writing, there is no other unsafe
|
|
|
|
//! code in the parallel traversal.
|
|
|
|
|
2016-12-31 11:19:02 +00:00
|
|
|
#![deny(missing_docs)]
|
|
|
|
|
2017-01-19 23:27:34 +00:00
|
|
|
use context::TraversalStatistics;
|
2016-12-22 20:02:38 +00:00
|
|
|
use dom::{OpaqueNode, SendNode, TElement, TNode};
|
2016-11-14 20:47:21 +00:00
|
|
|
use rayon;
|
2016-12-21 19:11:12 +00:00
|
|
|
use scoped_tls::ScopedTLS;
|
2017-01-19 23:27:34 +00:00
|
|
|
use std::borrow::Borrow;
|
2017-02-15 11:05:07 +00:00
|
|
|
use time;
|
2016-12-16 19:38:27 +00:00
|
|
|
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
|
2016-01-08 08:27:39 +00:00
|
|
|
|
2016-12-31 11:19:02 +00:00
|
|
|
/// The chunk size used to split the parallel traversal nodes.
|
|
|
|
///
|
|
|
|
/// We send each `CHUNK_SIZE` nodes as a different work unit to the work queue.
|
2016-01-08 08:27:39 +00:00
|
|
|
pub const CHUNK_SIZE: usize = 64;
|
|
|
|
|
2016-12-31 11:19:02 +00:00
|
|
|
/// A parallel top down traversal, generic over `D`.
|
2016-12-22 20:02:38 +00:00
|
|
|
#[allow(unsafe_code)]
|
2017-01-10 01:26:50 +00:00
|
|
|
pub fn traverse_dom<E, D>(traversal: &D,
|
|
|
|
root: E,
|
2016-12-10 01:01:05 +00:00
|
|
|
token: PreTraverseToken,
|
2016-11-14 20:47:21 +00:00
|
|
|
queue: &rayon::ThreadPool)
|
2017-01-10 01:26:50 +00:00
|
|
|
where E: TElement,
|
|
|
|
D: DomTraversal<E>,
|
2016-08-17 21:34:30 +00:00
|
|
|
{
|
2017-04-12 07:27:02 +00:00
|
|
|
let dump_stats = traversal.shared_context().options.dump_style_statistics;
|
2017-02-15 11:05:07 +00:00
|
|
|
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
|
|
|
|
|
2017-01-25 01:02:41 +00:00
|
|
|
debug_assert!(traversal.is_parallel());
|
2016-12-13 03:13:03 +00:00
|
|
|
// Handle Gecko's eager initial styling. We don't currently support it
|
|
|
|
// in conjunction with bottom-up traversal. If we did, we'd need to put
|
|
|
|
// it on the context to make it available to the bottom-up phase.
|
|
|
|
let (nodes, depth) = if token.traverse_unstyled_children_only() {
|
2016-12-16 19:38:27 +00:00
|
|
|
debug_assert!(!D::needs_postorder_traversal());
|
2016-12-10 01:01:05 +00:00
|
|
|
let mut children = vec![];
|
2016-12-13 03:13:03 +00:00
|
|
|
for kid in root.as_node().children() {
|
|
|
|
if kid.as_element().map_or(false, |el| el.get_data().is_none()) {
|
2016-12-22 20:02:38 +00:00
|
|
|
children.push(unsafe { SendNode::new(kid) });
|
2016-12-13 03:13:03 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-09 06:55:09 +00:00
|
|
|
(children, root.depth() + 1)
|
2016-12-10 01:01:05 +00:00
|
|
|
} else {
|
2017-04-09 06:55:09 +00:00
|
|
|
(vec![unsafe { SendNode::new(root.as_node()) }], root.depth())
|
2016-12-10 01:01:05 +00:00
|
|
|
};
|
|
|
|
|
2016-12-21 19:11:12 +00:00
|
|
|
let traversal_data = PerLevelTraversalData {
|
2016-12-10 01:01:05 +00:00
|
|
|
current_dom_depth: depth,
|
2016-11-28 18:30:19 +00:00
|
|
|
};
|
2016-12-21 19:11:12 +00:00
|
|
|
let tls = ScopedTLS::<D::ThreadLocalContext>::new(queue);
|
2016-12-10 01:01:05 +00:00
|
|
|
let root = root.as_node().opaque();
|
2016-12-21 19:11:12 +00:00
|
|
|
|
2016-11-14 20:47:21 +00:00
|
|
|
queue.install(|| {
|
|
|
|
rayon::scope(|scope| {
|
2016-12-21 19:11:12 +00:00
|
|
|
traverse_nodes(nodes, root, traversal_data, scope, traversal, &tls);
|
2016-01-08 08:27:39 +00:00
|
|
|
});
|
2016-11-14 20:47:21 +00:00
|
|
|
});
|
2016-08-17 21:34:30 +00:00
|
|
|
|
2017-01-19 23:27:34 +00:00
|
|
|
// Dump statistics to stdout if requested.
|
2017-02-15 11:05:07 +00:00
|
|
|
if dump_stats {
|
2017-01-19 23:27:34 +00:00
|
|
|
let slots = unsafe { tls.unsafe_get() };
|
2017-02-15 11:05:07 +00:00
|
|
|
let mut aggregate = slots.iter().fold(TraversalStatistics::default(), |acc, t| {
|
2017-01-19 23:27:34 +00:00
|
|
|
match *t.borrow() {
|
|
|
|
None => acc,
|
|
|
|
Some(ref cx) => &cx.borrow().statistics + &acc,
|
|
|
|
}
|
|
|
|
});
|
2017-03-11 10:31:25 +00:00
|
|
|
aggregate.finish(traversal, start_time.unwrap());
|
2017-01-19 23:27:34 +00:00
|
|
|
println!("{}", aggregate);
|
2016-08-17 21:34:30 +00:00
|
|
|
}
|
2016-01-08 08:27:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A parallel top-down DOM traversal.
|
|
|
|
#[inline(always)]
|
2016-11-14 20:47:21 +00:00
|
|
|
#[allow(unsafe_code)]
|
2017-01-10 01:26:50 +00:00
|
|
|
fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
2016-11-14 20:47:21 +00:00
|
|
|
root: OpaqueNode,
|
2016-12-21 19:11:12 +00:00
|
|
|
mut traversal_data: PerLevelTraversalData,
|
2016-11-14 20:47:21 +00:00
|
|
|
scope: &'a rayon::Scope<'scope>,
|
2016-12-21 19:11:12 +00:00
|
|
|
traversal: &'scope D,
|
|
|
|
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
|
2017-01-10 01:26:50 +00:00
|
|
|
where E: TElement + 'scope,
|
|
|
|
D: DomTraversal<E>,
|
2016-11-05 22:11:24 +00:00
|
|
|
{
|
servo: Merge #11766 - Add `@keyframes` and `animation-*` support (from emilio:keyframes-parsing); r=SimonSapin,pcwalton
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
<!-- Either: -->
- [x] There are tests for these changes
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This adds support for parsing `@keyframes` rules, and animation properties. Stylo will need it sometime soonish, plus I want to make animations work in Servo.
The remaining part is doin the math and trigger the animations correctly from servo. I don't expect it to be *that* hard, but probaby I'll need to learn a bit more about the current animation infra (e.g. why the heck is the `new_animations_sender` guarded by a `Mutex`?).
I'd expect to land this, since this is already a bunch of work, this is the part exclusively required by stylo (at least if we don't use Servo's machinery), the media query parsing is tested, and the properties land after a flag, but if you prefer to wait until I finish this up it's fine for me too.
r? @SimonSapin
cc @pcwalton @bholley
Source-Repo: https://github.com/servo/servo
Source-Revision: d3a81373e44634c30d31da0457e1c1e86c0911ed
2016-06-28 22:31:01 +00:00
|
|
|
let mut discovered_child_nodes = vec![];
|
2016-12-21 19:11:12 +00:00
|
|
|
{
|
|
|
|
// Scope the borrow of the TLS so that the borrow is dropped before
|
|
|
|
// potentially traversing a child on this thread.
|
|
|
|
let mut tlc = tls.ensure(|| traversal.create_thread_local_context());
|
|
|
|
|
2016-12-22 20:02:38 +00:00
|
|
|
for n in nodes {
|
2016-12-21 19:11:12 +00:00
|
|
|
// Perform the appropriate traversal.
|
2016-12-22 20:02:38 +00:00
|
|
|
let node = **n;
|
2016-12-21 19:11:12 +00:00
|
|
|
let mut children_to_process = 0isize;
|
|
|
|
traversal.process_preorder(&mut traversal_data, &mut *tlc, node);
|
|
|
|
if let Some(el) = node.as_element() {
|
2017-01-10 01:26:50 +00:00
|
|
|
traversal.traverse_children(&mut *tlc, el, |_tlc, kid| {
|
2016-12-21 19:11:12 +00:00
|
|
|
children_to_process += 1;
|
2016-12-22 20:02:38 +00:00
|
|
|
discovered_child_nodes.push(unsafe { SendNode::new(kid) })
|
2016-12-21 19:11:12 +00:00
|
|
|
});
|
|
|
|
}
|
2016-01-08 08:27:39 +00:00
|
|
|
|
2017-04-09 06:55:09 +00:00
|
|
|
traversal.handle_postorder_traversal(&mut *tlc, root, node,
|
|
|
|
children_to_process);
|
2016-01-08 08:27:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-09 06:55:09 +00:00
|
|
|
traversal_data.current_dom_depth += 1;
|
2016-12-21 19:11:12 +00:00
|
|
|
traverse_nodes(discovered_child_nodes, root, traversal_data, scope, traversal, tls);
|
2016-12-10 01:01:05 +00:00
|
|
|
}
|
|
|
|
|
2017-01-10 01:26:50 +00:00
|
|
|
fn traverse_nodes<'a, 'scope, E, D>(nodes: Vec<SendNode<E::ConcreteNode>>, root: OpaqueNode,
|
2016-12-21 19:11:12 +00:00
|
|
|
traversal_data: PerLevelTraversalData,
|
2016-12-10 01:01:05 +00:00
|
|
|
scope: &'a rayon::Scope<'scope>,
|
2016-12-21 19:11:12 +00:00
|
|
|
traversal: &'scope D,
|
|
|
|
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
|
2017-01-10 01:26:50 +00:00
|
|
|
where E: TElement + 'scope,
|
|
|
|
D: DomTraversal<E>,
|
2016-12-10 01:01:05 +00:00
|
|
|
{
|
|
|
|
if nodes.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Optimization: traverse directly and avoid a heap-allocating spawn() call if
|
|
|
|
// we're only pushing one work unit.
|
|
|
|
if nodes.len() <= CHUNK_SIZE {
|
|
|
|
let nodes = nodes.into_boxed_slice();
|
2016-12-21 19:11:12 +00:00
|
|
|
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
|
2016-12-10 01:01:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// General case.
|
|
|
|
for chunk in nodes.chunks(CHUNK_SIZE) {
|
2016-11-14 20:47:21 +00:00
|
|
|
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
2016-12-21 19:11:12 +00:00
|
|
|
let traversal_data = traversal_data.clone();
|
2016-11-14 20:47:21 +00:00
|
|
|
scope.spawn(move |scope| {
|
|
|
|
let nodes = nodes;
|
2016-12-21 19:11:12 +00:00
|
|
|
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls)
|
2016-11-14 20:47:21 +00:00
|
|
|
})
|
2016-01-08 08:27:39 +00:00
|
|
|
}
|
|
|
|
}
|