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:
Glenn Watson
2015-05-13 18:37:54 -05:00
parent 13030f59a2
commit 76f1d2ef46
17 changed files with 363 additions and 390 deletions

View File

@@ -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);