diff --git a/components/layout/block.rs b/components/layout/block.rs index 9d38627b80f..f7370e00a4e 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -55,7 +55,7 @@ use std::fmt; use std::sync::Arc; use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y}; use style::computed_values::{position, text_align}; -use style::context::{SharedStyleContext, StyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPOSITION}; diff --git a/components/layout/context.rs b/components/layout/context.rs index 23ec3e41d88..9497d736b47 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -25,22 +25,39 @@ use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use style::context::{LocalStyleContext, StyleContext, SharedStyleContext}; +use style::context::{SharedStyleContext, ThreadLocalStyleContext}; -struct LocalLayoutContext { - style_context: LocalStyleContext, - - font_context: RefCell, +pub struct ThreadLocalLayoutContext { + pub style_context: ThreadLocalStyleContext, + pub font_context: RefCell, } -impl HeapSizeOf for LocalLayoutContext { +impl ThreadLocalLayoutContext { + pub fn new(shared: &SharedLayoutContext) -> Rc { + let font_cache_thread = shared.font_cache_thread.lock().unwrap().clone(); + let local_style_data = shared.style_context.local_context_creation_data.lock().unwrap(); + + Rc::new(ThreadLocalLayoutContext { + style_context: ThreadLocalStyleContext::new(&local_style_data), + font_context: RefCell::new(FontContext::new(font_cache_thread)), + }) + } +} + +impl Borrow for ThreadLocalLayoutContext { + fn borrow(&self) -> &ThreadLocalStyleContext { + &self.style_context + } +} + +impl HeapSizeOf for ThreadLocalLayoutContext { // FIXME(njn): measure other fields eventually. fn heap_size_of_children(&self) -> usize { self.font_context.heap_size_of_children() } } -thread_local!(static LOCAL_CONTEXT_KEY: RefCell>> = RefCell::new(None)); +thread_local!(static LOCAL_CONTEXT_KEY: RefCell>> = RefCell::new(None)); pub fn heap_size_of_local_context() -> usize { LOCAL_CONTEXT_KEY.with(|r| { @@ -49,20 +66,14 @@ pub fn heap_size_of_local_context() -> usize { } // Keep this implementation in sync with the one in ports/geckolib/traversal.rs. -fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) - -> Rc { +pub fn create_or_get_local_context(shared: &SharedLayoutContext) + -> Rc { LOCAL_CONTEXT_KEY.with(|r| { let mut r = r.borrow_mut(); if let Some(context) = r.clone() { context } else { - let font_cache_thread = shared_layout_context.font_cache_thread.lock().unwrap().clone(); - let local_style_data = shared_layout_context.style_context.local_context_creation_data.lock().unwrap(); - - let context = Rc::new(LocalLayoutContext { - style_context: LocalStyleContext::new(&local_style_data), - font_context: RefCell::new(FontContext::new(font_cache_thread)), - }); + let context = ThreadLocalLayoutContext::new(shared); *r = Some(context.clone()); context } @@ -97,27 +108,27 @@ impl Borrow for SharedLayoutContext { pub struct LayoutContext<'a> { pub shared: &'a SharedLayoutContext, - cached_local_layout_context: Rc, + pub thread_local: &'a ThreadLocalLayoutContext, } -impl<'a> StyleContext<'a> for LayoutContext<'a> { - fn shared_context(&self) -> &'a SharedStyleContext { - &self.shared.style_context - } - - fn local_context(&self) -> &LocalStyleContext { - &self.cached_local_layout_context.style_context +impl<'a> LayoutContext<'a> { + pub fn new(shared: &'a SharedLayoutContext, + thread_local: &'a ThreadLocalLayoutContext) -> Self + { + LayoutContext { + shared: shared, + thread_local: thread_local, + } } } impl<'a> LayoutContext<'a> { - pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> { - let local_context = create_or_get_local_context(shared_layout_context); - - LayoutContext { - shared: shared_layout_context, - cached_local_layout_context: local_context, - } + // FIXME(bholley): The following two methods are identical and should be merged. + // shared_context() is the appropriate name, but it involves renaming a lot of + // calls. + #[inline(always)] + pub fn shared_context(&self) -> &SharedStyleContext { + &self.shared.style_context } #[inline(always)] @@ -127,7 +138,7 @@ impl<'a> LayoutContext<'a> { #[inline(always)] pub fn font_context(&self) -> RefMut { - self.cached_local_layout_context.font_context.borrow_mut() + self.thread_local.font_context.borrow_mut() } } diff --git a/components/layout/flex.rs b/components/layout/flex.rs index 7b169674363..7b8d694494c 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -26,7 +26,7 @@ use std::ops::Range; use std::sync::Arc; use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content}; use style::computed_values::border_collapse; -use style::context::{SharedStyleContext, StyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::{Direction, LogicalSize}; use style::properties::ServoComputedValues; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW}; diff --git a/components/layout/inline.rs b/components/layout/inline.rs index cefb77bb592..372aa2ea088 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -32,7 +32,7 @@ use std::sync::Arc; use style::arc_ptr_eq; use style::computed_values::{display, overflow_x, position, text_align, text_justify}; use style::computed_values::{vertical_align, white_space}; -use style::context::{SharedStyleContext, StyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use style::properties::{longhands, ServoComputedValues}; use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPOSITION, RESOLVE_GENERATED_CONTENT}; diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs index 24d90069aa6..b5651369ebf 100644 --- a/components/layout/multicol.rs +++ b/components/layout/multicol.rs @@ -21,7 +21,7 @@ use gfx_traits::print_tree::PrintTree; use std::cmp::{min, max}; use std::fmt; use std::sync::Arc; -use style::context::{StyleContext, SharedStyleContext}; +use style::context::SharedStyleContext; use style::logical_geometry::LogicalSize; use style::properties::ServoComputedValues; use style::values::Either; diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 6422ab674a9..505afa7897f 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -8,7 +8,8 @@ #![allow(unsafe_code)] -use context::{LayoutContext, SharedLayoutContext}; +use context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext}; +use context::create_or_get_local_context; use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTraversal}; use flow_ref::FlowRef; use profile_traits::time::{self, TimerMetadata, profile}; @@ -52,7 +53,7 @@ pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow { pub type ChunkedFlowTraversalFunction<'scope> = extern "Rust" fn(Box<[UnsafeFlow]>, &'scope SharedLayoutContext, &rayon::Scope<'scope>); -pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &SharedLayoutContext); +pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &LayoutContext); /// Information that we need stored in each flow. pub struct FlowParallelInfo { @@ -140,11 +141,14 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { #[inline(always)] fn run_parallel_helper<'scope>(&self, unsafe_flows: &[UnsafeFlow], - layout_context: &'scope SharedLayoutContext, + shared: &'scope SharedLayoutContext, scope: &rayon::Scope<'scope>, top_down_func: ChunkedFlowTraversalFunction<'scope>, bottom_up_func: FlowTraversalFunction) { + let tlc = create_or_get_local_context(shared); + let context = LayoutContext::new(&shared, &*tlc); + let mut discovered_child_flows = vec![]; for unsafe_flow in unsafe_flows { let mut had_children = false; @@ -175,7 +179,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { // If there were no more children, start assigning block-sizes. if !had_children { - bottom_up_func(*unsafe_flow, layout_context) + bottom_up_func(*unsafe_flow, &context) } } @@ -183,7 +187,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { let nodes = chunk.iter().cloned().collect::>().into_boxed_slice(); scope.spawn(move |scope| { - top_down_func(nodes, layout_context, scope); + top_down_func(nodes, shared, scope); }); } } @@ -220,10 +224,9 @@ fn assign_inline_sizes<'scope>(unsafe_flows: Box<[UnsafeFlow]>, fn assign_block_sizes_and_store_overflow( unsafe_flow: UnsafeFlow, - shared_layout_context: &SharedLayoutContext) { - let layout_context = LayoutContext::new(shared_layout_context); + context: &LayoutContext) { let assign_block_sizes_traversal = AssignBSizes { - layout_context: &layout_context, + layout_context: context, }; assign_block_sizes_traversal.run_parallel(unsafe_flow) } @@ -232,11 +235,12 @@ pub fn traverse_flow_tree_preorder( root: &mut Flow, profiler_metadata: Option, time_profiler_chan: time::ProfilerChan, - shared_layout_context: &SharedLayoutContext, + shared: &SharedLayoutContext, queue: &rayon::ThreadPool) { if opts::get().bubble_inline_sizes_separately { - let layout_context = LayoutContext::new(shared_layout_context); - let bubble_inline_sizes = BubbleISizes { layout_context: &layout_context }; + let tlc = ThreadLocalLayoutContext::new(shared); + let context = LayoutContext::new(shared, &*tlc); + let bubble_inline_sizes = BubbleISizes { layout_context: &context }; root.traverse_postorder(&bubble_inline_sizes); } @@ -246,7 +250,7 @@ pub fn traverse_flow_tree_preorder( rayon::scope(move |scope| { profile(time::ProfilerCategory::LayoutParallelWarmup, profiler_metadata, time_profiler_chan, move || { - assign_inline_sizes(nodes, &shared_layout_context, scope); + assign_inline_sizes(nodes, &shared, scope); }); }); }); diff --git a/components/layout/query.rs b/components/layout/query.rs index 81d0ce5e320..b6eb7cfeb39 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -6,6 +6,8 @@ use app_units::Au; use construct::ConstructionResult; +use context::SharedLayoutContext; +use context::create_or_get_local_context; use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; @@ -624,13 +626,12 @@ pub fn process_node_scroll_area_request< N: LayoutNode>(requested_node: N, layou /// Return the resolved value of property for a given (pseudo)element. /// https://drafts.csswg.org/cssom/#resolved-value -pub fn process_resolved_style_request<'a, N, C>(requested_node: N, - style_context: &'a C, - pseudo: &Option, - property: &PropertyId, - layout_root: &mut Flow) -> String +pub fn process_resolved_style_request<'a, N>(shared: &SharedLayoutContext, + requested_node: N, + pseudo: &Option, + property: &PropertyId, + layout_root: &mut Flow) -> String where N: LayoutNode, - C: StyleContext<'a> { use style::traversal::{clear_descendant_data, style_element_in_display_none_subtree}; let element = requested_node.as_element().unwrap(); @@ -645,8 +646,14 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, // we'd need a mechanism to prevent detect when it's stale (since we don't // traverse display:none subtrees during restyle). let display_none_root = if element.get_data().is_none() { - Some(style_element_in_display_none_subtree(element, &|e| e.as_node().initialize_data(), - style_context)) + let tlc = create_or_get_local_context(shared); + let context = StyleContext { + shared: &shared.style_context, + thread_local: &tlc.style_context, + }; + + Some(style_element_in_display_none_subtree(&context, element, + &|e| e.as_node().initialize_data())) } else { None }; diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index b4d31269629..e5f62fe1cea 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -5,7 +5,7 @@ //! Implements sequential traversals over the DOM and flow trees. use app_units::Au; -use context::{LayoutContext, SharedLayoutContext}; +use context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext}; use display_list_builder::DisplayListBuildState; use euclid::point::Point2D; use floats::SpeculatedFloatPlacement; @@ -16,13 +16,12 @@ use fragment::FragmentBorderBoxIterator; use generated_content::ResolveGeneratedContent; use gfx_traits::ScrollRootId; use servo_config::opts; -use style::context::StyleContext; use style::servo::restyle_damage::{REFLOW, STORE_OVERFLOW}; use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList}; pub use style::sequential::traverse_dom; -pub fn resolve_generated_content(root: &mut Flow, shared_layout_context: &SharedLayoutContext) { +pub fn resolve_generated_content(root: &mut Flow, shared: &SharedLayoutContext) { fn doit(flow: &mut Flow, level: u32, traversal: &mut ResolveGeneratedContent) { if !traversal.should_process(flow) { return @@ -35,13 +34,14 @@ pub fn resolve_generated_content(root: &mut Flow, shared_layout_context: &Shared } } - let layout_context = LayoutContext::new(shared_layout_context); + let tlc = ThreadLocalLayoutContext::new(shared); + let layout_context = LayoutContext::new(shared, &*tlc); let mut traversal = ResolveGeneratedContent::new(&layout_context); doit(root, 0, &mut traversal) } pub fn traverse_flow_tree_preorder(root: &mut Flow, - shared_layout_context: &SharedLayoutContext) { + shared: &SharedLayoutContext) { fn doit(flow: &mut Flow, assign_inline_sizes: AssignISizes, assign_block_sizes: AssignBSizes) { @@ -58,7 +58,8 @@ pub fn traverse_flow_tree_preorder(root: &mut Flow, } } - let layout_context = LayoutContext::new(shared_layout_context); + let tlc = ThreadLocalLayoutContext::new(shared); + let layout_context = LayoutContext::new(shared, &*tlc); if opts::get().bubble_inline_sizes_separately { let bubble_inline_sizes = BubbleISizes { layout_context: &layout_context }; diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index bda40df574f..43ef8e14fda 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -5,74 +5,59 @@ //! Traversals over the DOM and flow trees, running the layout computations. use construct::FlowConstructor; -use context::{LayoutContext, SharedLayoutContext}; +use context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext}; +use context::create_or_get_local_context; use display_list_builder::DisplayListBuildState; use flow::{self, PreorderFlowTraversal}; use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal}; use gfx::display_list::OpaqueNode; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use servo_config::opts; -use std::mem; +use std::rc::Rc; use style::atomic_refcell::AtomicRefCell; -use style::context::{LocalStyleContext, SharedStyleContext, StyleContext}; +use style::context::{SharedStyleContext, StyleContext}; use style::data::ElementData; use style::dom::{TElement, TNode}; use style::selector_parser::RestyleDamage; use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT}; -use style::traversal::{DomTraversalContext, recalc_style_at, remove_from_bloom_filter}; +use style::traversal::{DomTraversal, recalc_style_at, remove_from_bloom_filter}; use style::traversal::PerLevelTraversalData; use wrapper::{GetRawData, LayoutNodeHelpers, LayoutNodeLayoutData}; -pub struct RecalcStyleAndConstructFlows<'lc> { - context: LayoutContext<'lc>, +pub struct RecalcStyleAndConstructFlows { + shared: SharedLayoutContext, root: OpaqueNode, } -#[allow(unsafe_code)] -impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> - where N: LayoutNode + TNode, - N::ConcreteElement: TElement +impl RecalcStyleAndConstructFlows { + pub fn shared_layout_context(&self) -> &SharedLayoutContext { + &self.shared + } +} -{ - type SharedContext = SharedLayoutContext; - #[allow(unsafe_code)] - fn new<'a>(shared: &'a Self::SharedContext, root: OpaqueNode) -> Self { - // FIXME(bholley): This transmutation from &'a to &'lc is very unfortunate, but I haven't - // found a way to avoid it despite spending several days on it (and consulting Manishearth, - // brson, and nmatsakis). - // - // The crux of the problem is that parameterizing DomTraversalContext on the lifetime of - // the SharedContext doesn't work for a variety of reasons [1]. However, the code in - // parallel.rs needs to be able to use the DomTraversalContext trait (or something similar) - // to stack-allocate a struct (a generalized LayoutContext<'a>) that holds a borrowed - // SharedContext, which means that the struct needs to be parameterized on a lifetime. - // Given the aforementioned constraint, the only way to accomplish this is to avoid - // propagating the borrow lifetime from the struct to the trait, but that means that the - // new() method on the trait cannot require the lifetime of its argument to match the - // lifetime of the Self object it creates. - // - // This could be solved with an associated type with an unbound lifetime parameter, but - // that would require higher-kinded types, which don't exist yet and probably aren't coming - // for a while. - // - // So we transmute. :-( This is safe because the DomTravesalContext is stack-allocated on - // the worker thread while processing a WorkUnit, whereas the borrowed SharedContext is - // live for the entire duration of the restyle. This really could _almost_ compile: all - // we'd need to do is change the signature to to |new<'a: 'lc>|, and everything would - // work great. But we can't do that, because that would cause a mismatch with the signature - // in the trait we're implementing, and we can't mention 'lc in that trait at all for the - // reasons described above. - // - // [1] For example, the WorkQueue type needs to be parameterized on the concrete type of - // DomTraversalContext::SharedContext, and the WorkQueue lifetime is similar to that of the - // LayoutThread, generally much longer than that of a given SharedLayoutContext borrow. - let shared_lc: &'lc SharedLayoutContext = unsafe { mem::transmute(shared) }; +impl RecalcStyleAndConstructFlows { + /// Creates a traversal context, taking ownership of the shared layout context. + pub fn new(shared: SharedLayoutContext, root: OpaqueNode) -> Self { RecalcStyleAndConstructFlows { - context: LayoutContext::new(shared_lc), + shared: shared, root: root, } } + /// Consumes this traversal context, returning ownership of the shared layout + /// context to the caller. + pub fn destroy(self) -> SharedLayoutContext { + self.shared + } +} + +#[allow(unsafe_code)] +impl DomTraversal for RecalcStyleAndConstructFlows + where N: LayoutNode + TNode, + N::ConcreteElement: TElement +{ + type ThreadLocalContext = ThreadLocalLayoutContext; + fn process_preorder(&self, node: N, traversal_data: &mut PerLevelTraversalData) { // FIXME(pcwalton): Stop allocating here. Ideally this should just be // done by the HTML parser. @@ -81,12 +66,19 @@ impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> if !node.is_text_node() { let el = node.as_element().unwrap(); let mut data = el.mutate_data().unwrap(); - recalc_style_at::<_, _, Self>(&self.context, traversal_data, el, &mut data); + let tlc = create_or_get_local_context(&self.shared); + let context = StyleContext { + shared: &self.shared.style_context, + thread_local: &tlc.style_context, + }; + recalc_style_at(self, traversal_data, &context, el, &mut data); } } fn process_postorder(&self, node: N) { - construct_flows_at(&self.context, self.root, node); + let tlc = create_or_get_local_context(&self.shared); + let context = LayoutContext::new(&self.shared, &*tlc); + construct_flows_at(&context, self.root, node); } fn text_node_needs_traversal(node: N) -> bool { @@ -107,8 +99,12 @@ impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> element.as_node().clear_data(); } - fn local_context(&self) -> &LocalStyleContext { - self.context.local_context() + fn shared_context(&self) -> &SharedStyleContext { + &self.shared.style_context + } + + fn create_or_get_thread_local_context(&self) -> Rc { + create_or_get_local_context(&self.shared) } } @@ -121,7 +117,9 @@ pub trait PostorderNodeMutTraversal(context: &'a LayoutContext<'a>, root: OpaqueNode, node: N) { +fn construct_flows_at<'a, N>(context: &LayoutContext<'a>, root: OpaqueNode, node: N) + where N: LayoutNode, +{ debug!("construct_flows_at: {:?}", node); // Construct flows for this node. @@ -146,7 +144,7 @@ fn construct_flows_at<'a, N: LayoutNode>(context: &'a LayoutContext<'a>, root: O el.mutate_data().unwrap().persist(); unsafe { el.unset_dirty_descendants(); } - remove_from_bloom_filter(context, root, el); + remove_from_bloom_filter(&context.shared.style_context, root, el); } } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 20ef9ff6cb4..490ff0afb4c 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -63,7 +63,7 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use layout::animation; use layout::construct::ConstructionResult; -use layout::context::{LayoutContext, SharedLayoutContext, heap_size_of_local_context}; +use layout::context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext, heap_size_of_local_context}; use layout::display_list_builder::ToGfxColor; use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow_ref::FlowRef; @@ -110,7 +110,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::{Receiver, Sender, channel}; use std::thread; use style::animation::Animation; -use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext}; +use style::context::{ReflowGoal, SharedStyleContext, ThreadLocalStyleContextCreationInfo}; use style::data::StoredRestyleHint; use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode}; use style::error_reporting::{ParseErrorReporter, StdoutErrorReporter}; @@ -122,7 +122,7 @@ use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets}; use style::stylist::Stylist; use style::thread_state; use style::timer::Timer; -use style::traversal::DomTraversalContext; +use style::traversal::DomTraversal; /// Information needed by the layout thread. pub struct LayoutThread { @@ -507,7 +507,8 @@ impl LayoutThread { screen_size_changed: bool, goal: ReflowGoal) -> SharedLayoutContext { - let local_style_context_creation_data = LocalStyleContextCreationInfo::new(self.new_animations_sender.clone()); + let thread_local_style_context_creation_data = + ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone()); SharedLayoutContext { style_context: SharedStyleContext { @@ -519,7 +520,7 @@ impl LayoutThread { running_animations: self.running_animations.clone(), expired_animations: self.expired_animations.clone(), error_reporter: self.error_reporter.clone(), - local_context_creation_data: Mutex::new(local_style_context_creation_data), + local_context_creation_data: Mutex::new(thread_local_style_context_creation_data), timer: self.timer.clone(), }, image_cache_thread: Mutex::new(self.image_cache_thread.clone()), @@ -1143,9 +1144,16 @@ impl LayoutThread { viewport_size_changed, data.reflow_info.goal); + // NB: Type inference falls apart here for some reason, so we need to be very verbose. :-( + let traversal = RecalcStyleAndConstructFlows::new(shared_layout_context, element.as_node().opaque()); let dom_depth = Some(0); // This is always the root node. - let token = > - ::pre_traverse(element, &shared_layout_context.style_context.stylist, /* skip_root = */ false); + let token = { + let stylist = &>::shared_context(&traversal).stylist; + >::pre_traverse(element, stylist, /* skip_root = */ false) + }; + if token.should_traverse() { // Recalculate CSS styles and rebuild flows and fragments. profile(time::ProfilerCategory::LayoutStyleRecalc, @@ -1153,14 +1161,14 @@ impl LayoutThread { self.time_profiler_chan.clone(), || { // Perform CSS selector matching and flow construction. - if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) { + if let (true, Some(pool)) = (self.parallel_flag, self.parallel_traversal.as_mut()) { // Parallel mode parallel::traverse_dom::( - element, dom_depth, &shared_layout_context, token, traversal); + &traversal, element, dom_depth, token, pool); } else { // Sequential mode sequential::traverse_dom::( - element, &shared_layout_context, token); + &traversal, element, token); } }); // TODO(pcwalton): Measure energy usage of text shaping, perhaps? @@ -1179,6 +1187,8 @@ impl LayoutThread { self.root_flow = self.try_get_layout_root(element.as_node()); } + shared_layout_context = traversal.destroy(); + if opts::get().dump_style_tree { println!("{:?}", ShowSubtreeDataAndPrimaryValues(element.as_node())); } @@ -1205,7 +1215,7 @@ impl LayoutThread { fn respond_to_query_if_necessary(&mut self, query_type: &ReflowQueryType, rw_data: &mut LayoutThreadData, - shared_layout_context: &mut SharedLayoutContext) { + shared: &mut SharedLayoutContext) { let mut root_flow = match self.root_flow.clone() { Some(root_flow) => root_flow, None => return, @@ -1253,10 +1263,9 @@ impl LayoutThread { }, ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => { let node = unsafe { ServoLayoutNode::new(&node) }; - let layout_context = LayoutContext::new(&shared_layout_context); rw_data.resolved_style_response = - process_resolved_style_request(node, - &layout_context, + process_resolved_style_request(shared, + node, pseudo, property, root_flow); @@ -1365,7 +1374,7 @@ impl LayoutThread { query_type: Option<&ReflowQueryType>, document: Option<&ServoLayoutDocument>, rw_data: &mut LayoutThreadData, - layout_context: &mut SharedLayoutContext) { + shared: &mut SharedLayoutContext) { if let Some(mut root_flow) = self.root_flow.clone() { // Kick off animations if any were triggered, expire completed ones. animation::update_animation_state(&self.constellation_chan, @@ -1397,7 +1406,7 @@ impl LayoutThread { profile(time::ProfilerCategory::LayoutGeneratedContent, self.profiler_metadata(), self.time_profiler_chan.clone(), - || sequential::resolve_generated_content(FlowRef::deref_mut(&mut root_flow), &layout_context)); + || sequential::resolve_generated_content(FlowRef::deref_mut(&mut root_flow), &shared)); // Guess float placement. profile(time::ProfilerCategory::LayoutFloatPlacementSpeculation, @@ -1420,10 +1429,10 @@ impl LayoutThread { FlowRef::deref_mut(&mut root_flow), profiler_metadata, self.time_profiler_chan.clone(), - &*layout_context); + &*shared); } else { //Sequential mode - LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &layout_context) + LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &shared) } }); } @@ -1432,8 +1441,9 @@ impl LayoutThread { self.profiler_metadata(), self.time_profiler_chan.clone(), || { - let layout_context = LayoutContext::new(&*layout_context); - sequential::store_overflow(&layout_context, + let tlc = ThreadLocalLayoutContext::new(&shared); + let context = LayoutContext::new(&shared, &*tlc); + sequential::store_overflow(&context, FlowRef::deref_mut(&mut root_flow) as &mut Flow); }); @@ -1441,7 +1451,7 @@ impl LayoutThread { query_type, document, rw_data, - layout_context); + shared); } } diff --git a/components/style/context.rs b/components/style/context.rs index d5819555cf6..824c3bfeb94 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -19,13 +19,13 @@ use stylist::Stylist; use timer::Timer; /// This structure is used to create a local style context from a shared one. -pub struct LocalStyleContextCreationInfo { +pub struct ThreadLocalStyleContextCreationInfo { new_animations_sender: Sender, } -impl LocalStyleContextCreationInfo { +impl ThreadLocalStyleContextCreationInfo { pub fn new(animations_sender: Sender) -> Self { - LocalStyleContextCreationInfo { + ThreadLocalStyleContextCreationInfo { new_animations_sender: animations_sender, } } @@ -57,33 +57,33 @@ pub struct SharedStyleContext { ///The CSS error reporter for all CSS loaded in this layout thread pub error_reporter: Box, - /// Data needed to create the local style context from the shared one. - pub local_context_creation_data: Mutex, + /// Data needed to create the thread-local style context from the shared one. + pub local_context_creation_data: Mutex, /// The current timer for transitions and animations. This is needed to test /// them. pub timer: Timer, } -pub struct LocalStyleContext { +pub struct ThreadLocalStyleContext { pub style_sharing_candidate_cache: RefCell, /// A channel on which new animations that have been triggered by style /// recalculation can be sent. pub new_animations_sender: Sender, } -impl LocalStyleContext { - pub fn new(local_context_creation_data: &LocalStyleContextCreationInfo) -> Self { - LocalStyleContext { +impl ThreadLocalStyleContext { + pub fn new(local_context_creation_data: &ThreadLocalStyleContextCreationInfo) -> Self { + ThreadLocalStyleContext { style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()), new_animations_sender: local_context_creation_data.new_animations_sender.clone(), } } } -pub trait StyleContext<'a> { - fn shared_context(&self) -> &'a SharedStyleContext; - fn local_context(&self) -> &LocalStyleContext; +pub struct StyleContext<'a> { + pub shared: &'a SharedStyleContext, + pub thread_local: &'a ThreadLocalStyleContext, } /// Why we're doing reflow. diff --git a/components/style/gecko/context.rs b/components/style/gecko/context.rs index 1313030f3dc..c7ba65dce61 100644 --- a/components/style/gecko/context.rs +++ b/components/style/gecko/context.rs @@ -2,20 +2,20 @@ * 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/. */ -use context::{LocalStyleContext, StyleContext, SharedStyleContext}; +use context::{SharedStyleContext, ThreadLocalStyleContext}; use std::cell::RefCell; use std::rc::Rc; -thread_local!(static LOCAL_CONTEXT_KEY: RefCell>> = RefCell::new(None)); +thread_local!(static LOCAL_CONTEXT_KEY: RefCell>> = RefCell::new(None)); // Keep this implementation in sync with the one in components/layout/context.rs. -fn create_or_get_local_context(shared: &SharedStyleContext) -> Rc { +pub fn create_or_get_local_context(shared: &SharedStyleContext) -> Rc { LOCAL_CONTEXT_KEY.with(|r| { let mut r = r.borrow_mut(); if let Some(context) = r.clone() { context } else { - let context = Rc::new(LocalStyleContext::new(&shared.local_context_creation_data.lock().unwrap())); + let context = Rc::new(ThreadLocalStyleContext::new(&shared.local_context_creation_data.lock().unwrap())); *r = Some(context.clone()); context } @@ -25,28 +25,3 @@ fn create_or_get_local_context(shared: &SharedStyleContext) -> Rc { - pub shared: &'a SharedStyleContext, - cached_local_context: Rc, -} - -impl<'a> StandaloneStyleContext<'a> { - pub fn new(shared: &'a SharedStyleContext) -> Self { - let local_context = create_or_get_local_context(shared); - StandaloneStyleContext { - shared: shared, - cached_local_context: local_context, - } - } -} - -impl<'a> StyleContext<'a> for StandaloneStyleContext<'a> { - fn shared_context(&self) -> &'a SharedStyleContext { - &self.shared - } - - fn local_context(&self) -> &LocalStyleContext { - &self.cached_local_context - } -} diff --git a/components/style/gecko/traversal.rs b/components/style/gecko/traversal.rs index 43e3ab8b861..005da9620d0 100644 --- a/components/style/gecko/traversal.rs +++ b/components/style/gecko/traversal.rs @@ -3,35 +3,38 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use atomic_refcell::AtomicRefCell; -use context::{LocalStyleContext, SharedStyleContext, StyleContext}; +use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use data::ElementData; -use dom::{NodeInfo, OpaqueNode, TNode}; -use gecko::context::StandaloneStyleContext; +use dom::{NodeInfo, TNode}; +use gecko::context::create_or_get_local_context; use gecko::wrapper::{GeckoElement, GeckoNode}; -use std::mem; -use traversal::{DomTraversalContext, PerLevelTraversalData, recalc_style_at}; +use std::rc::Rc; use traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at}; -pub struct RecalcStyleOnly<'lc> { - context: StandaloneStyleContext<'lc>, +pub struct RecalcStyleOnly { + shared: SharedStyleContext, } -impl<'lc, 'ln> DomTraversalContext> for RecalcStyleOnly<'lc> { - type SharedContext = SharedStyleContext; - #[allow(unsafe_code)] - fn new<'a>(shared: &'a Self::SharedContext, _root: OpaqueNode) -> Self { - // See the comment in RecalcStyleAndConstructFlows::new for an explanation of why this is - // necessary. - let shared_lc: &'lc Self::SharedContext = unsafe { mem::transmute(shared) }; +impl RecalcStyleOnly { + pub fn new(shared: SharedStyleContext) -> Self { RecalcStyleOnly { - context: StandaloneStyleContext::new(shared_lc), + shared: shared, } } +} + +impl<'ln> DomTraversal> for RecalcStyleOnly { + type ThreadLocalContext = ThreadLocalStyleContext; fn process_preorder(&self, node: GeckoNode<'ln>, traversal_data: &mut PerLevelTraversalData) { if node.is_element() { let el = node.as_element().unwrap(); let mut data = unsafe { el.ensure_data() }.borrow_mut(); - recalc_style_at::<_, _, Self>(&self.context, traversal_data, el, &mut data); + let tlc = self.create_or_get_thread_local_context(); + let context = StyleContext { + shared: &self.shared, + thread_local: &*tlc, + }; + recalc_style_at(self, traversal_data, &context, el, &mut data); } } @@ -50,7 +53,11 @@ impl<'lc, 'ln> DomTraversalContext> for RecalcStyleOnly<'lc> { element.clear_data() } - fn local_context(&self) -> &LocalStyleContext { - self.context.local_context() + fn shared_context(&self) -> &SharedStyleContext { + &self.shared + } + + fn create_or_get_thread_local_context(&self) -> Rc { + create_or_get_local_context(&self.shared) } } diff --git a/components/style/matching.rs b/components/style/matching.rs index ce04d8fde0f..23b832e2587 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -391,16 +391,15 @@ trait PrivateMatchMethods: TElement { /// /// Note that animations only apply to nodes or ::before or ::after /// pseudo-elements. - fn cascade_node_pseudo_element<'a, Ctx>(&self, - context: &Ctx, - parent_style: Option<&Arc>, - old_style: Option<&Arc>, - rule_node: &StrongRuleNode, - possibly_expired_animations: &[PropertyAnimation], - booleans: CascadeBooleans) - -> Arc - where Ctx: StyleContext<'a> { - let shared_context = context.shared_context(); + fn cascade_node_pseudo_element<'a>(&self, + context: &StyleContext, + parent_style: Option<&Arc>, + old_style: Option<&Arc>, + rule_node: &StrongRuleNode, + possibly_expired_animations: &[PropertyAnimation], + booleans: CascadeBooleans) + -> Arc { + let shared_context = context.shared; let mut cascade_info = CascadeInfo::new(); let mut cascade_flags = CascadeFlags::empty(); if booleans.shareable { @@ -433,7 +432,7 @@ trait PrivateMatchMethods: TElement { let mut this_style = Arc::new(this_style); if booleans.animate { - let new_animations_sender = &context.local_context().new_animations_sender; + let new_animations_sender = &context.thread_local.new_animations_sender; let this_opaque = self.as_node().opaque(); // Trigger any present animations if necessary. animation::maybe_start_animations(&shared_context, @@ -509,26 +508,23 @@ trait PrivateMatchMethods: TElement { } } -fn compute_rule_node<'a, Ctx>(context: &Ctx, - applicable_declarations: &mut Vec) - -> StrongRuleNode - where Ctx: StyleContext<'a> +fn compute_rule_node(context: &StyleContext, + applicable_declarations: &mut Vec) + -> StrongRuleNode { - let shared_context = context.shared_context(); let rules = applicable_declarations.drain(..).map(|d| (d.source, d.importance)); - let rule_node = shared_context.stylist.rule_tree.insert_ordered_rules(rules); + let rule_node = context.shared.stylist.rule_tree.insert_ordered_rules(rules); rule_node } impl PrivateMatchMethods for E {} pub trait MatchMethods : TElement { - fn match_element<'a, Ctx>(&self, context: &Ctx, parent_bf: Option<&BloomFilter>) - -> MatchResults - where Ctx: StyleContext<'a> + fn match_element(&self, context: &StyleContext, parent_bf: Option<&BloomFilter>) + -> MatchResults { let mut applicable_declarations: Vec = Vec::with_capacity(16); - let stylist = &context.shared_context().stylist; + let stylist = &context.shared.stylist; let style_attribute = self.style_attribute(); // Compute the primary rule node. @@ -721,14 +717,13 @@ pub trait MatchMethods : TElement { } } - unsafe fn cascade_node<'a, Ctx>(&self, - context: &Ctx, - mut data: &mut AtomicRefMut, - parent: Option, - primary_rule_node: StrongRuleNode, - pseudo_rule_nodes: PseudoRuleNodes, - primary_is_shareable: bool) - where Ctx: StyleContext<'a> + unsafe fn cascade_node(&self, + context: &StyleContext, + mut data: &mut AtomicRefMut, + parent: Option, + primary_rule_node: StrongRuleNode, + pseudo_rule_nodes: PseudoRuleNodes, + primary_is_shareable: bool) { // Get our parent's style. let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap()); @@ -753,7 +748,7 @@ pub trait MatchMethods : TElement { Some(previous) => { // Update animations before the cascade. This may modify the // value of the old primary style. - self.update_animations_for_cascade(context.shared_context(), + self.update_animations_for_cascade(&context.shared, &mut previous.primary.values, &mut possibly_expired_animations); (Some(&previous.primary.values), Some(&mut previous.pseudos)) @@ -794,17 +789,16 @@ pub trait MatchMethods : TElement { data.finish_styling(new_styles, damage); } - fn compute_damage_and_cascade_pseudos<'a, Ctx>( + fn compute_damage_and_cascade_pseudos( &self, old_primary: Option<&Arc>, mut old_pseudos: Option<&mut PseudoStyles>, new_primary: &Arc, new_pseudos: &mut PseudoStyles, - context: &Ctx, + context: &StyleContext, mut pseudo_rule_nodes: PseudoRuleNodes, possibly_expired_animations: &mut Vec) -> RestyleDamage - where Ctx: StyleContext<'a> { // Here we optimise the case of the style changing but both the // previous and the new styles having display: none. In this @@ -859,7 +853,7 @@ pub trait MatchMethods : TElement { if let Some(ref mut old_pseudo_style) = maybe_old_pseudo_style { // Update animations before the cascade. This may modify // the value of old_pseudo_style. - self.update_animations_for_cascade(context.shared_context(), + self.update_animations_for_cascade(&context.shared, &mut old_pseudo_style.values, possibly_expired_animations); } diff --git a/components/style/parallel.rs b/components/style/parallel.rs index 19b2af74b59..5fa27f8ac96 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -9,19 +9,20 @@ use dom::{OpaqueNode, TElement, TNode, UnsafeNode}; use rayon; use servo_config::opts; +use std::borrow::Borrow; use std::sync::atomic::Ordering; -use traversal::{DomTraversalContext, PerLevelTraversalData, PreTraverseToken}; +use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken}; use traversal::{STYLE_SHARING_CACHE_HITS, STYLE_SHARING_CACHE_MISSES}; pub const CHUNK_SIZE: usize = 64; -pub fn traverse_dom(root: N::ConcreteElement, +pub fn traverse_dom(traversal: &D, + root: N::ConcreteElement, known_root_dom_depth: Option, - shared_context: &C::SharedContext, token: PreTraverseToken, queue: &rayon::ThreadPool) where N: TNode, - C: DomTraversalContext + D: DomTraversal { if opts::get().style_sharing_stats { STYLE_SHARING_CACHE_HITS.store(0, Ordering::SeqCst); @@ -32,7 +33,7 @@ pub fn traverse_dom(root: N::ConcreteElement, // 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() { - debug_assert!(!C::needs_postorder_traversal()); + debug_assert!(!D::needs_postorder_traversal()); let mut children = vec![]; for kid in root.as_node().children() { if kid.as_element().map_or(false, |el| el.get_data().is_none()) { @@ -51,7 +52,7 @@ pub fn traverse_dom(root: N::ConcreteElement, let root = root.as_node().opaque(); queue.install(|| { rayon::scope(|scope| { - traverse_nodes::<_, C>(nodes, root, data, scope, shared_context); + traverse_nodes(nodes, root, data, scope, traversal); }); }); @@ -68,16 +69,14 @@ pub fn traverse_dom(root: N::ConcreteElement, /// A parallel top-down DOM traversal. #[inline(always)] #[allow(unsafe_code)] -fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode], +fn top_down_dom<'a, 'scope, N, D>(unsafe_nodes: &'a [UnsafeNode], root: OpaqueNode, mut data: PerLevelTraversalData, scope: &'a rayon::Scope<'scope>, - shared_context: &'scope C::SharedContext) + traversal: &'scope D) where N: TNode, - C: DomTraversalContext, + D: DomTraversal, { - let context = C::new(shared_context, root); - let mut discovered_child_nodes = vec![]; for unsafe_node in unsafe_nodes { // Get a real layout node. @@ -85,9 +84,9 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode], // Perform the appropriate traversal. let mut children_to_process = 0isize; - context.process_preorder(node, &mut data); + traversal.process_preorder(node, &mut data); if let Some(el) = node.as_element() { - C::traverse_children(el, |kid| { + D::traverse_children(el, |kid| { children_to_process += 1; discovered_child_nodes.push(kid.to_unsafe()) }); @@ -95,10 +94,10 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode], // Reset the count of children if we need to do a bottom-up traversal // after the top up. - if C::needs_postorder_traversal() { + if D::needs_postorder_traversal() { if children_to_process == 0 { // If there were no more children, start walking back up. - bottom_up_dom::(root, *unsafe_node, shared_context) + bottom_up_dom(root, *unsafe_node, traversal) } else { // Otherwise record the number of children to process when the // time comes. @@ -109,21 +108,22 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode], // NB: In parallel traversal mode we have to purge the LRU cache in order to // be able to access it without races. - context.local_context().style_sharing_candidate_cache.borrow_mut().clear(); + let tlc = traversal.create_or_get_thread_local_context(); + (*tlc).borrow().style_sharing_candidate_cache.borrow_mut().clear(); if let Some(ref mut depth) = data.current_dom_depth { *depth += 1; } - traverse_nodes::<_, C>(discovered_child_nodes, root, data, scope, shared_context); + traverse_nodes(discovered_child_nodes, root, data, scope, traversal); } -fn traverse_nodes<'a, 'scope, N, C>(nodes: Vec, root: OpaqueNode, +fn traverse_nodes<'a, 'scope, N, D>(nodes: Vec, root: OpaqueNode, data: PerLevelTraversalData, scope: &'a rayon::Scope<'scope>, - shared_context: &'scope C::SharedContext) + traversal: &'scope D) where N: TNode, - C: DomTraversalContext, + D: DomTraversal, { if nodes.is_empty() { return; @@ -133,7 +133,7 @@ fn traverse_nodes<'a, 'scope, N, C>(nodes: Vec, root: OpaqueNode, // we're only pushing one work unit. if nodes.len() <= CHUNK_SIZE { let nodes = nodes.into_boxed_slice(); - top_down_dom::(&nodes, root, data, scope, shared_context); + top_down_dom(&nodes, root, data, scope, traversal); return; } @@ -143,7 +143,7 @@ fn traverse_nodes<'a, 'scope, N, C>(nodes: Vec, root: OpaqueNode, let data = data.clone(); scope.spawn(move |scope| { let nodes = nodes; - top_down_dom::(&nodes, root, data, scope, shared_context) + top_down_dom(&nodes, root, data, scope, traversal) }) } } @@ -160,19 +160,17 @@ fn traverse_nodes<'a, 'scope, N, C>(nodes: Vec, root: OpaqueNode, /// The only communication between siblings is that they both /// fetch-and-subtract the parent's children count. #[allow(unsafe_code)] -fn bottom_up_dom(root: OpaqueNode, +fn bottom_up_dom(root: OpaqueNode, unsafe_node: UnsafeNode, - shared_context: &C::SharedContext) + traversal: &D) where N: TNode, - C: DomTraversalContext + D: DomTraversal { - let context = C::new(shared_context, root); - // Get a real layout node. let mut node = unsafe { N::from_unsafe(&unsafe_node) }; loop { // Perform the appropriate operation. - context.process_postorder(node); + traversal.process_postorder(node); if node.opaque() == root { break; diff --git a/components/style/sequential.rs b/components/style/sequential.rs index 614b3d6509d..1b8d602a0f4 100644 --- a/components/style/sequential.rs +++ b/components/style/sequential.rs @@ -5,53 +5,54 @@ //! Implements sequential traversal over the DOM tree. use dom::{TElement, TNode}; -use traversal::{DomTraversalContext, PerLevelTraversalData, PreTraverseToken}; +use std::borrow::Borrow; +use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken}; -pub fn traverse_dom(root: N::ConcreteElement, - shared: &C::SharedContext, +pub fn traverse_dom(traversal: &D, + root: N::ConcreteElement, token: PreTraverseToken) where N: TNode, - C: DomTraversalContext + D: DomTraversal { debug_assert!(token.should_traverse()); - fn doit<'a, N, C>(context: &'a C, node: N, data: &mut PerLevelTraversalData) + fn doit(traversal: &D, node: N, data: &mut PerLevelTraversalData) where N: TNode, - C: DomTraversalContext + D: DomTraversal { - context.process_preorder(node, data); + traversal.process_preorder(node, data); if let Some(el) = node.as_element() { if let Some(ref mut depth) = data.current_dom_depth { *depth += 1; } - C::traverse_children(el, |kid| doit::(context, kid, data)); + D::traverse_children(el, |kid| doit(traversal, kid, data)); if let Some(ref mut depth) = data.current_dom_depth { *depth -= 1; } } - if C::needs_postorder_traversal() { - context.process_postorder(node); + if D::needs_postorder_traversal() { + traversal.process_postorder(node); } } let mut data = PerLevelTraversalData { current_dom_depth: None, }; - let context = C::new(shared, root.as_node().opaque()); if token.traverse_unstyled_children_only() { for kid in root.as_node().children() { if kid.as_element().map_or(false, |el| el.get_data().is_none()) { - doit::(&context, kid, &mut data); + doit(traversal, kid, &mut data); } } } else { - doit::(&context, root.as_node(), &mut data); + doit(traversal, root.as_node(), &mut data); } // Clear the local LRU cache since we store stateful elements inside. - context.local_context().style_sharing_candidate_cache.borrow_mut().clear(); + let tlc = traversal.create_or_get_thread_local_context(); + (*tlc).borrow().style_sharing_candidate_cache.borrow_mut().clear(); } diff --git a/components/style/traversal.rs b/components/style/traversal.rs index bacf6dcc8bc..4a44619cef4 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -6,7 +6,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use bloom::StyleBloom; -use context::{LocalStyleContext, SharedStyleContext, StyleContext}; +use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use data::{ElementData, StoredRestyleHint}; use dom::{OpaqueNode, TElement, TNode}; use matching::{MatchMethods, StyleSharingResult}; @@ -18,6 +18,7 @@ use servo_config::opts; use std::borrow::Borrow; use std::cell::RefCell; use std::mem; +use std::rc::Rc; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use stylist::Stylist; @@ -64,9 +65,8 @@ pub fn put_thread_local_bloom_filter(bf: StyleBloom) { /// /// This is mostly useful for sequential traversal, where the element will /// always be the last one. -pub fn remove_from_bloom_filter<'a, E, C>(context: &C, root: OpaqueNode, element: E) - where E: TElement, - C: StyleContext<'a> +pub fn remove_from_bloom_filter(context: &SharedStyleContext, + root: OpaqueNode, element: E) { trace!("[{}] remove_from_bloom_filter", ::tid::tid()); @@ -78,7 +78,7 @@ pub fn remove_from_bloom_filter<'a, E, C>(context: &C, root: OpaqueNode, element }); if let Some(mut bf) = bf { - if context.shared_context().generation == bf.generation() { + if context.generation == bf.generation() { bf.maybe_pop(element); // If we're the root of the reflow, just get rid of the bloom @@ -127,10 +127,8 @@ impl LogBehavior { fn allow(&self) -> bool { match *self { MayLog => true, DontLog => false, } } } -pub trait DomTraversalContext { - type SharedContext: Sync + 'static + Borrow; - - fn new<'a>(&'a Self::SharedContext, OpaqueNode) -> Self; +pub trait DomTraversal : Sync { + type ThreadLocalContext: Borrow; /// Process `node` on the way down, before its children have been processed. fn process_preorder(&self, node: N, data: &mut PerLevelTraversalData); @@ -321,7 +319,9 @@ pub trait DomTraversalContext { /// children of |element|. unsafe fn clear_element_data(element: &N::ConcreteElement); - fn local_context(&self) -> &LocalStyleContext; + fn shared_context(&self) -> &SharedStyleContext; + + fn create_or_get_thread_local_context(&self) -> Rc; } /// Determines the amount of relations where we're going to share style. @@ -337,11 +337,9 @@ pub fn relations_are_shareable(relations: &StyleRelations) -> bool { /// Handles lazy resolution of style in display:none subtrees. See the comment /// at the callsite in query.rs. -pub fn style_element_in_display_none_subtree<'a, E, C, F>(element: E, - init_data: &F, - context: &'a C) -> E +pub fn style_element_in_display_none_subtree(context: &StyleContext, + element: E, init_data: &F) -> E where E: TElement, - C: StyleContext<'a>, F: Fn(E), { // Check the base case. @@ -354,7 +352,7 @@ pub fn style_element_in_display_none_subtree<'a, E, C, F>(element: E, // Ensure the parent is styled. let parent = element.parent_element().unwrap(); - let display_none_root = style_element_in_display_none_subtree(parent, init_data, context); + let display_none_root = style_element_in_display_none_subtree(context, parent, init_data); // Initialize our data. init_data(element); @@ -376,13 +374,13 @@ pub fn style_element_in_display_none_subtree<'a, E, C, F>(element: E, /// Calculates the style for a single node. #[inline] #[allow(unsafe_code)] -pub fn recalc_style_at<'a, E, C, D>(context: &'a C, - traversal_data: &mut PerLevelTraversalData, - element: E, - mut data: &mut AtomicRefMut) +pub fn recalc_style_at(traversal: &D, + traversal_data: &mut PerLevelTraversalData, + context: &StyleContext, + element: E, + mut data: &mut AtomicRefMut) where E: TElement, - C: StyleContext<'a>, - D: DomTraversalContext + D: DomTraversal { debug_assert!(data.as_restyle().map_or(true, |r| r.snapshot.is_none()), "Snapshots should be expanded by the caller"); @@ -395,7 +393,7 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C, // Compute style for this element if necessary. if compute_self { - inherited_style_changed = compute_style::<_, _, D>(context, &mut data, traversal_data, element); + inherited_style_changed = compute_style(traversal, traversal_data, context, element, &mut data); } // Now that matching and cascading is done, clear the bits corresponding to @@ -414,7 +412,7 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C, // Preprocess children, propagating restyle hints and handling sibling relationships. if D::should_traverse_children(element, &data, DontLog) && (element.has_dirty_descendants() || !propagated_hint.is_empty() || inherited_style_changed) { - preprocess_children::<_, _, D>(context, element, propagated_hint, inherited_style_changed); + preprocess_children(traversal, element, propagated_hint, inherited_style_changed); } } @@ -423,15 +421,15 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C, // // FIXME(bholley): This should differentiate between matching and cascading, // since we have separate bits for each now. -fn compute_style<'a, E, C, D>(context: &'a C, - mut data: &mut AtomicRefMut, - traversal_data: &mut PerLevelTraversalData, - element: E) -> bool +fn compute_style(_traversal: &D, + traversal_data: &mut PerLevelTraversalData, + context: &StyleContext, + element: E, + mut data: &mut AtomicRefMut) -> bool where E: TElement, - C: StyleContext<'a>, - D: DomTraversalContext, + D: DomTraversal, { - let shared_context = context.shared_context(); + let shared_context = context.shared; let mut bf = take_thread_local_bloom_filter(shared_context); // Ensure the bloom filter is up to date. let dom_depth = bf.insert_parents_recovering(element, @@ -447,13 +445,13 @@ fn compute_style<'a, E, C, D>(context: &'a C, bf.assert_complete(element); // Check to see whether we can share a style with someone. - let style_sharing_candidate_cache = - &mut context.local_context().style_sharing_candidate_cache.borrow_mut(); + let mut style_sharing_candidate_cache = + context.thread_local.style_sharing_candidate_cache.borrow_mut(); let sharing_result = if element.parent_element().is_none() { StyleSharingResult::CannotShare } else { - unsafe { element.share_style_if_possible(style_sharing_candidate_cache, + unsafe { element.share_style_if_possible(&mut style_sharing_candidate_cache, shared_context, &mut data) } }; @@ -522,13 +520,12 @@ fn compute_style<'a, E, C, D>(context: &'a C, inherited_styles_changed } -fn preprocess_children<'a, E, C, D>(context: &'a C, - element: E, - mut propagated_hint: StoredRestyleHint, - parent_inherited_style_changed: bool) +fn preprocess_children(traversal: &D, + element: E, + mut propagated_hint: StoredRestyleHint, + parent_inherited_style_changed: bool) where E: TElement, - C: StyleContext<'a>, - D: DomTraversalContext + D: DomTraversal { // Loop over all the children. for child in element.as_node().children() { @@ -554,7 +551,7 @@ fn preprocess_children<'a, E, C, D>(context: &'a C, } // Handle element snapshots. - let stylist = &context.shared_context().stylist; + let stylist = &traversal.shared_context().stylist; let later_siblings = restyle_data.expand_snapshot(child, stylist); if later_siblings { propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into()); diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 394653afd8e..e83c9eb5817 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -16,11 +16,10 @@ use std::ptr; use std::sync::{Arc, Mutex}; use style::arc_ptr_eq; use style::atomic_refcell::AtomicRefMut; -use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext}; +use style::context::{ThreadLocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext, StyleContext}; use style::data::{ElementData, RestyleData}; use style::dom::{ShowSubtreeData, TElement, TNode}; use style::error_reporting::StdoutErrorReporter; -use style::gecko::context::StandaloneStyleContext; use style::gecko::context::clear_local_context; use style::gecko::data::{NUM_THREADS, PerDocumentStyleData, PerDocumentStyleDataImpl}; use style::gecko::restyle_damage::GeckoRestyleDamage; @@ -58,7 +57,7 @@ use style::string_cache::Atom; use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule}; use style::thread_state; use style::timer::Timer; -use style::traversal::{recalc_style_at, DomTraversalContext, PerLevelTraversalData}; +use style::traversal::{recalc_style_at, DomTraversal, PerLevelTraversalData}; use style_traits::ToCss; /* @@ -88,7 +87,7 @@ pub extern "C" fn Servo_Shutdown() -> () { // Destroy our default computed values. unsafe { ComputedValues::shutdown(); } - // In general, LocalStyleContexts will get destroyed when the worker thread + // In general, ThreadLocalStyleContexts will get destroyed when the worker thread // is joined and the TLS is dropped. However, under some configurations we // may do sequential style computation on the main thread, so we need to be // sure to clear the main thread TLS entry as well. @@ -100,7 +99,7 @@ fn create_shared_context(mut per_doc_data: &mut AtomicRefMut(element, &shared_style_context, token); + sequential::traverse_dom(&traversal, element, token); } else { - parallel::traverse_dom::<_, RecalcStyleOnly>(element, known_depth, - &shared_style_context, token, - per_doc_data.work_queue.as_mut().unwrap()); + parallel::traverse_dom(&traversal, element, known_depth, token, + per_doc_data.work_queue.as_mut().unwrap()); } } @@ -807,8 +806,6 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed, consume: structs::ConsumeStyleBehavior, compute: structs::LazyComputeBehavior) -> ServoComputedValuesStrong { - use style::context::StyleContext; - let element = GeckoElement(element); debug!("Servo_ResolveStyle: {:?}, consume={:?}, compute={:?}", element, consume, compute); @@ -827,16 +824,22 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed, let mut per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let shared_style_context = create_shared_context(&mut per_doc_data); - let context = StandaloneStyleContext::new(&shared_style_context); + let traversal = RecalcStyleOnly::new(shared_style_context); + let tlc = traversal.create_or_get_thread_local_context(); let mut traversal_data = PerLevelTraversalData { current_dom_depth: None, }; - recalc_style_at::<_, _, RecalcStyleOnly>(&context, &mut traversal_data, element, &mut data); + let context = StyleContext { + shared: traversal.shared_context(), + thread_local: &*tlc, + }; + + recalc_style_at(&traversal, &mut traversal_data, &context, element, &mut data); // We don't want to keep any cached style around after this one-off style resolution. - context.local_context().style_sharing_candidate_cache.borrow_mut().clear(); + tlc.style_sharing_candidate_cache.borrow_mut().clear(); // The element was either unstyled or needed restyle. If it was unstyled, it may have // additional unstyled children that subsequent traversals won't find now that the style