mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #6031 - glennw:reftest-race-conditions, r=larsberg,jdm
The basic idea is it's safe to output an image for reftest by testing: - That the compositor doesn't have any animations active. - That the compositor is not waiting on any outstanding paint messages to arrive. - That the script tasks are "idle" and therefore won't cause reflow. - This currently means page loaded, onload fired, reftest-wait not active, first reflow triggered. - It could easily be expanded to handle pending timers etc. - That the "epoch" that the layout tasks have last laid out after script went idle, is reflected by the compositor in all visible layers for that pipeline. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/6031) <!-- Reviewable:end -->
This commit is contained in:
commit
5e61ebaa05
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.
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -93,8 +90,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()),
|
||||
};
|
||||
|
||||
|
@ -476,16 +471,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())
|
||||
}
|
||||
|
@ -670,14 +655,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