From 7a435fc6edf3c261c56f083b01b132aa6a37724f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 4 Jun 2013 22:00:33 -0700 Subject: [PATCH] Refactor document damage to distinguish it from layout/style damage. Also, standardize on the name "reflow" instead of "relayout" or "build". --- src/components/main/layout/layout_task.rs | 21 ++--- src/components/script/dom/window.rs | 4 +- src/components/script/layout_interface.rs | 46 ++++++----- src/components/script/script_task.rs | 93 ++++++++++++++++------- 4 files changed, 106 insertions(+), 58 deletions(-) diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 659272fa62f..19580507eeb 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -32,11 +32,12 @@ use newcss::stylesheet::Stylesheet; use newcss::types::OriginAuthor; use script::dom::event::ReflowEvent; use script::dom::node::{AbstractNode, LayoutView}; -use script::layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, ContentBoxQuery}; +use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery}; use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse}; use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery}; -use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, Msg, NoDamage}; -use script::layout_interface::{QueryMsg, ReflowDamage, ReflowForDisplay}; +use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDocumentDamage, Msg}; +use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage, ReflowForDisplay}; +use script::layout_interface::{ReflowMsg}; use script::script_task::{ScriptMsg, SendEventMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::LocalImageCache; @@ -126,11 +127,11 @@ impl Layout { fn handle_request(&mut self) -> bool { match self.from_script.recv() { AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet), - BuildMsg(data) => { + ReflowMsg(data) => { let data = Cell(data); do profile(time::LayoutPerformCategory, self.profiler_chan.clone()) { - self.handle_build(data.take()); + self.handle_reflow(data.take()); } } QueryMsg(query, chan) => { @@ -154,10 +155,10 @@ impl Layout { } /// The high-level routine that performs layout tasks. - fn handle_build(&mut self, data: &BuildData) { + fn handle_reflow(&mut self, data: &Reflow) { // FIXME: Isolate this transmutation into a "bridge" module. let node: &AbstractNode = unsafe { - transmute(&data.node) + transmute(&data.document_root) }; // FIXME: Bad copy! @@ -187,9 +188,9 @@ impl Layout { } // Perform CSS selector matching if necessary. - match data.damage { - NoDamage | ReflowDamage => {} - MatchSelectorsDamage => { + match data.damage.level { + ReflowDocumentDamage => {} + MatchSelectorsDocumentDamage => { do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) { node.restyle_subtree(self.css_select_ctx); } diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index c94e6488a67..a5904b642c7 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -4,7 +4,7 @@ use dom::bindings::utils::WrapperCache; use dom::bindings::window; -use layout_interface::MatchSelectorsDamage; +use layout_interface::ReflowForScriptQuery; use script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext}; use core::comm::{Chan, SharedChan}; @@ -83,7 +83,7 @@ pub impl Window { fn content_changed(&self) { unsafe { - (*self.script_context).trigger_relayout(MatchSelectorsDamage); + (*self.script_context).reflow_all(ReflowForScriptQuery) } } diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index b4570f2a972..f9ab0a8eb2e 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -25,9 +25,7 @@ pub enum Msg { AddStylesheetMsg(Stylesheet), /// Requests a reflow. - /// - /// FIXME(pcwalton): Call this `reflow` instead? - BuildMsg(~BuildData), + ReflowMsg(~Reflow), /// Performs a synchronous layout request. /// @@ -61,31 +59,37 @@ pub enum LayoutResponse { HitTestResponse(AbstractNode), } -/// Dirty bits for layout. -pub enum Damage { - /// The document is clean; nothing needs to be done. - NoDamage, - /// Reflow, but do not perform CSS selector matching. - ReflowDamage, +/// Determines which part of the +pub enum DocumentDamageLevel { /// Perform CSS selector matching and reflow. - MatchSelectorsDamage, + MatchSelectorsDocumentDamage, + /// Reflow, but do not perform CSS selector matching. + ReflowDocumentDamage, } -impl Damage { +impl DocumentDamageLevel { /// Sets this damage to the maximum of this damage and the given damage. /// /// FIXME(pcwalton): This could be refactored to use `max` and the `Ord` trait, and this /// function removed. - fn add(&mut self, new_damage: Damage) { + fn add(&mut self, new_damage: DocumentDamageLevel) { match (*self, new_damage) { - (NoDamage, _) => *self = new_damage, - (ReflowDamage, NoDamage) => *self = ReflowDamage, - (ReflowDamage, new_damage) => *self = new_damage, - (MatchSelectorsDamage, _) => *self = MatchSelectorsDamage + (ReflowDocumentDamage, new_damage) => *self = new_damage, + (MatchSelectorsDocumentDamage, _) => *self = MatchSelectorsDocumentDamage, } } } +/// What parts of the document have changed, as far as the script task can tell. +/// +/// Note that this is fairly coarse-grained and is separate from layout's notion of the document +pub struct DocumentDamage { + /// The topmost node in the tree that has changed. + root: AbstractNode, + /// The amount of damage that occurred. + level: DocumentDamageLevel, +} + /// Why we're doing reflow. #[deriving(Eq)] pub enum ReflowGoal { @@ -96,10 +100,11 @@ pub enum ReflowGoal { } /// Information needed for a reflow. -pub struct BuildData { - node: AbstractNode, - /// What reflow needs to be done. - damage: Damage, +pub struct Reflow { + /// The document node. + document_root: AbstractNode, + /// The style changes that need to be done. + damage: DocumentDamage, /// The goal of reflow: either to render to the screen or to flush layout info for script. goal: ReflowGoal, /// The URL of the page. @@ -108,6 +113,7 @@ pub struct BuildData { script_chan: SharedChan, /// The current window size. window_size: Size2D, + /// The channel that we send a notification to. script_join_chan: Chan<()>, } diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index d82f4e0895e..66a4e9e4727 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -8,11 +8,12 @@ use dom::bindings::utils::GlobalStaticData; use dom::document::Document; use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent}; -use dom::node::define_bindings; +use dom::node::{AbstractNode, ScriptView, define_bindings}; use dom::window::Window; -use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery, HitTestQuery}; -use layout_interface::{LayoutResponse, HitTestResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; -use layout_interface::{QueryMsg, ReflowDamage, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal}; +use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery}; +use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutTask}; +use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage}; +use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg}; use layout_interface; use core::cast::transmute; @@ -132,8 +133,8 @@ pub struct ScriptContext { /// The current size of the window, in pixels. window_size: Size2D, - /// What parts of layout are dirty. - damage: Damage, + /// What parts of the document are dirty, if any. + damage: Option, } fn global_script_context_key(_: @ScriptContext) {} @@ -199,7 +200,7 @@ impl ScriptContext { root_frame: None, window_size: Size2D(800, 600), - damage: MatchSelectorsDamage, + damage: None, }; // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended let script_context_ptr = { @@ -283,7 +284,7 @@ impl ScriptContext { null(), &rval); - self.relayout(ReflowForScriptQuery) + self.reflow(ReflowForScriptQuery) } /// Handles a request to exit the script task and shut down layout. @@ -348,8 +349,11 @@ impl ScriptContext { }); // Perform the initial reflow. - self.damage.add(MatchSelectorsDamage); - self.relayout(ReflowForDisplay); + self.damage = Some(DocumentDamage { + root: root_node, + level: MatchSelectorsDocumentDamage, + }); + self.reflow(ReflowForDisplay); // Define debug functions. self.js_compartment.define_functions(debug_fns); @@ -383,19 +387,13 @@ impl ScriptContext { } } - /// Initiate an asynchronous relayout operation to handle a script layout query. - pub fn trigger_relayout(&mut self, damage: Damage) { - self.damage.add(damage); - self.relayout(ReflowForScriptQuery); - } - /// This method will wait until the layout task has completed its current action, join the /// layout task, and then request a new layout run. It won't wait for the new layout /// computation to finish. /// /// This function fails if there is no root frame. - fn relayout(&mut self, goal: ReflowGoal) { - debug!("script: performing relayout"); + fn reflow(&mut self, goal: ReflowGoal) { + debug!("script: performing reflow"); // Now, join the layout so that they will see the latest changes we have made. self.join_layout(); @@ -408,23 +406,36 @@ impl ScriptContext { None => fail!(~"Tried to relayout with no root frame!"), Some(ref root_frame) => { // Send new document and relevant styles to layout. - let data = ~BuildData { - node: root_frame.document.root, + let reflow = ~Reflow { + document_root: root_frame.document.root, url: copy root_frame.url, goal: goal, script_chan: self.script_chan.clone(), window_size: self.window_size, script_join_chan: join_chan, - damage: replace(&mut self.damage, NoDamage), + damage: replace(&mut self.damage, None).unwrap(), }; - self.layout_task.chan.send(BuildMsg(data)) + self.layout_task.chan.send(ReflowMsg(reflow)) } } debug!("script: layout forked") } + /// Reflows the entire document. + /// + /// FIXME: This should basically never be used. + pub fn reflow_all(&mut self, goal: ReflowGoal) { + for self.root_frame.each |root_frame| { + ScriptContext::damage(&mut self.damage, + root_frame.document.root, + MatchSelectorsDocumentDamage) + } + + self.reflow(goal) + } + /// Sends the given query to layout. pub fn query_layout(&mut self, query: LayoutQuery) -> Result { self.join_layout(); @@ -434,6 +445,26 @@ impl ScriptContext { response_port.recv() } + /// Adds the given damage. + fn damage(damage: &mut Option, + root: AbstractNode, + level: DocumentDamageLevel) { + match *damage { + None => {} + Some(ref mut damage) => { + // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor. + damage.root = root; + damage.level.add(level); + return + } + } + + *damage = Some(DocumentDamage { + root: root, + level: level, + }) + } + /// This is the main entry point for receiving and dispatching DOM events. /// /// TODO: Actually perform DOM event dispatch. @@ -442,23 +473,33 @@ impl ScriptContext { ResizeEvent(new_width, new_height, response_chan) => { debug!("script got resize event: %u, %u", new_width, new_height); - self.damage.add(ReflowDamage); self.window_size = Size2D(new_width, new_height); + for self.root_frame.each |root_frame| { + ScriptContext::damage(&mut self.damage, + root_frame.document.root, + ReflowDocumentDamage); + } + if self.root_frame.is_some() { - self.relayout(ReflowForDisplay) + self.reflow(ReflowForDisplay) } response_chan.send(()) } + // FIXME(pcwalton): This reflows the entire document and is not incremental-y. ReflowEvent => { debug!("script got reflow event"); - self.damage.add(MatchSelectorsDamage); + for self.root_frame.each |root_frame| { + ScriptContext::damage(&mut self.damage, + root_frame.document.root, + MatchSelectorsDocumentDamage); + } if self.root_frame.is_some() { - self.relayout(ReflowForDisplay) + self.reflow(ReflowForDisplay) } }