mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +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::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::CreateOrUpdateDescendantLayer(layer_properties),
|
||||
ShutdownState::NotShuttingDown) => {
|
||||
self.create_or_update_descendant_layer(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) {
|
||||
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,41 +1131,96 @@ 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;
|
||||
}
|
||||
|
||||
if self.get_earliest_pipeline_ready_state() != ReadyState::FinishedLoading {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.has_outstanding_paint_msgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.all_pipelines_in_idle_paint_state() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.frame_tree_id == FrameTreeId(0) {
|
||||
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;
|
||||
}
|
||||
|
||||
for child in layer.children().iter() {
|
||||
if self.does_layer_have_outstanding_paint_messages(child) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
let target = self.composite_target;
|
||||
self.composite_specific_target(target);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -20,8 +20,8 @@ use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsCo
|
|||
use layers::platform::surface::NativeSurface;
|
||||
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
|
||||
use layers;
|
||||
use msg::compositor_msg::{Epoch, FrameTreeId, PaintState, LayerId};
|
||||
use msg::compositor_msg::{LayerMetadata, PaintListener, ScrollPolicy};
|
||||
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId};
|
||||
use msg::compositor_msg::{LayerProperties, PaintListener, ScrollPolicy};
|
||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
|
||||
use msg::constellation_msg::PipelineExitType;
|
||||
|
@ -67,7 +67,7 @@ pub struct PaintRequest {
|
|||
}
|
||||
|
||||
pub enum Msg {
|
||||
PaintInit(Arc<StackingContext>),
|
||||
PaintInit(Epoch, Arc<StackingContext>),
|
||||
Paint(Vec<PaintRequest>, FrameTreeId),
|
||||
UnusedBuffer(Vec<Box<LayerBuffer>>),
|
||||
PaintPermissionGranted,
|
||||
|
@ -112,8 +112,8 @@ pub struct PaintTask<C> {
|
|||
/// Permission to send paint messages to the compositor
|
||||
paint_permission: bool,
|
||||
|
||||
/// A counter for epoch messages
|
||||
epoch: Epoch,
|
||||
/// The current epoch counter is passed by the layout task
|
||||
current_epoch: Option<Epoch>,
|
||||
|
||||
/// A data structure to store unused LayerBuffers
|
||||
buffer_map: BufferMap,
|
||||
|
@ -165,7 +165,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
native_graphics_context: native_graphics_context,
|
||||
root_stacking_context: None,
|
||||
paint_permission: false,
|
||||
epoch: Epoch(0),
|
||||
current_epoch: None,
|
||||
buffer_map: BufferMap::new(10000000),
|
||||
worker_threads: worker_threads,
|
||||
used_buffer_count: 0,
|
||||
|
@ -197,7 +197,8 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
let mut waiting_for_compositor_buffers_to_exit = false;
|
||||
loop {
|
||||
match self.port.recv().unwrap() {
|
||||
Msg::PaintInit(stacking_context) => {
|
||||
Msg::PaintInit(epoch, stacking_context) => {
|
||||
self.current_epoch = Some(epoch);
|
||||
self.root_stacking_context = Some(stacking_context.clone());
|
||||
|
||||
if !self.paint_permission {
|
||||
|
@ -207,7 +208,6 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
continue;
|
||||
}
|
||||
|
||||
self.epoch.next();
|
||||
self.initialize_layers();
|
||||
}
|
||||
Msg::Paint(requests, frame_tree_id) => {
|
||||
|
@ -215,30 +215,26 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
debug!("PaintTask: paint ready msg");
|
||||
let ConstellationChan(ref mut c) = self.constellation_chan;
|
||||
c.send(ConstellationMsg::PainterReady(self.id)).unwrap();
|
||||
self.compositor.paint_msg_discarded();
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut replies = Vec::new();
|
||||
self.compositor.set_paint_state(self.id, PaintState::Painting);
|
||||
for PaintRequest { buffer_requests, scale, layer_id, epoch }
|
||||
in requests.into_iter() {
|
||||
if self.epoch == epoch {
|
||||
if self.current_epoch == Some(epoch) {
|
||||
self.paint(&mut replies, buffer_requests, scale, layer_id);
|
||||
} else {
|
||||
debug!("painter epoch mismatch: {:?} != {:?}", self.epoch, epoch);
|
||||
debug!("painter epoch mismatch: {:?} != {:?}", self.current_epoch, epoch);
|
||||
}
|
||||
}
|
||||
|
||||
self.compositor.set_paint_state(self.id, PaintState::Idle);
|
||||
|
||||
for reply in replies.iter() {
|
||||
let &(_, ref buffer_set) = reply;
|
||||
self.used_buffer_count += (*buffer_set).buffers.len();
|
||||
}
|
||||
|
||||
debug!("PaintTask: returning surfaces");
|
||||
self.compositor.assign_painted_buffers(self.id, self.epoch, replies, frame_tree_id);
|
||||
self.compositor.assign_painted_buffers(self.id, self.current_epoch.unwrap(), replies, frame_tree_id);
|
||||
}
|
||||
Msg::UnusedBuffer(unused_buffers) => {
|
||||
debug!("PaintTask: Received {} unused buffers", unused_buffers.len());
|
||||
|
@ -258,7 +254,6 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
self.paint_permission = true;
|
||||
|
||||
if self.root_stacking_context.is_some() {
|
||||
self.epoch.next();
|
||||
self.initialize_layers();
|
||||
}
|
||||
}
|
||||
|
@ -379,11 +374,11 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
Some(ref root_stacking_context) => root_stacking_context,
|
||||
};
|
||||
|
||||
let mut metadata = Vec::new();
|
||||
build(&mut metadata, &**root_stacking_context, &ZERO_POINT);
|
||||
self.compositor.initialize_layers_for_pipeline(self.id, metadata, self.epoch);
|
||||
let mut properties = Vec::new();
|
||||
build(&mut properties, &**root_stacking_context, &ZERO_POINT);
|
||||
self.compositor.initialize_layers_for_pipeline(self.id, properties, self.current_epoch.unwrap());
|
||||
|
||||
fn build(metadata: &mut Vec<LayerMetadata>,
|
||||
fn build(properties: &mut Vec<LayerProperties>,
|
||||
stacking_context: &StackingContext,
|
||||
page_position: &Point2D<Au>) {
|
||||
let page_position = stacking_context.bounds.origin + *page_position;
|
||||
|
@ -392,20 +387,20 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
// the compositor is concerned.
|
||||
let overflow_relative_page_position = page_position + stacking_context.overflow.origin;
|
||||
let layer_position =
|
||||
Rect(Point2D(overflow_relative_page_position.x.to_nearest_px() as i32,
|
||||
overflow_relative_page_position.y.to_nearest_px() as i32),
|
||||
Size2D(stacking_context.overflow.size.width.to_nearest_px() as i32,
|
||||
stacking_context.overflow.size.height.to_nearest_px() as i32));
|
||||
metadata.push(LayerMetadata {
|
||||
Rect(Point2D(overflow_relative_page_position.x.to_nearest_px() as f32,
|
||||
overflow_relative_page_position.y.to_nearest_px() as f32),
|
||||
Size2D(stacking_context.overflow.size.width.to_nearest_px() as f32,
|
||||
stacking_context.overflow.size.height.to_nearest_px() as f32));
|
||||
properties.push(LayerProperties {
|
||||
id: paint_layer.id,
|
||||
position: layer_position,
|
||||
rect: layer_position,
|
||||
background_color: paint_layer.background_color,
|
||||
scroll_policy: paint_layer.scroll_policy,
|
||||
})
|
||||
}
|
||||
|
||||
for kid in stacking_context.display_list.children.iter() {
|
||||
build(metadata, &**kid, &page_position)
|
||||
build(properties, &**kid, &page_position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ use gfx::paint_task::Msg as PaintMsg;
|
|||
use gfx::paint_task::{PaintChan, PaintLayer};
|
||||
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
|
||||
use log;
|
||||
use msg::compositor_msg::ScrollPolicy;
|
||||
use msg::compositor_msg::{Epoch, ScrollPolicy};
|
||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||
use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId};
|
||||
use profile_traits::mem::{self, Report, ReportsChan};
|
||||
|
@ -124,6 +124,9 @@ pub struct LayoutTaskData {
|
|||
/// A channel on which new animations that have been triggered by style recalculation can be
|
||||
/// sent.
|
||||
pub new_animations_sender: Sender<Animation>,
|
||||
|
||||
/// A counter for epoch messages
|
||||
epoch: Epoch,
|
||||
}
|
||||
|
||||
/// Information needed by the layout task.
|
||||
|
@ -329,6 +332,7 @@ impl LayoutTask {
|
|||
running_animations: Vec::new(),
|
||||
new_animations_receiver: new_animations_receiver,
|
||||
new_animations_sender: new_animations_sender,
|
||||
epoch: Epoch(0),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
@ -405,6 +409,10 @@ impl LayoutTask {
|
|||
LayoutControlMsg::TickAnimations => {
|
||||
self.handle_request_helper(Msg::TickAnimations, possibly_locked_rw_data)
|
||||
}
|
||||
LayoutControlMsg::GetCurrentEpoch(sender) => {
|
||||
self.handle_request_helper(Msg::GetCurrentEpoch(sender),
|
||||
possibly_locked_rw_data)
|
||||
}
|
||||
LayoutControlMsg::ExitNow(exit_type) => {
|
||||
self.handle_request_helper(Msg::ExitNow(exit_type),
|
||||
possibly_locked_rw_data)
|
||||
|
@ -509,8 +517,11 @@ impl LayoutTask {
|
|||
Msg::CollectReports(reports_chan) => {
|
||||
self.collect_reports(reports_chan, possibly_locked_rw_data);
|
||||
},
|
||||
Msg::GetCurrentEpoch(sender) => {
|
||||
let rw_data = self.lock_rw_data(possibly_locked_rw_data);
|
||||
sender.send(rw_data.epoch).unwrap();
|
||||
},
|
||||
Msg::PrepareToExit(response_chan) => {
|
||||
debug!("layout: PrepareToExitMsg received");
|
||||
self.prepare_to_exit(response_chan, possibly_locked_rw_data);
|
||||
return false
|
||||
},
|
||||
|
@ -825,7 +836,8 @@ impl LayoutTask {
|
|||
|
||||
debug!("Layout done!");
|
||||
|
||||
self.paint_chan.send(PaintMsg::PaintInit(stacking_context));
|
||||
rw_data.epoch.next();
|
||||
self.paint_chan.send(PaintMsg::PaintInit(rw_data.epoch, stacking_context));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ extern crate util;
|
|||
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use gfx::paint_task::PaintChan;
|
||||
use msg::compositor_msg::Epoch;
|
||||
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType};
|
||||
use profile_traits::mem;
|
||||
use profile_traits::time;
|
||||
|
@ -28,6 +29,7 @@ use std::sync::mpsc::{Sender, Receiver};
|
|||
/// Messages sent to the layout task from the constellation
|
||||
pub enum LayoutControlMsg {
|
||||
ExitNow(PipelineExitType),
|
||||
GetCurrentEpoch(Sender<Epoch>),
|
||||
TickAnimations,
|
||||
}
|
||||
|
||||
|
|
|
@ -13,27 +13,8 @@ use std::fmt;
|
|||
|
||||
use constellation_msg::PipelineId;
|
||||
|
||||
/// The status of the painter.
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum PaintState {
|
||||
Idle,
|
||||
Painting,
|
||||
}
|
||||
|
||||
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Copy)]
|
||||
pub enum ReadyState {
|
||||
/// Informs the compositor that nothing has been done yet. Used for setting status
|
||||
Blank,
|
||||
/// Informs the compositor that a page is loading. Used for setting status
|
||||
Loading,
|
||||
/// Informs the compositor that a page is performing layout. Used for setting status
|
||||
PerformingLayout,
|
||||
/// Informs the compositor that a page is finished loading. Used for setting status
|
||||
FinishedLoading,
|
||||
}
|
||||
|
||||
/// A newtype struct for denoting the age of messages; prevents race conditions.
|
||||
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||
#[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord)]
|
||||
pub struct Epoch(pub u32);
|
||||
|
||||
impl Epoch {
|
||||
|
@ -82,11 +63,11 @@ pub enum ScrollPolicy {
|
|||
/// All layer-specific information that the painting task sends to the compositor other than the
|
||||
/// buffer contents of the layer itself.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LayerMetadata {
|
||||
pub struct LayerProperties {
|
||||
/// An opaque ID. This is usually the address of the flow and index of the box within it.
|
||||
pub id: LayerId,
|
||||
/// The position and size of the layer in pixels.
|
||||
pub position: Rect<i32>,
|
||||
pub rect: Rect<f32>,
|
||||
/// The background color of the layer.
|
||||
pub background_color: Color,
|
||||
/// The scrolling policy of this layer.
|
||||
|
@ -102,7 +83,7 @@ pub trait PaintListener {
|
|||
/// creating and/or destroying paint layers as necessary.
|
||||
fn initialize_layers_for_pipeline(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
metadata: Vec<LayerMetadata>,
|
||||
properties: Vec<LayerProperties>,
|
||||
epoch: Epoch);
|
||||
|
||||
/// Sends new buffers for the given layers to the compositor.
|
||||
|
@ -112,14 +93,11 @@ pub trait PaintListener {
|
|||
replies: Vec<(LayerId, Box<LayerBufferSet>)>,
|
||||
frame_tree_id: FrameTreeId);
|
||||
|
||||
fn paint_msg_discarded(&mut self);
|
||||
fn set_paint_state(&mut self, PipelineId, PaintState);
|
||||
}
|
||||
|
||||
/// The interface used by the script task to tell the compositor to update its ready state,
|
||||
/// which is used in displaying the appropriate message in the window's title.
|
||||
pub trait ScriptListener {
|
||||
fn set_ready_state(&mut self, PipelineId, ReadyState);
|
||||
fn scroll_fragment_point(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! The high-level interface from script to constellation. Using this abstract interface helps
|
||||
//! reduce coupling between these two components.
|
||||
|
||||
use compositor_msg::Epoch;
|
||||
use geom::rect::Rect;
|
||||
use geom::size::TypedSize2D;
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
|
@ -14,6 +15,7 @@ use layers::geometry::DevicePixel;
|
|||
use png;
|
||||
use util::cursor::Cursor;
|
||||
use util::geometry::{PagePx, ViewportPx};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use style::viewport::ViewportConstraints;
|
||||
use webdriver_traits::WebDriverScriptCommand;
|
||||
|
@ -239,7 +241,9 @@ pub enum Msg {
|
|||
/// Notifies the constellation that the viewport has been constrained in some manner
|
||||
ViewportConstrained(PipelineId, ViewportConstraints),
|
||||
/// Create a PNG of the window contents
|
||||
CompositePng(Sender<Option<png::Image>>)
|
||||
CompositePng(Sender<Option<png::Image>>),
|
||||
/// Query the constellation to see if the current compositor output is stable
|
||||
IsReadyToSaveImage(HashMap<PipelineId, Epoch>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
|
|
|
@ -12,6 +12,7 @@ use geom::point::Point2D;
|
|||
use geom::rect::Rect;
|
||||
use libc::uintptr_t;
|
||||
use msg::constellation_msg::{PipelineExitType, WindowSizeData};
|
||||
use msg::compositor_msg::Epoch;
|
||||
use net_traits::PendingAsyncLoad;
|
||||
use profile_traits::mem::{Reporter, ReportsChan};
|
||||
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
|
||||
|
@ -63,6 +64,9 @@ pub enum Msg {
|
|||
/// Requests that the layout task immediately shut down. There must be no more nodes left after
|
||||
/// this, or layout will crash.
|
||||
ExitNow(PipelineExitType),
|
||||
|
||||
/// Get the last epoch counter for this layout task.
|
||||
GetCurrentEpoch(Sender<Epoch>)
|
||||
}
|
||||
|
||||
/// Synchronous messages that script can send to layout.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -60,6 +60,13 @@ pub trait StylesheetLoadResponder {
|
|||
fn respond(self: Box<Self>);
|
||||
}
|
||||
|
||||
/// Used to determine if a script has any pending asynchronous activity.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ScriptState {
|
||||
DocumentLoaded,
|
||||
DocumentLoading,
|
||||
}
|
||||
|
||||
/// Messages sent from the constellation to the script task
|
||||
pub enum ConstellationControlMsg {
|
||||
/// Gives a channel and ID to a layout task, as well as the ID of that layout's parent
|
||||
|
@ -96,6 +103,8 @@ pub enum ConstellationControlMsg {
|
|||
TickAllAnimations(PipelineId),
|
||||
/// Notifies script that a stylesheet has finished loading.
|
||||
StylesheetLoadComplete(PipelineId, Url, Box<StylesheetLoadResponder+Send>),
|
||||
/// Get the current state of the script task for a given pipeline.
|
||||
GetCurrentState(Sender<ScriptState>, PipelineId),
|
||||
}
|
||||
|
||||
/// The mouse button involved in the event.
|
||||
|
|
|
@ -22,7 +22,6 @@ use layers::geometry::DevicePixel;
|
|||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use libc::{c_char, c_void};
|
||||
use msg::constellation_msg::{Key, KeyModifiers};
|
||||
use msg::compositor_msg::{ReadyState, PaintState};
|
||||
use std::ptr;
|
||||
use std_url::Url;
|
||||
use util::cursor::Cursor;
|
||||
|
@ -212,26 +211,6 @@ impl WindowMethods for Window {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_ready_state(&self, ready_state: ReadyState) {
|
||||
let browser = self.cef_browser.borrow();
|
||||
let browser = match *browser {
|
||||
None => return,
|
||||
Some(ref browser) => browser,
|
||||
};
|
||||
let is_loading = match ready_state {
|
||||
ReadyState::Blank | ReadyState::FinishedLoading => 0,
|
||||
ReadyState::Loading | ReadyState::PerformingLayout => 1,
|
||||
};
|
||||
browser.get_host()
|
||||
.get_client()
|
||||
.get_load_handler()
|
||||
.on_loading_state_change(browser.clone(), is_loading, 1, 1);
|
||||
}
|
||||
|
||||
fn set_paint_state(&self, _: PaintState) {
|
||||
// TODO(pcwalton)
|
||||
}
|
||||
|
||||
fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
|
||||
let browser = self.cef_browser.borrow();
|
||||
match *browser {
|
||||
|
|
|
@ -14,7 +14,6 @@ use layers::geometry::DevicePixel;
|
|||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use msg::constellation_msg;
|
||||
use msg::constellation_msg::Key;
|
||||
use msg::compositor_msg::{PaintState, ReadyState};
|
||||
use NestedEventLoopListener;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
|
@ -64,8 +63,6 @@ pub struct Window {
|
|||
event_queue: RefCell<Vec<WindowEvent>>,
|
||||
|
||||
mouse_pos: Cell<Point2D<i32>>,
|
||||
ready_state: Cell<ReadyState>,
|
||||
paint_state: Cell<PaintState>,
|
||||
key_modifiers: Cell<KeyModifiers>,
|
||||
}
|
||||
|
||||
|
@ -92,8 +89,6 @@ impl Window {
|
|||
mouse_down_point: Cell::new(Point2D(0, 0)),
|
||||
|
||||
mouse_pos: Cell::new(Point2D(0, 0)),
|
||||
ready_state: Cell::new(ReadyState::Blank),
|
||||
paint_state: Cell::new(PaintState::Idle),
|
||||
key_modifiers: Cell::new(KeyModifiers::empty()),
|
||||
};
|
||||
|
||||
|
@ -475,16 +470,6 @@ impl WindowMethods for Window {
|
|||
box receiver as Box<CompositorReceiver>)
|
||||
}
|
||||
|
||||
/// Sets the ready state.
|
||||
fn set_ready_state(&self, ready_state: ReadyState) {
|
||||
self.ready_state.set(ready_state);
|
||||
}
|
||||
|
||||
/// Sets the paint state.
|
||||
fn set_paint_state(&self, paint_state: PaintState) {
|
||||
self.paint_state.set(paint_state);
|
||||
}
|
||||
|
||||
fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
|
||||
ScaleFactor::new(self.window.hidpi_factor())
|
||||
}
|
||||
|
@ -669,14 +654,6 @@ impl WindowMethods for Window {
|
|||
box receiver as Box<CompositorReceiver>)
|
||||
}
|
||||
|
||||
/// Sets the ready state.
|
||||
fn set_ready_state(&self, _: ReadyState) {
|
||||
}
|
||||
|
||||
/// Sets the paint state.
|
||||
fn set_paint_state(&self, _: PaintState) {
|
||||
}
|
||||
|
||||
fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
|
||||
ScaleFactor::new(1.0)
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ use geom::size::TypedSize2D;
|
|||
use layers::geometry::DevicePixel;
|
||||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use libc::c_int;
|
||||
use msg::compositor_msg::{ReadyState, PaintState};
|
||||
use msg::constellation_msg::{Key, KeyModifiers};
|
||||
use std::cell::Cell;
|
||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use std::rc::Rc;
|
||||
use std::mem::transmute;
|
||||
|
@ -622,9 +620,6 @@ pub struct Window {
|
|||
dpy: EGLDisplay,
|
||||
ctx: EGLContext,
|
||||
surf: EGLSurface,
|
||||
|
||||
ready_state: Cell<ReadyState>,
|
||||
paint_state: Cell<PaintState>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
@ -750,9 +745,6 @@ impl Window {
|
|||
dpy: dpy,
|
||||
ctx: ctx,
|
||||
surf: eglwindow,
|
||||
|
||||
ready_state: Cell::new(ReadyState::Blank),
|
||||
paint_state: Cell::new(PaintState::Idle),
|
||||
};
|
||||
|
||||
Rc::new(window)
|
||||
|
@ -787,16 +779,6 @@ impl WindowMethods for Window {
|
|||
let _ = egl::SwapBuffers(self.dpy, self.surf);
|
||||
}
|
||||
|
||||
/// Sets the ready state.
|
||||
fn set_ready_state(&self, ready_state: ReadyState) {
|
||||
self.ready_state.set(ready_state);
|
||||
}
|
||||
|
||||
/// Sets the paint state.
|
||||
fn set_paint_state(&self, paint_state: PaintState) {
|
||||
self.paint_state.set(paint_state);
|
||||
}
|
||||
|
||||
fn set_page_title(&self, _: Option<String>) {
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue