servo: Merge #6031 - Fixes a number of race conditions and reliability issues with reftests and compositor (from glennw:reftest-race-conditions); r=larsberg,jdm
The basic idea is it's safe to output an image for reftest by testing:
- That the compositor doesn't have any animations active.
- That the compositor is not waiting on any outstanding paint messages to arrive.
- That the script tasks are "idle" and therefore won't cause reflow.
- This currently means page loaded, onload fired, reftest-wait not active, first reflow triggered.
- It could easily be expanded to handle pending timers etc.
- That the "epoch" that the layout tasks have last laid out after script went idle, is reflected by the compositor in all visible layers for that pipeline.
Source-Repo: https://github.com/servo/servo
Source-Revision: 5e61ebaa05e5babb7b2fdd1347b6cdd23df38e62
This commit is contained in:
@@ -58,9 +58,8 @@ use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
|
||||
use script_traits::CompositorEvent::{MouseMoveEvent, KeyEvent};
|
||||
use script_traits::{NewLayoutInfo, OpaqueScriptLayoutChannel};
|
||||
use script_traits::{ConstellationControlMsg, ScriptControlChan};
|
||||
use script_traits::ScriptTaskFactory;
|
||||
use script_traits::{ScriptState, ScriptTaskFactory};
|
||||
use webdriver_traits::WebDriverScriptCommand;
|
||||
use msg::compositor_msg::ReadyState::{FinishedLoading, Loading, PerformingLayout};
|
||||
use msg::compositor_msg::{LayerId, ScriptListener};
|
||||
use msg::constellation_msg::{ConstellationChan, FocusType};
|
||||
use msg::constellation_msg::{LoadData, PipelineId, SubpageId, MozBrowserEvent, WorkerId};
|
||||
@@ -735,6 +734,10 @@ impl ScriptTask {
|
||||
responder.respond();
|
||||
self.handle_resource_loaded(id, LoadType::Stylesheet(url));
|
||||
}
|
||||
ConstellationControlMsg::GetCurrentState(sender, pipeline_id) => {
|
||||
let state = self.handle_get_current_state(pipeline_id);
|
||||
sender.send(state).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,6 +862,40 @@ impl ScriptTask {
|
||||
doc.r().finish_load(load);
|
||||
}
|
||||
|
||||
/// 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().root();
|
||||
|
||||
// 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.r().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.r().GetDocumentElement().root();
|
||||
let reftest_wait = html_element.r().map_or(false, |elem| elem.has_class(&Atom::from_slice("reftest-wait")));
|
||||
if reftest_wait {
|
||||
return ScriptState::DocumentLoading;
|
||||
}
|
||||
|
||||
return ScriptState::DocumentLoaded;
|
||||
}
|
||||
|
||||
fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
|
||||
let NewLayoutInfo {
|
||||
containing_pipeline_id,
|
||||
@@ -993,14 +1030,6 @@ impl ScriptTask {
|
||||
with this script task. This is a bug.");
|
||||
let window = page.window().root();
|
||||
window.r().handle_reflow_complete_msg(reflow_id);
|
||||
|
||||
let doc = page.document().root();
|
||||
let html_element = doc.r().GetDocumentElement().root();
|
||||
let reftest_wait = html_element.r().map_or(false, |elem| elem.has_class(&Atom::from_slice("reftest-wait")));
|
||||
|
||||
if !reftest_wait {
|
||||
self.compositor.borrow_mut().set_ready_state(pipeline_id, FinishedLoading);
|
||||
}
|
||||
}
|
||||
|
||||
/// Window was resized, but this script was not active, so don't reflow yet
|
||||
@@ -1102,8 +1131,6 @@ impl ScriptTask {
|
||||
})
|
||||
}).root();
|
||||
|
||||
self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading);
|
||||
|
||||
// Create a new frame tree entry.
|
||||
let page = Rc::new(Page::new(incomplete.pipeline_id, final_url.clone()));
|
||||
if !root_page_exists {
|
||||
@@ -1462,7 +1489,6 @@ impl ScriptTask {
|
||||
let final_url = document.r().url();
|
||||
|
||||
document.r().set_ready_state(DocumentReadyState::Interactive);
|
||||
self.compositor.borrow_mut().set_ready_state(id, PerformingLayout);
|
||||
|
||||
// Kick off the initial reflow of the page.
|
||||
debug!("kicking off initial reflow of {:?}", final_url);
|
||||
|
||||
Reference in New Issue
Block a user