mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
auto merge of #748 : eschweic/servo/iframes, r=pcwalton
Fixes tests and replaces #702.
This commit is contained in:
commit
e4bfad144e
16 changed files with 804 additions and 450 deletions
|
@ -8,7 +8,7 @@ use azure::{AzFloat, AzGLContext};
|
|||
use azure::azure_hl::{B8G8R8A8, DrawTarget};
|
||||
use display_list::DisplayList;
|
||||
use servo_msg::compositor_msg::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer};
|
||||
use servo_msg::compositor_msg::{LayerBufferSet};
|
||||
use servo_msg::compositor_msg::{LayerBufferSet, Epoch};
|
||||
use servo_msg::constellation_msg::PipelineId;
|
||||
use font_context::FontContext;
|
||||
use geom::matrix2d::Matrix2D;
|
||||
|
@ -32,7 +32,7 @@ pub struct RenderLayer {
|
|||
|
||||
pub enum Msg {
|
||||
RenderMsg(RenderLayer),
|
||||
ReRenderMsg(~[BufferRequest], f32, PipelineId),
|
||||
ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch),
|
||||
PaintPermissionGranted,
|
||||
PaintPermissionRevoked,
|
||||
ExitMsg(Chan<()>),
|
||||
|
@ -89,6 +89,8 @@ struct RenderTask<C> {
|
|||
paint_permission: bool,
|
||||
/// Cached copy of last layers rendered
|
||||
last_paint_msg: Option<(arc::Arc<LayerBufferSet>, Size2D<uint>)>,
|
||||
/// A counter for epoch messages
|
||||
epoch: Epoch,
|
||||
}
|
||||
|
||||
impl<C: RenderListener + Send> RenderTask<C> {
|
||||
|
@ -123,6 +125,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
|||
|
||||
paint_permission: false,
|
||||
last_paint_msg: None,
|
||||
epoch: Epoch(0),
|
||||
};
|
||||
|
||||
render_task.start();
|
||||
|
@ -136,18 +139,24 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
|||
match self.port.recv() {
|
||||
RenderMsg(render_layer) => {
|
||||
if self.paint_permission {
|
||||
self.compositor.new_layer(self.id, render_layer.size);
|
||||
self.epoch.next();
|
||||
self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch);
|
||||
}
|
||||
self.render_layer = Some(render_layer);
|
||||
}
|
||||
ReRenderMsg(tiles, scale, id) => {
|
||||
self.render(tiles, scale, id);
|
||||
ReRenderMsg(tiles, scale, id, epoch) => {
|
||||
if self.epoch == epoch {
|
||||
self.render(tiles, scale, id);
|
||||
} else {
|
||||
debug!("renderer epoch mismatch: %? != %?", self.epoch, epoch);
|
||||
}
|
||||
}
|
||||
PaintPermissionGranted => {
|
||||
self.paint_permission = true;
|
||||
match self.render_layer {
|
||||
Some(ref render_layer) => {
|
||||
self.compositor.new_layer(self.id, render_layer.size);
|
||||
self.epoch.next();
|
||||
self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
@ -235,7 +244,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
|||
|
||||
debug!("render_task: returning surface");
|
||||
if self.paint_permission {
|
||||
self.compositor.paint(id, layer_buffer_set.clone());
|
||||
self.compositor.paint(id, layer_buffer_set.clone(), self.epoch);
|
||||
}
|
||||
debug!("caching paint msg");
|
||||
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));
|
||||
|
|
|
@ -7,7 +7,7 @@ use geom::size::Size2D;
|
|||
use geom::rect::Rect;
|
||||
use geom::matrix::identity;
|
||||
use gfx::render_task::ReRenderMsg;
|
||||
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet};
|
||||
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch};
|
||||
use servo_msg::constellation_msg::PipelineId;
|
||||
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
|
||||
use script::script_task::SendEventMsg;
|
||||
|
@ -15,6 +15,7 @@ use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEve
|
|||
use compositing::quadtree::{Quadtree, Invalid};
|
||||
use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
|
||||
use pipeline::Pipeline;
|
||||
use constellation::{SendableChildFrameTree, SendableFrameTree};
|
||||
|
||||
/// The CompositorLayer represents an element on a page that has a unique scroll
|
||||
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
|
||||
|
@ -42,6 +43,9 @@ pub struct CompositorLayer {
|
|||
/// When set to true, this layer is ignored by its parents. This is useful for
|
||||
/// soft deletion or when waiting on a page size.
|
||||
hidden: bool,
|
||||
/// A monotonically increasing counter that keeps track of the current epoch.
|
||||
/// add_buffer() calls that don't match the current epoch will be ignored.
|
||||
epoch: Epoch,
|
||||
}
|
||||
|
||||
/// Helper struct for keeping CompositorLayer children organized.
|
||||
|
@ -61,7 +65,8 @@ enum MaybeQuadtree {
|
|||
}
|
||||
|
||||
impl CompositorLayer {
|
||||
/// Creates a new CompositorLayer without a page size that is initially hidden.
|
||||
/// Creates a new CompositorLayer with an optional page size. If no page size is given,
|
||||
/// the layer is initially hidden and initialized without a quadtree.
|
||||
pub fn new(pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint, max_mem: Option<uint>)
|
||||
-> CompositorLayer {
|
||||
CompositorLayer {
|
||||
|
@ -78,9 +83,41 @@ impl CompositorLayer {
|
|||
},
|
||||
root_layer: @mut ContainerLayer(),
|
||||
hidden: true,
|
||||
epoch: Epoch(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a CompositorLayer tree from a frame tree.
|
||||
pub fn from_frame_tree(frame_tree: SendableFrameTree,
|
||||
tile_size: uint,
|
||||
max_mem: Option<uint>) -> CompositorLayer {
|
||||
let SendableFrameTree { pipeline, children } = frame_tree;
|
||||
let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem);
|
||||
layer.children = (do children.move_iter().map |child| {
|
||||
let SendableChildFrameTree { frame_tree, rect } = child;
|
||||
let container = @mut ContainerLayer();
|
||||
match rect {
|
||||
Some(rect) => {
|
||||
container.scissor = Some(rect);
|
||||
container.common.transform = identity().translate(rect.origin.x,
|
||||
rect.origin.y,
|
||||
0f32);
|
||||
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let child_layer = ~CompositorLayer::from_frame_tree(frame_tree, tile_size, max_mem);
|
||||
container.add_child(ContainerLayerKind(child_layer.root_layer));
|
||||
|
||||
CompositorLayerChild {
|
||||
child: child_layer,
|
||||
container: container,
|
||||
}
|
||||
}).collect();
|
||||
layer
|
||||
}
|
||||
|
||||
// Move the layer by as relative specified amount in page coordinates. Does not change
|
||||
// the position of the layer relative to its parent. This also takes in a cursor position
|
||||
// to see if the mouse is over child layers first. If a layer successfully scrolled, returns
|
||||
|
@ -166,13 +203,14 @@ impl CompositorLayer {
|
|||
let mut redisplay: bool;
|
||||
{ // block here to prevent double mutable borrow of self
|
||||
let quadtree = match self.quadtree {
|
||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
|
||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?,
|
||||
no quadtree initialized", self.pipeline.id),
|
||||
Tree(ref mut quadtree) => quadtree,
|
||||
};
|
||||
let (request, r) = quadtree.get_tile_rects_page(rect, scale);
|
||||
redisplay = r; // workaround to make redisplay visible outside block
|
||||
if !request.is_empty() {
|
||||
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone()));
|
||||
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone(), self.epoch));
|
||||
}
|
||||
}
|
||||
if redisplay {
|
||||
|
@ -204,9 +242,10 @@ impl CompositorLayer {
|
|||
|
||||
// Move the sublayer to an absolute position in page coordinates relative to its parent,
|
||||
// and clip the layer to the specified size in page coordinates.
|
||||
// If the layer is hidden and has a defined page size, unhide it.
|
||||
// This method returns false if the specified layer is not found.
|
||||
pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect<f32>) -> bool {
|
||||
for child_node in self.children.iter() {
|
||||
for child_node in self.children.mut_iter() {
|
||||
if pipeline_id != child_node.child.pipeline.id {
|
||||
loop;
|
||||
}
|
||||
|
@ -215,6 +254,10 @@ impl CompositorLayer {
|
|||
new_rect.origin.y,
|
||||
0.0));
|
||||
con.scissor = Some(new_rect);
|
||||
// If possible, unhide child
|
||||
if child_node.child.hidden && child_node.child.page_size.is_some() {
|
||||
child_node.child.hidden = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -223,10 +266,12 @@ impl CompositorLayer {
|
|||
}
|
||||
|
||||
|
||||
// Called when the layer changes size (NOT as a result of a zoom event).
|
||||
// Set the layer's page size. This signals that the renderer is ready for BufferRequests.
|
||||
// If the layer is hidden and has a defined clipping rect, unhide it.
|
||||
// This method returns false if the specified layer is not found.
|
||||
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>) -> bool {
|
||||
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>, epoch: Epoch) -> bool {
|
||||
if self.pipeline.id == pipeline_id {
|
||||
self.epoch = epoch;
|
||||
self.page_size = Some(new_size);
|
||||
// TODO: might get buffers back here
|
||||
match self.quadtree {
|
||||
|
@ -236,27 +281,48 @@ impl CompositorLayer {
|
|||
tile_size,
|
||||
max_mem)),
|
||||
}
|
||||
// Call scroll for bounds checking of the page shrunk. Use (-1, -1) as the cursor position
|
||||
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
|
||||
// to make sure the scroll isn't propagated downwards.
|
||||
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
|
||||
self.hidden = false;
|
||||
return true;
|
||||
}
|
||||
self.resize_helper(pipeline_id, new_size, epoch)
|
||||
}
|
||||
|
||||
// A helper method to resize sublayers.
|
||||
fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, epoch: Epoch) -> bool {
|
||||
for child_node in self.children.mut_iter() {
|
||||
if pipeline_id != child_node.child.pipeline.id {
|
||||
loop;
|
||||
}
|
||||
let child = &mut child_node.child;
|
||||
child.epoch = epoch;
|
||||
child.page_size = Some(new_size);
|
||||
// TODO: might get buffers back here
|
||||
match child.quadtree {
|
||||
Tree(ref mut quadtree) => quadtree.resize(new_size.width as uint, new_size.height as uint),
|
||||
NoTree(tile_size, max_mem) => child.quadtree = Tree(Quadtree::new(new_size.width as uint,
|
||||
new_size.height as uint,
|
||||
tile_size,
|
||||
max_mem)),
|
||||
}
|
||||
match child_node.container.scissor {
|
||||
Some(scissor) => {
|
||||
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
|
||||
// to make sure the scroll isn't propagated downwards.
|
||||
child.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), scissor.size);
|
||||
child.hidden = false;
|
||||
}
|
||||
None => {} // Nothing to do
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ID does not match ours, so recurse on descendents (including hidden children)
|
||||
let transform = |x: &mut CompositorLayerChild| -> bool {
|
||||
match x.container.scissor {
|
||||
Some(scissor) => {
|
||||
x.child.resize(pipeline_id, new_size, scissor.size)
|
||||
}
|
||||
None => {
|
||||
fail!("CompositorLayer: Child layer not clipped");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.children.mut_iter().any(transform)
|
||||
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size, epoch))
|
||||
}
|
||||
|
||||
|
||||
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
|
||||
// are not rebuilt directly from this method.
|
||||
pub fn build_layer_tree(&mut self) {
|
||||
|
@ -287,10 +353,11 @@ impl CompositorLayer {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Add new tiles.
|
||||
let quadtree = match self.quadtree {
|
||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
|
||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?,
|
||||
no quadtree initialized", self.pipeline.id),
|
||||
Tree(ref mut quadtree) => quadtree,
|
||||
};
|
||||
|
||||
|
@ -331,11 +398,18 @@ impl CompositorLayer {
|
|||
}
|
||||
|
||||
// Add LayerBuffers to the specified layer. Returns false if the layer is not found.
|
||||
pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet) -> bool {
|
||||
// If the epoch of the message does not match the layer's epoch, the message is ignored.
|
||||
pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet, epoch: Epoch) -> bool {
|
||||
if self.pipeline.id == pipeline_id {
|
||||
if self.epoch != epoch {
|
||||
debug!("compositor epoch mismatch: %? != %?, id: %?", self.epoch, epoch, self.pipeline.id);
|
||||
// TODO: send buffers back
|
||||
return true;
|
||||
}
|
||||
{ // block here to prevent double mutable borrow of self
|
||||
let quadtree = match self.quadtree {
|
||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
|
||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?,
|
||||
no quadtree initialized", self.pipeline.id),
|
||||
Tree(ref mut quadtree) => quadtree,
|
||||
};
|
||||
|
||||
|
@ -349,7 +423,7 @@ impl CompositorLayer {
|
|||
return true;
|
||||
}
|
||||
// ID does not match ours, so recurse on descendents (including hidden children).
|
||||
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers))
|
||||
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers, epoch))
|
||||
}
|
||||
|
||||
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use platform::{Application, Window};
|
||||
use script::dom::event::ResizeEvent;
|
||||
use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg};
|
||||
|
||||
pub use windowing;
|
||||
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
|
||||
|
@ -13,8 +11,8 @@ use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, Finis
|
|||
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
||||
|
||||
use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState};
|
||||
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
|
||||
use servo_msg::constellation_msg::PipelineId;
|
||||
use servo_msg::compositor_msg::{ReadyState, ScriptListener, Epoch};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, PipelineId, ResizedWindowMsg, LoadUrlMsg};
|
||||
use servo_msg::constellation_msg;
|
||||
use gfx::opts::Opts;
|
||||
|
||||
|
@ -40,11 +38,11 @@ use servo_util::{time, url};
|
|||
use servo_util::time::profile;
|
||||
use servo_util::time::ProfilerChan;
|
||||
|
||||
use extra::future::from_value;
|
||||
use extra::time::precise_time_s;
|
||||
use extra::arc;
|
||||
|
||||
use constellation::SendableFrameTree;
|
||||
use pipeline::Pipeline;
|
||||
use compositing::compositor_layer::CompositorLayer;
|
||||
|
||||
mod quadtree;
|
||||
|
@ -81,16 +79,26 @@ impl RenderListener for CompositorChan {
|
|||
port.recv()
|
||||
}
|
||||
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>) {
|
||||
self.chan.send(Paint(id, layer_buffer_set))
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>, epoch: Epoch) {
|
||||
self.chan.send(Paint(id, layer_buffer_set, epoch))
|
||||
}
|
||||
|
||||
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||
self.chan.send(NewLayer(id, page_size))
|
||||
let Size2D { width, height } = page_size;
|
||||
self.chan.send(NewLayer(id, Size2D(width as f32, height as f32)))
|
||||
}
|
||||
fn resize_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||
self.chan.send(ResizeLayer(id, page_size))
|
||||
fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D<uint>, epoch: Epoch) {
|
||||
let Size2D { width, height } = page_size;
|
||||
self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32), epoch))
|
||||
}
|
||||
fn set_layer_clip_rect(&self, id: PipelineId, new_rect: Rect<uint>) {
|
||||
let new_rect = Rect(Point2D(new_rect.origin.x as f32,
|
||||
new_rect.origin.y as f32),
|
||||
Size2D(new_rect.size.width as f32,
|
||||
new_rect.size.height as f32));
|
||||
self.chan.send(SetLayerClipRect(id, new_rect))
|
||||
}
|
||||
|
||||
fn delete_layer(&self, id: PipelineId) {
|
||||
self.chan.send(DeleteLayer(id))
|
||||
}
|
||||
|
@ -128,24 +136,25 @@ pub enum Msg {
|
|||
/// Requests the compositors GL context.
|
||||
GetGLContext(Chan<AzGLContext>),
|
||||
|
||||
// TODO: Attach epochs to these messages
|
||||
/// Alerts the compositor that there is a new layer to be rendered.
|
||||
NewLayer(PipelineId, Size2D<uint>),
|
||||
/// Alerts the compositor that the specified layer has changed size.
|
||||
ResizeLayer(PipelineId, Size2D<uint>),
|
||||
NewLayer(PipelineId, Size2D<f32>),
|
||||
/// Alerts the compositor that the specified layer's page has changed size.
|
||||
SetLayerPageSize(PipelineId, Size2D<f32>, Epoch),
|
||||
/// Alerts the compositor that the specified layer's clipping rect has changed.
|
||||
SetLayerClipRect(PipelineId, Rect<f32>),
|
||||
/// Alerts the compositor that the specified layer has been deleted.
|
||||
DeleteLayer(PipelineId),
|
||||
/// Invalidate a rect for a given layer
|
||||
InvalidateRect(PipelineId, Rect<uint>),
|
||||
|
||||
/// Requests that the compositor paint the given layer buffer set for the given page size.
|
||||
Paint(PipelineId, arc::Arc<LayerBufferSet>),
|
||||
Paint(PipelineId, arc::Arc<LayerBufferSet>, Epoch),
|
||||
/// Alerts the compositor to the current status of page loading.
|
||||
ChangeReadyState(ReadyState),
|
||||
/// Alerts the compositor to the current status of rendering.
|
||||
ChangeRenderState(RenderState),
|
||||
/// Sets the channel to the current layout and render tasks, along with their id
|
||||
SetIds(SendableFrameTree, Chan<()>),
|
||||
SetIds(SendableFrameTree, Chan<()>, ConstellationChan),
|
||||
}
|
||||
|
||||
/// Azure surface wrapping to work with the layers infrastructure.
|
||||
|
@ -207,7 +216,7 @@ impl CompositorTask {
|
|||
let root_layer = @mut ContainerLayer();
|
||||
let window_size = window.size();
|
||||
let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity());
|
||||
let mut window_size = Size2D(window_size.width as int, window_size.height as int);
|
||||
let mut window_size = Size2D(window_size.width as uint, window_size.height as uint);
|
||||
let mut done = false;
|
||||
let mut recomposite = false;
|
||||
|
||||
|
@ -216,21 +225,21 @@ impl CompositorTask {
|
|||
let mut zoom_action = false;
|
||||
let mut zoom_time = 0f;
|
||||
|
||||
// Channel to the outermost frame's pipeline.
|
||||
// FIXME: Events are only forwarded to this pipeline, but they should be
|
||||
// routed to the appropriate pipeline via the constellation.
|
||||
let mut pipeline: Option<Pipeline> = None;
|
||||
|
||||
// The root CompositorLayer
|
||||
let mut compositor_layer: Option<CompositorLayer> = None;
|
||||
let mut constellation_chan: Option<ConstellationChan> = None;
|
||||
|
||||
// Get BufferRequests from each layer.
|
||||
let ask_for_tiles = || {
|
||||
let window_size_page = Size2D(window_size.width as f32 / world_zoom,
|
||||
window_size.height as f32 / world_zoom);
|
||||
for layer in compositor_layer.mut_iter() {
|
||||
recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
|
||||
world_zoom) || recomposite;
|
||||
if !layer.hidden {
|
||||
recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
|
||||
world_zoom) || recomposite;
|
||||
} else {
|
||||
debug!("Compositor: root layer is hidden!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -243,9 +252,22 @@ impl CompositorTask {
|
|||
ChangeReadyState(ready_state) => window.set_ready_state(ready_state),
|
||||
ChangeRenderState(render_state) => window.set_render_state(render_state),
|
||||
|
||||
SetIds(frame_tree, response_chan) => {
|
||||
pipeline = Some(frame_tree.pipeline);
|
||||
SetIds(frame_tree, response_chan, new_constellation_chan) => {
|
||||
response_chan.send(());
|
||||
|
||||
// This assumes there is at most one child, which should be the case.
|
||||
match root_layer.first_child {
|
||||
Some(old_layer) => root_layer.remove_child(old_layer),
|
||||
None => {}
|
||||
}
|
||||
|
||||
let layer = CompositorLayer::from_frame_tree(frame_tree,
|
||||
self.opts.tile_size,
|
||||
Some(10000000u));
|
||||
root_layer.add_child(ContainerLayerKind(layer.root_layer));
|
||||
compositor_layer = Some(layer);
|
||||
|
||||
constellation_chan = Some(new_constellation_chan);
|
||||
}
|
||||
|
||||
GetSize(chan) => {
|
||||
|
@ -259,12 +281,12 @@ impl CompositorTask {
|
|||
// FIXME: This should create an additional layer instead of replacing the current one.
|
||||
// Once ResizeLayer messages are set up, we can switch to the new functionality.
|
||||
|
||||
let p = match pipeline {
|
||||
Some(ref pipeline) => pipeline,
|
||||
let p = match compositor_layer {
|
||||
Some(ref compositor_layer) => compositor_layer.pipeline.clone(),
|
||||
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
||||
};
|
||||
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||
let new_layer = CompositorLayer::new(p.clone(), Some(page_size),
|
||||
let new_layer = CompositorLayer::new(p, Some(page_size),
|
||||
self.opts.tile_size, Some(10000000u));
|
||||
|
||||
let current_child = root_layer.first_child;
|
||||
|
@ -279,14 +301,22 @@ impl CompositorTask {
|
|||
ask_for_tiles();
|
||||
}
|
||||
|
||||
ResizeLayer(id, new_size) => {
|
||||
SetLayerPageSize(id, new_size, epoch) => {
|
||||
match compositor_layer {
|
||||
Some(ref mut layer) => {
|
||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||
window_size.height as f32 / world_zoom);
|
||||
assert!(layer.resize(id, Size2D(new_size.width as f32,
|
||||
new_size.height as f32),
|
||||
page_window));
|
||||
assert!(layer.resize(id, new_size, page_window, epoch));
|
||||
ask_for_tiles();
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
SetLayerClipRect(id, new_rect) => {
|
||||
match compositor_layer {
|
||||
Some(ref mut layer) => {
|
||||
assert!(layer.set_clipping_rect(id, new_rect));
|
||||
ask_for_tiles();
|
||||
}
|
||||
None => {}
|
||||
|
@ -303,12 +333,12 @@ impl CompositorTask {
|
|||
}
|
||||
}
|
||||
|
||||
Paint(id, new_layer_buffer_set) => {
|
||||
Paint(id, new_layer_buffer_set, epoch) => {
|
||||
debug!("osmain: received new frame");
|
||||
|
||||
match compositor_layer {
|
||||
Some(ref mut layer) => {
|
||||
assert!(layer.add_buffers(id, new_layer_buffer_set.get()));
|
||||
assert!(layer.add_buffers(id, new_layer_buffer_set.get(), epoch));
|
||||
recomposite = true;
|
||||
}
|
||||
None => {
|
||||
|
@ -340,12 +370,12 @@ impl CompositorTask {
|
|||
IdleWindowEvent => {}
|
||||
|
||||
ResizeWindowEvent(width, height) => {
|
||||
let new_size = Size2D(width as int, height as int);
|
||||
let new_size = Size2D(width, height);
|
||||
if window_size != new_size {
|
||||
debug!("osmain: window resized to %ux%u", width, height);
|
||||
window_size = new_size;
|
||||
match pipeline {
|
||||
Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), ResizeEvent(width, height))),
|
||||
match constellation_chan {
|
||||
Some(ref chan) => chan.send(ResizedWindowMsg(new_size)),
|
||||
None => error!("Compositor: Recieved resize event without initialized layout chan"),
|
||||
}
|
||||
} else {
|
||||
|
@ -355,8 +385,14 @@ impl CompositorTask {
|
|||
|
||||
LoadUrlWindowEvent(url_string) => {
|
||||
debug!("osmain: loading URL `%s`", url_string);
|
||||
match pipeline {
|
||||
Some(ref pipeline) => pipeline.script_chan.send(LoadMsg(pipeline.id.clone(), url::make_url(url_string.to_str(), None))),
|
||||
let root_pipeline_id = match compositor_layer {
|
||||
Some(ref layer) => layer.pipeline.id.clone(),
|
||||
None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"),
|
||||
};
|
||||
match constellation_chan {
|
||||
Some(ref chan) => chan.send(LoadUrlMsg(root_pipeline_id,
|
||||
url::make_url(url_string.to_str(), None),
|
||||
from_value(window_size))),
|
||||
None => error!("Compositor: Recieved loadurl event without initialized layout chan"),
|
||||
}
|
||||
}
|
||||
|
@ -413,8 +449,8 @@ impl CompositorTask {
|
|||
windowing::Forward => constellation_msg::Forward,
|
||||
windowing::Back => constellation_msg::Back,
|
||||
};
|
||||
match pipeline {
|
||||
Some(ref pipeline) => pipeline.script_chan.send(NavigateMsg(direction)),
|
||||
match constellation_chan {
|
||||
Some(ref chan) => chan.send(NavigateMsg(direction)),
|
||||
None => error!("Compositor: Recieved navigation event without initialized layout chan"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,11 +211,30 @@ impl<T: Tile> Quadtree<T> {
|
|||
(ret, redisplay)
|
||||
}
|
||||
|
||||
|
||||
/// Resize the quadtree. This can add more space, changing the root node, or it can shrink, making
|
||||
/// an internal node the new root.
|
||||
/// TODO: return tiles after shrinking
|
||||
/// Creates a new quadtree at the specified size. This should be called when the window changes size.
|
||||
/// TODO: return old tiles.
|
||||
pub fn resize(&mut self, width: uint, height: uint) {
|
||||
// Spaces must be squares and powers of 2, so expand the space until it is
|
||||
let longer = width.max(&height);
|
||||
let num_tiles = div_ceil(longer, self.max_tile_size);
|
||||
let power_of_two = next_power_of_two(num_tiles);
|
||||
let size = power_of_two * self.max_tile_size;
|
||||
|
||||
self.root = ~QuadtreeNode {
|
||||
tile: None,
|
||||
origin: Point2D(0f32, 0f32),
|
||||
size: size as f32,
|
||||
quadrants: [None, None, None, None],
|
||||
status: Normal,
|
||||
tile_mem: 0,
|
||||
};
|
||||
self.clip_size = Size2D(width, height);
|
||||
}
|
||||
|
||||
/// Resize the underlying quadtree without removing tiles already in place.
|
||||
/// Might be useful later on, but resize() should be used for now.
|
||||
/// TODO: return tiles after shrinking
|
||||
pub fn bad_resize(&mut self, width: uint, height: uint) {
|
||||
self.clip_size = Size2D(width, height);
|
||||
let longer = width.max(&height);
|
||||
let new_num_tiles = div_ceil(longer, self.max_tile_size);
|
||||
|
@ -731,13 +750,13 @@ pub fn test_resize() {
|
|||
let mut q = Quadtree::new(6, 6, 1, None);
|
||||
q.add_tile_pixel(0, 0, 1f32, T{a: 0});
|
||||
q.add_tile_pixel(5, 5, 1f32, T{a: 1});
|
||||
q.resize(8, 1);
|
||||
q.bad_resize(8, 1);
|
||||
assert!(q.root.size == 8.0);
|
||||
q.resize(18, 1);
|
||||
q.bad_resize(18, 1);
|
||||
assert!(q.root.size == 32.0);
|
||||
q.resize(8, 1);
|
||||
q.bad_resize(8, 1);
|
||||
assert!(q.root.size == 8.0);
|
||||
q.resize(3, 1);
|
||||
q.bad_resize(3, 1);
|
||||
assert!(q.root.size == 4.0);
|
||||
assert!(q.get_all_tiles().len() == 1);
|
||||
}
|
||||
|
|
|
@ -2,28 +2,31 @@
|
|||
* 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 compositing::{CompositorChan, SetIds};
|
||||
use compositing::{CompositorChan, SetIds, SetLayerClipRect};
|
||||
use script::dom::event::ResizeEvent;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::comm;
|
||||
use std::comm::Port;
|
||||
use std::task;
|
||||
use geom::size::Size2D;
|
||||
use geom::rect::Rect;
|
||||
use gfx::opts::Opts;
|
||||
use pipeline::Pipeline;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FrameRectMsg};
|
||||
use servo_msg::constellation_msg::{InitLoadUrlMsg, LoadIframeUrlMsg, LoadUrlMsg};
|
||||
use servo_msg::constellation_msg::{Msg, NavigateMsg};
|
||||
use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowBroadcast};
|
||||
use servo_msg::constellation_msg::{Msg, NavigateMsg, NavigationType};
|
||||
use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowMsg, SubpageId};
|
||||
use servo_msg::constellation_msg;
|
||||
use script::script_task::{ResizeInactiveMsg, ExecuteMsg};
|
||||
use script::script_task::{SendEventMsg, ResizeInactiveMsg, ExecuteMsg};
|
||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
||||
use servo_net::resource_task::ResourceTask;
|
||||
use servo_net::resource_task;
|
||||
use servo_util::time::ProfilerChan;
|
||||
use std::hashmap::HashMap;
|
||||
use std::hashmap::{HashMap, HashSet};
|
||||
use std::util::replace;
|
||||
use extra::future::from_value;
|
||||
use extra::url::Url;
|
||||
use extra::future::{Future, from_value};
|
||||
|
||||
/// Maintains the pipelines and navigation context and grants permission to composite
|
||||
pub struct Constellation {
|
||||
|
@ -36,6 +39,7 @@ pub struct Constellation {
|
|||
navigation_context: NavigationContext,
|
||||
priv next_pipeline_id: PipelineId,
|
||||
pending_frames: ~[FrameChange],
|
||||
pending_sizes: HashMap<(PipelineId, SubpageId), Rect<f32>>,
|
||||
profiler_chan: ProfilerChan,
|
||||
opts: Opts,
|
||||
}
|
||||
|
@ -44,13 +48,14 @@ pub struct Constellation {
|
|||
struct FrameTree {
|
||||
pipeline: @mut Pipeline,
|
||||
parent: Option<@mut Pipeline>,
|
||||
children: ~[@mut FrameTree],
|
||||
children: ~[ChildFrameTree],
|
||||
}
|
||||
|
||||
// Need to clone the FrameTrees, but _not_ the Pipelines
|
||||
impl Clone for FrameTree {
|
||||
fn clone(&self) -> FrameTree {
|
||||
let mut children = do self.children.iter().map |&frame_tree| {
|
||||
@mut (*frame_tree).clone()
|
||||
let mut children = do self.children.iter().map |child_frame_tree| {
|
||||
child_frame_tree.clone()
|
||||
};
|
||||
FrameTree {
|
||||
pipeline: self.pipeline,
|
||||
|
@ -60,54 +65,67 @@ impl Clone for FrameTree {
|
|||
}
|
||||
}
|
||||
|
||||
struct ChildFrameTree {
|
||||
frame_tree: @mut FrameTree,
|
||||
/// Clipping rect representing the size and position, in page coordinates, of the visible
|
||||
/// region of the child frame relative to the parent.
|
||||
rect: Option<Rect<f32>>,
|
||||
}
|
||||
|
||||
impl Clone for ChildFrameTree {
|
||||
fn clone(&self) -> ChildFrameTree {
|
||||
ChildFrameTree {
|
||||
frame_tree: @mut (*self.frame_tree).clone(),
|
||||
rect: self.rect.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SendableFrameTree {
|
||||
pipeline: Pipeline,
|
||||
children: ~[SendableFrameTree],
|
||||
children: ~[SendableChildFrameTree],
|
||||
}
|
||||
|
||||
pub struct SendableChildFrameTree {
|
||||
frame_tree: SendableFrameTree,
|
||||
rect: Option<Rect<f32>>,
|
||||
}
|
||||
|
||||
impl SendableFrameTree {
|
||||
fn contains(&self, id: PipelineId) -> bool {
|
||||
self.pipeline.id == id ||
|
||||
do self.children.iter().any |frame_tree| {
|
||||
do self.children.iter().any |&SendableChildFrameTree { frame_tree: ref frame_tree, _ }| {
|
||||
frame_tree.contains(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameTree {
|
||||
fn contains(&self, id: PipelineId) -> bool {
|
||||
self.pipeline.id == id ||
|
||||
do self.children.iter().any |frame_tree| {
|
||||
frame_tree.contains(id)
|
||||
fn contains(@mut self, id: PipelineId) -> bool {
|
||||
do self.iter().any |frame_tree| {
|
||||
id == frame_tree.pipeline.id
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the frame tree whose key is id
|
||||
fn find_mut(@mut self, id: PipelineId) -> Option<@mut FrameTree> {
|
||||
if self.pipeline.id == id { return Some(self); }
|
||||
let mut finder = do self.children.iter().filter_map |frame_tree| {
|
||||
frame_tree.find_mut(id)
|
||||
};
|
||||
finder.next()
|
||||
do self.iter().find |frame_tree| {
|
||||
id == frame_tree.pipeline.id
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces a node of the frame tree in place. Returns the node that was removed or the original node
|
||||
/// if the node to replace could not be found.
|
||||
fn replace_child(&mut self, id: PipelineId, new_child: @mut FrameTree) -> Either<@mut FrameTree, @mut FrameTree> {
|
||||
let new_child_cell = Cell::new(new_child);
|
||||
for child in self.children.mut_iter() {
|
||||
let new_child = new_child_cell.take();
|
||||
if child.pipeline.id == id {
|
||||
new_child.parent = child.parent;
|
||||
return Left(replace(child, new_child));
|
||||
}
|
||||
let replaced = child.replace_child(id, new_child);
|
||||
if replaced.is_left() {
|
||||
return replaced;
|
||||
fn replace_child(@mut self, id: PipelineId, new_child: @mut FrameTree) -> Either<@mut FrameTree, @mut FrameTree> {
|
||||
for frame_tree in self.iter() {
|
||||
let mut child = frame_tree.children.mut_iter()
|
||||
.find(|child| child.frame_tree.pipeline.id == id);
|
||||
for child in child.mut_iter() {
|
||||
new_child.parent = child.frame_tree.parent;
|
||||
return Left(replace(&mut child.frame_tree, new_child));
|
||||
}
|
||||
new_child_cell.put_back(replaced.unwrap_right());
|
||||
}
|
||||
Right(new_child_cell.take())
|
||||
Right(new_child)
|
||||
}
|
||||
|
||||
fn to_sendable(&self) -> SendableFrameTree {
|
||||
|
@ -125,6 +143,18 @@ impl FrameTree {
|
|||
}
|
||||
}
|
||||
|
||||
impl ChildFrameTree {
|
||||
fn to_sendable(&self) -> SendableChildFrameTree {
|
||||
SendableChildFrameTree {
|
||||
frame_tree: self.frame_tree.to_sendable(),
|
||||
rect: self.rect,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over a frame tree, returning nodes in depth-first order.
|
||||
/// Note that this iterator should _not_ be used to mutate nodes _during_
|
||||
/// iteration. Mutating nodes once the iterator is out of scope is OK.
|
||||
pub struct FrameTreeIterator {
|
||||
priv stack: ~[@mut FrameTree],
|
||||
}
|
||||
|
@ -133,7 +163,9 @@ impl Iterator<@mut FrameTree> for FrameTreeIterator {
|
|||
fn next(&mut self) -> Option<@mut FrameTree> {
|
||||
if !self.stack.is_empty() {
|
||||
let next = self.stack.pop();
|
||||
self.stack.push_all(next.children);
|
||||
for &ChildFrameTree { frame_tree, _ } in next.children.rev_iter() {
|
||||
self.stack.push(frame_tree);
|
||||
}
|
||||
Some(next)
|
||||
} else {
|
||||
None
|
||||
|
@ -145,6 +177,7 @@ impl Iterator<@mut FrameTree> for FrameTreeIterator {
|
|||
struct FrameChange {
|
||||
before: Option<PipelineId>,
|
||||
after: @mut FrameTree,
|
||||
navigation_type: NavigationType,
|
||||
}
|
||||
|
||||
/// Stores the Id's of the pipelines previous and next in the browser's history
|
||||
|
@ -248,6 +281,7 @@ impl Constellation {
|
|||
navigation_context: NavigationContext::new(),
|
||||
next_pipeline_id: PipelineId(0),
|
||||
pending_frames: ~[],
|
||||
pending_sizes: HashMap::new(),
|
||||
profiler_chan: profiler_chan.take(),
|
||||
opts: opts.take(),
|
||||
};
|
||||
|
@ -281,330 +315,433 @@ impl Constellation {
|
|||
/// Handles loading pages, navigation, and granting access to the compositor
|
||||
fn handle_request(&mut self, request: Msg) -> bool {
|
||||
match request {
|
||||
|
||||
ExitMsg(sender) => {
|
||||
for (_id, ref pipeline) in self.pipelines.iter() {
|
||||
pipeline.exit();
|
||||
}
|
||||
self.image_cache_task.exit();
|
||||
self.resource_task.send(resource_task::Exit);
|
||||
|
||||
sender.send(());
|
||||
return false
|
||||
self.handle_exit(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
// This should only be called once per constellation, and only by the browser
|
||||
InitLoadUrlMsg(url) => {
|
||||
let pipeline = @mut Pipeline::create(self.get_next_pipeline_id(),
|
||||
None,
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
{
|
||||
let size = self.compositor_chan.get_size();
|
||||
from_value(Size2D(size.width as uint, size.height as uint))
|
||||
});
|
||||
if url.path.ends_with(".js") {
|
||||
pipeline.script_chan.send(ExecuteMsg(pipeline.id, url));
|
||||
} else {
|
||||
pipeline.load(url, Some(constellation_msg::Load));
|
||||
|
||||
self.pending_frames.push(FrameChange{
|
||||
before: None,
|
||||
after: @mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: None,
|
||||
children: ~[],
|
||||
},
|
||||
});
|
||||
}
|
||||
self.pipelines.insert(pipeline.id, pipeline);
|
||||
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.
|
||||
FrameRectMsg(pipeline_id, subpage_id, rect) => {
|
||||
self.handle_frame_rect_msg(pipeline_id, subpage_id, rect);
|
||||
}
|
||||
|
||||
LoadIframeUrlMsg(url, source_pipeline_id, subpage_id, size_future) => {
|
||||
// A message from the script associated with pipeline_id that it has
|
||||
// parsed an iframe during html parsing. This iframe will result in a
|
||||
// new pipeline being spawned and a frame tree being added to pipeline_id's
|
||||
// frame tree's children. This message is never the result of a link clicked
|
||||
// or a new url entered.
|
||||
// Start by finding the frame trees matching the pipeline id,
|
||||
// and add the new pipeline to their sub frames.
|
||||
let frame_trees: ~[@mut FrameTree] = {
|
||||
let matching_navi_frames = self.navigation_context.find_all(source_pipeline_id);
|
||||
let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| {
|
||||
frame_change.after.find_mut(source_pipeline_id)
|
||||
};
|
||||
matching_navi_frames.move_iter().chain(matching_pending_frames).collect()
|
||||
};
|
||||
|
||||
if frame_trees.is_empty() {
|
||||
fail!("Constellation: source pipeline id of LoadIframeUrlMsg is not in
|
||||
navigation context, nor is it in a pending frame. This should be
|
||||
impossible.");
|
||||
}
|
||||
|
||||
let next_pipeline_id = self.get_next_pipeline_id();
|
||||
|
||||
// Compare the pipeline's url to the new url. If the origin is the same,
|
||||
// then reuse the script task in creating the new pipeline
|
||||
let source_pipeline = *self.pipelines.find(&source_pipeline_id).expect("Constellation:
|
||||
source Id of LoadIframeUrlMsg does have an associated pipeline in
|
||||
constellation. This should be impossible.");
|
||||
|
||||
let source_url = source_pipeline.url.clone().expect("Constellation: LoadUrlIframeMsg's
|
||||
source's Url is None. There should never be a LoadUrlIframeMsg from a pipeline
|
||||
that was never given a url to load.");
|
||||
|
||||
// FIXME(tkuehn): Need to follow the standardized spec for checking same-origin
|
||||
let pipeline = @mut if (source_url.host == url.host &&
|
||||
source_url.port == url.port) {
|
||||
// Reuse the script task if same-origin url's
|
||||
Pipeline::with_script(next_pipeline_id,
|
||||
Some(subpage_id),
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
source_pipeline,
|
||||
size_future)
|
||||
} else {
|
||||
// Create a new script task if not same-origin url's
|
||||
Pipeline::create(next_pipeline_id,
|
||||
Some(subpage_id),
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
size_future)
|
||||
};
|
||||
|
||||
if url.path.ends_with(".js") {
|
||||
pipeline.execute(url);
|
||||
} else {
|
||||
pipeline.load(url, None);
|
||||
}
|
||||
for frame_tree in frame_trees.iter() {
|
||||
frame_tree.children.push(@mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: Some(source_pipeline),
|
||||
children: ~[],
|
||||
});
|
||||
}
|
||||
self.pipelines.insert(pipeline.id, pipeline);
|
||||
self.handle_load_iframe_url_msg(url, source_pipeline_id, subpage_id, size_future);
|
||||
}
|
||||
|
||||
// Load a new page, usually -- but not always -- from a mouse click or typed url
|
||||
// If there is already a pending page (self.pending_frames), it will not be overridden;
|
||||
// However, if the id is not encompassed by another change, it will be.
|
||||
LoadUrlMsg(source_id, url, size_future) => {
|
||||
debug!("received message to load %s", url.to_str());
|
||||
// Make sure no pending page would be overridden.
|
||||
let source_frame = self.current_frame().get_ref().find_mut(source_id).expect(
|
||||
"Constellation: received a LoadUrlMsg from a pipeline_id associated
|
||||
with a pipeline not in the active frame tree. This should be
|
||||
impossible.");
|
||||
|
||||
for frame_change in self.pending_frames.iter() {
|
||||
let old_id = frame_change.before.expect("Constellation: Received load msg
|
||||
from pipeline, but there is no currently active page. This should
|
||||
be impossible.");
|
||||
let changing_frame = self.current_frame().get_ref().find_mut(old_id).expect("Constellation:
|
||||
Pending change has non-active source pipeline. This should be
|
||||
impossible.");
|
||||
if changing_frame.contains(source_id) || source_frame.contains(old_id) {
|
||||
// id that sent load msg is being changed already; abort
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Being here means either there are no pending frames, or none of the pending
|
||||
// changes would be overriden by changing the subframe associated with source_id.
|
||||
|
||||
let parent = source_frame.parent.clone();
|
||||
let subpage_id = source_frame.pipeline.subpage_id.clone();
|
||||
let next_pipeline_id = self.get_next_pipeline_id();
|
||||
|
||||
let pipeline = @mut Pipeline::create(next_pipeline_id,
|
||||
subpage_id,
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
size_future);
|
||||
|
||||
if url.path.ends_with(".js") {
|
||||
pipeline.script_chan.send(ExecuteMsg(pipeline.id, url));
|
||||
} else {
|
||||
pipeline.load(url, Some(constellation_msg::Load));
|
||||
|
||||
self.pending_frames.push(FrameChange{
|
||||
before: Some(source_id),
|
||||
after: @mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: parent,
|
||||
children: ~[],
|
||||
},
|
||||
});
|
||||
}
|
||||
self.pipelines.insert(pipeline.id, pipeline);
|
||||
self.handle_load_url_msg(source_id, url, size_future);
|
||||
}
|
||||
|
||||
// Handle a forward or back request
|
||||
NavigateMsg(direction) => {
|
||||
debug!("received message to navigate %?", direction);
|
||||
|
||||
// TODO(tkuehn): what is the "critical point" beyond which pending frames
|
||||
// should not be cleared? Currently, the behavior is that forward/back
|
||||
// navigation always has navigation priority, and after that new page loading is
|
||||
// first come, first served.
|
||||
let destination_frame = match direction {
|
||||
constellation_msg::Forward => {
|
||||
if self.navigation_context.next.is_empty() {
|
||||
debug!("no next page to navigate to");
|
||||
return true
|
||||
} else {
|
||||
let old = self.current_frame().get_ref();
|
||||
for frame in old.iter() {
|
||||
frame.pipeline.revoke_paint_permission();
|
||||
}
|
||||
}
|
||||
self.navigation_context.forward()
|
||||
}
|
||||
constellation_msg::Back => {
|
||||
if self.navigation_context.previous.is_empty() {
|
||||
debug!("no previous page to navigate to");
|
||||
return true
|
||||
} else {
|
||||
let old = self.current_frame().get_ref();
|
||||
for frame in old.iter() {
|
||||
frame.pipeline.revoke_paint_permission();
|
||||
}
|
||||
}
|
||||
self.navigation_context.back()
|
||||
}
|
||||
};
|
||||
|
||||
for frame in destination_frame.iter() {
|
||||
let pipeline = &frame.pipeline;
|
||||
pipeline.reload(Some(constellation_msg::Navigate));
|
||||
}
|
||||
self.grant_paint_permission(destination_frame);
|
||||
|
||||
self.handle_navigate_msg(direction);
|
||||
}
|
||||
|
||||
// Notification that rendering has finished and is requesting permission to paint.
|
||||
RendererReadyMsg(pipeline_id) => {
|
||||
// This message could originate from a pipeline in the navigation context or
|
||||
// from a pending frame. The only time that we will grant paint permission is
|
||||
// when the message originates from a pending frame or the current frame.
|
||||
|
||||
for ¤t_frame in self.current_frame().iter() {
|
||||
// Messages originating in the current frame are not navigations;
|
||||
// TODO(tkuehn): In fact, this kind of message might be provably
|
||||
// impossible to occur.
|
||||
if current_frame.contains(pipeline_id) {
|
||||
self.set_ids(current_frame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the pending frame change whose new pipeline id is pipeline_id.
|
||||
// If it is not found, it simply means that this pipeline will not receive
|
||||
// permission to paint.
|
||||
let pending_index = do self.pending_frames.rposition |frame_change| {
|
||||
frame_change.after.pipeline.id == pipeline_id
|
||||
};
|
||||
for &pending_index in pending_index.iter() {
|
||||
let frame_change = self.pending_frames.swap_remove(pending_index);
|
||||
let to_add = frame_change.after;
|
||||
|
||||
// Create the next frame tree that will be given to the compositor
|
||||
let next_frame_tree = match to_add.parent {
|
||||
None => to_add, // to_add is the root
|
||||
Some(_parent) => @mut (*self.current_frame().unwrap()).clone(),
|
||||
};
|
||||
|
||||
// If there are frames to revoke permission from, do so now.
|
||||
match frame_change.before {
|
||||
Some(revoke_id) => {
|
||||
let current_frame = self.current_frame().unwrap();
|
||||
|
||||
let to_revoke = current_frame.find_mut(revoke_id).expect(
|
||||
"Constellation: pending frame change refers to an old
|
||||
frame not contained in the current frame. This is a bug");
|
||||
|
||||
for frame in to_revoke.iter() {
|
||||
frame.pipeline.revoke_paint_permission();
|
||||
}
|
||||
|
||||
// If to_add is not the root frame, then replace revoked_frame with it
|
||||
if to_add.parent.is_some() {
|
||||
next_frame_tree.replace_child(revoke_id, to_add);
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
// Add to_add to parent's children, if it is not the root
|
||||
let parent = &to_add.parent;
|
||||
let to_add = Cell::new(to_add);
|
||||
for parent in parent.iter() {
|
||||
let parent = next_frame_tree.find_mut(parent.id).expect(
|
||||
"Constellation: pending frame has a parent frame that is not
|
||||
active. This is a bug.");
|
||||
parent.children.push(to_add.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.grant_paint_permission(next_frame_tree);
|
||||
}
|
||||
self.handle_renderer_ready_msg(pipeline_id);
|
||||
}
|
||||
|
||||
ResizedWindowBroadcast(new_size) => match *self.current_frame() {
|
||||
Some(ref current_frame) => {
|
||||
let current_frame_id = current_frame.pipeline.id.clone();
|
||||
for frame_tree in self.navigation_context.previous.iter() {
|
||||
let pipeline = &frame_tree.pipeline;
|
||||
if current_frame_id != pipeline.id {
|
||||
pipeline.script_chan.send(ResizeInactiveMsg(new_size));
|
||||
}
|
||||
}
|
||||
for frame_tree in self.navigation_context.next.iter() {
|
||||
let pipeline = &frame_tree.pipeline;
|
||||
if current_frame_id != pipeline.id {
|
||||
pipeline.script_chan.send(ResizeInactiveMsg(new_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
for frame_tree in self.navigation_context.previous.iter() {
|
||||
frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size));
|
||||
}
|
||||
for frame_tree in self.navigation_context.next.iter() {
|
||||
frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size));
|
||||
}
|
||||
}
|
||||
ResizedWindowMsg(new_size) => {
|
||||
self.handle_resized_window_msg(new_size);
|
||||
}
|
||||
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_exit(&self, sender: Chan<()>) {
|
||||
for (_id, ref pipeline) in self.pipelines.iter() {
|
||||
pipeline.exit();
|
||||
}
|
||||
self.image_cache_task.exit();
|
||||
self.resource_task.send(resource_task::Exit);
|
||||
|
||||
sender.send(());
|
||||
}
|
||||
|
||||
fn handle_init_load(&mut self, url: Url) {
|
||||
let pipeline = @mut Pipeline::create(self.get_next_pipeline_id(),
|
||||
None,
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
{
|
||||
let size = self.compositor_chan.get_size();
|
||||
from_value(Size2D(size.width as uint, size.height as uint))
|
||||
});
|
||||
if url.path.ends_with(".js") {
|
||||
pipeline.script_chan.send(ExecuteMsg(pipeline.id, url));
|
||||
} else {
|
||||
pipeline.load(url);
|
||||
|
||||
self.pending_frames.push(FrameChange{
|
||||
before: None,
|
||||
after: @mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: None,
|
||||
children: ~[],
|
||||
},
|
||||
navigation_type: constellation_msg::Load,
|
||||
});
|
||||
}
|
||||
self.pipelines.insert(pipeline.id, pipeline);
|
||||
}
|
||||
|
||||
fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId, rect: Rect<f32>) {
|
||||
debug!("Received frame rect %? from %?, %?", rect, pipeline_id, subpage_id);
|
||||
let mut already_sent = HashSet::new();
|
||||
|
||||
// If the subframe is in the current frame tree, the compositor needs the new size
|
||||
for current_frame in self.current_frame().iter() {
|
||||
debug!("Constellation: Sending size for frame in current frame tree.");
|
||||
let source_frame = current_frame.find_mut(pipeline_id);
|
||||
for source_frame in source_frame.iter() {
|
||||
for child_frame_tree in source_frame.children.mut_iter() {
|
||||
let pipeline = &child_frame_tree.frame_tree.pipeline;
|
||||
if pipeline.subpage_id.expect("Constellation: child frame does not have a
|
||||
subpage id. This should not be possible.") == subpage_id {
|
||||
child_frame_tree.rect = Some(rect.clone());
|
||||
let Rect { size: Size2D { width, height }, _ } = rect;
|
||||
pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(),
|
||||
ResizeEvent(width as uint,
|
||||
height as uint)));
|
||||
self.compositor_chan.send(SetLayerClipRect(pipeline.id, rect));
|
||||
already_sent.insert(pipeline.id.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Traverse the navigation context and pending frames and tell each associated pipeline to resize.
|
||||
let frame_trees: ~[@mut FrameTree] = {
|
||||
let matching_navi_frames = self.navigation_context.find_all(pipeline_id);
|
||||
let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| {
|
||||
frame_change.after.find_mut(pipeline_id)
|
||||
};
|
||||
matching_navi_frames.move_iter().chain(matching_pending_frames).collect()
|
||||
};
|
||||
for frame_tree in frame_trees.iter() {
|
||||
for child_frame_tree in frame_tree.children.mut_iter() {
|
||||
let pipeline = &child_frame_tree.frame_tree.pipeline;
|
||||
if pipeline.subpage_id.expect("Constellation: child frame does not have a
|
||||
subpage id. This should not be possible.") == subpage_id {
|
||||
child_frame_tree.rect = Some(rect.clone());
|
||||
if !already_sent.contains(&pipeline.id) {
|
||||
let Size2D { width, height } = rect.size;
|
||||
pipeline.script_chan.send(ResizeInactiveMsg(pipeline.id.clone(),
|
||||
Size2D(width as uint, height as uint)));
|
||||
already_sent.insert(pipeline.id.clone());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, if no pipelines were sent a resize msg, then this subpage id
|
||||
// should be added to pending sizes
|
||||
if already_sent.len() == 0 {
|
||||
self.pending_sizes.insert((pipeline_id, subpage_id), rect);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_load_iframe_url_msg(&mut self,
|
||||
url: Url,
|
||||
source_pipeline_id: PipelineId,
|
||||
subpage_id: SubpageId,
|
||||
size_future: Future<Size2D<uint>>) {
|
||||
// A message from the script associated with pipeline_id that it has
|
||||
// parsed an iframe during html parsing. This iframe will result in a
|
||||
// new pipeline being spawned and a frame tree being added to pipeline_id's
|
||||
// frame tree's children. This message is never the result of a link clicked
|
||||
// or a new url entered.
|
||||
// Start by finding the frame trees matching the pipeline id,
|
||||
// and add the new pipeline to their sub frames.
|
||||
let frame_trees: ~[@mut FrameTree] = {
|
||||
let matching_navi_frames = self.navigation_context.find_all(source_pipeline_id);
|
||||
let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| {
|
||||
frame_change.after.find_mut(source_pipeline_id)
|
||||
};
|
||||
matching_navi_frames.move_iter().chain(matching_pending_frames).collect()
|
||||
};
|
||||
|
||||
if frame_trees.is_empty() {
|
||||
fail!("Constellation: source pipeline id of LoadIframeUrlMsg is not in
|
||||
navigation context, nor is it in a pending frame. This should be
|
||||
impossible.");
|
||||
}
|
||||
|
||||
let next_pipeline_id = self.get_next_pipeline_id();
|
||||
|
||||
// Compare the pipeline's url to the new url. If the origin is the same,
|
||||
// then reuse the script task in creating the new pipeline
|
||||
let source_pipeline = *self.pipelines.find(&source_pipeline_id).expect("Constellation:
|
||||
source Id of LoadIframeUrlMsg does have an associated pipeline in
|
||||
constellation. This should be impossible.");
|
||||
|
||||
let source_url = source_pipeline.url.clone().expect("Constellation: LoadUrlIframeMsg's
|
||||
source's Url is None. There should never be a LoadUrlIframeMsg from a pipeline
|
||||
that was never given a url to load.");
|
||||
|
||||
// FIXME(tkuehn): Need to follow the standardized spec for checking same-origin
|
||||
let pipeline = @mut if (source_url.host == url.host &&
|
||||
source_url.port == url.port) {
|
||||
debug!("Constellation: loading same-origin iframe at %?", url);
|
||||
// Reuse the script task if same-origin url's
|
||||
Pipeline::with_script(next_pipeline_id,
|
||||
Some(subpage_id),
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
source_pipeline,
|
||||
size_future)
|
||||
} else {
|
||||
debug!("Constellation: loading cross-origin iframe at %?", url);
|
||||
// Create a new script task if not same-origin url's
|
||||
Pipeline::create(next_pipeline_id,
|
||||
Some(subpage_id),
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
size_future)
|
||||
};
|
||||
|
||||
if url.path.ends_with(".js") {
|
||||
pipeline.execute(url);
|
||||
} else {
|
||||
debug!("Constellation: sending load msg to %?", pipeline);
|
||||
pipeline.load(url);
|
||||
}
|
||||
let rect = self.pending_sizes.pop(&(source_pipeline_id, subpage_id));
|
||||
for frame_tree in frame_trees.iter() {
|
||||
frame_tree.children.push(ChildFrameTree {
|
||||
frame_tree: @mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: Some(source_pipeline),
|
||||
children: ~[],
|
||||
},
|
||||
rect: rect,
|
||||
});
|
||||
}
|
||||
self.pipelines.insert(pipeline.id, pipeline);
|
||||
}
|
||||
|
||||
fn handle_load_url_msg(&mut self, source_id: PipelineId, url: Url, size_future: Future<Size2D<uint>>) {
|
||||
debug!("Constellation: received message to load %s", url.to_str());
|
||||
// Make sure no pending page would be overridden.
|
||||
let source_frame = self.current_frame().get_ref().find_mut(source_id).expect(
|
||||
"Constellation: received a LoadUrlMsg from a pipeline_id associated
|
||||
with a pipeline not in the active frame tree. This should be
|
||||
impossible.");
|
||||
|
||||
for frame_change in self.pending_frames.iter() {
|
||||
let old_id = frame_change.before.expect("Constellation: Received load msg
|
||||
from pipeline, but there is no currently active page. This should
|
||||
be impossible.");
|
||||
let changing_frame = self.current_frame().get_ref().find_mut(old_id).expect("Constellation:
|
||||
Pending change has non-active source pipeline. This should be
|
||||
impossible.");
|
||||
if changing_frame.contains(source_id) || source_frame.contains(old_id) {
|
||||
// id that sent load msg is being changed already; abort
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Being here means either there are no pending frames, or none of the pending
|
||||
// changes would be overriden by changing the subframe associated with source_id.
|
||||
|
||||
let parent = source_frame.parent.clone();
|
||||
let subpage_id = source_frame.pipeline.subpage_id.clone();
|
||||
let next_pipeline_id = self.get_next_pipeline_id();
|
||||
|
||||
let pipeline = @mut Pipeline::create(next_pipeline_id,
|
||||
subpage_id,
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.opts.clone(),
|
||||
size_future);
|
||||
|
||||
if url.path.ends_with(".js") {
|
||||
pipeline.script_chan.send(ExecuteMsg(pipeline.id, url));
|
||||
} else {
|
||||
pipeline.load(url);
|
||||
|
||||
self.pending_frames.push(FrameChange{
|
||||
before: Some(source_id),
|
||||
after: @mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: parent,
|
||||
children: ~[],
|
||||
},
|
||||
navigation_type: constellation_msg::Load,
|
||||
});
|
||||
}
|
||||
self.pipelines.insert(pipeline.id, pipeline);
|
||||
}
|
||||
|
||||
fn handle_navigate_msg(&mut self, direction: constellation_msg::NavigationDirection) {
|
||||
debug!("received message to navigate %?", direction);
|
||||
|
||||
// TODO(tkuehn): what is the "critical point" beyond which pending frames
|
||||
// should not be cleared? Currently, the behavior is that forward/back
|
||||
// navigation always has navigation priority, and after that new page loading is
|
||||
// first come, first served.
|
||||
let destination_frame = match direction {
|
||||
constellation_msg::Forward => {
|
||||
if self.navigation_context.next.is_empty() {
|
||||
debug!("no next page to navigate to");
|
||||
return;
|
||||
} else {
|
||||
let old = self.current_frame().get_ref();
|
||||
for frame in old.iter() {
|
||||
frame.pipeline.revoke_paint_permission();
|
||||
}
|
||||
}
|
||||
self.navigation_context.forward()
|
||||
}
|
||||
constellation_msg::Back => {
|
||||
if self.navigation_context.previous.is_empty() {
|
||||
debug!("no previous page to navigate to");
|
||||
return;
|
||||
} else {
|
||||
let old = self.current_frame().get_ref();
|
||||
for frame in old.iter() {
|
||||
frame.pipeline.revoke_paint_permission();
|
||||
}
|
||||
}
|
||||
self.navigation_context.back()
|
||||
}
|
||||
};
|
||||
|
||||
for frame in destination_frame.iter() {
|
||||
let pipeline = &frame.pipeline;
|
||||
pipeline.reload();
|
||||
}
|
||||
self.grant_paint_permission(destination_frame, constellation_msg::Navigate);
|
||||
|
||||
}
|
||||
|
||||
fn handle_renderer_ready_msg(&mut self, pipeline_id: PipelineId) {
|
||||
debug!("Renderer %? ready to send paint msg", pipeline_id);
|
||||
// This message could originate from a pipeline in the navigation context or
|
||||
// from a pending frame. The only time that we will grant paint permission is
|
||||
// when the message originates from a pending frame or the current frame.
|
||||
|
||||
for ¤t_frame in self.current_frame().iter() {
|
||||
// Messages originating in the current frame are not navigations;
|
||||
// TODO(tkuehn): In fact, this kind of message might be provably
|
||||
// impossible to occur.
|
||||
if current_frame.contains(pipeline_id) {
|
||||
for frame in current_frame.iter() {
|
||||
frame.pipeline.grant_paint_permission();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the pending frame change whose new pipeline id is pipeline_id.
|
||||
// If it is not found, it simply means that this pipeline will not receive
|
||||
// permission to paint.
|
||||
let pending_index = do self.pending_frames.rposition |frame_change| {
|
||||
frame_change.after.pipeline.id == pipeline_id
|
||||
};
|
||||
for &pending_index in pending_index.iter() {
|
||||
let frame_change = self.pending_frames.swap_remove(pending_index);
|
||||
let to_add = frame_change.after;
|
||||
|
||||
// Create the next frame tree that will be given to the compositor
|
||||
let next_frame_tree = match to_add.parent {
|
||||
None => to_add, // to_add is the root
|
||||
Some(_parent) => @mut (*self.current_frame().unwrap()).clone(),
|
||||
};
|
||||
|
||||
// If there are frames to revoke permission from, do so now.
|
||||
match frame_change.before {
|
||||
Some(revoke_id) => {
|
||||
debug!("Constellation: revoking permission from %?", revoke_id);
|
||||
let current_frame = self.current_frame().unwrap();
|
||||
|
||||
let to_revoke = current_frame.find_mut(revoke_id).expect(
|
||||
"Constellation: pending frame change refers to an old
|
||||
frame not contained in the current frame. This is a bug");
|
||||
|
||||
for frame in to_revoke.iter() {
|
||||
frame.pipeline.revoke_paint_permission();
|
||||
}
|
||||
|
||||
// If to_add is not the root frame, then replace revoked_frame with it.
|
||||
// This conveniently keeps scissor rect size intact.
|
||||
debug!("Constellation: replacing %? with %? in %?", revoke_id, to_add, next_frame_tree);
|
||||
if to_add.parent.is_some() {
|
||||
let replaced = next_frame_tree.replace_child(revoke_id, to_add);
|
||||
debug!("Replaced child: %?", replaced);
|
||||
}
|
||||
debug!("Constellation: frame tree after replacing: %?", next_frame_tree);
|
||||
}
|
||||
|
||||
None => {
|
||||
// Add to_add to parent's children, if it is not the root
|
||||
let parent = &to_add.parent;
|
||||
let to_add = Cell::new(to_add);
|
||||
for parent in parent.iter() {
|
||||
let to_add = to_add.take();
|
||||
let subpage_id = to_add.pipeline.subpage_id.expect("Constellation:
|
||||
Child frame's subpage id is None. This should be impossible.");
|
||||
let rect = self.pending_sizes.pop(&(parent.id, subpage_id));
|
||||
let parent = next_frame_tree.find_mut(parent.id).expect(
|
||||
"Constellation: pending frame has a parent frame that is not
|
||||
active. This is a bug.");
|
||||
parent.children.push(ChildFrameTree {
|
||||
frame_tree: to_add,
|
||||
rect: rect,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
self.grant_paint_permission(next_frame_tree, frame_change.navigation_type);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_resized_window_msg(&mut self, new_size: Size2D<uint>) {
|
||||
let mut already_seen = HashSet::new();
|
||||
for &@FrameTree { pipeline: ref pipeline, _ } in self.current_frame().iter() {
|
||||
let Size2D { width, height } = new_size;
|
||||
pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(),
|
||||
ResizeEvent(width, height)));
|
||||
already_seen.insert(pipeline.id.clone());
|
||||
}
|
||||
for frame_tree in self.navigation_context.previous.iter()
|
||||
.chain(self.navigation_context.next.iter()) {
|
||||
let pipeline = &frame_tree.pipeline;
|
||||
if !already_seen.contains(&pipeline.id) {
|
||||
pipeline.script_chan.send(ResizeInactiveMsg(pipeline.id.clone(), new_size));
|
||||
already_seen.insert(pipeline.id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grants a frame tree permission to paint; optionally updates navigation to reflect a new page
|
||||
fn grant_paint_permission(&mut self, frame_tree: @mut FrameTree) {
|
||||
fn grant_paint_permission(&mut self, frame_tree: @mut FrameTree, navigation_type: NavigationType) {
|
||||
// Give permission to paint to the new frame and all child frames
|
||||
self.set_ids(frame_tree);
|
||||
|
||||
// Don't call navigation_context.load() on a Navigate type (or None, as in the case of
|
||||
// parsed iframes that finish loading)
|
||||
match frame_tree.pipeline.navigation_type {
|
||||
Some(constellation_msg::Load) => {
|
||||
match navigation_type {
|
||||
constellation_msg::Load => {
|
||||
let evicted = self.navigation_context.load(frame_tree);
|
||||
for frame_tree in evicted.iter() {
|
||||
// exit any pipelines that don't exist outside the evicted frame trees
|
||||
|
@ -622,7 +759,7 @@ impl Constellation {
|
|||
|
||||
fn set_ids(&self, frame_tree: @mut FrameTree) {
|
||||
let (port, chan) = comm::stream();
|
||||
self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan));
|
||||
self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
|
||||
port.recv();
|
||||
for frame in frame_tree.iter() {
|
||||
frame.pipeline.grant_paint_permission();
|
||||
|
|
|
@ -14,9 +14,10 @@ use layout::float_context::{FloatContext, Invalid};
|
|||
|
||||
use std::cell::Cell;
|
||||
use geom::point::Point2D;
|
||||
use geom::size::Size2D;
|
||||
use geom::rect::Rect;
|
||||
use gfx::display_list::DisplayList;
|
||||
use gfx::geometry::Au;
|
||||
use gfx::geometry::{Au, to_frac_px};
|
||||
use gfx::geometry;
|
||||
use servo_util::tree::TreeNodeRef;
|
||||
|
||||
|
@ -364,6 +365,27 @@ impl BlockFlowData {
|
|||
list: &Cell<DisplayList<E>>)
|
||||
-> bool {
|
||||
|
||||
if self.common.node.is_iframe_element() {
|
||||
let x = self.common.abs_position.x + do self.box.map_default(Au(0)) |box| {
|
||||
box.with_model(|model| model.margin.left + model.border.left + model.padding.left)
|
||||
};
|
||||
let y = self.common.abs_position.y + do self.box.map_default(Au(0)) |box| {
|
||||
box.with_model(|model| model.margin.top + model.border.top + model.padding.top)
|
||||
};
|
||||
let w = self.common.position.size.width - do self.box.map_default(Au(0)) |box| {
|
||||
box.with_model(|model| model.noncontent_width())
|
||||
};
|
||||
let h = self.common.position.size.height - do self.box.map_default(Au(0)) |box| {
|
||||
box.with_model(|model| model.noncontent_height())
|
||||
};
|
||||
do self.common.node.with_mut_iframe_element |iframe_element| {
|
||||
iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32,
|
||||
to_frac_px(y) as f32),
|
||||
Size2D(to_frac_px(w) as f32,
|
||||
to_frac_px(h) as f32)));
|
||||
}
|
||||
}
|
||||
|
||||
let abs_rect = Rect(self.common.abs_position, self.common.position.size);
|
||||
if !abs_rect.intersects(dirty) {
|
||||
return false;
|
||||
|
|
|
@ -295,6 +295,10 @@ impl FloatFlowData {
|
|||
list: &Cell<DisplayList<E>>)
|
||||
-> bool {
|
||||
|
||||
//TODO: implement iframe size messaging
|
||||
if self.common.node.is_iframe_element() {
|
||||
error!("float iframe size messaging not implemented yet");
|
||||
}
|
||||
let abs_rect = Rect(self.common.abs_position, self.common.position.size);
|
||||
if !abs_rect.intersects(dirty) {
|
||||
return false;
|
||||
|
|
|
@ -753,6 +753,11 @@ impl InlineFlowData {
|
|||
list: &Cell<DisplayList<E>>)
|
||||
-> bool {
|
||||
|
||||
//TODO: implement inline iframe size messaging
|
||||
if self.common.node.is_iframe_element() {
|
||||
error!("inline iframe size messaging not implemented yet");
|
||||
}
|
||||
|
||||
let abs_rect = Rect(self.common.abs_position, self.common.position.size);
|
||||
if !abs_rect.intersects(dirty) {
|
||||
return false;
|
||||
|
|
|
@ -117,6 +117,12 @@ impl BoxModel {
|
|||
left + right
|
||||
}
|
||||
|
||||
pub fn noncontent_height(&self) -> Au {
|
||||
let top = self.margin.top + self.border.top + self.padding.top;
|
||||
let bottom = self.margin.bottom + self.border.bottom + self.padding.bottom;
|
||||
top + bottom
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> Au {
|
||||
self.margin.left + self.border.left + self.padding.left
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use gfx::opts::Opts;
|
|||
use layout::layout_task::LayoutTask;
|
||||
use script::layout_interface::LayoutChan;
|
||||
use script::script_task::{ExecuteMsg, LoadMsg};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, NavigationType, PipelineId, SubpageId};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId};
|
||||
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
|
||||
use script::script_task;
|
||||
use servo_net::image_cache_task::ImageCacheTask;
|
||||
|
@ -31,7 +31,6 @@ pub struct Pipeline {
|
|||
render_chan: RenderChan,
|
||||
/// The most recently loaded url
|
||||
url: Option<Url>,
|
||||
navigation_type: Option<NavigationType>,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
|
@ -140,13 +139,11 @@ impl Pipeline {
|
|||
layout_chan: layout_chan,
|
||||
render_chan: render_chan,
|
||||
url: None,
|
||||
navigation_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&mut self, url: Url, navigation_type: Option<NavigationType>) {
|
||||
pub fn load(&mut self, url: Url) {
|
||||
self.url = Some(url.clone());
|
||||
self.navigation_type = navigation_type;
|
||||
self.script_chan.send(LoadMsg(self.id, url));
|
||||
}
|
||||
|
||||
|
@ -163,11 +160,10 @@ impl Pipeline {
|
|||
self.render_chan.send(PaintPermissionRevoked);
|
||||
}
|
||||
|
||||
pub fn reload(&mut self, navigation_type: Option<NavigationType>) {
|
||||
if self.url.is_some() {
|
||||
let url = self.url.get_ref().clone();
|
||||
self.load(url, navigation_type);
|
||||
}
|
||||
pub fn reload(&mut self) {
|
||||
do self.url.clone().map_move() |url| {
|
||||
self.load(url);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
|
|
|
@ -54,14 +54,25 @@ pub enum ReadyState {
|
|||
FinishedLoading,
|
||||
}
|
||||
|
||||
/// A newtype struct for denoting the age of messages; prevents race conditions.
|
||||
#[deriving(Eq)]
|
||||
pub struct Epoch(uint);
|
||||
|
||||
impl Epoch {
|
||||
pub fn next(&mut self) {
|
||||
**self += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// The interface used by the renderer to acquire draw targets for each render frame and
|
||||
/// submit them to be drawn to the display.
|
||||
pub trait RenderListener {
|
||||
fn get_gl_context(&self) -> AzGLContext;
|
||||
fn new_layer(&self, PipelineId, Size2D<uint>);
|
||||
fn resize_layer(&self, PipelineId, Size2D<uint>);
|
||||
fn set_layer_page_size(&self, PipelineId, Size2D<uint>, Epoch);
|
||||
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
|
||||
fn delete_layer(&self, PipelineId);
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>);
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>, Epoch);
|
||||
fn set_render_state(&self, render_state: RenderState);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::comm::{Chan, SharedChan};
|
|||
use extra::url::Url;
|
||||
use extra::future::Future;
|
||||
use geom::size::Size2D;
|
||||
use geom::rect::Rect;
|
||||
|
||||
#[deriving(Clone)]
|
||||
pub struct ConstellationChan {
|
||||
|
@ -29,11 +30,12 @@ impl ConstellationChan {
|
|||
pub enum Msg {
|
||||
ExitMsg(Chan<()>),
|
||||
InitLoadUrlMsg(Url),
|
||||
FrameRectMsg(PipelineId, SubpageId, Rect<f32>),
|
||||
LoadUrlMsg(PipelineId, Url, Future<Size2D<uint>>),
|
||||
LoadIframeUrlMsg(Url, PipelineId, SubpageId, Future<Size2D<uint>>),
|
||||
NavigateMsg(NavigationDirection),
|
||||
RendererReadyMsg(PipelineId),
|
||||
ResizedWindowBroadcast(Size2D<uint>),
|
||||
ResizedWindowMsg(Size2D<uint>),
|
||||
}
|
||||
|
||||
/// Represents the two different ways to which a page can be navigated
|
||||
|
|
|
@ -385,8 +385,6 @@ impl Element {
|
|||
assert!(node.is_element());
|
||||
let page = win.page;
|
||||
let (port, chan) = comm::stream();
|
||||
// TODO(tkuehn): currently just queries top-level page layout. Needs to query
|
||||
// subframe layout if this element is in a subframe. Probably need an ID field.
|
||||
match unsafe {(*page).query_layout(ContentBoxesQuery(node, chan), port)} {
|
||||
Ok(ContentBoxesResponse(rects)) => {
|
||||
let cx = unsafe {(*page).js_info.get_ref().js_compartment.cx.ptr};
|
||||
|
|
|
@ -7,19 +7,39 @@ use dom::document::AbstractDocument;
|
|||
use dom::htmlelement::HTMLElement;
|
||||
use dom::windowproxy::WindowProxy;
|
||||
use geom::size::Size2D;
|
||||
use geom::rect::Rect;
|
||||
|
||||
use servo_msg::constellation_msg::SubpageId;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
|
||||
|
||||
use std::comm::ChanOne;
|
||||
use extra::url::Url;
|
||||
use std::util::replace;
|
||||
|
||||
pub struct HTMLIFrameElement {
|
||||
parent: HTMLElement,
|
||||
frame: Option<Url>,
|
||||
subpage_id: Option<SubpageId>,
|
||||
size_future_chan: Option<ChanOne<Size2D<uint>>>,
|
||||
size: Option<IFrameSize>,
|
||||
}
|
||||
|
||||
struct IFrameSize {
|
||||
pipeline_id: PipelineId,
|
||||
subpage_id: SubpageId,
|
||||
future_chan: Option<ChanOne<Size2D<uint>>>,
|
||||
constellation_chan: ConstellationChan,
|
||||
}
|
||||
|
||||
impl IFrameSize {
|
||||
pub fn set_rect(&mut self, rect: Rect<f32>) {
|
||||
let future_chan = replace(&mut self.future_chan, None);
|
||||
do future_chan.map_move |future_chan| {
|
||||
let Size2D { width, height } = rect.size;
|
||||
future_chan.send(Size2D(width as uint, height as uint));
|
||||
};
|
||||
self.constellation_chan.send(FrameRectMsg(self.pipeline_id, self.subpage_id, rect));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl HTMLIFrameElement {
|
||||
pub fn Src(&self) -> DOMString {
|
||||
null_string
|
||||
|
@ -123,4 +143,4 @@ impl HTMLIFrameElement {
|
|||
pub fn GetSVGDocument(&self) -> Option<AbstractDocument> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ use dom::htmlanchorelement::HTMLAnchorElement;
|
|||
use dom::htmlbodyelement::HTMLBodyElement;
|
||||
use dom::htmlcanvaselement::HTMLCanvasElement;
|
||||
use dom::htmlhrelement::HTMLHRElement;
|
||||
use dom::htmliframeelement::HTMLIFrameElement;
|
||||
use dom::htmliframeelement::{IFrameSize, HTMLIFrameElement};
|
||||
use dom::htmlimageelement::HTMLImageElement;
|
||||
use dom::htmlmetaelement::HTMLMetaElement;
|
||||
use dom::htmlolistelement::HTMLOListElement;
|
||||
|
@ -50,6 +50,7 @@ use dom::bindings::utils::str;
|
|||
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
|
||||
use js::jsapi::JSContext;
|
||||
use newcss::stylesheet::Stylesheet;
|
||||
use script_task::page_from_context;
|
||||
|
||||
use std::cast;
|
||||
use std::cell::Cell;
|
||||
|
@ -59,7 +60,7 @@ use std::str::eq_slice;
|
|||
use std::task;
|
||||
use std::from_str::FromStr;
|
||||
use hubbub::hubbub;
|
||||
use servo_msg::constellation_msg::SubpageId;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, SubpageId};
|
||||
use servo_net::image_cache_task::ImageCacheTask;
|
||||
use servo_net::image_cache_task;
|
||||
use servo_net::resource_task::{Done, Load, Payload, ResourceTask};
|
||||
|
@ -252,7 +253,7 @@ fn build_element_from_tag(cx: *JSContext, tag: &str) -> AbstractNode<ScriptView>
|
|||
handle_element!(cx, tag, "ul", HTMLUListElementTypeId, HTMLUListElement, []);
|
||||
|
||||
handle_element!(cx, tag, "img", HTMLImageElementTypeId, HTMLImageElement, [(image: None)]);
|
||||
handle_element!(cx, tag, "iframe", HTMLIframeElementTypeId, HTMLIFrameElement, [(frame: None), (size_future_chan: None), (subpage_id: None)]);
|
||||
handle_element!(cx, tag, "iframe", HTMLIframeElementTypeId, HTMLIFrameElement, [(frame: None), (size: None)]);
|
||||
|
||||
handle_element!(cx, tag, "h1", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading1)]);
|
||||
handle_element!(cx, tag, "h2", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading2)]);
|
||||
|
@ -276,7 +277,8 @@ pub fn parse_html(cx: *JSContext,
|
|||
url: Url,
|
||||
resource_task: ResourceTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
next_subpage_id: SubpageId) -> HtmlParserResult {
|
||||
next_subpage_id: SubpageId,
|
||||
constellation_chan: ConstellationChan) -> HtmlParserResult {
|
||||
debug!("Hubbub: parsing %?", url);
|
||||
// Spawn a CSS parser to receive links to CSS style sheets.
|
||||
let resource_task2 = resource_task.clone();
|
||||
|
@ -381,14 +383,24 @@ pub fn parse_html(cx: *JSContext,
|
|||
|
||||
// Size future
|
||||
let (port, chan) = comm::oneshot();
|
||||
iframe_element.size_future_chan = Some(chan);
|
||||
let size_future = from_port(port);
|
||||
|
||||
// Subpage Id
|
||||
let subpage_id = next_subpage_id.take();
|
||||
iframe_element.subpage_id = Some(subpage_id);
|
||||
next_subpage_id.put_back(SubpageId(*subpage_id + 1));
|
||||
|
||||
// Pipeline Id
|
||||
let pipeline_id = {
|
||||
let page = page_from_context(cx);
|
||||
unsafe { (*page).id }
|
||||
};
|
||||
|
||||
iframe_element.size = Some(IFrameSize {
|
||||
pipeline_id: pipeline_id,
|
||||
subpage_id: subpage_id,
|
||||
future_chan: Some(chan),
|
||||
constellation_chan: constellation_chan.clone(),
|
||||
});
|
||||
iframe_chan.send(HtmlDiscoveredIFrame((iframe_url, subpage_id, size_future)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use layout_interface::{ReflowDocumentDamage, ReflowForDisplay, ReflowGoal};
|
|||
use layout_interface::ReflowMsg;
|
||||
use layout_interface;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, LoadUrlMsg, NavigationDirection};
|
||||
use servo_msg::constellation_msg::{PipelineId, SubpageId, RendererReadyMsg, ResizedWindowBroadcast};
|
||||
use servo_msg::constellation_msg::{PipelineId, SubpageId, RendererReadyMsg};
|
||||
use servo_msg::constellation_msg::{LoadIframeUrlMsg};
|
||||
use servo_msg::constellation_msg;
|
||||
|
||||
|
@ -68,7 +68,7 @@ pub enum ScriptMsg {
|
|||
/// Notifies script that reflow is finished.
|
||||
ReflowCompleteMsg(PipelineId),
|
||||
/// Notifies script that window has been resized but to not take immediate action.
|
||||
ResizeInactiveMsg(Size2D<uint>),
|
||||
ResizeInactiveMsg(PipelineId, Size2D<uint>),
|
||||
/// Exits the constellation.
|
||||
ExitMsg,
|
||||
}
|
||||
|
@ -454,7 +454,7 @@ impl ScriptTask {
|
|||
FireTimerMsg(id, timer_data) => self.handle_fire_timer_msg(id, timer_data),
|
||||
NavigateMsg(direction) => self.handle_navigate_msg(direction),
|
||||
ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id),
|
||||
ResizeInactiveMsg(new_size) => self.handle_resize_inactive_msg(new_size),
|
||||
ResizeInactiveMsg(id, new_size) => self.handle_resize_inactive_msg(id, new_size),
|
||||
ExitMsg => {
|
||||
self.handle_exit_msg();
|
||||
return false
|
||||
|
@ -464,6 +464,7 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
fn handle_new_layout(&mut self, new_layout_info: NewLayoutInfo) {
|
||||
debug!("Script: new layout: %?", new_layout_info);
|
||||
let NewLayoutInfo {
|
||||
old_id,
|
||||
new_id,
|
||||
|
@ -529,6 +530,7 @@ impl ScriptTask {
|
|||
|
||||
/// Handles a notification that reflow completed.
|
||||
fn handle_reflow_complete_msg(&mut self, pipeline_id: PipelineId) {
|
||||
debug!("Script: Reflow complete for %?", pipeline_id);
|
||||
self.page_tree.find(pipeline_id).expect("ScriptTask: received a load
|
||||
message for a layout channel that is not associated with this script task. This
|
||||
is a bug.").page.layout_join_port = None;
|
||||
|
@ -543,11 +545,13 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
/// Window was resized, but this script was not active, so don't reflow yet
|
||||
fn handle_resize_inactive_msg(&mut self, new_size: Size2D<uint>) {
|
||||
self.page_tree.page.window_size = from_value(new_size);
|
||||
let last_loaded_url = replace(&mut self.page_tree.page.url, None);
|
||||
fn handle_resize_inactive_msg(&mut self, id: PipelineId, new_size: Size2D<uint>) {
|
||||
let page = self.page_tree.find(id).expect("Received resize message for PipelineId not associated
|
||||
with a page in the page tree. This is a bug.").page;
|
||||
page.window_size = from_value(new_size);
|
||||
let last_loaded_url = replace(&mut page.url, None);
|
||||
for url in last_loaded_url.iter() {
|
||||
self.page_tree.page.url = Some((url.first(), true));
|
||||
page.url = Some((url.first(), true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -565,7 +569,7 @@ impl ScriptTask {
|
|||
/// The entry point to document loading. Defines bindings, sets up the window and document
|
||||
/// objects, parses HTML and CSS, and kicks off initial layout.
|
||||
fn load(&mut self, pipeline_id: PipelineId, url: Url) {
|
||||
debug!("ScriptTask: loading %?", url);
|
||||
debug!("ScriptTask: loading %? on page %?", url, pipeline_id);
|
||||
|
||||
let page = self.page_tree.find(pipeline_id).expect("ScriptTask: received a load
|
||||
message for a layout channel that is not associated with this script task. This
|
||||
|
@ -601,7 +605,8 @@ impl ScriptTask {
|
|||
url.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
page.next_subpage_id.clone());
|
||||
page.next_subpage_id.clone(),
|
||||
self.constellation_chan.clone());
|
||||
|
||||
let HtmlParserResult {root, discovery_port} = html_parsing_result;
|
||||
|
||||
|
@ -700,8 +705,6 @@ impl ScriptTask {
|
|||
page.damage(ReflowDocumentDamage);
|
||||
page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor)
|
||||
}
|
||||
|
||||
self.constellation_chan.send(ResizedWindowBroadcast(page.window_size.get().clone()));
|
||||
}
|
||||
|
||||
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue