diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 324c765280e..62cf1fc6e46 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -884,11 +884,8 @@ impl LayoutTask { } } if needs_reflow { - match self.try_get_layout_root(*node) { - None => {} - Some(mut flow) => { - LayoutTask::reflow_all_nodes(&mut *flow); - } + if let Some(mut flow) = self.try_get_layout_root(*node) { + LayoutTask::reflow_all_nodes(&mut *flow); } } @@ -899,28 +896,30 @@ impl LayoutTask { &self.url, data.reflow_info.goal); - // Recalculate CSS styles and rebuild flows and fragments. - profile(time::ProfilerCategory::LayoutStyleRecalc, - self.profiler_metadata(), - self.time_profiler_chan.clone(), - || { - // Perform CSS selector matching and flow construction. - let rw_data = &mut *rw_data; - match rw_data.parallel_traversal { - None => { - sequential::traverse_dom_preorder(*node, &shared_layout_context); + if node.is_dirty() || node.has_dirty_descendants() || rw_data.stylist.is_dirty() { + // Recalculate CSS styles and rebuild flows and fragments. + profile(time::ProfilerCategory::LayoutStyleRecalc, + self.profiler_metadata(), + self.time_profiler_chan.clone(), + || { + // Perform CSS selector matching and flow construction. + let rw_data = &mut *rw_data; + match rw_data.parallel_traversal { + None => { + sequential::traverse_dom_preorder(*node, &shared_layout_context); + } + Some(ref mut traversal) => { + parallel::traverse_dom_preorder(*node, &shared_layout_context, traversal); + } } - Some(ref mut traversal) => { - parallel::traverse_dom_preorder(*node, &shared_layout_context, traversal); - } - } - }); + }); - // Retrieve the (possibly rebuilt) root flow. - rw_data.root_flow = Some(self.get_layout_root((*node).clone())); + // Retrieve the (possibly rebuilt) root flow. + rw_data.root_flow = Some(self.get_layout_root((*node).clone())); - // Kick off animations if any were triggered. - animation::process_new_animations(&mut *rw_data, self.id); + // Kick off animations if any were triggered. + animation::process_new_animations(&mut *rw_data, self.id); + } // Perform post-style recalculation layout passes. self.perform_post_style_recalc_layout_passes(&data.reflow_info, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index facc50b51e9..131c6231b8c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -490,6 +490,7 @@ pub trait WindowHelpers { fn init_browser_context(self, doc: JSRef, frame_element: Option>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); + fn force_reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason); fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason); fn join_layout(self); fn layout(&self) -> &LayoutRPC; @@ -566,24 +567,19 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { *self.browser_context.borrow_mut() = None; } - /// Reflows the page if it's possible to do so and the page is dirty. This method will wait - /// for the layout thread to complete (but see the `TODO` below). If there is no window size - /// yet, the page is presumed invisible and no reflow is performed. + /// Reflows the page unconditionally. This method will wait for the layout thread to complete + /// (but see the `TODO` below). If there is no window size yet, the page is presumed invisible + /// and no reflow is performed. /// /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. - fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) { + fn force_reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) { let document = self.Document().root(); let root = document.r().GetDocumentElement().root(); let root = match root.r() { Some(root) => root, None => return, }; - let root: JSRef = NodeCast::from_ref(root); - if query_type == ReflowQueryType::NoQuery && !root.get_has_dirty_descendants() { - debug!("root has no dirty descendants; avoiding reflow (reason {:?})", reason); - return - } let window_size = match self.window_size.get() { Some(window_size) => window_size, @@ -642,6 +638,28 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } } + /// Reflows the page if it's possible to do so and the page is dirty. This method will wait + /// for the layout thread to complete (but see the `TODO` below). If there is no window size + /// yet, the page is presumed invisible and no reflow is performed. + /// + /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) { + let document = self.Document().root(); + let root = document.r().GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return, + }; + + let root: JSRef = NodeCast::from_ref(root); + if query_type == ReflowQueryType::NoQuery && !root.get_has_dirty_descendants() { + debug!("root has no dirty descendants; avoiding reflow (reason {:?})", reason); + return + } + + self.force_reflow(goal, query_type, reason) + } + // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a // reflow between the "join complete" message and returning from this // function? diff --git a/components/script/script_task.rs b/components/script/script_task.rs index b7fc3198511..12ae363c95b 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -820,7 +820,7 @@ impl ScriptTask { let window = inner_page.window().root(); if window.r().set_page_clip_rect_with_new_viewport(rect) { let page = get_page(page, id); - self.force_reflow(&*page, ReflowReason::Viewport); + self.rebuild_and_force_reflow(&*page, ReflowReason::Viewport); } return; } @@ -884,7 +884,7 @@ impl ScriptTask { let needed_reflow = page.set_reflow_status(false); if needed_reflow { - self.force_reflow(&*page, ReflowReason::CachedPageNeededReflow); + self.rebuild_and_force_reflow(&*page, ReflowReason::CachedPageNeededReflow); } let window = page.window().root(); @@ -1200,8 +1200,8 @@ impl ScriptTask { self.compositor.borrow_mut().scroll_fragment_point(pipeline_id, LayerId::null(), point); } - /// Reflows non-incrementally. - fn force_reflow(&self, page: &Page, reason: ReflowReason) { + /// Reflows non-incrementally, rebuilding the entire layout tree in the process. + fn rebuild_and_force_reflow(&self, page: &Page, reason: ReflowReason) { let document = page.document().root(); document.r().dirty_all_nodes(); let window = window_from_node(document.r()).root(); @@ -1322,7 +1322,9 @@ impl ScriptTask { let page = get_page(&self.root_page(), pipeline_id); let window = page.window().root(); window.r().set_window_size(new_size); - self.force_reflow(&*page, ReflowReason::WindowResize); + window.r().force_reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::WindowResize); let document = page.document().root(); let fragment_node = window.r().steal_fragment_name() diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index a9c0839f5e8..796eac4817b 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -251,6 +251,10 @@ impl Stylist { shareable } + + pub fn is_dirty(&self) -> bool { + self.is_dirty + } } struct PerOriginSelectorMap {