mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Fixes a number of race conditions and reliability issues with reftests and compositor.
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.
This commit is contained in:
parent
b3b9deafa7
commit
eec3fad93d
17 changed files with 363 additions and 390 deletions
|
@ -4,7 +4,7 @@
|
|||
|
||||
use compositor_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag};
|
||||
use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
|
||||
use compositor_task::{CompositorTask, LayerProperties, Msg};
|
||||
use compositor_task::{CompositorTask, Msg};
|
||||
use constellation::SendableFrameTree;
|
||||
use pipeline::CompositionPipeline;
|
||||
use scrolling::ScrollingTimerProxy;
|
||||
|
@ -14,7 +14,7 @@ use windowing::{MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}
|
|||
use geom::point::{Point2D, TypedPoint2D};
|
||||
use geom::rect::{Rect, TypedRect};
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use geom::size::{Size2D, TypedSize2D};
|
||||
use geom::size::TypedSize2D;
|
||||
use gfx::color;
|
||||
use gfx::paint_task::Msg as PaintMsg;
|
||||
use gfx::paint_task::PaintRequest;
|
||||
|
@ -26,7 +26,7 @@ use layers::rendergl::RenderContext;
|
|||
use layers::rendergl;
|
||||
use layers::scene::Scene;
|
||||
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId};
|
||||
use msg::compositor_msg::{ReadyState, PaintState, ScrollPolicy};
|
||||
use msg::compositor_msg::{LayerProperties, ScrollPolicy};
|
||||
use msg::constellation_msg::AnimationState;
|
||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||
use msg::constellation_msg::{ConstellationChan, NavigationDirection};
|
||||
|
@ -36,7 +36,6 @@ use png;
|
|||
use profile_traits::mem;
|
||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||
use script_traits::{ConstellationControlMsg, ScriptControlChan};
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::mem as std_mem;
|
||||
|
@ -49,6 +48,15 @@ use url::Url;
|
|||
use util::geometry::{PagePx, ScreenPx, ViewportPx};
|
||||
use util::opts;
|
||||
|
||||
/// Holds the state when running reftests that determines when it is
|
||||
/// safe to save the output image.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum ReadyState {
|
||||
Unknown,
|
||||
WaitingForConstellationReply,
|
||||
ReadyToSaveImage,
|
||||
}
|
||||
|
||||
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
|
||||
pub struct IOCompositor<Window: WindowMethods> {
|
||||
/// The application window.
|
||||
|
@ -102,9 +110,6 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
/// the compositor.
|
||||
shutdown_state: ShutdownState,
|
||||
|
||||
/// Tracks outstanding paint_msg's sent to the paint tasks.
|
||||
outstanding_paint_msgs: u32,
|
||||
|
||||
/// Tracks the last composite time.
|
||||
last_composite_time: u64,
|
||||
|
||||
|
@ -139,6 +144,10 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
|
||||
/// Has a Quit event been seen?
|
||||
has_seen_quit_event: bool,
|
||||
|
||||
/// Used by the logic that determines when it is safe to output an
|
||||
/// image for the reftest framework.
|
||||
ready_to_save_state: ReadyState,
|
||||
}
|
||||
|
||||
pub struct ScrollEvent {
|
||||
|
@ -169,11 +178,8 @@ struct PipelineDetails {
|
|||
/// The pipeline associated with this PipelineDetails object.
|
||||
pipeline: Option<CompositionPipeline>,
|
||||
|
||||
/// The status of this pipeline's ScriptTask.
|
||||
ready_state: ReadyState,
|
||||
|
||||
/// The status of this pipeline's PaintTask.
|
||||
paint_state: PaintState,
|
||||
/// The current layout epoch that this pipeline wants to draw.
|
||||
current_epoch: Epoch,
|
||||
|
||||
/// Whether animations are running
|
||||
animations_running: bool,
|
||||
|
@ -186,15 +192,14 @@ impl PipelineDetails {
|
|||
fn new() -> PipelineDetails {
|
||||
PipelineDetails {
|
||||
pipeline: None,
|
||||
ready_state: ReadyState::Blank,
|
||||
paint_state: PaintState::Painting,
|
||||
current_epoch: Epoch(0),
|
||||
animations_running: false,
|
||||
animation_callbacks_running: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
enum CompositeTarget {
|
||||
/// Normal composition to a window
|
||||
Window,
|
||||
|
@ -274,9 +279,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
time_profiler_chan: time_profiler_chan,
|
||||
mem_profiler_chan: mem_profiler_chan,
|
||||
fragment_point: None,
|
||||
outstanding_paint_msgs: 0,
|
||||
last_composite_time: 0,
|
||||
has_seen_quit_event: false,
|
||||
ready_to_save_state: ReadyState::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,14 +327,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
return false;
|
||||
}
|
||||
|
||||
(Msg::ChangeReadyState(pipeline_id, ready_state), ShutdownState::NotShuttingDown) => {
|
||||
self.change_ready_state(pipeline_id, ready_state);
|
||||
}
|
||||
|
||||
(Msg::ChangePaintState(pipeline_id, paint_state), ShutdownState::NotShuttingDown) => {
|
||||
self.change_paint_state(pipeline_id, paint_state);
|
||||
}
|
||||
|
||||
(Msg::ChangeRunningAnimationsState(pipeline_id, animation_state),
|
||||
ShutdownState::NotShuttingDown) => {
|
||||
self.change_running_animations_state(pipeline_id, animation_state);
|
||||
|
@ -343,10 +340,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.change_page_url(pipeline_id, url);
|
||||
}
|
||||
|
||||
(Msg::PaintMsgDiscarded, ShutdownState::NotShuttingDown) => {
|
||||
self.remove_outstanding_paint_msg();
|
||||
}
|
||||
|
||||
(Msg::SetFrameTree(frame_tree, response_chan, new_constellation_chan),
|
||||
ShutdownState::NotShuttingDown) => {
|
||||
self.set_frame_tree(&frame_tree, response_chan, new_constellation_chan);
|
||||
|
@ -354,13 +347,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.get_title_for_main_frame();
|
||||
}
|
||||
|
||||
(Msg::CreateOrUpdateBaseLayer(layer_properties), ShutdownState::NotShuttingDown) => {
|
||||
self.create_or_update_base_layer(layer_properties);
|
||||
}
|
||||
|
||||
(Msg::CreateOrUpdateDescendantLayer(layer_properties),
|
||||
ShutdownState::NotShuttingDown) => {
|
||||
self.create_or_update_descendant_layer(layer_properties);
|
||||
(Msg::InitializeLayersForPipeline(pipeline_id, epoch, properties), ShutdownState::NotShuttingDown) => {
|
||||
self.get_or_create_pipeline_details(pipeline_id).current_epoch = epoch;
|
||||
for (index, layer_properties) in properties.iter().enumerate() {
|
||||
if index == 0 {
|
||||
self.create_or_update_base_layer(pipeline_id, *layer_properties);
|
||||
} else {
|
||||
self.create_or_update_descendant_layer(pipeline_id, *layer_properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(Msg::GetGraphicsMetadata(chan), ShutdownState::NotShuttingDown) => {
|
||||
|
@ -381,7 +376,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
epoch,
|
||||
frame_tree_id);
|
||||
}
|
||||
self.remove_outstanding_paint_msg();
|
||||
}
|
||||
|
||||
(Msg::ScrollFragmentPoint(pipeline_id, layer_id, point),
|
||||
|
@ -446,6 +440,16 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.constrain_viewport(pipeline_id, constraints);
|
||||
}
|
||||
|
||||
(Msg::IsReadyToSaveImageReply(is_ready), ShutdownState::NotShuttingDown) => {
|
||||
assert!(self.ready_to_save_state == ReadyState::WaitingForConstellationReply);
|
||||
if is_ready {
|
||||
self.ready_to_save_state = ReadyState::ReadyToSaveImage;
|
||||
} else {
|
||||
self.ready_to_save_state = ReadyState::Unknown;
|
||||
}
|
||||
self.composite_if_necessary(CompositingReason::Headless);
|
||||
}
|
||||
|
||||
// When we are shutting_down, we need to avoid performing operations
|
||||
// such as Paint that may crash because we have begun tearing down
|
||||
// the rest of our resources.
|
||||
|
@ -455,31 +459,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
true
|
||||
}
|
||||
|
||||
fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
self.get_or_create_pipeline_details(pipeline_id).ready_state = ready_state;
|
||||
self.window.set_ready_state(self.get_earliest_pipeline_ready_state());
|
||||
|
||||
// If we're painting in headless mode, schedule a recomposite.
|
||||
if let CompositeTarget::PngFile = self.composite_target {
|
||||
self.composite_if_necessary(CompositingReason::Headless)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_earliest_pipeline_ready_state(&self) -> ReadyState {
|
||||
if self.pipeline_details.len() == 0 {
|
||||
return ReadyState::Blank;
|
||||
}
|
||||
return self.pipeline_details.values().fold(ReadyState::FinishedLoading,
|
||||
|v, ref details| {
|
||||
cmp::min(v, details.ready_state)
|
||||
});
|
||||
}
|
||||
|
||||
fn change_paint_state(&mut self, pipeline_id: PipelineId, paint_state: PaintState) {
|
||||
self.get_or_create_pipeline_details(pipeline_id).paint_state = paint_state;
|
||||
self.window.set_paint_state(paint_state);
|
||||
}
|
||||
|
||||
/// Sets or unsets the animations-running flag for the given pipeline, and schedules a
|
||||
/// recomposite if necessary.
|
||||
fn change_running_animations_state(&mut self,
|
||||
|
@ -539,54 +518,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.window.set_page_url(url);
|
||||
}
|
||||
|
||||
fn all_pipelines_in_idle_paint_state(&self) -> bool {
|
||||
if self.pipeline_details.len() == 0 {
|
||||
return false;
|
||||
}
|
||||
return self.pipeline_details.values().all(|ref details| {
|
||||
// If a pipeline exists and has a root layer that has
|
||||
// zero size, it will never be painted. In this case,
|
||||
// consider it as idle to avoid hangs in reftests.
|
||||
if let Some(ref pipeline) = details.pipeline {
|
||||
if let Some(root_layer) = self.find_pipeline_root_layer(pipeline.id) {
|
||||
if root_layer.bounds.borrow().size == Size2D::zero() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
details.paint_state == PaintState::Idle
|
||||
});
|
||||
}
|
||||
|
||||
fn has_paint_msg_tracking(&self) -> bool {
|
||||
// only track PaintMsg's if the compositor outputs to a file.
|
||||
opts::get().output_file.is_some()
|
||||
}
|
||||
|
||||
fn has_outstanding_paint_msgs(&self) -> bool {
|
||||
self.has_paint_msg_tracking() && self.outstanding_paint_msgs > 0
|
||||
}
|
||||
|
||||
fn add_outstanding_paint_msg(&mut self, count: u32) {
|
||||
// return early if not tracking paint_msg's
|
||||
if !self.has_paint_msg_tracking() {
|
||||
return;
|
||||
}
|
||||
debug!("add_outstanding_paint_msg {:?}", self.outstanding_paint_msgs);
|
||||
self.outstanding_paint_msgs += count;
|
||||
}
|
||||
|
||||
fn remove_outstanding_paint_msg(&mut self) {
|
||||
if !self.has_paint_msg_tracking() {
|
||||
return;
|
||||
}
|
||||
if self.outstanding_paint_msgs > 0 {
|
||||
self.outstanding_paint_msgs -= 1;
|
||||
} else {
|
||||
debug!("too many repaint msgs completed");
|
||||
}
|
||||
}
|
||||
|
||||
fn set_frame_tree(&mut self,
|
||||
frame_tree: &SendableFrameTree,
|
||||
response_chan: Sender<()>,
|
||||
|
@ -616,15 +547,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
frame_rect: Option<TypedRect<PagePx, f32>>)
|
||||
-> Rc<Layer<CompositorData>> {
|
||||
let layer_properties = LayerProperties {
|
||||
pipeline_id: pipeline.id,
|
||||
epoch: Epoch(0),
|
||||
id: LayerId::null(),
|
||||
rect: Rect::zero(),
|
||||
background_color: color::transparent(),
|
||||
scroll_policy: ScrollPolicy::Scrollable,
|
||||
};
|
||||
|
||||
let root_layer = CompositorData::new_layer(layer_properties,
|
||||
let root_layer = CompositorData::new_layer(pipeline.id,
|
||||
layer_properties,
|
||||
WantsScrollEventsFlag::WantsScrollEvents,
|
||||
opts::get().tile_size);
|
||||
|
||||
|
@ -661,8 +591,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.find_layer_with_pipeline_and_layer_id(pipeline_id, LayerId::null())
|
||||
}
|
||||
|
||||
fn update_layer_if_exists(&mut self, properties: LayerProperties) -> bool {
|
||||
match self.find_layer_with_pipeline_and_layer_id(properties.pipeline_id, properties.id) {
|
||||
fn update_layer_if_exists(&mut self, pipeline_id: PipelineId, properties: LayerProperties) -> bool {
|
||||
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, properties.id) {
|
||||
Some(existing_layer) => {
|
||||
existing_layer.update_layer(properties);
|
||||
true
|
||||
|
@ -671,8 +601,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_or_update_base_layer(&mut self, layer_properties: LayerProperties) {
|
||||
let pipeline_id = layer_properties.pipeline_id;
|
||||
fn create_or_update_base_layer(&mut self, pipeline_id: PipelineId, layer_properties: LayerProperties) {
|
||||
let root_layer = match self.find_pipeline_root_layer(pipeline_id) {
|
||||
Some(root_layer) => root_layer,
|
||||
None => {
|
||||
|
@ -683,11 +612,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
};
|
||||
|
||||
let need_new_base_layer = !self.update_layer_if_exists(layer_properties);
|
||||
let need_new_base_layer = !self.update_layer_if_exists(pipeline_id, layer_properties);
|
||||
if need_new_base_layer {
|
||||
root_layer.update_layer_except_bounds(layer_properties);
|
||||
|
||||
let base_layer = CompositorData::new_layer(
|
||||
pipeline_id,
|
||||
layer_properties,
|
||||
WantsScrollEventsFlag::DoesntWantScrollEvents,
|
||||
opts::get().tile_size);
|
||||
|
@ -699,27 +629,28 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
root_layer.children().insert(0, base_layer);
|
||||
}
|
||||
|
||||
self.scroll_layer_to_fragment_point_if_necessary(layer_properties.pipeline_id,
|
||||
self.scroll_layer_to_fragment_point_if_necessary(pipeline_id,
|
||||
layer_properties.id);
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
fn create_or_update_descendant_layer(&mut self, layer_properties: LayerProperties) {
|
||||
if !self.update_layer_if_exists(layer_properties) {
|
||||
self.create_descendant_layer(layer_properties);
|
||||
fn create_or_update_descendant_layer(&mut self, pipeline_id: PipelineId, layer_properties: LayerProperties) {
|
||||
if !self.update_layer_if_exists(pipeline_id, layer_properties) {
|
||||
self.create_descendant_layer(pipeline_id, layer_properties);
|
||||
}
|
||||
self.scroll_layer_to_fragment_point_if_necessary(layer_properties.pipeline_id,
|
||||
self.scroll_layer_to_fragment_point_if_necessary(pipeline_id,
|
||||
layer_properties.id);
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
fn create_descendant_layer(&self, layer_properties: LayerProperties) {
|
||||
let root_layer = match self.find_pipeline_root_layer(layer_properties.pipeline_id) {
|
||||
fn create_descendant_layer(&self, pipeline_id: PipelineId, layer_properties: LayerProperties) {
|
||||
let root_layer = match self.find_pipeline_root_layer(pipeline_id) {
|
||||
Some(root_layer) => root_layer,
|
||||
None => return, // This pipeline is in the process of shutting down.
|
||||
};
|
||||
|
||||
let new_layer = CompositorData::new_layer(layer_properties,
|
||||
let new_layer = CompositorData::new_layer(pipeline_id,
|
||||
layer_properties,
|
||||
WantsScrollEventsFlag::DoesntWantScrollEvents,
|
||||
root_layer.tile_size);
|
||||
root_layer.add_child(new_layer);
|
||||
|
@ -806,8 +737,17 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
// has already drawn the most recently painted buffer, and missing a frame.
|
||||
if frame_tree_id == self.frame_tree_id {
|
||||
if let Some(layer) = self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
|
||||
self.assign_painted_buffers_to_layer(layer, new_layer_buffer_set, epoch);
|
||||
return
|
||||
let requested_epoch = layer.extra_data.borrow().requested_epoch;
|
||||
if requested_epoch == epoch {
|
||||
self.assign_painted_buffers_to_layer(layer, new_layer_buffer_set, epoch);
|
||||
return
|
||||
} else {
|
||||
debug!("assign_painted_buffers epoch mismatch {:?} {:?} req={:?} actual={:?}",
|
||||
pipeline_id,
|
||||
layer_id,
|
||||
requested_epoch,
|
||||
epoch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -830,7 +770,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
// FIXME(pcwalton): This is going to cause problems with inconsistent frames since
|
||||
// we only composite one layer at a time.
|
||||
assert!(layer.add_buffers(self, new_layer_buffer_set, epoch));
|
||||
layer.add_buffers(self, new_layer_buffer_set, epoch);
|
||||
self.composite_if_necessary(CompositingReason::NewPaintedBuffers);
|
||||
}
|
||||
|
||||
|
@ -1112,9 +1052,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
let mut results: HashMap<PipelineId, Vec<PaintRequest>> = HashMap::new();
|
||||
|
||||
for (layer, mut layer_requests) in requests.into_iter() {
|
||||
let vec = match results.entry(layer.get_pipeline_id()) {
|
||||
Occupied(mut entry) => {
|
||||
*entry.get_mut() = Vec::new();
|
||||
let pipeline_id = layer.get_pipeline_id();
|
||||
let current_epoch = self.pipeline_details.get(&pipeline_id).unwrap().current_epoch;
|
||||
layer.extra_data.borrow_mut().requested_epoch = current_epoch;
|
||||
let vec = match results.entry(pipeline_id) {
|
||||
Occupied(entry) => {
|
||||
entry.into_mut()
|
||||
}
|
||||
Vacant(entry) => {
|
||||
|
@ -1132,7 +1074,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
buffer_requests: layer_requests,
|
||||
scale: scale.get(),
|
||||
layer_id: layer.extra_data.borrow().id,
|
||||
epoch: layer.extra_data.borrow().epoch,
|
||||
epoch: layer.extra_data.borrow().requested_epoch,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1189,39 +1131,94 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
let pipeline_requests =
|
||||
self.convert_buffer_requests_to_pipeline_requests_map(layers_and_requests);
|
||||
|
||||
let mut num_paint_msgs_sent = 0;
|
||||
for (pipeline_id, requests) in pipeline_requests.into_iter() {
|
||||
num_paint_msgs_sent += 1;
|
||||
let msg = PaintMsg::Paint(requests, self.frame_tree_id);
|
||||
let _ = self.get_pipeline(pipeline_id).paint_chan.send(msg);
|
||||
}
|
||||
|
||||
self.add_outstanding_paint_msg(num_paint_msgs_sent);
|
||||
true
|
||||
}
|
||||
|
||||
fn is_ready_to_paint_image_output(&self) -> bool {
|
||||
if !self.got_load_complete_message {
|
||||
return false;
|
||||
/// Check if a layer (or its children) have any outstanding paint
|
||||
/// results to arrive yet.
|
||||
fn does_layer_have_outstanding_paint_messages(&self, layer: &Rc<Layer<CompositorData>>) -> bool {
|
||||
let layer_data = layer.extra_data.borrow();
|
||||
let current_epoch = self.pipeline_details.get(&layer_data.pipeline_id).unwrap().current_epoch;
|
||||
|
||||
// Only check layers that have requested the current epoch, as there may be
|
||||
// layers that are not visible in the current viewport, and therefore
|
||||
// have not requested a paint of the current epoch.
|
||||
// If a layer has sent a request for the current epoch, but it hasn't
|
||||
// arrived yet then this layer is waiting for a paint message.
|
||||
if layer_data.requested_epoch == current_epoch && layer_data.painted_epoch != current_epoch {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.get_earliest_pipeline_ready_state() != ReadyState::FinishedLoading {
|
||||
return false;
|
||||
for child in layer.children().iter() {
|
||||
if self.does_layer_have_outstanding_paint_messages(child) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if self.has_outstanding_paint_msgs() {
|
||||
return false;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
if !self.all_pipelines_in_idle_paint_state() {
|
||||
return false;
|
||||
}
|
||||
/// Query the constellation to see if the current compositor
|
||||
/// output matches the current frame tree output, and if the
|
||||
/// associated script tasks are idle.
|
||||
fn is_ready_to_paint_image_output(&mut self) -> bool {
|
||||
match self.ready_to_save_state {
|
||||
ReadyState::Unknown => {
|
||||
// Unsure if the output image is stable.
|
||||
|
||||
if self.frame_tree_id == FrameTreeId(0) {
|
||||
return false;
|
||||
}
|
||||
// Check if any layers are waiting for paints to complete
|
||||
// of their current epoch request. If so, early exit
|
||||
// from this check.
|
||||
match self.scene.root {
|
||||
Some(ref root_layer) => {
|
||||
if self.does_layer_have_outstanding_paint_messages(root_layer) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// Collect the currently painted epoch of each pipeline that is
|
||||
// complete (i.e. has *all* layers painted to the requested epoch).
|
||||
// This gets sent to the constellation for comparison with the current
|
||||
// frame tree.
|
||||
let mut pipeline_epochs = HashMap::new();
|
||||
for (id, details) in self.pipeline_details.iter() {
|
||||
// If animations are currently running, then don't bother checking
|
||||
// with the constellation if the output image is stable.
|
||||
if details.animations_running || details.animation_callbacks_running {
|
||||
return false;
|
||||
}
|
||||
|
||||
pipeline_epochs.insert(*id, details.current_epoch);
|
||||
}
|
||||
|
||||
// Pass the pipeline/epoch states to the constellation and check
|
||||
// if it's safe to output the image.
|
||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(ConstellationMsg::IsReadyToSaveImage(pipeline_epochs)).unwrap();
|
||||
self.ready_to_save_state = ReadyState::WaitingForConstellationReply;
|
||||
false
|
||||
}
|
||||
ReadyState::WaitingForConstellationReply => {
|
||||
// If waiting on a reply from the constellation to the last
|
||||
// query if the image is stable, then assume not ready yet.
|
||||
false
|
||||
}
|
||||
ReadyState::ReadyToSaveImage => {
|
||||
// Constellation has replied at some point in the past
|
||||
// that the current output image is stable and ready
|
||||
// for saving.
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn composite(&mut self) {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use compositor_task::LayerProperties;
|
||||
use compositor::IOCompositor;
|
||||
use windowing::{MouseWindowEvent, WindowMethods};
|
||||
|
||||
|
@ -18,7 +17,7 @@ use layers::geometry::LayerPixel;
|
|||
use layers::layers::{Layer, LayerBufferSet};
|
||||
use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
||||
use script_traits::{ScriptControlChan, ConstellationControlMsg};
|
||||
use msg::compositor_msg::{Epoch, LayerId, ScrollPolicy};
|
||||
use msg::compositor_msg::{Epoch, LayerId, LayerProperties, ScrollPolicy};
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -36,9 +35,11 @@ pub struct CompositorData {
|
|||
/// Whether an ancestor layer that receives scroll events moves this layer.
|
||||
pub scroll_policy: ScrollPolicy,
|
||||
|
||||
/// A monotonically increasing counter that keeps track of the current epoch.
|
||||
/// add_buffer() calls that don't match the current epoch will be ignored.
|
||||
pub epoch: Epoch,
|
||||
/// The epoch that has been requested for this layer (via send_buffer_requests).
|
||||
pub requested_epoch: Epoch,
|
||||
|
||||
/// The last accepted painted buffer for this layer (via assign_pained_buffers).
|
||||
pub painted_epoch: Epoch,
|
||||
|
||||
/// The scroll offset originating from this scrolling root. This allows scrolling roots
|
||||
/// to track their current scroll position even while their content_offset does not change.
|
||||
|
@ -46,16 +47,18 @@ pub struct CompositorData {
|
|||
}
|
||||
|
||||
impl CompositorData {
|
||||
pub fn new_layer(layer_properties: LayerProperties,
|
||||
pub fn new_layer(pipeline_id: PipelineId,
|
||||
layer_properties: LayerProperties,
|
||||
wants_scroll_events: WantsScrollEventsFlag,
|
||||
tile_size: usize)
|
||||
-> Rc<Layer<CompositorData>> {
|
||||
let new_compositor_data = CompositorData {
|
||||
pipeline_id: layer_properties.pipeline_id,
|
||||
pipeline_id: pipeline_id,
|
||||
id: layer_properties.id,
|
||||
wants_scroll_events: wants_scroll_events,
|
||||
scroll_policy: layer_properties.scroll_policy,
|
||||
epoch: layer_properties.epoch,
|
||||
requested_epoch: Epoch(0),
|
||||
painted_epoch: Epoch(0),
|
||||
scroll_offset: TypedPoint2D(0., 0.),
|
||||
};
|
||||
|
||||
|
@ -76,7 +79,6 @@ pub trait CompositorLayer {
|
|||
compositor: &IOCompositor<Window>,
|
||||
new_buffers: Box<LayerBufferSet>,
|
||||
epoch: Epoch)
|
||||
-> bool
|
||||
where Window: WindowMethods;
|
||||
|
||||
/// Destroys all layer tiles, sending the buffers back to the painter to be destroyed or
|
||||
|
@ -178,7 +180,6 @@ pub enum ScrollEventResult {
|
|||
|
||||
impl CompositorLayer for Layer<CompositorData> {
|
||||
fn update_layer_except_bounds(&self, layer_properties: LayerProperties) {
|
||||
self.extra_data.borrow_mut().epoch = layer_properties.epoch;
|
||||
self.extra_data.borrow_mut().scroll_policy = layer_properties.scroll_policy;
|
||||
|
||||
*self.background_color.borrow_mut() = to_layers_color(&layer_properties.background_color);
|
||||
|
@ -205,17 +206,9 @@ impl CompositorLayer for Layer<CompositorData> {
|
|||
compositor: &IOCompositor<Window>,
|
||||
new_buffers: Box<LayerBufferSet>,
|
||||
epoch: Epoch)
|
||||
-> bool
|
||||
where Window: WindowMethods {
|
||||
if self.extra_data.borrow().epoch != epoch {
|
||||
debug!("add_buffers: compositor epoch mismatch: {:?} != {:?}, id: {:?}",
|
||||
self.extra_data.borrow().epoch,
|
||||
epoch,
|
||||
self.get_pipeline_id());
|
||||
let pipeline = compositor.get_pipeline(self.get_pipeline_id());
|
||||
let _ = pipeline.paint_chan.send(PaintMsg::UnusedBuffer(new_buffers.buffers));
|
||||
return false;
|
||||
}
|
||||
self.extra_data.borrow_mut().painted_epoch = epoch;
|
||||
assert!(self.extra_data.borrow().painted_epoch == self.extra_data.borrow().requested_epoch);
|
||||
|
||||
for buffer in new_buffers.buffers.into_iter().rev() {
|
||||
self.add_buffer(buffer);
|
||||
|
@ -226,8 +219,6 @@ impl CompositorLayer for Layer<CompositorData> {
|
|||
let pipeline = compositor.get_pipeline(self.get_pipeline_id());
|
||||
let _ = pipeline.paint_chan.send(PaintMsg::UnusedBuffer(unused_buffers));
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn clear<Window>(&self, compositor: &IOCompositor<Window>) where Window: WindowMethods {
|
||||
|
|
|
@ -11,14 +11,12 @@ use compositor;
|
|||
use headless;
|
||||
use windowing::{WindowEvent, WindowMethods};
|
||||
|
||||
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
||||
use geom::point::Point2D;
|
||||
use geom::rect::Rect;
|
||||
use geom::size::Size2D;
|
||||
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
|
||||
use layers::layers::LayerBufferSet;
|
||||
use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, FrameTreeId, ReadyState};
|
||||
use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy};
|
||||
use msg::compositor_msg::{Epoch, LayerId, LayerProperties, FrameTreeId};
|
||||
use msg::compositor_msg::{PaintListener, ScriptListener};
|
||||
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
|
||||
use msg::constellation_msg::{Key, KeyState, KeyModifiers};
|
||||
use profile_traits::mem;
|
||||
|
@ -65,11 +63,6 @@ impl CompositorReceiver for Receiver<Msg> {
|
|||
|
||||
/// Implementation of the abstract `ScriptListener` interface.
|
||||
impl ScriptListener for Box<CompositorProxy+'static+Send> {
|
||||
fn set_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
let msg = Msg::ChangeReadyState(pipeline_id, ready_state);
|
||||
self.send(msg);
|
||||
}
|
||||
|
||||
fn scroll_fragment_point(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
|
@ -96,33 +89,6 @@ impl ScriptListener for Box<CompositorProxy+'static+Send> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Information about each layer that the compositor keeps.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LayerProperties {
|
||||
pub pipeline_id: PipelineId,
|
||||
pub epoch: Epoch,
|
||||
pub id: LayerId,
|
||||
pub rect: Rect<f32>,
|
||||
pub background_color: Color,
|
||||
pub scroll_policy: ScrollPolicy,
|
||||
}
|
||||
|
||||
impl LayerProperties {
|
||||
fn new(pipeline_id: PipelineId, epoch: Epoch, metadata: &LayerMetadata) -> LayerProperties {
|
||||
LayerProperties {
|
||||
pipeline_id: pipeline_id,
|
||||
epoch: epoch,
|
||||
id: metadata.id,
|
||||
rect: Rect(Point2D(metadata.position.origin.x as f32,
|
||||
metadata.position.origin.y as f32),
|
||||
Size2D(metadata.position.size.width as f32,
|
||||
metadata.position.size.height as f32)),
|
||||
background_color: metadata.background_color,
|
||||
scroll_policy: metadata.scroll_policy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the abstract `PaintListener` interface.
|
||||
impl PaintListener for Box<CompositorProxy+'static+Send> {
|
||||
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata> {
|
||||
|
@ -141,29 +107,12 @@ impl PaintListener for Box<CompositorProxy+'static+Send> {
|
|||
|
||||
fn initialize_layers_for_pipeline(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
metadata: Vec<LayerMetadata>,
|
||||
properties: Vec<LayerProperties>,
|
||||
epoch: Epoch) {
|
||||
// FIXME(#2004, pcwalton): This assumes that the first layer determines the page size, and
|
||||
// that all other layers are immediate children of it. This is sufficient to handle
|
||||
// `position: fixed` but will not be sufficient to handle `overflow: scroll` or transforms.
|
||||
let mut first = true;
|
||||
for metadata in metadata.iter() {
|
||||
let layer_properties = LayerProperties::new(pipeline_id, epoch, metadata);
|
||||
if first {
|
||||
self.send(Msg::CreateOrUpdateBaseLayer(layer_properties));
|
||||
first = false
|
||||
} else {
|
||||
self.send(Msg::CreateOrUpdateDescendantLayer(layer_properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_msg_discarded(&mut self) {
|
||||
self.send(Msg::PaintMsgDiscarded);
|
||||
}
|
||||
|
||||
fn set_paint_state(&mut self, pipeline_id: PipelineId, paint_state: PaintState) {
|
||||
self.send(Msg::ChangePaintState(pipeline_id, paint_state))
|
||||
self.send(Msg::InitializeLayersForPipeline(pipeline_id, epoch, properties));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,30 +133,21 @@ pub enum Msg {
|
|||
/// The headless compositor returns `None`.
|
||||
GetGraphicsMetadata(Sender<Option<NativeGraphicsMetadata>>),
|
||||
|
||||
/// Tells the compositor to create the root layer for a pipeline if necessary (i.e. if no layer
|
||||
/// with that ID exists).
|
||||
CreateOrUpdateBaseLayer(LayerProperties),
|
||||
/// Tells the compositor to create a descendant layer for a pipeline if necessary (i.e. if no
|
||||
/// layer with that ID exists).
|
||||
CreateOrUpdateDescendantLayer(LayerProperties),
|
||||
/// Tells the compositor to create or update the layers for a pipeline if necessary
|
||||
/// (i.e. if no layer with that ID exists).
|
||||
InitializeLayersForPipeline(PipelineId, Epoch, Vec<LayerProperties>),
|
||||
/// Alerts the compositor that the specified layer's rect has changed.
|
||||
SetLayerRect(PipelineId, LayerId, Rect<f32>),
|
||||
/// Scroll a page in a window
|
||||
ScrollFragmentPoint(PipelineId, LayerId, Point2D<f32>),
|
||||
/// Requests that the compositor assign the painted buffers to the given layers.
|
||||
AssignPaintedBuffers(PipelineId, Epoch, Vec<(LayerId, Box<LayerBufferSet>)>, FrameTreeId),
|
||||
/// Alerts the compositor to the current status of page loading.
|
||||
ChangeReadyState(PipelineId, ReadyState),
|
||||
/// Alerts the compositor to the current status of painting.
|
||||
ChangePaintState(PipelineId, PaintState),
|
||||
/// Alerts the compositor that the current page has changed its title.
|
||||
ChangePageTitle(PipelineId, Option<String>),
|
||||
/// Alerts the compositor that the current page has changed its URL.
|
||||
ChangePageUrl(PipelineId, Url),
|
||||
/// Alerts the compositor that the given pipeline has changed whether it is running animations.
|
||||
ChangeRunningAnimationsState(PipelineId, AnimationState),
|
||||
/// Alerts the compositor that a `PaintMsg` has been discarded.
|
||||
PaintMsgDiscarded,
|
||||
/// Replaces the current frame tree, typically called during main frame navigation.
|
||||
SetFrameTree(SendableFrameTree, Sender<()>, ConstellationChan),
|
||||
/// The load of a page has completed.
|
||||
|
@ -226,6 +166,8 @@ pub enum Msg {
|
|||
PaintTaskExited(PipelineId),
|
||||
/// Alerts the compositor that the viewport has been constrained in some manner
|
||||
ViewportConstrained(PipelineId, ViewportConstraints),
|
||||
/// A reply to the compositor asking if the output image is stable.
|
||||
IsReadyToSaveImageReply(bool),
|
||||
}
|
||||
|
||||
impl Debug for Msg {
|
||||
|
@ -234,17 +176,13 @@ impl Debug for Msg {
|
|||
Msg::Exit(..) => write!(f, "Exit"),
|
||||
Msg::ShutdownComplete(..) => write!(f, "ShutdownComplete"),
|
||||
Msg::GetGraphicsMetadata(..) => write!(f, "GetGraphicsMetadata"),
|
||||
Msg::CreateOrUpdateBaseLayer(..) => write!(f, "CreateOrUpdateBaseLayer"),
|
||||
Msg::CreateOrUpdateDescendantLayer(..) => write!(f, "CreateOrUpdateDescendantLayer"),
|
||||
Msg::InitializeLayersForPipeline(..) => write!(f, "InitializeLayersForPipeline"),
|
||||
Msg::SetLayerRect(..) => write!(f, "SetLayerRect"),
|
||||
Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
|
||||
Msg::AssignPaintedBuffers(..) => write!(f, "AssignPaintedBuffers"),
|
||||
Msg::ChangeReadyState(..) => write!(f, "ChangeReadyState"),
|
||||
Msg::ChangePaintState(..) => write!(f, "ChangePaintState"),
|
||||
Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"),
|
||||
Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
|
||||
Msg::ChangePageUrl(..) => write!(f, "ChangePageUrl"),
|
||||
Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),
|
||||
Msg::SetFrameTree(..) => write!(f, "SetFrameTree"),
|
||||
Msg::LoadComplete => write!(f, "LoadComplete"),
|
||||
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
|
||||
|
@ -254,6 +192,7 @@ impl Debug for Msg {
|
|||
Msg::CreatePng(..) => write!(f, "CreatePng"),
|
||||
Msg::PaintTaskExited(..) => write!(f, "PaintTaskExited"),
|
||||
Msg::ViewportConstrained(..) => write!(f, "ViewportConstrained"),
|
||||
Msg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,12 @@ use compositor_task::Msg as CompositorMsg;
|
|||
use devtools_traits::{DevtoolsControlChan, DevtoolsControlMsg};
|
||||
use geom::point::Point2D;
|
||||
use geom::rect::{Rect, TypedRect};
|
||||
use geom::size::Size2D;
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
|
||||
use layout_traits::{LayoutControlChan, LayoutControlMsg, LayoutTaskFactory};
|
||||
use libc;
|
||||
use msg::compositor_msg::LayerId;
|
||||
use msg::compositor_msg::{Epoch, LayerId};
|
||||
use msg::constellation_msg::AnimationState;
|
||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||
use msg::constellation_msg::{FrameId, PipelineExitType, PipelineId};
|
||||
|
@ -34,7 +35,7 @@ use net_traits::storage_task::{StorageTask, StorageTaskMsg};
|
|||
use profile_traits::mem;
|
||||
use profile_traits::time;
|
||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||
use script_traits::{ScriptControlChan, ScriptState, ScriptTaskFactory};
|
||||
use std::borrow::ToOwned;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Write};
|
||||
|
@ -438,6 +439,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
debug!("constellation got viewport-constrained event message");
|
||||
self.handle_viewport_constrained_msg(pipeline_id, constraints);
|
||||
}
|
||||
ConstellationMsg::IsReadyToSaveImage(pipeline_states) => {
|
||||
let is_ready = self.handle_is_ready_to_save_image(pipeline_states);
|
||||
self.compositor_proxy.send(CompositorMsg::IsReadyToSaveImageReply(is_ready));
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -938,6 +943,87 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
self.compositor_proxy.send(CompositorMsg::ViewportConstrained(pipeline_id, constraints));
|
||||
}
|
||||
|
||||
/// Checks the state of all script and layout pipelines to see if they are idle
|
||||
/// and compares the current layout state to what the compositor has. This is used
|
||||
/// to check if the output image is "stable" and can be written as a screenshot
|
||||
/// for reftests.
|
||||
fn handle_is_ready_to_save_image(&mut self,
|
||||
pipeline_states: HashMap<PipelineId, Epoch>) -> bool {
|
||||
// If there is no root frame yet, the initial page has
|
||||
// not loaded, so there is nothing to save yet.
|
||||
if self.root_frame_id.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are pending changes to the current frame
|
||||
// tree, the image is not stable yet.
|
||||
if self.pending_frames.len() > 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
// are met, then the output image should not change and a reftest
|
||||
// screenshot can safely be written.
|
||||
for frame in self.current_frame_tree_iter(self.root_frame_id) {
|
||||
let pipeline = self.pipeline(frame.current);
|
||||
|
||||
// Synchronously query the script task for this pipeline
|
||||
// to see if it is idle.
|
||||
let ScriptControlChan(ref script_chan) = pipeline.script_chan;
|
||||
let (sender, receiver) = channel();
|
||||
let msg = ConstellationControlMsg::GetCurrentState(sender, frame.current);
|
||||
script_chan.send(msg).unwrap();
|
||||
if receiver.recv().unwrap() == ScriptState::DocumentLoading {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the visible rectangle for this pipeline. If the constellation
|
||||
// hasn't received a rectangle for this pipeline yet, then assume
|
||||
// that the output image isn't stable yet.
|
||||
match pipeline.rect {
|
||||
Some(rect) => {
|
||||
// If the rectangle for this pipeline is zero sized, it will
|
||||
// never be painted. In this case, don't query the layout
|
||||
// task as it won't contribute to the final output image.
|
||||
if rect.size == Size2D::zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the epoch that the compositor has drawn for this pipeline.
|
||||
let compositor_epoch = pipeline_states.get(&frame.current);
|
||||
match compositor_epoch {
|
||||
Some(compositor_epoch) => {
|
||||
// Synchronously query the layout task to see if the current
|
||||
// epoch matches what the compositor has drawn. If they match
|
||||
// (and script is idle) then this pipeline won't change again
|
||||
// and can be considered stable.
|
||||
let (sender, receiver) = channel();
|
||||
let LayoutControlChan(ref layout_chan) = pipeline.layout_chan;
|
||||
layout_chan.send(LayoutControlMsg::GetCurrentEpoch(sender)).unwrap();
|
||||
let layout_task_epoch = receiver.recv().unwrap();
|
||||
if layout_task_epoch != *compositor_epoch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// The compositor doesn't know about this pipeline yet.
|
||||
// Assume it hasn't rendered yet.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All script tasks are idle and layout epochs match compositor, so output image!
|
||||
true
|
||||
}
|
||||
|
||||
// Close a frame (and all children)
|
||||
fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
|
||||
let frame = self.frames.remove(&frame_id).unwrap();
|
||||
|
|
|
@ -92,16 +92,12 @@ impl CompositorEventListener for NullCompositor {
|
|||
// we'll notice and think about whether it needs a response, like
|
||||
// SetFrameTree.
|
||||
|
||||
Msg::CreateOrUpdateBaseLayer(..) |
|
||||
Msg::CreateOrUpdateDescendantLayer(..) |
|
||||
Msg::InitializeLayersForPipeline(..) |
|
||||
Msg::SetLayerRect(..) |
|
||||
Msg::AssignPaintedBuffers(..) |
|
||||
Msg::ChangeReadyState(..) |
|
||||
Msg::ChangePaintState(..) |
|
||||
Msg::ChangeRunningAnimationsState(..) |
|
||||
Msg::ScrollFragmentPoint(..) |
|
||||
Msg::LoadComplete |
|
||||
Msg::PaintMsgDiscarded(..) |
|
||||
Msg::ScrollTimeout(..) |
|
||||
Msg::RecompositeAfterScroll |
|
||||
Msg::ChangePageTitle(..) |
|
||||
|
@ -110,7 +106,8 @@ impl CompositorEventListener for NullCompositor {
|
|||
Msg::SetCursor(..) |
|
||||
Msg::ViewportConstrained(..) => {}
|
||||
Msg::CreatePng(..) |
|
||||
Msg::PaintTaskExited(..) => {}
|
||||
Msg::PaintTaskExited(..) |
|
||||
Msg::IsReadyToSaveImageReply(..) => {}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ use geom::scale_factor::ScaleFactor;
|
|||
use geom::size::TypedSize2D;
|
||||
use layers::geometry::DevicePixel;
|
||||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use msg::compositor_msg::{PaintState, ReadyState};
|
||||
use msg::constellation_msg::{Key, KeyState, KeyModifiers};
|
||||
use script_traits::MouseButton;
|
||||
use url::Url;
|
||||
|
@ -100,10 +99,6 @@ pub trait WindowMethods {
|
|||
/// Presents the window to the screen (perhaps by page flipping).
|
||||
fn present(&self);
|
||||
|
||||
/// Sets the ready state of the current page.
|
||||
fn set_ready_state(&self, ready_state: ReadyState);
|
||||
/// Sets the paint state of the current page.
|
||||
fn set_paint_state(&self, paint_state: PaintState);
|
||||
/// Sets the page title for the current page.
|
||||
fn set_page_title(&self, title: Option<String>);
|
||||
/// Sets the load data for the current page.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue