aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/page.rs
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-10-28 19:49:17 -0700
committerPatrick Walton <pcwalton@mimiga.net>2014-12-15 14:16:04 -0800
commitd101c1dd91ddcd030cd85a22825835bc3eca50ea (patch)
treeec02e406356645ffe4bac4d9cf33256c92c31de5 /components/script/page.rs
parent1bc2c8a6397382b4db8fb09582434f4798d43868 (diff)
downloadservo-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.rs153
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.