aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/constellation.rs58
-rw-r--r--components/gfx/paint_task.rs52
-rw-r--r--components/msg/constellation_msg.rs10
-rw-r--r--components/script/dom/window.rs46
-rw-r--r--components/script/script_task.rs44
-rw-r--r--components/script_traits/lib.rs11
-rw-r--r--components/script_traits/script_msg.rs6
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/root-box-003.htm.ini2
8 files changed, 110 insertions, 119 deletions
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs
index 4056d95e8b0..40c86ed1828 100644
--- a/components/compositing/constellation.rs
+++ b/components/compositing/constellation.rs
@@ -29,7 +29,7 @@ use msg::compositor_msg::Epoch;
use msg::constellation_msg::AnimationState;
use msg::constellation_msg::PaintMsg as FromPaintMsg;
use msg::constellation_msg::WebDriverCommandMsg;
-use msg::constellation_msg::{FrameId, PipelineId};
+use msg::constellation_msg::{DocumentState, FrameId, PipelineId};
use msg::constellation_msg::{IframeLoadInfo, IFrameSandboxState, MozBrowserEvent, NavigationDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData};
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId};
@@ -45,7 +45,7 @@ use profile_traits::mem;
use profile_traits::time;
use sandboxing;
use script_traits::{CompositorEvent, ConstellationControlMsg, LayoutControlMsg};
-use script_traits::{ScriptMsg as FromScriptMsg, ScriptState, ScriptTaskFactory};
+use script_traits::{ScriptMsg as FromScriptMsg, ScriptTaskFactory};
use script_traits::{TimerEventRequest};
use std::borrow::ToOwned;
use std::collections::HashMap;
@@ -66,6 +66,7 @@ use util::{opts, prefs};
#[derive(Debug, PartialEq)]
enum ReadyToSave {
NoRootFrame,
+ PendingFrames,
WebFontNotLoaded,
DocumentLoading,
EpochMismatch,
@@ -170,6 +171,9 @@ pub struct Constellation<LTF, STF> {
/// A list of child content processes.
child_processes: Vec<ChildProcess>,
+
+ /// Document states for loaded pipelines (used only when writing screenshots).
+ document_states: HashMap<PipelineId, DocumentState>,
}
/// State needed to construct a constellation.
@@ -225,7 +229,7 @@ impl Frame {
struct FrameChange {
old_pipeline_id: Option<PipelineId>,
new_pipeline_id: PipelineId,
- painter_ready: bool,
+ document_ready: bool,
}
/// An iterator over a frame tree, returning nodes in depth-first order.
@@ -330,6 +334,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
webgl_paint_tasks: Vec::new(),
scheduler_chan: TimerScheduler::start(),
child_processes: Vec::new(),
+ document_states: HashMap::new(),
};
let namespace_id = constellation.next_pipeline_namespace_id();
PipelineNamespace::install(namespace_id);
@@ -428,7 +433,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.pending_frames.push(FrameChange {
old_pipeline_id: old_pipeline_id,
new_pipeline_id: new_pipeline_id,
- painter_ready: false,
+ document_ready: false,
});
}
@@ -600,6 +605,11 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
debug!("constellation got navigation message from script");
self.handle_navigate_msg(pipeline_info, direction);
}
+ // Notification that the new document is ready to become active
+ Request::Script(FromScriptMsg::ActivateDocument(pipeline_id)) => {
+ debug!("constellation got activate document message");
+ self.handle_activate_document_msg(pipeline_id);
+ }
Request::Script(FromScriptMsg::MozBrowserEvent(pipeline_id,
subpage_id,
event)) => {
@@ -670,16 +680,16 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
debug!("constellation got NodeStatus message");
self.compositor_proxy.send(ToCompositorMsg::Status(message));
}
+ Request::Script(FromScriptMsg::SetDocumentState(pipeline_id, state)) => {
+ debug!("constellation got SetDocumentState message");
+ self.document_states.insert(pipeline_id, state);
+ }
// Messages from paint task
// Notification that painting has finished and is requesting permission to paint.
- Request::Paint(FromPaintMsg::Ready(pipeline_id)) => {
- debug!("constellation got painter ready message");
- self.handle_painter_ready_msg(pipeline_id);
- }
Request::Paint(FromPaintMsg::Failure(Failure { pipeline_id, parent_info })) => {
debug!("handling paint failure message from pipeline {:?}, {:?}", pipeline_id, parent_info);
self.handle_failure_msg(pipeline_id, parent_info);
@@ -1257,11 +1267,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
}
- fn handle_painter_ready_msg(&mut self, pipeline_id: PipelineId) {
- debug!("Painter {:?} ready to send paint msg", pipeline_id);
- // This message could originate from a pipeline in the navigation context or
- // from a pending frame. The only time that we will grant paint permission is
- // when the message originates from a pending frame or the current frame.
+ fn handle_activate_document_msg(&mut self, pipeline_id: PipelineId) {
+ debug!("Document ready to activate {:?}", pipeline_id);
// If this pipeline is already part of the current frame tree,
// we don't need to do anything.
@@ -1275,12 +1282,12 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
frame_change.new_pipeline_id == pipeline_id
});
if let Some(pending_index) = pending_index {
- self.pending_frames[pending_index].painter_ready = true;
+ self.pending_frames[pending_index].document_ready = true;
}
// This is a bit complex. We need to loop through pending frames and find
// ones that can be swapped. A frame can be swapped (enabled) once it is
- // ready to paint (has painter_ready set), and also has no dependencies
+ // ready to layout (has document_ready set), and also has no dependencies
// (i.e. the pipeline it is replacing has been enabled and now has a frame).
// The outer loop is required because any time a pipeline is enabled, that
// may affect whether other pending frames are now able to be enabled. On the
@@ -1291,7 +1298,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let waiting_on_dependency = frame_change.old_pipeline_id.map_or(false, |old_pipeline_id| {
self.pipeline_to_frame_map.get(&old_pipeline_id).is_none()
});
- frame_change.painter_ready && !waiting_on_dependency
+ frame_change.document_ready && !waiting_on_dependency
}) {
let frame_change = self.pending_frames.swap_remove(valid_frame_change);
self.add_or_replace_pipeline_in_frame_tree(frame_change);
@@ -1348,6 +1355,11 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
return ReadyToSave::NoRootFrame;
}
+ // If there are pending loads, wait for those to complete.
+ if self.pending_frames.len() > 0 {
+ return ReadyToSave::PendingFrames;
+ }
+
// Step through the current frame tree, checking that the script
// task is idle, and that the current epoch of the layout task
// matches what the compositor has painted. If all these conditions
@@ -1371,14 +1383,12 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
return ReadyToSave::WebFontNotLoaded;
}
- // Synchronously query the script task for this pipeline
- // to see if it is idle.
- let (sender, receiver) = ipc::channel().unwrap();
- let msg = ConstellationControlMsg::GetCurrentState(sender, frame.current);
- pipeline.script_chan.send(msg).unwrap();
- let result = receiver.recv().unwrap();
- if result == ScriptState::DocumentLoading {
- return ReadyToSave::DocumentLoading;
+ // See if this pipeline has reached idle script state yet.
+ match self.document_states.get(&frame.current) {
+ Some(&DocumentState::Idle) => {}
+ Some(&DocumentState::Pending) | None => {
+ return ReadyToSave::DocumentLoading;
+ }
}
// Check the visible rectangle for this pipeline. If the constellation has received a
diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs
index fd619721732..6924b35f2c8 100644
--- a/components/gfx/paint_task.rs
+++ b/components/gfx/paint_task.rs
@@ -212,7 +212,6 @@ pub struct PaintTask<C> {
layout_to_paint_port: Receiver<LayoutToPaintMsg>,
chrome_to_paint_port: Receiver<ChromeToPaintMsg>,
compositor: C,
- constellation_chan: ConstellationChan<ConstellationMsg>,
/// A channel to the time profiler.
time_profiler_chan: time::ProfilerChan,
@@ -274,7 +273,6 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
layout_to_paint_port: layout_to_paint_port,
chrome_to_paint_port: chrome_to_paint_port,
compositor: compositor,
- constellation_chan: constellation_chan,
time_profiler_chan: time_profiler_chan,
root_paint_layer: None,
paint_permission: false,
@@ -326,14 +324,9 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
self.current_epoch = Some(epoch);
self.root_paint_layer = Some(Arc::new(paint_layer));
- if !self.paint_permission {
- debug!("PaintTask: paint ready msg");
- let ConstellationChan(ref mut c) = self.constellation_chan;
- c.send(ConstellationMsg::Ready(self.id)).unwrap();
- continue;
+ if self.paint_permission {
+ self.initialize_layers();
}
-
- self.initialize_layers();
}
// Inserts a new canvas renderer to the layer map
Msg::FromLayout(LayoutToPaintMsg::CanvasLayer(layer_id, canvas_renderer)) => {
@@ -341,31 +334,26 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
self.canvas_map.insert(layer_id, canvas_renderer);
}
Msg::FromChrome(ChromeToPaintMsg::Paint(requests, frame_tree_id)) => {
- if !self.paint_permission {
- debug!("PaintTask: paint ready msg");
- let ConstellationChan(ref mut c) = self.constellation_chan;
- c.send(ConstellationMsg::Ready(self.id)).unwrap();
- continue;
- }
-
- let mut replies = Vec::new();
- for PaintRequest { buffer_requests, scale, layer_id, epoch, layer_kind }
- in requests {
- if self.current_epoch == Some(epoch) {
- self.paint(&mut replies, buffer_requests, scale, layer_id, layer_kind);
- } else {
- debug!("PaintTask: Ignoring requests with epoch mismatch: {:?} != {:?}",
- self.current_epoch,
- epoch);
- self.compositor.ignore_buffer_requests(buffer_requests);
+ if self.paint_permission && self.root_paint_layer.is_some() {
+ let mut replies = Vec::new();
+ for PaintRequest { buffer_requests, scale, layer_id, epoch, layer_kind }
+ in requests {
+ if self.current_epoch == Some(epoch) {
+ self.paint(&mut replies, buffer_requests, scale, layer_id, layer_kind);
+ } else {
+ debug!("PaintTask: Ignoring requests with epoch mismatch: {:?} != {:?}",
+ self.current_epoch,
+ epoch);
+ self.compositor.ignore_buffer_requests(buffer_requests);
+ }
}
- }
- debug!("PaintTask: returning surfaces");
- self.compositor.assign_painted_buffers(self.id,
- self.current_epoch.unwrap(),
- replies,
- frame_tree_id);
+ debug!("PaintTask: returning surfaces");
+ self.compositor.assign_painted_buffers(self.id,
+ self.current_epoch.unwrap(),
+ replies,
+ frame_tree_id);
+ }
}
Msg::FromChrome(ChromeToPaintMsg::PaintPermissionGranted) => {
self.paint_permission = true;
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 0469fd95672..deebe4ba13c 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -244,7 +244,6 @@ pub enum MouseButton {
/// Messages from the paint task to the constellation.
#[derive(Deserialize, Serialize)]
pub enum PaintMsg {
- Ready(PipelineId),
Failure(Failure),
}
@@ -256,6 +255,15 @@ pub enum AnimationState {
NoAnimationCallbacksPresent,
}
+/// Used to determine if a script has any pending asynchronous activity.
+#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub enum DocumentState {
+ /// The document has been loaded and is idle.
+ Idle,
+ /// The document is either loading or waiting on an event.
+ Pending,
+}
+
// https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API#Events
#[derive(Deserialize, Serialize)]
pub enum MozBrowserEvent {
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 0c0b1c01445..47639f0c362 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -6,7 +6,7 @@ use app_units::Au;
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType, WorkerId};
use dom::bindings::callback::ExceptionHandling;
use dom::bindings::cell::DOMRefCell;
-use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
use dom::bindings::codegen::Bindings::EventHandlerBinding::{EventHandlerNonNull, OnErrorEventHandlerNonNull};
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
@@ -44,7 +44,8 @@ use layout_interface::{LayoutChan, LayoutRPC, Msg, Reflow, ReflowGoal, ReflowQue
use libc;
use msg::ParseErrorReporter;
use msg::compositor_msg::{LayerId, ScriptToCompositorMsg};
-use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId, SubpageId, WindowSizeData};
+use msg::constellation_msg::{ConstellationChan, DocumentState, LoadData};
+use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData};
use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use net_traits::ResourceTask;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask};
@@ -990,16 +991,43 @@ impl Window {
///
/// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout.
pub fn reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
- if query_type == ReflowQueryType::NoQuery && !self.Document().needs_reflow() {
+ let for_display = query_type == ReflowQueryType::NoQuery;
+
+ if !for_display || self.Document().needs_reflow() {
+ self.force_reflow(goal, query_type, reason);
+
+ // If window_size is `None`, we don't reflow, so the document stays dirty.
+ // Otherwise, we shouldn't need a reflow immediately after a reflow.
+ assert!(!self.Document().needs_reflow() || self.window_size.get().is_none());
+ } else {
debug!("Document doesn't need reflow - skipping it (reason {:?})", reason);
- return
}
- self.force_reflow(goal, query_type, reason);
-
- // If window_size is `None`, we don't reflow, so the document stays dirty.
- // Otherwise, we shouldn't need a reflow immediately after a reflow.
- assert!(!self.Document().needs_reflow() || self.window_size.get().is_none());
+ // If writing a screenshot, check if the script has reached a state
+ // where it's safe to write the image. This means that:
+ // 1) The reflow is for display (otherwise it could be a query)
+ // 2) The html element doesn't contain the 'reftest-wait' class
+ // 3) The load event has fired.
+ // When all these conditions are met, notify the constellation
+ // that this pipeline is ready to write the image (from the script task
+ // perspective at least).
+ if opts::get().output_file.is_some() && for_display {
+ let document = self.Document();
+
+ // Checks if the html element has reftest-wait attribute present.
+ // See http://testthewebforward.org/docs/reftests.html
+ let html_element = document.GetDocumentElement();
+ let reftest_wait = html_element.map_or(false, |elem| {
+ elem.has_class(&Atom::from("reftest-wait"))
+ });
+
+ let ready_state = document.ReadyState();
+
+ if ready_state == DocumentReadyState::Complete && !reftest_wait {
+ let event = ConstellationMsg::SetDocumentState(self.id, DocumentState::Idle);
+ self.constellation_chan().0.send(event).unwrap();
+ }
+ }
}
pub fn layout(&self) -> &LayoutRPC {
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index ef9f06aa1c2..a3cff5b0835 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -80,7 +80,7 @@ use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
use script_traits::CompositorEvent::{TouchEvent};
use script_traits::{CompositorEvent, ConstellationControlMsg, InitialScriptState, NewLayoutInfo};
-use script_traits::{OpaqueScriptLayoutChannel, ScriptMsg as ConstellationMsg, ScriptState};
+use script_traits::{OpaqueScriptLayoutChannel, ScriptMsg as ConstellationMsg};
use script_traits::{ScriptTaskFactory, TimerEvent, TimerEventRequest, TimerSource};
use script_traits::{TouchEventType, TouchId};
use std::any::Any;
@@ -97,7 +97,6 @@ use std::result::Result;
use std::sync::atomic::{Ordering, AtomicBool};
use std::sync::mpsc::{Receiver, Select, Sender, channel};
use std::sync::{Arc, Mutex};
-use string_cache::Atom;
use time::{Tm, now};
use url::{Url, UrlParser};
use util::opts;
@@ -1014,10 +1013,6 @@ impl ScriptTask {
ConstellationControlMsg::DispatchFrameLoadEvent {
target: pipeline_id, parent: containing_id } =>
self.handle_frame_load_event(containing_id, pipeline_id),
- ConstellationControlMsg::GetCurrentState(sender, pipeline_id) => {
- let state = self.handle_get_current_state(pipeline_id);
- sender.send(state).unwrap();
- },
ConstellationControlMsg::ReportCSSError(pipeline_id, filename, line, column, msg) =>
self.handle_css_error_reporting(pipeline_id, filename, line, column, msg),
}
@@ -1173,40 +1168,6 @@ impl ScriptTask {
panic!("Page rect message sent to nonexistent pipeline");
}
- /// Get the current state of a given pipeline.
- fn handle_get_current_state(&self, pipeline_id: PipelineId) -> ScriptState {
- // Check if the main page load is still pending
- let loads = self.incomplete_loads.borrow();
- if let Some(_) = loads.iter().find(|load| load.pipeline_id == pipeline_id) {
- return ScriptState::DocumentLoading;
- }
-
- // If not in pending loads, the page should exist by now.
- let page = self.root_page();
- let page = page.find(pipeline_id).expect("GetCurrentState sent to nonexistent pipeline");
- let doc = page.document();
-
- // Check if document load event has fired. If the document load
- // event has fired, this also guarantees that the first reflow
- // has been kicked off. Since the script task does a join with
- // layout, this ensures there are no race conditions that can occur
- // between load completing and the first layout completing.
- let load_pending = doc.ReadyState() != DocumentReadyState::Complete;
- if load_pending {
- return ScriptState::DocumentLoading;
- }
-
- // Checks if the html element has reftest-wait attribute present.
- // See http://testthewebforward.org/docs/reftests.html
- let html_element = doc.GetDocumentElement();
- let reftest_wait = html_element.map_or(false, |elem| elem.has_class(&Atom::from("reftest-wait")));
- if reftest_wait {
- return ScriptState::DocumentLoading;
- }
-
- ScriptState::DocumentLoaded
- }
-
fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
let NewLayoutInfo {
containing_pipeline_id,
@@ -1715,6 +1676,9 @@ impl ScriptTask {
window: JS::from_rooted(&window),
}));
+ let ConstellationChan(ref chan) = self.constellation_chan;
+ chan.send(ConstellationMsg::ActivateDocument(incomplete.pipeline_id)).unwrap();
+
let is_javascript = incomplete.url.scheme == "javascript";
let parse_input = if is_javascript {
use url::percent_encoding::percent_decode_to;
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 159fe3c5549..67130403194 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -97,15 +97,6 @@ pub struct NewLayoutInfo {
pub content_process_shutdown_chan: IpcSender<()>,
}
-/// Used to determine if a script has any pending asynchronous activity.
-#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub enum ScriptState {
- /// The document has been loaded.
- DocumentLoaded,
- /// The document is still loading.
- DocumentLoading,
-}
-
/// Messages sent from the constellation or layout to the script task.
#[derive(Deserialize, Serialize)]
pub enum ConstellationControlMsg {
@@ -142,8 +133,6 @@ pub enum ConstellationControlMsg {
/// Notifies the script task that a new Web font has been loaded, and thus the page should be
/// reflowed.
WebFontLoaded(PipelineId),
- /// Get the current state of the script task for a given pipeline.
- GetCurrentState(IpcSender<ScriptState>, PipelineId),
/// Cause a `load` event to be dispatched at the appropriate frame element.
DispatchFrameLoadEvent {
/// The pipeline that has been marked as loaded.
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index 7769dbe8597..b19212dc2d1 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -6,7 +6,7 @@ use canvas_traits::CanvasMsg;
use euclid::point::Point2D;
use euclid::size::Size2D;
use ipc_channel::ipc::IpcSender;
-use msg::constellation_msg::{AnimationState, IframeLoadInfo, NavigationDirection};
+use msg::constellation_msg::{AnimationState, DocumentState, IframeLoadInfo, NavigationDirection};
use msg::constellation_msg::{Failure, MozBrowserEvent, PipelineId};
use msg::constellation_msg::{LoadData, SubpageId};
use msg::constellation_msg::{MouseButton, MouseEventType};
@@ -66,4 +66,8 @@ pub enum ScriptMsg {
SetCursor(Cursor),
/// Notifies the constellation that the viewport has been constrained in some manner
ViewportConstrained(PipelineId, ViewportConstraints),
+ /// Mark a new document as active
+ ActivateDocument(PipelineId),
+ /// Set the document state for a pipeline (used by screenshot / reftests)
+ SetDocumentState(PipelineId, DocumentState),
}
diff --git a/tests/wpt/metadata-css/css21_dev/html4/root-box-003.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/root-box-003.htm.ini
index 3143cf72d07..c65c5b6cadb 100644
--- a/tests/wpt/metadata-css/css21_dev/html4/root-box-003.htm.ini
+++ b/tests/wpt/metadata-css/css21_dev/html4/root-box-003.htm.ini
@@ -1,3 +1,3 @@
[root-box-003.htm]
type: reftest
- expected: TIMEOUT
+ expected: FAIL