diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-10-28 19:49:17 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-12-15 14:16:04 -0800 |
commit | d101c1dd91ddcd030cd85a22825835bc3eca50ea (patch) | |
tree | ec02e406356645ffe4bac4d9cf33256c92c31de5 /components/script/page.rs | |
parent | 1bc2c8a6397382b4db8fb09582434f4798d43868 (diff) | |
download | servo-d101c1dd91ddcd030cd85a22825835bc3eca50ea.tar.gz servo-d101c1dd91ddcd030cd85a22825835bc3eca50ea.zip |
script: Improve dirty propagation and fix script-layout synchronization.
This fixes race conditions whereby layout and script could be running
simultaneously.
Diffstat (limited to 'components/script/page.rs')
-rw-r--r-- | components/script/page.rs | 153 |
1 files changed, 63 insertions, 90 deletions
diff --git a/components/script/page.rs b/components/script/page.rs index d634e86a84a..c5b1f74c73a 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -13,9 +13,9 @@ use dom::node::{Node, NodeHelpers}; use dom::window::Window; use layout_interface::{ ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse, - GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery, - Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg, - ReflowQueryType, TrustedNodeAddress + GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, Reflow, + ReflowForScriptQuery, ReflowGoal, ReflowMsg, ReflowQueryType, + TrustedNodeAddress }; use script_traits::{UntrustedNodeAddress, ScriptControlChan}; @@ -29,7 +29,7 @@ use servo_net::storage_task::StorageTask; use servo_util::geometry::{Au, MAX_RECT}; use servo_util::geometry; use servo_util::str::DOMString; -use servo_util::smallvec::{SmallVec1, SmallVec}; +use servo_util::smallvec::SmallVec; use std::cell::{Cell, Ref, RefMut}; use std::comm::{channel, Receiver, Empty, Disconnected}; use std::mem::replace; @@ -77,9 +77,6 @@ pub struct Page { /// Pending resize event, if any. pub resize_event: Cell<Option<WindowSizeData>>, - /// Any nodes that need to be dirtied before the next reflow. - pub pending_dirty_nodes: DOMRefCell<SmallVec1<UntrustedNodeAddress>>, - /// Pending scroll to fragment event, if any pub fragment_name: DOMRefCell<Option<String>>, @@ -95,15 +92,6 @@ pub struct Page { // Child Pages. pub children: DOMRefCell<Vec<Rc<Page>>>, - /// Whether layout needs to be run at all. - pub damaged: Cell<bool>, - - /// Number of pending reflows that were sent while layout was active. - pub pending_reflows: Cell<int>, - - /// Number of unnecessary potential reflows that were skipped since the last reflow - pub avoided_reflows: Cell<int>, - /// An enlarged rectangle around the page contents visible in the viewport, used /// to prevent creating display list items for content that is far away from the viewport. pub page_clip_rect: Cell<Rect<Au>>, @@ -165,57 +153,35 @@ impl Page { url: DOMRefCell::new(None), next_subpage_id: Cell::new(SubpageId(0)), resize_event: Cell::new(None), - pending_dirty_nodes: DOMRefCell::new(SmallVec1::new()), fragment_name: DOMRefCell::new(None), last_reflow_id: Cell::new(0), resource_task: resource_task, storage_task: storage_task, constellation_chan: constellation_chan, children: DOMRefCell::new(vec!()), - damaged: Cell::new(false), - pending_reflows: Cell::new(0), - avoided_reflows: Cell::new(0), page_clip_rect: Cell::new(MAX_RECT), } } - pub fn flush_layout(&self, query: ReflowQueryType) { - // If we are damaged, we need to force a full reflow, so that queries interact with - // an accurate flow tree. - let (reflow_goal, force_reflow) = if self.damaged.get() { - (ReflowForDisplay, true) - } else { - match query { - ContentBoxQuery(_) | ContentBoxesQuery(_) => (ReflowForScriptQuery, true), - NoQuery => (ReflowForDisplay, false), - } - }; - - if force_reflow { - let frame = self.frame(); - let window = frame.as_ref().unwrap().window.root(); - self.reflow(reflow_goal, window.control_chan().clone(), &mut **window.compositor(), query); - } else { - self.avoided_reflows.set(self.avoided_reflows.get() + 1); - } + pub fn flush_layout(&self, goal: ReflowGoal, query: ReflowQueryType) { + let frame = self.frame(); + let window = frame.as_ref().unwrap().window.root(); + self.reflow(goal, window.control_chan().clone(), &mut **window.compositor(), query); } - pub fn layout(&self) -> &LayoutRPC { - self.flush_layout(NoQuery); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let layout_rpc: &LayoutRPC = &*self.layout_rpc; - layout_rpc + pub fn layout(&self) -> &LayoutRPC { + &*self.layout_rpc } pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> { - self.flush_layout(ContentBoxQuery(content_box_request)); + self.flush_layout(ReflowForScriptQuery, ContentBoxQuery(content_box_request)); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let ContentBoxResponse(rect) = self.layout_rpc.content_box(); rect } pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> { - self.flush_layout(ContentBoxesQuery(content_boxes_request)); + self.flush_layout(ReflowForScriptQuery, ContentBoxesQuery(content_boxes_request)); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); rects @@ -276,6 +242,13 @@ impl Page { } } } + + pub fn dirty_all_nodes(&self) { + match *self.frame.borrow() { + None => {} + Some(ref frame) => frame.document.root().dirty_all_nodes(), + } + } } impl Iterator<Rc<Page>> for PageIterator { @@ -334,7 +307,7 @@ impl Page { /// Sends a ping to layout and waits for the response. The response will arrive when the /// layout task has finished any pending request messages. - pub fn join_layout(&self) { + fn join_layout(&self) { let mut layout_join_port = self.layout_join_port.borrow_mut(); if layout_join_port.is_some() { let join_port = replace(&mut *layout_join_port, None); @@ -358,11 +331,11 @@ impl Page { } } - /// Reflows the page if it's possible to do so. 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. + /// 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. /// - /// 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. pub fn reflow(&self, goal: ReflowGoal, script_chan: ScriptControlChan, @@ -375,54 +348,54 @@ impl Page { } }; - match root.root() { - None => {}, - Some(root) => { - debug!("avoided {:d} reflows", self.avoided_reflows.get()); - self.avoided_reflows.set(0); - - debug!("script: performing reflow for goal {}", goal); + let root = match root.root() { + None => return, + Some(root) => root, + }; - // Now, join the layout so that they will see the latest changes we have made. - self.join_layout(); + debug!("script: performing reflow for goal {}", goal); - // Layout will let us know when it's done. - let (join_chan, join_port) = channel(); - let mut layout_join_port = self.layout_join_port.borrow_mut(); - *layout_join_port = Some(join_port); + let root: JSRef<Node> = NodeCast::from_ref(*root); + if !root.get_has_dirty_descendants() { + debug!("root has no dirty descendants; avoiding reflow"); + return + } - let last_reflow_id = &self.last_reflow_id; - last_reflow_id.set(last_reflow_id.get() + 1); + debug!("script: performing reflow for goal {}", goal); - let root: JSRef<Node> = NodeCast::from_ref(*root); + // Layout will let us know when it's done. + let (join_chan, join_port) = channel(); - let window_size = self.window_size.get(); - self.damaged.set(false); + { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + *layout_join_port = Some(join_port); + } - // Send new document and relevant styles to layout. - let reflow = box Reflow { - document_root: root.to_trusted_node_address(), - url: self.get_url(), - iframe: self.subpage_id.is_some(), - goal: goal, - window_size: window_size, - script_chan: script_chan, - script_join_chan: join_chan, - id: last_reflow_id.get(), - query_type: query_type, - page_clip_rect: self.page_clip_rect.get(), - }; + let last_reflow_id = &self.last_reflow_id; + last_reflow_id.set(last_reflow_id.get() + 1); + + let window_size = self.window_size.get(); + + // Send new document and relevant styles to layout. + let reflow = box Reflow { + document_root: root.to_trusted_node_address(), + url: self.get_url(), + iframe: self.subpage_id.is_some(), + goal: goal, + window_size: window_size, + script_chan: script_chan, + script_join_chan: join_chan, + id: last_reflow_id.get(), + query_type: query_type, + page_clip_rect: self.page_clip_rect.get(), + }; - let LayoutChan(ref chan) = self.layout_chan; - chan.send(ReflowMsg(reflow)); + let LayoutChan(ref chan) = self.layout_chan; + chan.send(ReflowMsg(reflow)); - debug!("script: layout forked") - } - } - } + debug!("script: layout forked"); - pub fn damage(&self) { - self.damaged.set(true); + self.join_layout(); } /// Attempt to find a named element in this page's document. |