layout: Make the compositor rather than layout determine the position of

each iframe.

The old code that attempted to do this during layout wasn't able to work
for multiple reasons: it couldn't know where the iframe was going to be
on the page (because of nested iframes), and at the time it was building
the display list for a fragment it couldn't know where that fragment was
going to be in page coordinates.

This patch rewrites that code so that both the sizes and positions of
iframes are determined by the compositor. Layout layerizes all iframes
and marks the iframe layers with the appropriate pipeline and subpage
IDs so that the compositor can place them correctly. This approach is
similar in spirit to Gecko's `RefLayer` infrastructure. The logic that
determines when it is time to take the screenshot for reftests has been
significantly revamped to deal with this change in delegation of
responsibility.

Additionally, this code removes the infrastructure that sends layout
data back to the layout task to be destroyed, since it is now all
thread-safe and can be destroyed on the script task.

The failing tests now fail because of a pre-existing bug related to
intrinsic heights and borders on inline replaced elements. They happened
to pass before because we never rendered the iframes at all, which meant
they never had a chance to draw the red border the tests expect to not
render!

Closes #7377.
This commit is contained in:
Patrick Walton 2015-08-27 16:29:57 -07:00
parent ed0d70e234
commit c72d0c2ed0
20 changed files with 602 additions and 389 deletions

View file

@ -2,7 +2,7 @@
* 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_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag};
use compositor_layer::{CompositorData, CompositorLayer, RcCompositorLayer, WantsScrollEventsFlag};
use compositor_task::{CompositorEventListener, CompositorProxy};
use compositor_task::{CompositorReceiver, InitialCompositorState, Msg};
use constellation::SendableFrameTree;
@ -28,17 +28,16 @@ use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerKind};
use msg::compositor_msg::{LayerProperties, ScrollPolicy};
use msg::constellation_msg::AnimationState;
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, NavigationDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData};
use msg::constellation_msg::{PipelineId, WindowSizeData};
use msg::constellation_msg::{ConstellationChan, Key, KeyModifiers, KeyState, LoadData};
use msg::constellation_msg::{NavigationDirection, PipelineId, SubpageId, WindowSizeData};
use pipeline::CompositionPipeline;
use png;
use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
use scrolling::ScrollingTimerProxy;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{HashMap, HashSet};
use std::mem as std_mem;
use std::rc::Rc;
use std::slice::bytes::copy_memory;
@ -49,8 +48,7 @@ use time::{precise_time_ns, precise_time_s};
use url::Url;
use util::geometry::{Au, PagePx, ScreenPx, ViewportPx};
use util::opts;
use windowing;
use windowing::{MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg};
use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg};
const BUFFER_MAP_SIZE: usize = 10000000;
@ -164,6 +162,14 @@ pub struct IOCompositor<Window: WindowMethods> {
/// A data structure to cache unused NativeSurfaces.
surface_map: SurfaceMap,
/// Information about subpage layers that are pending. The keys in this map are the
/// pipeline/subpage IDs of the parent.
pending_subpage_layers: HashMap<(PipelineId, SubpageId), PendingSubpageLayerInfo>,
/// Pipeline IDs of subpages that the compositor has seen in a layer tree but which have not
/// yet been painted.
pending_subpages: HashSet<PipelineId>,
}
pub struct ScrollEvent {
@ -308,6 +314,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
has_seen_quit_event: false,
ready_to_save_state: ReadyState::Unknown,
surface_map: SurfaceMap::new(BUFFER_MAP_SIZE),
pending_subpage_layers: HashMap::new(),
pending_subpages: HashSet::new(),
}
}
@ -369,6 +377,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
(Msg::InitializeLayersForPipeline(pipeline_id, epoch, properties),
ShutdownState::NotShuttingDown) => {
self.pipeline_details(pipeline_id).current_epoch = epoch;
self.collect_old_layers(pipeline_id, &properties);
for (index, layer_properties) in properties.iter().enumerate() {
if index == 0 {
@ -377,6 +386,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.create_or_update_descendant_layer(pipeline_id, *layer_properties);
}
}
self.send_buffer_requests_for_all_layers();
}
@ -385,13 +395,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
chan.send(Some(self.native_display.clone())).unwrap();
}
(Msg::SetLayerRect(pipeline_id, layer_id, rect),
ShutdownState::NotShuttingDown) => {
self.set_layer_rect(pipeline_id, layer_id, &rect);
}
(Msg::AssignPaintedBuffers(pipeline_id, epoch, replies, frame_tree_id),
ShutdownState::NotShuttingDown) => {
self.pending_subpages.remove(&pipeline_id);
for (layer_id, new_layer_buffer_set) in replies {
self.assign_painted_buffers(pipeline_id,
layer_id,
@ -509,6 +516,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.window.head_parsed();
}
(Msg::CreateLayerForSubpage(parent_pipeline_id,
parent_subpage_id,
subpage_pipeline_id),
ShutdownState::NotShuttingDown) => {
self.create_layer_for_subpage(parent_pipeline_id,
parent_subpage_id,
subpage_pipeline_id);
}
(Msg::CollectMemoryReports(reports_chan), ShutdownState::NotShuttingDown) => {
let mut reports = vec![];
let name = "compositor-task";
@ -527,6 +543,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
reports_chan.send(reports);
}
(Msg::PipelineExited(pipeline_id), _) => {
self.pending_subpages.remove(&pipeline_id);
// FIXME(pcwalton): This is a total hack. But it'll get complicated to do this
// properly, since we need to get rid of the pending subpage layers if either the
// parent or the child layer goes away.
self.pending_subpage_layers.clear();
}
// 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.
@ -573,16 +598,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
return self.pipeline_details.get_mut(&pipeline_id).unwrap();
}
pub fn pipeline<'a>(&'a self, pipeline_id: PipelineId) -> &'a CompositionPipeline {
pub fn pipeline<'a>(&'a self, pipeline_id: PipelineId) -> Option<&'a CompositionPipeline> {
match self.pipeline_details.get(&pipeline_id) {
Some(ref details) => {
match details.pipeline {
Some(ref pipeline) => pipeline,
None => panic!("Compositor layer has an unitialized pipeline ({:?}).",
pipeline_id),
}
}
Some(ref details) => details.pipeline.as_ref(),
None => panic!("Compositor layer has an unknown pipeline ({:?}).", pipeline_id),
}
}
@ -606,6 +624,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
new_constellation_chan: ConstellationChan) {
response_chan.send(()).unwrap();
// There are now no more pending iframes.
self.pending_subpages.clear();
self.root_pipeline = Some(frame_tree.pipeline.clone());
// If we have an old root layer, release all old tiles before replacing it.
@ -614,9 +635,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
old_root_layer.clear_all_tiles(self)
}
self.scene.root = Some(self.create_frame_tree_root_layers(frame_tree, None));
self.scene.root = Some(self.create_root_layer_for_pipeline_and_size(&frame_tree.pipeline,
None));
self.scene.set_root_layer_size(self.window_size.as_f32());
self.create_pipeline_details_for_frame_tree(&frame_tree);
// Initialize the new constellation channel by sending it the root window size.
self.constellation_chan = new_constellation_chan;
self.send_window_size();
@ -625,9 +649,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.composite_if_necessary(CompositingReason::NewFrameTree);
}
fn create_root_layer_for_pipeline_and_rect(&mut self,
fn create_root_layer_for_pipeline_and_size(&mut self,
pipeline: &CompositionPipeline,
frame_rect: Option<TypedRect<PagePx, f32>>)
frame_size: Option<TypedSize2D<PagePx, f32>>)
-> Rc<Layer<CompositorData>> {
let layer_properties = LayerProperties {
id: LayerId::null(),
@ -637,6 +661,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
scroll_policy: ScrollPolicy::Scrollable,
transform: Matrix4::identity(),
perspective: Matrix4::identity(),
subpage_layer_info: None,
establishes_3d_context: true,
scrolls_overflow_area: false,
};
@ -651,24 +676,20 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// All root layers mask to bounds.
*root_layer.masks_to_bounds.borrow_mut() = true;
if let Some(ref frame_rect) = frame_rect {
let frame_rect = frame_rect.to_untyped();
*root_layer.bounds.borrow_mut() = Rect::from_untyped(&frame_rect);
if let Some(ref frame_size) = frame_size {
let frame_size = frame_size.to_untyped();
root_layer.bounds.borrow_mut().size = Size2D::from_untyped(&frame_size);
}
return root_layer;
}
fn create_frame_tree_root_layers(&mut self,
frame_tree: &SendableFrameTree,
frame_rect: Option<TypedRect<PagePx, f32>>)
-> Rc<Layer<CompositorData>> {
let root_layer = self.create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline,
frame_rect);
fn create_pipeline_details_for_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
self.pipeline_details(frame_tree.pipeline.id).pipeline = Some(frame_tree.pipeline.clone());
for kid in &frame_tree.children {
root_layer.add_child(self.create_frame_tree_root_layers(kid, kid.rect));
self.create_pipeline_details_for_frame_tree(kid);
}
return root_layer;
}
fn find_pipeline_root_layer(&self, pipeline_id: PipelineId)
@ -687,7 +708,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {
None => return,
};
root_layer.collect_old_layers(self, pipeline_id, new_layers);
let mut pipelines_removed = Vec::new();
root_layer.collect_old_layers(self, pipeline_id, new_layers, &mut pipelines_removed);
for pipeline_removed in pipelines_removed.into_iter() {
self.pending_subpage_layers.remove(&(pipeline_removed.parent_pipeline_id,
pipeline_removed.parent_subpage_id));
self.pending_subpages.remove(&pipeline_removed.child_pipeline_id);
}
}
fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) {
@ -702,7 +730,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.pipeline_details.remove(&pipeline_id);
}
fn update_layer_if_exists(&mut self, pipeline_id: PipelineId, properties: LayerProperties) -> bool {
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);
@ -748,19 +777,23 @@ impl<Window: WindowMethods> IOCompositor<Window> {
layer_properties.id);
}
fn create_or_update_descendant_layer(&mut self, pipeline_id: PipelineId, layer_properties: LayerProperties) {
fn create_or_update_descendant_layer(&mut self,
pipeline_id: PipelineId,
layer_properties: LayerProperties) {
debug_assert!(layer_properties.parent_id.is_some());
if !self.update_layer_if_exists(pipeline_id, layer_properties) {
self.create_descendant_layer(pipeline_id, layer_properties);
}
self.update_subpage_size_if_necessary(&layer_properties);
self.scroll_layer_to_fragment_point_if_necessary(pipeline_id,
layer_properties.id);
}
fn create_descendant_layer(&self, pipeline_id: PipelineId, layer_properties: LayerProperties) {
fn create_descendant_layer(&mut self,
pipeline_id: PipelineId,
layer_properties: LayerProperties) {
let parent_id = layer_properties.parent_id.unwrap();
if let Some(parent_layer) = self.find_layer_with_pipeline_and_layer_id(pipeline_id,
parent_id) {
let wants_scroll_events = if layer_properties.scrolls_overflow_area {
@ -778,7 +811,69 @@ impl<Window: WindowMethods> IOCompositor<Window> {
*new_layer.masks_to_bounds.borrow_mut() = true
}
parent_layer.add_child(new_layer);
// If this layer contains a subpage, then create the root layer for that subpage now.
if let Some(ref subpage_layer_info) = layer_properties.subpage_layer_info {
let subpage_layer_properties = LayerProperties {
id: LayerId::null(),
parent_id: None,
rect: Rect::new(Point2D::new(subpage_layer_info.origin.x.to_f32_px(),
subpage_layer_info.origin.y.to_f32_px()),
layer_properties.rect.size),
background_color: layer_properties.background_color,
scroll_policy: ScrollPolicy::Scrollable,
transform: Matrix4::identity(),
perspective: Matrix4::identity(),
subpage_layer_info: layer_properties.subpage_layer_info,
establishes_3d_context: true,
scrolls_overflow_area: true,
};
// We need to map from the (pipeline ID, subpage ID) pair to the pipeline ID of
// the subpage itself. The constellation is the source of truth for that
// information, so go ask it. In the meantime, store the information in the list of
// pending subpage layers.
self.pending_subpage_layers.insert((pipeline_id, subpage_layer_info.subpage_id),
PendingSubpageLayerInfo {
subpage_layer_properties: subpage_layer_properties,
container_layer_id: layer_properties.id,
});
self.constellation_chan.0.send(ConstellationMsg::PrepareForSubpageLayerCreation(
pipeline_id,
subpage_layer_info.subpage_id)).unwrap();
}
parent_layer.add_child(new_layer.clone());
}
}
fn create_layer_for_subpage(&mut self,
parent_pipeline_id: PipelineId,
parent_subpage_id: SubpageId,
subpage_pipeline_id: Option<PipelineId>) {
let subpage_pipeline_id = match subpage_pipeline_id {
Some(subpage_pipeline_id) => subpage_pipeline_id,
None => return,
};
if let Some(PendingSubpageLayerInfo {
subpage_layer_properties,
container_layer_id
}) = self.pending_subpage_layers.remove(&(parent_pipeline_id, parent_subpage_id)) {
if let Some(container_layer) =
self.find_layer_with_pipeline_and_layer_id(parent_pipeline_id,
container_layer_id) {
let wants_scroll_events = if subpage_layer_properties.scrolls_overflow_area {
WantsScrollEventsFlag::WantsScrollEvents
} else {
WantsScrollEventsFlag::DoesntWantScrollEvents
};
let subpage_layer = CompositorData::new_layer(subpage_pipeline_id,
subpage_layer_properties,
wants_scroll_events,
container_layer.tile_size);
*subpage_layer.masks_to_bounds.borrow_mut() = true;
container_layer.add_child(subpage_layer);
self.pending_subpages.insert(subpage_pipeline_id);
}
}
}
@ -795,6 +890,20 @@ impl<Window: WindowMethods> IOCompositor<Window> {
})).unwrap()
}
/// Sends the size of the given subpage up to the constellation. This will often trigger a
/// reflow of that subpage.
fn update_subpage_size_if_necessary(&self, layer_properties: &LayerProperties) {
let subpage_layer_info = match layer_properties.subpage_layer_info {
Some(ref subpage_layer_info) => *subpage_layer_info,
None => return,
};
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ConstellationMsg::FrameSize(subpage_layer_info.pipeline_id,
subpage_layer_info.subpage_id,
layer_properties.rect.size)).unwrap();
}
pub fn move_layer(&self,
pipeline_id: PipelineId,
layer_id: LayerId,
@ -835,21 +944,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.composition_request = CompositionRequest::CompositeOnScrollTimeout(timestamp);
}
fn set_layer_rect(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
new_rect: &Rect<f32>) {
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
Some(ref layer) => {
*layer.bounds.borrow_mut() = Rect::from_untyped(new_rect)
}
None => panic!("Compositor received SetLayerRect for nonexistent \
layer: {:?}", pipeline_id),
};
self.send_buffer_requests_for_all_layers();
}
fn assign_painted_buffers(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
@ -862,7 +956,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// loaded and the frame tree changes. This can result in the compositor thinking it
// 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) {
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);
@ -1250,7 +1345,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
for (layer, mut layer_requests) in requests {
let pipeline_id = layer.pipeline_id();
let current_epoch = self.pipeline_details.get(&pipeline_id).unwrap().current_epoch;
let current_epoch = self.pipeline_details(pipeline_id).current_epoch;
layer.extra_data.borrow_mut().requested_epoch = current_epoch;
let vec = match results.entry(pipeline_id) {
Occupied(entry) => {
@ -1291,8 +1386,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if layer.extra_data.borrow().id == LayerId::null() {
let layer_rect = Rect::new(-layer.extra_data.borrow().scroll_offset.to_untyped(),
layer.bounds.borrow().size.to_untyped());
let pipeline = self.pipeline(layer.pipeline_id());
pipeline.script_chan.send(ConstellationControlMsg::Viewport(pipeline.id.clone(), layer_rect)).unwrap();
if let Some(pipeline) = self.pipeline(layer.pipeline_id()) {
pipeline.script_chan.send(ConstellationControlMsg::Viewport(pipeline.id.clone(),
layer_rect)).unwrap();
}
}
for kid in &*layer.children() {
@ -1333,7 +1430,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
for (pipeline_id, requests) in pipeline_requests {
let msg = ChromeToPaintMsg::Paint(requests, self.frame_tree_id);
let _ = self.pipeline(pipeline_id).chrome_to_paint_chan.send(msg);
if let Some(pipeline) = self.pipeline(pipeline_id) {
pipeline.chrome_to_paint_chan.send(msg).unwrap();
}
}
true
@ -1341,9 +1440,13 @@ impl<Window: WindowMethods> IOCompositor<Window> {
/// 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 {
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;
let current_epoch = match self.pipeline_details.get(&layer_data.pipeline_id) {
None => return false,
Some(ref details) => details.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
@ -1390,6 +1493,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
// Check if there are any pending frames. If so, the image is not stable yet.
if self.pending_subpage_layers.len() > 0 || self.pending_subpages.len() > 0 {
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
@ -1805,3 +1913,16 @@ pub enum CompositingReason {
/// The window has been zoomed.
Zoom,
}
struct PendingSubpageLayerInfo {
subpage_layer_properties: LayerProperties,
container_layer_id: LayerId,
}
#[derive(Copy, Clone)]
pub struct RemovedPipelineInfo {
pub parent_pipeline_id: PipelineId,
pub parent_subpage_id: SubpageId,
pub child_pipeline_id: PipelineId,
}

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use azure::azure_hl;
use compositor::IOCompositor;
use compositor::{IOCompositor, RemovedPipelineInfo};
use euclid::length::Length;
use euclid::point::{Point2D, TypedPoint2D};
use euclid::rect::Rect;
@ -12,7 +12,7 @@ use layers::color::Color;
use layers::geometry::LayerPixel;
use layers::layers::{Layer, LayerBufferSet};
use msg::compositor_msg::{Epoch, LayerId, LayerProperties, ScrollPolicy};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{PipelineId, SubpageId};
use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
use script_traits::ConstellationControlMsg;
use std::rc::Rc;
@ -42,6 +42,9 @@ pub struct CompositorData {
/// 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.
pub scroll_offset: TypedPoint2D<LayerPixel, f32>,
/// The pipeline ID and subpage ID of this layer, if it represents a subpage.
pub subpage_info: Option<(PipelineId, SubpageId)>,
}
impl CompositorData {
@ -58,6 +61,9 @@ impl CompositorData {
requested_epoch: Epoch(0),
painted_epoch: Epoch(0),
scroll_offset: Point2D::typed(0., 0.),
subpage_info: layer_properties.subpage_layer_info.map(|subpage_layer_info| {
(subpage_layer_info.pipeline_id, subpage_layer_info.subpage_id)
}),
};
Rc::new(Layer::new(Rect::from_untyped(&layer_properties.rect),
@ -97,14 +103,6 @@ pub trait CompositorLayer {
pipeline_id: PipelineId)
where Window: WindowMethods;
/// Traverses the existing layer hierarchy and removes any layers that
/// currently exist but which are no longer required.
fn collect_old_layers<Window>(&self,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId,
new_layers: &[LayerProperties])
where Window: WindowMethods;
/// Destroys all tiles of all layers, including children, *without* sending them back to the
/// painter. You must call this only when the paint task is destined to be going down;
/// otherwise, you will leak tiles.
@ -151,6 +149,17 @@ pub trait CompositorLayer {
fn pipeline_id(&self) -> PipelineId;
}
pub trait RcCompositorLayer {
/// Traverses the existing layer hierarchy and removes any layers that
/// currently exist but which are no longer required.
fn collect_old_layers<Window>(&self,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId,
new_layers: &[LayerProperties],
pipelines_removed: &mut Vec<RemovedPipelineInfo>)
where Window: WindowMethods;
}
#[derive(Copy, PartialEq, Clone, Debug)]
pub enum WantsScrollEventsFlag {
WantsScrollEvents,
@ -279,43 +288,6 @@ impl CompositorLayer for Layer<CompositorData> {
}
}
fn collect_old_layers<Window>(&self,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId,
new_layers: &[LayerProperties])
where Window: WindowMethods {
// Traverse children first so that layers are removed
// bottom up - allowing each layer being removed to properly
// clean up any tiles it owns.
for kid in &*self.children() {
kid.collect_old_layers(compositor, pipeline_id, new_layers);
}
// Retain child layers that also exist in the new layer list.
self.children().retain(|child| {
let extra_data = child.extra_data.borrow();
// Never remove root layers or layers from other pipelines.
if pipeline_id != extra_data.pipeline_id ||
extra_data.id == LayerId::null() {
true
} else {
// Keep this layer if it exists in the new layer list.
let keep_layer = new_layers.iter().any(|properties| {
properties.id == extra_data.id
});
// When removing a layer, clear any tiles and surfaces
// associated with the layer.
if !keep_layer {
child.clear_all_tiles(compositor);
}
keep_layer
}
});
}
/// Destroys all tiles of all layers, including children, *without* sending them back to the
/// painter. You must call this only when the paint task is destined to be going down;
/// otherwise, you will leak tiles.
@ -403,8 +375,11 @@ impl CompositorLayer for Layer<CompositorData> {
MouseUpEvent(button, event_point),
};
let pipeline = compositor.pipeline(self.pipeline_id());
let _ = pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message));
if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) {
pipeline.script_chan
.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message))
.unwrap();
}
}
fn send_mouse_move_event<Window>(&self,
@ -412,8 +387,11 @@ impl CompositorLayer for Layer<CompositorData> {
cursor: TypedPoint2D<LayerPixel, f32>)
where Window: WindowMethods {
let message = MouseMoveEvent(cursor.to_untyped());
let pipeline = compositor.pipeline(self.pipeline_id());
let _ = pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message));
if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) {
pipeline.script_chan
.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message))
.unwrap();
}
}
fn scroll_layer_and_all_child_layers(&self, new_offset: TypedPoint2D<LayerPixel, f32>)
@ -443,3 +421,97 @@ impl CompositorLayer for Layer<CompositorData> {
self.extra_data.borrow().pipeline_id
}
}
impl RcCompositorLayer for Rc<Layer<CompositorData>> {
fn collect_old_layers<Window>(&self,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId,
new_layers: &[LayerProperties],
pipelines_removed: &mut Vec<RemovedPipelineInfo>)
where Window: WindowMethods {
fn find_root_layer_for_pipeline(layer: &Rc<Layer<CompositorData>>, pipeline_id: PipelineId)
-> Option<Rc<Layer<CompositorData>>> {
let extra_data = layer.extra_data.borrow();
if extra_data.pipeline_id == pipeline_id {
return Some((*layer).clone())
}
for kid in &*layer.children() {
if let Some(layer) = find_root_layer_for_pipeline(kid, pipeline_id) {
return Some(layer.clone())
}
}
None
}
fn collect_old_layers_for_pipeline<Window>(
layer: &Layer<CompositorData>,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId,
new_layers: &[LayerProperties],
pipelines_removed: &mut Vec<RemovedPipelineInfo>)
where Window: WindowMethods {
// Traverse children first so that layers are removed
// bottom up - allowing each layer being removed to properly
// clean up any tiles it owns.
for kid in &*layer.children() {
collect_old_layers_for_pipeline(kid,
compositor,
pipeline_id,
new_layers,
pipelines_removed);
}
// Retain child layers that also exist in the new layer list.
layer.children().retain(|child| {
let extra_data = child.extra_data.borrow();
if pipeline_id == extra_data.pipeline_id {
// Never remove our own root layer.
if extra_data.id == LayerId::null() {
return true
}
// Keep this layer if it exists in the new layer list.
return new_layers.iter().any(|properties| properties.id == extra_data.id);
}
if let Some(ref subpage_info_for_this_layer) = extra_data.subpage_info {
for layer_properties in new_layers.iter() {
// Keep this layer if a reference to it exists.
if let Some(ref subpage_layer_info) = layer_properties.subpage_layer_info {
if subpage_layer_info.pipeline_id == subpage_info_for_this_layer.0 &&
subpage_layer_info.subpage_id ==
subpage_info_for_this_layer.1 {
return true
}
}
}
pipelines_removed.push(RemovedPipelineInfo {
parent_pipeline_id: subpage_info_for_this_layer.0,
parent_subpage_id: subpage_info_for_this_layer.1,
child_pipeline_id: extra_data.pipeline_id,
});
}
// When removing a layer, clear any tiles and surfaces associated with the layer.
child.clear_all_tiles(compositor);
false
});
}
// First, find the root layer with the given pipeline ID.
let root_layer = match find_root_layer_for_pipeline(self, pipeline_id) {
Some(root_layer) => root_layer,
None => return,
};
// Then collect all old layers underneath that layer.
collect_old_layers_for_pipeline(&root_layer,
compositor,
pipeline_id,
new_layers,
pipelines_removed);
}
}

View file

@ -5,14 +5,14 @@
//! Communication with the compositor task.
use compositor;
use euclid::{Point2D, Rect, Size2D};
use euclid::{Point2D, Size2D};
use headless;
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use layers::layers::{BufferRequest, LayerBufferSet};
use layers::platform::surface::{NativeDisplay, NativeSurface};
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerProperties};
use msg::compositor_msg::{PaintListener, ScriptToCompositorMsg};
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId, SubpageId};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use png;
use profile_traits::mem;
@ -64,7 +64,10 @@ pub fn run_script_listener_thread(compositor_proxy: Box<CompositorProxy + 'stati
while let Ok(msg) = receiver.recv() {
match msg {
ScriptToCompositorMsg::ScrollFragmentPoint(pipeline_id, layer_id, point, _smooth) => {
compositor_proxy.send(Msg::ScrollFragmentPoint(pipeline_id, layer_id, point, _smooth));
compositor_proxy.send(Msg::ScrollFragmentPoint(pipeline_id,
layer_id,
point,
_smooth));
}
ScriptToCompositorMsg::GetClientWindow(send) => {
@ -165,8 +168,6 @@ pub enum Msg {
/// 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>, bool),
/// Requests that the compositor assign the painted buffers to the given layers.
@ -216,6 +217,12 @@ pub enum Msg {
MoveTo(Point2D<i32>),
/// Resize the window to size
ResizeTo(Size2D<u32>),
/// A pipeline was shut down.
PipelineExited(PipelineId),
/// The layer for a subpage should be created. The first two IDs are the IDs of the *parent*
/// pipeline and subpage, respectively, while the last ID is the pipeline ID of the subpage
/// itself (or `None` if it has shut down).
CreateLayerForSubpage(PipelineId, SubpageId, Option<PipelineId>),
}
impl Debug for Msg {
@ -225,7 +232,6 @@ impl Debug for Msg {
Msg::ShutdownComplete(..) => write!(f, "ShutdownComplete"),
Msg::GetNativeDisplay(..) => write!(f, "GetNativeDisplay"),
Msg::InitializeLayersForPipeline(..) => write!(f, "InitializeLayersForPipeline"),
Msg::SetLayerRect(..) => write!(f, "SetLayerRect"),
Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
Msg::AssignPaintedBuffers(..) => write!(f, "AssignPaintedBuffers"),
Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"),
@ -250,6 +256,8 @@ impl Debug for Msg {
Msg::GetClientWindow(..) => write!(f, "GetClientWindow"),
Msg::MoveTo(..) => write!(f, "MoveTo"),
Msg::ResizeTo(..) => write!(f, "ResizeTo"),
Msg::PipelineExited(..) => write!(f, "PipelineExited"),
Msg::CreateLayerForSubpage(..) => write!(f, "CreateLayerForSubpage"),
}
}
}

View file

@ -16,14 +16,12 @@ use clipboard::ClipboardContext;
use compositor_task::CompositorProxy;
use compositor_task::Msg as CompositorMsg;
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg};
use euclid::point::Point2D;
use euclid::rect::{Rect, TypedRect};
use euclid::scale_factor::ScaleFactor;
use euclid::size::Size2D;
use euclid::size::{Size2D, TypedSize2D};
use gfx::font_cache_task::FontCacheTask;
use ipc_channel::ipc::{self, IpcSender};
use layout_traits::{LayoutControlChan, LayoutTaskFactory};
use msg::compositor_msg::{Epoch, LayerId};
use msg::compositor_msg::Epoch;
use msg::constellation_msg::AnimationState;
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::WebDriverCommandMsg;
@ -136,6 +134,10 @@ pub struct Constellation<LTF, STF> {
/// A list of in-process senders to `WebGLPaintTask`s.
webgl_paint_tasks: Vec<Sender<CanvasMsg>>,
/// A list of senders that are waiting to be notified whenever a pipeline or subpage ID comes
/// in.
subpage_id_senders: HashMap<(PipelineId, SubpageId), Vec<IpcSender<PipelineId>>>,
}
/// State needed to construct a constellation.
@ -217,7 +219,7 @@ impl<'a> Iterator for FrameTreeIterator<'a> {
pub struct SendableFrameTree {
pub pipeline: CompositionPipeline,
pub rect: Option<TypedRect<PagePx, f32>>,
pub size: Option<TypedSize2D<PagePx, f32>>,
pub children: Vec<SendableFrameTree>,
}
@ -280,6 +282,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
webdriver: WebDriverData::new(),
canvas_paint_tasks: Vec::new(),
webgl_paint_tasks: Vec::new(),
subpage_id_senders: HashMap::new(),
};
constellation.run();
});
@ -298,7 +301,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
/// Helper function for creating a pipeline
fn new_pipeline(&mut self,
parent_info: Option<(PipelineId, SubpageId)>,
initial_window_rect: Option<TypedRect<PagePx, f32>>,
initial_window_size: Option<TypedSize2D<PagePx, f32>>,
script_channel: Option<Sender<ConstellationControlMsg>>,
load_data: LoadData)
-> PipelineId {
@ -320,7 +323,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
storage_task: self.storage_task.clone(),
time_profiler_chan: self.time_profiler_chan.clone(),
mem_profiler_chan: self.mem_profiler_chan.clone(),
window_rect: initial_window_rect,
window_size: initial_window_size,
script_chan: script_channel,
load_data: load_data,
device_pixel_ratio: self.window_size.device_pixel_ratio,
@ -396,11 +399,11 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
debug!("constellation got init load URL message");
self.handle_init_load(url);
}
// A layout assigned a size and position to a subframe. This needs to be reflected by
// all frame trees in the navigation context containing the subframe.
ConstellationMsg::FrameRect(pipeline_id, subpage_id, rect) => {
debug!("constellation got frame rect message");
self.handle_frame_rect_msg(pipeline_id, subpage_id, Rect::from_untyped(&rect));
// The compositor discovered the size of a subframe. This needs to be reflected by all
// frame trees in the navigation context containing the subframe.
ConstellationMsg::FrameSize(pipeline_id, subpage_id, size) => {
debug!("constellation got frame size message");
self.handle_frame_size_msg(pipeline_id, subpage_id, &Size2D::from_untyped(&size));
}
ConstellationMsg::ScriptLoadedURLInIFrame(url,
source_pipeline_id,
@ -417,7 +420,9 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
old_subpage_id,
sandbox);
}
ConstellationMsg::SetCursor(cursor) => self.handle_set_cursor_msg(cursor),
ConstellationMsg::SetCursor(cursor) => {
self.handle_set_cursor_msg(cursor)
}
ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => {
self.handle_change_running_animations_state(pipeline_id, animation_state)
}
@ -537,6 +542,12 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
debug!("constellation got NodeStatus message");
self.compositor_proxy.send(CompositorMsg::Status(message));
}
ConstellationMsg::PrepareForSubpageLayerCreation(pipeline_id, subpage_id) => {
self.compositor_proxy.send(CompositorMsg::CreateLayerForSubpage(
pipeline_id,
subpage_id,
self.subpage_map.get(&(pipeline_id, subpage_id)).map(|x| *x)))
}
}
true
}
@ -585,10 +596,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
debug!("creating replacement pipeline for about:failure");
let window_rect = self.pipeline(pipeline_id).rect;
let window_size = self.pipeline(pipeline_id).size;
let new_pipeline_id =
self.new_pipeline(parent_info,
window_rect,
window_size,
None,
LoadData::new(Url::parse("about:failure").unwrap()));
@ -596,44 +607,39 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
fn handle_init_load(&mut self, url: Url) {
let window_rect = Rect::new(Point2D::zero(), self.window_size.visible_viewport);
let window_size = self.window_size.visible_viewport;
let root_pipeline_id =
self.new_pipeline(None, Some(window_rect), None, LoadData::new(url.clone()));
self.new_pipeline(None, Some(window_size), None, LoadData::new(url.clone()));
self.handle_load_start_msg(&root_pipeline_id);
self.push_pending_frame(root_pipeline_id, None);
self.compositor_proxy.send(CompositorMsg::ChangePageUrl(root_pipeline_id, url));
}
fn handle_frame_rect_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId,
rect: TypedRect<PagePx, f32>) {
fn handle_frame_size_msg(&mut self,
containing_pipeline_id: PipelineId,
subpage_id: SubpageId,
size: &TypedSize2D<PagePx, f32>) {
// Store the new rect inside the pipeline
let (pipeline_id, script_chan) = {
// Find the pipeline that corresponds to this rectangle. It's possible that this
// pipeline may have already exited before we process this message, so just
// early exit if that occurs.
let pipeline_id = self.subpage_map.get(&(containing_pipeline_id, subpage_id)).map(|id| *id);
let pipeline_id = self.subpage_map
.get(&(containing_pipeline_id, subpage_id))
.map(|id| *id);
let pipeline = match pipeline_id {
Some(pipeline_id) => self.mut_pipeline(pipeline_id),
None => return,
};
pipeline.rect = Some(rect);
pipeline.size = Some(*size);
(pipeline.id, pipeline.script_chan.clone())
};
script_chan.send(ConstellationControlMsg::Resize(pipeline_id, WindowSizeData {
visible_viewport: rect.size,
initial_viewport: rect.size * ScaleFactor::new(1.0),
visible_viewport: *size,
initial_viewport: *size * ScaleFactor::new(1.0),
device_pixel_ratio: self.window_size.device_pixel_ratio,
})).unwrap();
// If this pipeline is in the current frame tree,
// send the updated rect to the script and compositor tasks
if self.pipeline_is_in_current_frame(pipeline_id) {
self.compositor_proxy.send(CompositorMsg::SetLayerRect(
pipeline_id,
LayerId::null(),
rect.to_untyped()));
}
}
// The script task associated with pipeline_id has loaded a URL in an iframe via script. This
@ -674,14 +680,24 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let old_pipeline_id = old_subpage_id.map(|old_subpage_id| {
self.find_subpage(containing_pipeline_id, old_subpage_id).id
});
let window_rect = old_pipeline_id.and_then(|old_pipeline_id| {
self.pipeline(old_pipeline_id).rect
let window_size = old_pipeline_id.and_then(|old_pipeline_id| {
self.pipeline(old_pipeline_id).size
});
let new_pipeline_id = self.new_pipeline(Some((containing_pipeline_id, new_subpage_id)),
window_rect,
window_size,
script_chan,
LoadData::new(url));
self.subpage_map.insert((containing_pipeline_id, new_subpage_id), new_pipeline_id);
// If anyone is waiting to know the pipeline ID, send that information now.
if let Some(subpage_id_senders) = self.subpage_id_senders.remove(&(containing_pipeline_id,
new_subpage_id)) {
for subpage_id_sender in subpage_id_senders.into_iter() {
subpage_id_sender.send(new_pipeline_id).unwrap();
}
}
self.push_pending_frame(new_pipeline_id, old_pipeline_id);
}
@ -739,8 +755,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// changes would be overridden by changing the subframe associated with source_id.
// Create the new pipeline
let window_rect = self.pipeline(source_id).rect;
let new_pipeline_id = self.new_pipeline(None, window_rect, None, load_data);
let window_size = self.pipeline(source_id).size;
let new_pipeline_id = self.new_pipeline(None, window_size, None, load_data);
self.push_pending_frame(new_pipeline_id, Some(source_id));
// Send message to ScriptTask that will suspend all timers
@ -954,7 +970,9 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.focus_parent_pipeline(pipeline_id);
}
fn handle_remove_iframe_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) {
fn handle_remove_iframe_msg(&mut self,
containing_pipeline_id: PipelineId,
subpage_id: SubpageId) {
let pipeline_id = self.find_subpage(containing_pipeline_id, subpage_id).id;
let frame_id = self.pipeline_to_frame_map.get(&pipeline_id).map(|id| *id);
match frame_id {
@ -1172,7 +1190,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
for pending_frame in &self.pending_frames {
let pipeline = self.pipelines.get(&pending_frame.new_pipeline_id).unwrap();
if pipeline.parent_info.is_none() {
let _ = pipeline.script_chan.send(ConstellationControlMsg::Resize(pipeline.id, new_size));
let _ = pipeline.script_chan.send(ConstellationControlMsg::Resize(pipeline.id,
new_size));
}
}
@ -1180,7 +1199,9 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
/// Handle updating actual viewport / zoom due to @viewport rules
fn handle_viewport_constrained_msg(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) {
fn handle_viewport_constrained_msg(&mut self,
pipeline_id: PipelineId,
constraints: ViewportConstraints) {
self.compositor_proxy.send(CompositorMsg::ViewportConstrained(pipeline_id, constraints));
}
@ -1196,12 +1217,6 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
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
@ -1215,12 +1230,11 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let (sender, receiver) = channel();
let msg = ConstellationControlMsg::GetCurrentState(sender, frame.current);
pipeline.script_chan.send(msg).unwrap();
if receiver.recv().unwrap() == ScriptState::DocumentLoading {
let result = receiver.recv().unwrap();
if result == ScriptState::DocumentLoading {
return false;
}
// Synchronously query the layout task for this pipeline
// to see if it is idle.
let (sender, receiver) = ipc::channel().unwrap();
let msg = LayoutControlMsg::GetWebFontLoadState(sender);
pipeline.layout_chan.0.send(msg).unwrap();
@ -1228,43 +1242,39 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
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;
}
// Check the visible rectangle for this pipeline. If the constellation has received a
// size for the pipeline, then its painting should be up to date. If the constellation
// *hasn't* received a size, it could be that the layer was hidden by script before the
// compositor discovered it, so we just don't check the layer.
if let Some(size) = pipeline.size {
// 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 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) = ipc::channel().unwrap();
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.
// 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) = ipc::channel().unwrap();
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 => {
return false;
None => {
// The compositor doesn't know about this pipeline yet.
// Assume it hasn't rendered yet.
return false;
}
}
}
}
@ -1354,7 +1364,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let mut frame_tree = SendableFrameTree {
pipeline: pipeline.to_sendable(),
rect: pipeline.rect,
size: pipeline.size,
children: vec!(),
};
@ -1409,7 +1419,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// If this is an iframe, then send the event with new url
if let Some((containing_pipeline_id, subpage_id, url)) = event_info {
let parent_pipeline = self.pipeline(containing_pipeline_id);
parent_pipeline.trigger_mozbrowser_event(subpage_id, MozBrowserEvent::LocationChange(url));
parent_pipeline.trigger_mozbrowser_event(subpage_id,
MozBrowserEvent::LocationChange(url));
}
}
}
@ -1451,7 +1462,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.pipelines.get_mut(&pipeline_id).expect("unable to find pipeline - this is a bug")
}
fn find_subpage(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) -> &mut Pipeline {
fn find_subpage(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId)
-> &mut Pipeline {
let pipeline_id = *self.subpage_map
.get(&(containing_pipeline_id, subpage_id))
.expect("no subpage pipeline_id");

View file

@ -100,7 +100,6 @@ impl CompositorEventListener for NullCompositor {
// SetFrameTree.
Msg::InitializeLayersForPipeline(..) |
Msg::SetLayerRect(..) |
Msg::AssignPaintedBuffers(..) |
Msg::ScrollFragmentPoint(..) |
Msg::Status(..) |
@ -122,6 +121,8 @@ impl CompositorEventListener for NullCompositor {
Msg::HeadParsed => {}
Msg::ReturnUnusedNativeSurfaces(..) => {}
Msg::CollectMemoryReports(..) => {}
Msg::PipelineExited(..) => {}
Msg::CreateLayerForSubpage(..) => {}
}
true
}

View file

@ -4,9 +4,10 @@
use CompositorProxy;
use compositor_task;
use compositor_task::Msg as CompositorMsg;
use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
use euclid::rect::{TypedRect};
use euclid::scale_factor::ScaleFactor;
use euclid::size::TypedSize2D;
use gfx::font_cache_task::FontCacheTask;
use gfx::paint_task::{ChromeToPaintMsg, LayoutToPaintMsg, PaintTask};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
@ -39,6 +40,8 @@ pub struct Pipeline {
pub script_chan: Sender<ConstellationControlMsg>,
/// A channel to layout, for performing reflows and shutdown.
pub layout_chan: LayoutControlChan,
/// A channel to the compositor.
pub compositor_proxy: Box<CompositorProxy + 'static + Send>,
pub chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
pub layout_shutdown_port: Receiver<()>,
pub paint_shutdown_port: Receiver<()>,
@ -46,7 +49,7 @@ pub struct Pipeline {
pub url: Url,
/// The title of the most recently-loaded page.
pub title: Option<String>,
pub rect: Option<TypedRect<PagePx, f32>>,
pub size: Option<TypedSize2D<PagePx, f32>>,
/// Whether this pipeline is currently running animations. Pipelines that are running
/// animations cause composites to be continually scheduled.
pub running_animations: bool,
@ -88,7 +91,7 @@ pub struct InitialPipelineState {
/// A channel to the memory profiler thread.
pub mem_profiler_chan: profile_mem::ProfilerChan,
/// Information about the initial window size.
pub window_rect: Option<TypedRect<PagePx, f32>>,
pub window_size: Option<TypedSize2D<PagePx, f32>>,
/// Information about the device pixel ratio.
pub device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>,
/// A channel to the script thread, if applicable. If this is `Some`,
@ -116,10 +119,10 @@ impl Pipeline {
parent_info: state.parent_info,
};
let window_size = state.window_rect.map(|rect| {
let window_size = state.window_size.map(|size| {
WindowSizeData {
visible_viewport: rect.size,
initial_viewport: rect.size * ScaleFactor::new(1.0),
visible_viewport: size,
initial_viewport: size * ScaleFactor::new(1.0),
device_pixel_ratio: state.device_pixel_ratio,
}
});
@ -164,11 +167,12 @@ impl Pipeline {
state.parent_info,
script_chan.clone(),
LayoutControlChan(pipeline_chan),
state.compositor_proxy.clone_compositor_proxy(),
chrome_to_paint_chan.clone(),
layout_shutdown_port,
paint_shutdown_port,
state.load_data.url.clone(),
state.window_rect);
state.window_size);
let pipeline_content = PipelineContent {
id: state.id,
@ -203,24 +207,26 @@ impl Pipeline {
parent_info: Option<(PipelineId, SubpageId)>,
script_chan: Sender<ConstellationControlMsg>,
layout_chan: LayoutControlChan,
compositor_proxy: Box<CompositorProxy + 'static + Send>,
chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
layout_shutdown_port: Receiver<()>,
paint_shutdown_port: Receiver<()>,
url: Url,
rect: Option<TypedRect<PagePx, f32>>)
size: Option<TypedSize2D<PagePx, f32>>)
-> Pipeline {
Pipeline {
id: id,
parent_info: parent_info,
script_chan: script_chan,
layout_chan: layout_chan,
compositor_proxy: compositor_proxy,
chrome_to_paint_chan: chrome_to_paint_chan,
layout_shutdown_port: layout_shutdown_port,
paint_shutdown_port: paint_shutdown_port,
url: url,
title: None,
children: vec!(),
rect: rect,
size: size,
running_animations: false,
}
}
@ -239,13 +245,17 @@ impl Pipeline {
// Script task handles shutting down layout, and layout handles shutting down the painter.
// For now, if the script task has failed, we give up on clean shutdown.
if self.script_chan.send(ConstellationControlMsg::ExitPipeline(self.id, exit_type)).is_ok() {
if self.script_chan
.send(ConstellationControlMsg::ExitPipeline(self.id, exit_type))
.is_ok() {
// Wait until all slave tasks have terminated and run destructors
// NOTE: We don't wait for script task as we don't always own it
let _ = self.paint_shutdown_port.recv();
let _ = self.layout_shutdown_port.recv();
}
// The compositor wants to know when pipelines shut down too.
self.compositor_proxy.send(CompositorMsg::PipelineExited(self.id))
}
pub fn freeze(&self) {