aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/compositor.rs65
-rw-r--r--components/compositing/compositor_task.rs10
-rw-r--r--components/compositing/constellation.rs1220
-rw-r--r--components/compositing/headless.rs8
-rw-r--r--components/compositing/pipeline.rs47
-rw-r--r--components/msg/constellation_msg.rs14
-rw-r--r--components/script/dom/htmlformelement.rs2
-rw-r--r--components/script/dom/htmliframeelement.rs27
-rw-r--r--components/script/dom/window.rs21
-rw-r--r--components/script/script_task.rs66
-rw-r--r--components/script_traits/lib.rs10
11 files changed, 578 insertions, 912 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 895f08795bc..af816d5a9c2 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -291,18 +291,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.get_title_for_main_frame();
}
- (Msg::ChangeLayerPipelineAndRemoveChildren(old_pipeline, new_pipeline, response_channel),
- ShutdownState::NotShuttingDown) => {
- self.handle_change_layer_pipeline_and_remove_children(old_pipeline, new_pipeline);
- response_channel.send(()).unwrap();
- }
-
- (Msg::CreateRootLayerForPipeline(parent_pipeline, pipeline, rect, response_channel),
- ShutdownState::NotShuttingDown) => {
- self.handle_create_root_layer_for_pipeline(parent_pipeline, pipeline, rect);
- response_channel.send(()).unwrap();
- }
-
(Msg::CreateOrUpdateBaseLayer(layer_properties), ShutdownState::NotShuttingDown) => {
self.create_or_update_base_layer(layer_properties);
}
@@ -554,62 +542,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let root_layer = self.create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline,
frame_rect);
for kid in frame_tree.children.iter() {
- root_layer.add_child(self.create_frame_tree_root_layers(&kid.frame_tree, kid.rect));
+ root_layer.add_child(self.create_frame_tree_root_layers(kid, kid.rect));
}
return root_layer;
}
- fn handle_change_layer_pipeline_and_remove_children(&mut self,
- old_pipeline: CompositionPipeline,
- new_pipeline: CompositionPipeline) {
- let root_layer = match self.find_pipeline_root_layer(old_pipeline.id) {
- Some(root_layer) => root_layer,
- None => {
- debug!("Ignoring ChangeLayerPipelineAndRemoveChildren message \
- for pipeline ({:?}) shutting down.",
- old_pipeline.id);
- return;
- }
- };
-
- root_layer.clear_all_tiles(self);
- root_layer.children().clear();
-
- debug_assert!(root_layer.extra_data.borrow().pipeline_id == old_pipeline.id);
- root_layer.extra_data.borrow_mut().pipeline_id = new_pipeline.id;
-
- let new_pipeline_id = new_pipeline.id;
- self.get_or_create_pipeline_details(new_pipeline_id).pipeline = Some(new_pipeline);
- }
-
- fn handle_create_root_layer_for_pipeline(&mut self,
- parent_pipeline: CompositionPipeline,
- new_pipeline: CompositionPipeline,
- frame_rect: Option<TypedRect<PagePx, f32>>) {
- let root_layer = self.create_root_layer_for_pipeline_and_rect(&new_pipeline, frame_rect);
- match frame_rect {
- Some(ref frame_rect) => {
- *root_layer.masks_to_bounds.borrow_mut() = true;
-
- let frame_rect = frame_rect.to_untyped();
- *root_layer.bounds.borrow_mut() = Rect::from_untyped(&frame_rect);
- }
- None => {}
- }
-
- let pipeline_id = parent_pipeline.id;
- let parent_layer = match self.find_pipeline_root_layer(pipeline_id) {
- Some(root_layer) => root_layer,
- None => {
- debug!("Ignoring FrameTreeUpdate message for pipeline ({:?}) \
- shutting down.",
- pipeline_id);
- return;
- }
- };
- parent_layer.add_child(root_layer);
- }
-
fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) -> Option<Rc<Layer<CompositorData>>> {
if !self.pipeline_details.contains_key(&pipeline_id) {
panic!("Tried to create or update layer for unknown pipeline")
diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs
index 7edc6542202..3c615175c32 100644
--- a/components/compositing/compositor_task.rs
+++ b/components/compositing/compositor_task.rs
@@ -13,18 +13,16 @@ use windowing::{WindowEvent, WindowMethods};
use azure::azure_hl::{SourceSurfaceMethods, Color};
use geom::point::Point2D;
-use geom::rect::{Rect, TypedRect};
+use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
use layers::layers::LayerBufferSet;
-use pipeline::CompositionPipeline;
use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState};
use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy};
use msg::constellation_msg::{ConstellationChan, PipelineId};
use msg::constellation_msg::{Key, KeyState, KeyModifiers};
use url::Url;
use util::cursor::Cursor;
-use util::geometry::PagePx;
use util::memory::MemoryProfilerChan;
use util::time::TimeProfilerChan;
use std::sync::mpsc::{channel, Sender, Receiver};
@@ -207,10 +205,6 @@ pub enum Msg {
PaintMsgDiscarded,
/// Replaces the current frame tree, typically called during main frame navigation.
SetFrameTree(SendableFrameTree, Sender<()>, ConstellationChan),
- /// Requests the compositor to create a root layer for a new frame.
- CreateRootLayerForPipeline(CompositionPipeline, CompositionPipeline, Option<TypedRect<PagePx, f32>>, Sender<()>),
- /// Requests the compositor to change a root layer's pipeline and remove all child layers.
- ChangeLayerPipelineAndRemoveChildren(CompositionPipeline, CompositionPipeline, Sender<()>),
/// The load of a page has completed.
LoadComplete,
/// Indicates that the scrolling timeout with the given starting timestamp has happened and a
@@ -241,8 +235,6 @@ impl Debug for Msg {
Msg::ChangePageUrl(..) => write!(f, "ChangePageUrl"),
Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),
Msg::SetFrameTree(..) => write!(f, "SetFrameTree"),
- Msg::CreateRootLayerForPipeline(..) => write!(f, "CreateRootLayerForPipeline"),
- Msg::ChangeLayerPipelineAndRemoveChildren(..) => write!(f, "ChangeLayerPipelineAndRemoveChildren"),
Msg::LoadComplete => write!(f, "LoadComplete"),
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
Msg::KeyEvent(..) => write!(f, "KeyEvent"),
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs
index abebea1d215..df4a448d758 100644
--- a/components/compositing/constellation.rs
+++ b/components/compositing/constellation.rs
@@ -10,7 +10,6 @@ use devtools_traits::{DevtoolsControlChan, DevtoolsControlMsg};
use geom::rect::{Rect, TypedRect};
use geom::scale_factor::ScaleFactor;
use gfx::font_cache_task::FontCacheTask;
-use layers::geometry::DevicePixel;
use layout_traits::LayoutTaskFactory;
use libc;
use script_traits::{CompositorEvent, ConstellationControlMsg};
@@ -18,9 +17,8 @@ use script_traits::{ScriptControlChan, ScriptTaskFactory};
use msg::compositor_msg::LayerId;
use msg::constellation_msg::{self, ConstellationChan, Failure};
use msg::constellation_msg::{IFrameSandboxState, NavigationDirection};
-use msg::constellation_msg::{Key, KeyState, KeyModifiers};
-use msg::constellation_msg::{LoadData, NavigationType};
-use msg::constellation_msg::{PipelineExitType, PipelineId};
+use msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData};
+use msg::constellation_msg::{FrameId, PipelineExitType, PipelineId};
use msg::constellation_msg::{SubpageId, WindowSizeData};
use msg::constellation_msg::Msg as ConstellationMsg;
use net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
@@ -28,16 +26,14 @@ use net::resource_task::ResourceTask;
use net::resource_task;
use net::storage_task::{StorageTask, StorageTaskMsg};
use util::cursor::Cursor;
-use util::geometry::{PagePx, ViewportPx};
+use util::geometry::PagePx;
use util::opts;
use util::task::spawn_named;
use util::time::TimeProfilerChan;
use std::borrow::ToOwned;
-use std::cell::{Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::{HashMap};
use std::old_io as io;
use std::mem::replace;
-use std::rc::Rc;
use std::sync::mpsc::{Receiver, channel};
use url::Url;
@@ -66,268 +62,103 @@ pub struct Constellation<LTF, STF> {
storage_task: StorageTask,
/// A list of all the pipelines. (See the `pipeline` module for more details.)
- pipelines: HashMap<PipelineId, Rc<Pipeline>>,
+ pipelines: HashMap<PipelineId, Pipeline>,
+
+ /// A list of all the frames
+ frames: HashMap<FrameId, Frame>,
+
+ /// Maps from pipeline ID to the frame that contains it.
+ pipeline_to_frame_map: HashMap<PipelineId, FrameId>,
+
+ /// Maps from a (parent pipeline, subpage) to the actual child pipeline ID.
+ subpage_map: HashMap<(PipelineId, SubpageId), PipelineId>,
/// A channel through which messages can be sent to the font cache.
font_cache_task: FontCacheTask,
- navigation_context: NavigationContext,
+ /// ID of the root frame.
+ root_frame_id: Option<FrameId>,
/// The next free ID to assign to a pipeline.
next_pipeline_id: PipelineId,
+ /// The next free ID to assign to a frame.
+ next_frame_id: FrameId,
+
/// Navigation operations that are in progress.
pending_frames: Vec<FrameChange>,
- pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
-
/// A channel through which messages can be sent to the time profiler.
pub time_profiler_chan: TimeProfilerChan,
pub window_size: WindowSizeData,
}
-/// One frame in the hierarchy.
-struct FrameTree {
- /// The pipeline for this frame.
- pub pipeline: RefCell<Rc<Pipeline>>,
- /// The parent frame's pipeline.
- pub parent: RefCell<Option<Rc<Pipeline>>>,
- /// A vector of child frames.
- pub children: RefCell<Vec<ChildFrameTree>>,
- /// Whether this frame has a compositor layer.
- pub has_compositor_layer: Cell<bool>,
-}
-
-impl FrameTree {
- fn new(pipeline: Rc<Pipeline>, parent_pipeline: Option<Rc<Pipeline>>)
- -> FrameTree {
- FrameTree {
- pipeline: RefCell::new(pipeline.clone()),
- parent: RefCell::new(parent_pipeline),
- children: RefCell::new(vec!()),
- has_compositor_layer: Cell::new(false),
- }
- }
-
- fn add_child(&self, new_child: ChildFrameTree) {
- self.children.borrow_mut().push(new_child);
- }
-}
-
-#[derive(Clone)]
-struct ChildFrameTree {
- frame_tree: Rc<FrameTree>,
- /// Clipping rect representing the size and position, in page coordinates, of the visible
- /// region of the child frame relative to the parent.
- pub rect: Option<TypedRect<PagePx, f32>>,
-}
-
-impl ChildFrameTree {
- fn new(frame_tree: Rc<FrameTree>, rect: Option<TypedRect<PagePx, f32>>) -> ChildFrameTree {
- ChildFrameTree {
- frame_tree: frame_tree,
- rect: rect,
- }
- }
-}
-
-pub struct SendableFrameTree {
- pub pipeline: CompositionPipeline,
- pub children: Vec<SendableChildFrameTree>,
-}
-
-pub struct SendableChildFrameTree {
- pub frame_tree: SendableFrameTree,
- pub rect: Option<TypedRect<PagePx, f32>>,
-}
-
-enum ReplaceResult {
- ReplacedNode(Rc<FrameTree>),
- OriginalNode(Rc<FrameTree>),
-}
-
-impl FrameTree {
- fn to_sendable(&self) -> SendableFrameTree {
- SendableFrameTree {
- pipeline: self.pipeline.borrow().to_sendable(),
- children: self.children
- .borrow()
- .iter()
- .map(|frame_tree| frame_tree.to_sendable())
- .collect(),
- }
- }
+/// Stores the navigation context for a single frame in the frame tree.
+pub struct Frame {
+ prev: Vec<PipelineId>,
+ current: PipelineId,
+ next: Vec<PipelineId>,
}
-trait FrameTreeTraversal {
- fn contains(&self, id: PipelineId) -> bool;
- fn find(&self, id: PipelineId) -> Option<Self>;
- fn find_with_subpage_id(&self, id: Option<SubpageId>) -> Option<Rc<FrameTree>>;
- fn replace_child(&self, id: PipelineId, new_child: Self) -> ReplaceResult;
- fn iter(&self) -> FrameTreeIterator;
-}
-
-impl FrameTreeTraversal for Rc<FrameTree> {
- fn contains(&self, id: PipelineId) -> bool {
- self.iter().any(|frame_tree| id == frame_tree.pipeline.borrow().id)
- }
-
- /// Returns the frame tree whose key is id
- fn find(&self, id: PipelineId) -> Option<Rc<FrameTree>> {
- self.iter().find(|frame_tree| id == frame_tree.pipeline.borrow().id)
- }
-
- /// Returns the frame tree whose subpage is id
- fn find_with_subpage_id(&self, id: Option<SubpageId>) -> Option<Rc<FrameTree>> {
- self.iter().find(|frame_tree| id == frame_tree.pipeline.borrow().subpage_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(&self, id: PipelineId, new_child: Rc<FrameTree>) -> ReplaceResult {
- for frame_tree in self.iter() {
- let mut children = frame_tree.children.borrow_mut();
- let child = children.iter_mut()
- .find(|child| child.frame_tree.pipeline.borrow().id == id);
- match child {
- Some(child) => {
- *new_child.parent.borrow_mut() = child.frame_tree.parent.borrow().clone();
- return ReplaceResult::ReplacedNode(replace(&mut child.frame_tree, new_child));
- }
- None => (),
- }
+impl Frame {
+ fn new(pipeline_id: PipelineId) -> Frame {
+ Frame {
+ prev: vec!(),
+ current: pipeline_id,
+ next: vec!(),
}
- ReplaceResult::OriginalNode(new_child)
}
- fn iter(&self) -> FrameTreeIterator {
- FrameTreeIterator {
- stack: vec!(self.clone()),
- }
+ fn load(&mut self, pipeline_id: PipelineId) -> Vec<PipelineId> {
+ self.prev.push(self.current);
+ self.current = pipeline_id;
+ replace(&mut self.next, vec!())
}
}
-impl ChildFrameTree {
- fn to_sendable(&self) -> SendableChildFrameTree {
- SendableChildFrameTree {
- frame_tree: self.frame_tree.to_sendable(),
- rect: self.rect,
- }
- }
+/// Represents a pending change in the frame tree, that will be applied
+/// once the new pipeline has loaded and completed initial layout / paint.
+struct FrameChange {
+ old_pipeline_id: Option<PipelineId>,
+ new_pipeline_id: PipelineId,
+ painter_ready: bool,
}
/// 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.
-struct FrameTreeIterator {
- stack: Vec<Rc<FrameTree>>,
+struct FrameTreeIterator<'a> {
+ stack: Vec<FrameId>,
+ frames: &'a HashMap<FrameId, Frame>,
+ pipelines: &'a HashMap<PipelineId, Pipeline>,
}
-impl Iterator for FrameTreeIterator {
- type Item = Rc<FrameTree>;
- fn next(&mut self) -> Option<Rc<FrameTree>> {
+impl<'a> Iterator for FrameTreeIterator<'a> {
+ type Item = &'a Frame;
+ fn next(&mut self) -> Option<&'a Frame> {
match self.stack.pop() {
Some(next) => {
- for cft in next.children.borrow().iter() {
- self.stack.push(cft.frame_tree.clone());
- }
- Some(next)
+ let frame = self.frames.get(&next).unwrap();
+ let pipeline = self.pipelines.get(&frame.current).unwrap();
+ self.stack.extend(pipeline.children.iter().map(|&c| c));
+ Some(frame)
}
None => None,
}
}
}
-/// Represents the portion of a page that is changing in navigating.
-struct FrameChange {
- /// The old pipeline ID.
- pub before: Option<PipelineId>,
- /// The resulting frame tree after navigation.
- pub after: Rc<FrameTree>,
- /// The kind of navigation that is occurring.
- pub navigation_type: NavigationType,
-}
-
-/// Stores the Id's of the pipelines previous and next in the browser's history
-struct NavigationContext {
- previous: Vec<Rc<FrameTree>>,
- next: Vec<Rc<FrameTree>>,
- current: Option<Rc<FrameTree>>,
+pub struct SendableFrameTree {
+ pub pipeline: CompositionPipeline,
+ pub rect: Option<TypedRect<PagePx, f32>>,
+ pub children: Vec<SendableFrameTree>,
}
-impl NavigationContext {
- fn new() -> NavigationContext {
- NavigationContext {
- previous: vec!(),
- next: vec!(),
- current: None,
- }
- }
-
- /* Note that the following two methods can fail. They should only be called *
- * when it is known that there exists either a previous page or a next page. */
-
- fn back(&mut self, compositor_proxy: &mut CompositorProxy) -> Rc<FrameTree> {
- self.next.push(self.current.take().unwrap());
- let prev = self.previous.pop().unwrap();
- self.set_current(prev.clone(), compositor_proxy);
- prev
- }
-
- fn forward(&mut self, compositor_proxy: &mut CompositorProxy) -> Rc<FrameTree> {
- self.previous.push(self.current.take().unwrap());
- let next = self.next.pop().unwrap();
- self.set_current(next.clone(), compositor_proxy);
- next
- }
-
- /// Loads a new set of page frames, returning all evicted frame trees
- fn load(&mut self, frame_tree: Rc<FrameTree>, compositor_proxy: &mut CompositorProxy)
- -> Vec<Rc<FrameTree>> {
- debug!("navigating to {:?}", frame_tree.pipeline.borrow().id);
- let evicted = replace(&mut self.next, vec!());
- match self.current.take() {
- Some(current) => self.previous.push(current),
- None => (),
- }
- self.set_current(frame_tree, compositor_proxy);
- evicted
- }
-
- /// Returns the frame trees whose keys are pipeline_id.
- fn find_all(&mut self, pipeline_id: PipelineId) -> Vec<Rc<FrameTree>> {
- let from_current = self.current.iter().filter_map(|frame_tree| {
- frame_tree.find(pipeline_id)
- });
- let from_next = self.next.iter().filter_map(|frame_tree| {
- frame_tree.find(pipeline_id)
- });
- let from_prev = self.previous.iter().filter_map(|frame_tree| {
- frame_tree.find(pipeline_id)
- });
- from_prev.chain(from_current).chain(from_next).collect()
- }
-
- fn contains(&mut self, pipeline_id: PipelineId) -> bool {
- let from_current = self.current.iter();
- let from_next = self.next.iter();
- let from_prev = self.previous.iter();
-
- let mut all_contained = from_prev.chain(from_current).chain(from_next);
- all_contained.any(|frame_tree| {
- frame_tree.contains(pipeline_id)
- })
- }
-
- /// Always use this method to set the currently-displayed frame. It correctly informs the
- /// compositor of the new URLs.
- fn set_current(&mut self, new_frame: Rc<FrameTree>, compositor_proxy: &mut CompositorProxy) {
- self.current = Some(new_frame.clone());
- compositor_proxy.send(CompositorMsg::ChangePageUrl(
- new_frame.pipeline.borrow().id,
- new_frame.pipeline.borrow().url.clone()));
- }
+#[derive(Copy)]
+enum ExitPipelineMode {
+ Normal,
+ Force,
}
impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
@@ -352,10 +183,13 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
font_cache_task: font_cache_task,
storage_task: storage_task,
pipelines: HashMap::new(),
- navigation_context: NavigationContext::new(),
- next_pipeline_id: PipelineId(0),
+ frames: HashMap::new(),
+ pipeline_to_frame_map: HashMap::new(),
+ subpage_map: HashMap::new(),
pending_frames: vec!(),
- pending_sizes: HashMap::new(),
+ next_pipeline_id: PipelineId(0),
+ root_frame_id: None,
+ next_frame_id: FrameId(0),
time_profiler_chan: time_profiler_chan,
window_size: WindowSizeData {
visible_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
@@ -379,48 +213,81 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
/// Helper function for creating a pipeline
fn new_pipeline(&mut self,
- id: PipelineId,
- parent: Option<(PipelineId, SubpageId)>,
- script_pipeline: Option<Rc<Pipeline>>,
+ parent_info: Option<(PipelineId, SubpageId)>,
+ script_channel: Option<ScriptControlChan>,
load_data: LoadData)
- -> Rc<Pipeline> {
- let pipe = Pipeline::create::<LTF, STF>(id,
- parent,
- self.chan.clone(),
- self.compositor_proxy.clone_compositor_proxy(),
- self.devtools_chan.clone(),
- self.image_cache_task.clone(),
- self.font_cache_task.clone(),
- self.resource_task.clone(),
- self.storage_task.clone(),
- self.time_profiler_chan.clone(),
- self.window_size,
- script_pipeline,
- load_data);
- Rc::new(pipe)
- }
-
- /// Helper function for getting a unique pipeline ID.
- fn get_next_pipeline_id(&mut self) -> PipelineId {
- let id = self.next_pipeline_id;
+ -> PipelineId {
+ let pipeline_id = self.next_pipeline_id;
let PipelineId(ref mut i) = self.next_pipeline_id;
*i += 1;
- id
+
+ // If this is a child frame, don't attach the main window
+ // size to this pipeline - it will get its window size
+ // assigned by the parent layout task.
+ let window_size = if parent_info.is_some() {
+ None
+ } else {
+ Some(self.window_size)
+ };
+
+ let pipeline = Pipeline::create::<LTF, STF>(pipeline_id,
+ parent_info,
+ self.chan.clone(),
+ self.compositor_proxy.clone_compositor_proxy(),
+ self.devtools_chan.clone(),
+ self.image_cache_task.clone(),
+ self.font_cache_task.clone(),
+ self.resource_task.clone(),
+ self.storage_task.clone(),
+ self.time_profiler_chan.clone(),
+ window_size,
+ script_channel,
+ load_data);
+
+ assert!(!self.pipelines.contains_key(&pipeline_id));
+ self.pipelines.insert(pipeline_id, pipeline);
+ pipeline_id
+ }
+
+ // Push a new (loading) pipeline to the list of pending frame changes
+ fn push_pending_frame(&mut self, new_pipeline_id: PipelineId,
+ old_pipeline_id: Option<PipelineId>) {
+ self.pending_frames.push(FrameChange {
+ old_pipeline_id: old_pipeline_id,
+ new_pipeline_id: new_pipeline_id,
+ painter_ready: false,
+ });
}
- /// Convenience function for getting the currently active frame tree.
- /// The currently active frame tree should always be the current painter
- fn current_frame<'a>(&'a self) -> &'a Option<Rc<FrameTree>> {
- &self.navigation_context.current
+ // Get an iterator for the current frame tree. Specify self.root_frame_id to
+ // iterate the entire tree, or a specific frame id to iterate only that sub-tree.
+ fn current_frame_tree_iter(&self, frame_id_root: Option<FrameId>) -> FrameTreeIterator {
+ let mut stack = vec!();
+ if let Some(frame_id_root) = frame_id_root {
+ stack.push(frame_id_root);
+ }
+ FrameTreeIterator {
+ stack: stack,
+ pipelines: &self.pipelines,
+ frames: &self.frames,
+ }
}
- /// Returns both the navigation context and pending frame trees whose keys are pipeline_id.
- fn find_all(&mut self, pipeline_id: PipelineId) -> Vec<Rc<FrameTree>> {
- let mut matching_navi_frames = self.navigation_context.find_all(pipeline_id);
- matching_navi_frames.extend(self.pending_frames.iter().filter_map(|frame_change| {
- frame_change.after.find(pipeline_id)
- }));
- matching_navi_frames
+ // Create a new frame and update the internal bookkeeping.
+ fn new_frame(&mut self, pipeline_id: PipelineId) -> FrameId {
+ let id = self.next_frame_id;
+ let FrameId(ref mut i) = self.next_frame_id;
+ *i += 1;
+
+ let frame = Frame::new(pipeline_id);
+
+ assert!(!self.pipeline_to_frame_map.contains_key(&pipeline_id));
+ assert!(!self.frames.contains_key(&id));
+
+ self.pipeline_to_frame_map.insert(pipeline_id, id);
+ self.frames.insert(id, frame);
+
+ id
}
/// Handles loading pages, navigation, and granting access to the compositor
@@ -431,8 +298,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.handle_exit();
return false;
}
- ConstellationMsg::Failure(Failure { pipeline_id, parent }) => {
- self.handle_failure_msg(pipeline_id, parent);
+ ConstellationMsg::Failure(Failure { pipeline_id, parent_info }) => {
+ self.handle_failure_msg(pipeline_id, parent_info);
}
// This should only be called once per constellation, and only by the browser
ConstellationMsg::InitLoadUrl(url) => {
@@ -445,7 +312,11 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
debug!("constellation got frame rect message");
self.handle_frame_rect_msg(pipeline_id, subpage_id, Rect::from_untyped(&rect));
}
- ConstellationMsg::ScriptLoadedURLInIFrame(url, source_pipeline_id, new_subpage_id, old_subpage_id, sandbox) => {
+ ConstellationMsg::ScriptLoadedURLInIFrame(url,
+ source_pipeline_id,
+ new_subpage_id,
+ old_subpage_id,
+ sandbox) => {
debug!("constellation got iframe URL load message");
self.handle_script_loaded_url_in_iframe_msg(url,
source_pipeline_id,
@@ -507,8 +378,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.compositor_proxy.send(CompositorMsg::ShutdownComplete);
}
- fn handle_failure_msg(&mut self, pipeline_id: PipelineId, parent: Option<(PipelineId, SubpageId)>) {
- debug!("handling failure message from pipeline {:?}, {:?}", pipeline_id, parent);
+ fn handle_failure_msg(&mut self,
+ pipeline_id: PipelineId,
+ parent_info: Option<(PipelineId, SubpageId)>) {
+ debug!("handling failure message from pipeline {:?}, {:?}", pipeline_id, parent_info);
if opts::get().hard_fail {
// It's quite difficult to make Servo exit cleanly if some tasks have failed.
@@ -519,199 +392,56 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
unsafe { libc::exit(1); }
}
- let old_pipeline = match self.pipelines.get(&pipeline_id) {
- None => {
- debug!("no existing pipeline found; bailing out of failure recovery.");
- return; // already failed?
- }
- Some(pipeline) => pipeline.clone()
- };
-
- old_pipeline.force_exit();
- self.compositor_proxy.send(CompositorMsg::PaintTaskExited(old_pipeline.id));
- self.pipelines.remove(&pipeline_id);
+ self.close_pipeline(pipeline_id, ExitPipelineMode::Force);
loop {
- let idx = self.pending_frames.iter().position(|pending| {
- pending.after.pipeline.borrow().id == pipeline_id
- });
- match idx {
- Some(idx) => {
+ let pending_pipeline_id = self.pending_frames.iter().find(|pending| {
+ pending.old_pipeline_id == Some(pipeline_id)
+ }).map(|frame| frame.new_pipeline_id);
+ match pending_pipeline_id {
+ Some(pending_pipeline_id) => {
debug!("removing pending frame change for failed pipeline");
- self.pending_frames[idx].after.pipeline.borrow().force_exit();
- self.compositor_proxy.send(CompositorMsg::PaintTaskExited(old_pipeline.id));
- self.pending_frames.remove(idx);
+ self.close_pipeline(pending_pipeline_id, ExitPipelineMode::Force);
},
None => break,
}
}
debug!("creating replacement pipeline for about:failure");
- let new_id = self.get_next_pipeline_id();
- let pipeline = self.new_pipeline(new_id, parent, None,
- LoadData::new(Url::parse("about:failure").unwrap()));
-
- self.browse(Some(pipeline_id),
- Rc::new(FrameTree::new(pipeline.clone(), None)),
- NavigationType::Load);
+ let new_pipeline_id = self.new_pipeline(parent_info, None,
+ LoadData::new(Url::parse("about:failure").unwrap()));
- self.pipelines.insert(new_id, pipeline);
- }
-
- /// Performs navigation. This pushes a `FrameChange` object onto the list of pending frames.
- ///
- /// TODO(pcwalton): Send a `BeforeBrowse` message to the embedder and allow cancellation.
- fn browse(&mut self,
- before: Option<PipelineId>,
- after: Rc<FrameTree>,
- navigation_type: NavigationType) {
- self.pending_frames.push(FrameChange {
- before: before,
- after: after,
- navigation_type: navigation_type,
- });
+ self.push_pending_frame(new_pipeline_id, Some(pipeline_id));
}
fn handle_init_load(&mut self, url: Url) {
- let next_pipeline_id = self.get_next_pipeline_id();
- let pipeline = self.new_pipeline(next_pipeline_id, None, None, LoadData::new(url));
- self.browse(None,
- Rc::new(FrameTree::new(pipeline.clone(), None)),
- NavigationType::Load);
- self.pipelines.insert(pipeline.id, pipeline);
+ let root_pipeline_id = self.new_pipeline(None, None, LoadData::new(url));
+ self.push_pending_frame(root_pipeline_id, None);
}
- fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId,
+ fn handle_frame_rect_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId,
rect: TypedRect<PagePx, f32>) {
- debug!("Received frame rect {:?} from {:?}, {:?}", rect, pipeline_id, subpage_id);
- let mut already_sent = HashSet::new();
-
- // Returns true if a child frame tree's subpage id matches the given subpage id
- let subpage_eq = |&:child_frame_tree: & &mut ChildFrameTree| {
- child_frame_tree.frame_tree.pipeline.borrow().
- subpage_id().expect("Constellation:
- child frame does not have a subpage id. This should not be possible.")
- == subpage_id
- };
-
- let frames = self.find_all(pipeline_id);
-
- {
- // If the subframe is in the current frame tree, the compositor needs the new size
- for current_frame in self.navigation_context.current.iter() {
- debug!("Constellation: Sending size for frame in current frame tree.");
- let source_frame = current_frame.find(pipeline_id);
- for source_frame in source_frame.iter() {
- let mut children = source_frame.children.borrow_mut();
- match children.iter_mut().find(|child| subpage_eq(child)) {
- None => {}
- Some(child) => {
- let has_compositor_layer = child.frame_tree.has_compositor_layer.get();
- update_child_rect(child,
- rect,
- has_compositor_layer,
- &mut already_sent,
- &mut self.compositor_proxy,
- self.window_size.device_pixel_ratio)
- }
- }
- }
- }
-
- // Update all frames with matching pipeline- and subpage-ids
- for frame_tree in frames.iter() {
- let mut children = frame_tree.children.borrow_mut();
- let found_child = children.iter_mut().find(|child| subpage_eq(child));
- found_child.map(|child| {
- update_child_rect(child,
- rect,
- false,
- &mut already_sent,
- &mut self.compositor_proxy,
- self.window_size.device_pixel_ratio)
- });
- }
- }
-
- // 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);
- }
-
- // Update a child's frame rect and inform its script task of the change,
- // if it hasn't been already. Optionally inform the compositor if
- // resize happens immediately.
- fn update_child_rect(child_frame_tree: &mut ChildFrameTree,
- rect: TypedRect<PagePx,f32>,
- is_active: bool,
- already_sent: &mut HashSet<PipelineId>,
- compositor_proxy: &mut Box<CompositorProxy>,
- device_pixel_ratio: ScaleFactor<ViewportPx,DevicePixel,f32>) {
- child_frame_tree.rect = Some(rect);
- // NOTE: work around borrowchk issues
- let pipeline = &*child_frame_tree.frame_tree.pipeline.borrow();
- if !already_sent.contains(&pipeline.id) {
- if is_active {
- let ScriptControlChan(ref script_chan) = pipeline.script_chan;
- script_chan.send(ConstellationControlMsg::Resize(pipeline.id, WindowSizeData {
- visible_viewport: rect.size,
- initial_viewport: rect.size * ScaleFactor(1.0),
- device_pixel_ratio: device_pixel_ratio,
- })).unwrap();
- compositor_proxy.send(CompositorMsg::SetLayerRect(
- pipeline.id,
- LayerId::null(),
- rect.to_untyped()));
- } else {
- already_sent.insert(pipeline.id);
- }
- };
- }
- }
-
- fn update_child_pipeline(&mut self,
- frame_tree: Rc<FrameTree>,
- new_pipeline: Rc<Pipeline>,
- old_subpage_id: SubpageId) {
- let existing_tree = match frame_tree.find_with_subpage_id(Some(old_subpage_id)) {
- Some(existing_tree) => existing_tree.clone(),
- None => panic!("Tried to update non-existing frame tree with pipeline={:?} subpage={:?}",
- new_pipeline.id,
- old_subpage_id),
+ // Store the new rect inside the pipeline
+ let (pipeline_id, script_chan) = {
+ let pipeline = self.find_subpage(containing_pipeline_id, subpage_id);
+ pipeline.rect = Some(rect);
+ (pipeline.id, pipeline.script_chan.clone())
};
- let old_pipeline = existing_tree.pipeline.borrow().clone();
- *existing_tree.pipeline.borrow_mut() = new_pipeline.clone();
-
- // If we have not yet sent this frame to the compositor for layer creation, we don't
- // need to inform the compositor of updates to the pipeline.
- if !existing_tree.has_compositor_layer.get() {
- return;
- }
-
- let (chan, port) = channel();
- self.compositor_proxy.send(CompositorMsg::ChangeLayerPipelineAndRemoveChildren(
- old_pipeline.to_sendable(),
- new_pipeline.to_sendable(),
- chan));
- let _ = port.recv();
- }
+ let ScriptControlChan(ref script_chan) = script_chan;
+ script_chan.send(ConstellationControlMsg::Resize(pipeline_id, WindowSizeData {
+ visible_viewport: rect.size,
+ initial_viewport: rect.size * ScaleFactor(1.0),
+ device_pixel_ratio: self.window_size.device_pixel_ratio,
+ })).unwrap();
- fn create_or_update_child_pipeline(&mut self,
- frame_tree: Rc<FrameTree>,
- new_pipeline: Rc<Pipeline>,
- new_rect: Option<TypedRect<PagePx, f32>>,
- old_subpage_id: Option<SubpageId>) {
- match old_subpage_id {
- Some(old_subpage_id) =>
- self.update_child_pipeline(frame_tree.clone(), new_pipeline, old_subpage_id),
- None => {
- let child_tree = Rc::new(
- FrameTree::new(new_pipeline,
- Some(frame_tree.pipeline.borrow().clone())));
- frame_tree.add_child(ChildFrameTree::new(child_tree, new_rect));
- }
+ // 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()));
}
}
@@ -721,56 +451,41 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// page navigation.
fn handle_script_loaded_url_in_iframe_msg(&mut self,
url: Url,
- containing_page_pipeline_id: PipelineId,
+ containing_pipeline_id: PipelineId,
new_subpage_id: SubpageId,
old_subpage_id: Option<SubpageId>,
sandbox: IFrameSandboxState) {
- // Start by finding the frame trees matching the pipeline id,
- // and add the new pipeline to their sub frames.
- let frame_trees = self.find_all(containing_page_pipeline_id);
- if frame_trees.is_empty() {
- panic!("Constellation: source pipeline id of ScriptLoadedURLInIFrame is not in
- navigation context, nor is it in a pending frame. This should be
- impossible.");
- }
-
// 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.get(&containing_page_pipeline_id).expect("Constellation:
- source Id of ScriptLoadedURLInIFrameMsg does have an associated pipeline in
- constellation. This should be impossible.").clone();
-
- let source_url = source_pipeline.url.clone();
-
- let same_script = (source_url.host() == url.host() &&
- source_url.port() == url.port()) &&
- sandbox == IFrameSandboxState::IFrameUnsandboxed;
- // FIXME(tkuehn): Need to follow the standardized spec for checking same-origin
- // Reuse the script task if the URL is same-origin
- let script_pipeline = if same_script {
- debug!("Constellation: loading same-origin iframe at {:?}", url);
- Some(source_pipeline.clone())
- } else {
- debug!("Constellation: loading cross-origin iframe at {:?}", url);
- None
+ let script_chan = {
+ let source_pipeline = self.pipeline(containing_pipeline_id);
+
+ let source_url = source_pipeline.url.clone();
+
+ let same_script = (source_url.host() == url.host() &&
+ source_url.port() == url.port()) &&
+ sandbox == IFrameSandboxState::IFrameUnsandboxed;
+
+ // FIXME(tkuehn): Need to follow the standardized spec for checking same-origin
+ // Reuse the script task if the URL is same-origin
+ if same_script {
+ debug!("Constellation: loading same-origin iframe at {:?}", url);
+ Some(source_pipeline.script_chan.clone())
+ } else {
+ debug!("Constellation: loading cross-origin iframe at {:?}", url);
+ None
+ }
};
- let new_frame_pipeline_id = self.get_next_pipeline_id();
- let pipeline = self.new_pipeline(
- new_frame_pipeline_id,
- Some((containing_page_pipeline_id, new_subpage_id)),
- script_pipeline,
- LoadData::new(url)
- );
-
- let rect = self.pending_sizes.remove(&(containing_page_pipeline_id, new_subpage_id));
- for frame_tree in frame_trees.iter() {
- self.create_or_update_child_pipeline(frame_tree.clone(),
- pipeline.clone(),
- rect,
- old_subpage_id);
- }
- self.pipelines.insert(pipeline.id, pipeline);
+ // Create the new pipeline, attached to the parent and push to pending frames
+ let new_pipeline_id = self.new_pipeline(Some((containing_pipeline_id, new_subpage_id)),
+ script_chan,
+ LoadData::new(url));
+ self.subpage_map.insert((containing_pipeline_id, new_subpage_id), new_pipeline_id);
+ let old_pipeline_id = old_subpage_id.map(|old_subpage_id| {
+ self.find_subpage(containing_pipeline_id, old_subpage_id).id
+ });
+ self.push_pending_frame(new_pipeline_id, old_pipeline_id);
}
fn handle_set_cursor_msg(&mut self, cursor: Cursor) {
@@ -778,103 +493,113 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
- let url = load_data.url.to_string();
- debug!("Constellation: received message to load {:?}", url);
- // Make sure no pending page would be overridden.
- let source_frame = self.current_frame().as_ref().unwrap().find(source_id).expect(
- "Constellation: received a LoadUrl message 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().as_ref().unwrap().find(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;
+ // If this load targets an iframe, its framing element may exist
+ // in a separate script task than the framed document that initiated
+ // the new load. The framing element must be notified about the
+ // requested change so it can update its internal state.
+ match self.pipeline(source_id).parent_info {
+ Some((parent_pipeline_id, subpage_id)) => {
+ // Message the constellation to find the script task for this iframe
+ // and issue an iframe load through there.
+ let parent_pipeline = self.pipeline(parent_pipeline_id);
+ let ScriptControlChan(ref script_channel) = parent_pipeline.script_chan;
+ script_channel.send(ConstellationControlMsg::Navigate(parent_pipeline_id,
+ subpage_id,
+ load_data)).unwrap();
+ }
+ None => {
+ // Make sure no pending page would be overridden.
+ for frame_change in self.pending_frames.iter() {
+ if frame_change.old_pipeline_id == Some(source_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 overridden by changing the subframe associated with source_id.
+
+ // Create the new pipeline
+ let new_pipeline_id = self.new_pipeline(None, None, load_data);
+ self.push_pending_frame(new_pipeline_id, Some(source_id));
+
+ // Send message to ScriptTask that will suspend all timers
+ let old_pipeline = self.pipelines.get(&source_id).unwrap();
+ old_pipeline.freeze();
}
}
- // Being here means either there are no pending frames, or none of the pending
- // changes would be overridden by changing the subframe associated with source_id.
-
- let parent = source_frame.parent.clone();
- let parent_id = source_frame.pipeline.borrow().parent;
- let next_pipeline_id = self.get_next_pipeline_id();
- let pipeline = self.new_pipeline(next_pipeline_id, parent_id, None, load_data);
- self.browse(Some(source_id),
- Rc::new(FrameTree::new(pipeline.clone(),
- parent.borrow().clone())),
- NavigationType::Load);
- // Send message to ScriptTask that will suspend all timers
- source_frame.pipeline.borrow().freeze();
- 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 {
- NavigationDirection::Forward => {
- if self.navigation_context.next.is_empty() {
- debug!("no next page to navigate to");
- return;
- } else {
- let old = self.current_frame().as_ref().unwrap();
- for frame in old.iter() {
- frame.pipeline.borrow().revoke_paint_permission();
- frame.pipeline.borrow().freeze();
+ // TODO(gw): Extend handle_navigate_msg to allow passing the frame
+ // id that this navigation should apply to, instead of always referencing
+ // the frame history in the root frame. To also allow navigations within
+ // subframes to affect the parent navigation history, this should bubble
+ // up the navigation items to each parent.
+ let frame_id = self.root_frame_id.unwrap();
+
+ // Get the ids for the previous and next pipelines.
+ let (prev_pipeline_id, next_pipeline_id) = {
+ let frame = self.mut_frame(frame_id);
+
+ match direction {
+ NavigationDirection::Forward => {
+ if frame.next.is_empty() {
+ debug!("no next page to navigate to");
+ return;
+ } else {
+ let prev = frame.current;
+
+ frame.prev.push(frame.current);
+ let next = frame.next.pop().unwrap();
+ frame.current = next;
+
+ (prev, next)
}
}
- self.navigation_context.forward(&mut *self.compositor_proxy)
- }
- NavigationDirection::Back => {
- if self.navigation_context.previous.is_empty() {
- debug!("no previous page to navigate to");
- return;
- } else {
- let old = self.current_frame().as_ref().unwrap();
- for frame in old.iter() {
- frame.pipeline.borrow().revoke_paint_permission();
- frame.pipeline.borrow().freeze();
+ NavigationDirection::Back => {
+ if frame.prev.is_empty() {
+ debug!("no previous page to navigate to");
+ return;
+ } else {
+ let prev = frame.current;
+
+ frame.next.push(frame.current);
+ let next = frame.prev.pop().unwrap();
+ frame.current = next;
+
+ (prev, next)
}
}
- self.navigation_context.back(&mut *self.compositor_proxy)
}
};
- for frame in destination_frame.iter() {
- frame.pipeline.borrow().activate();
- frame.pipeline.borrow().thaw();
- }
- self.send_frame_tree_and_grant_paint_permission(destination_frame);
+ // Suspend the old pipeline, and resume the new one.
+ self.pipeline(prev_pipeline_id).freeze();
+ self.pipeline(next_pipeline_id).thaw();
- }
-
- fn pipeline_is_in_current_frame(&self, pipeline_id: PipelineId) -> bool {
- self.current_frame().iter()
- .any(|current_frame| current_frame.contains(pipeline_id))
+ // Set paint permissions correctly for the compositor layers.
+ self.revoke_paint_permission(prev_pipeline_id);
+ self.send_frame_tree_and_grant_paint_permission();
}
fn handle_key_msg(&self, key: Key, state: KeyState, mods: KeyModifiers) {
- match *self.current_frame() {
- Some(ref frame) => {
- let ScriptControlChan(ref chan) = frame.pipeline.borrow().script_chan;
- chan.send(ConstellationControlMsg::SendEvent(
- frame.pipeline.borrow().id,
- CompositorEvent::KeyEvent(key, state, mods))).unwrap();
- },
- None => self.compositor_proxy.clone_compositor_proxy()
- .send(CompositorMsg::KeyEvent(key, state, mods))
+ match self.root_frame_id {
+ Some(root_frame_id) => {
+ let root_frame = self.frame(root_frame_id);
+ let pipeline = self.pipeline(root_frame.current);
+ let ScriptControlChan(ref chan) = pipeline.script_chan;
+ let event = CompositorEvent::KeyEvent(key, state, mods);
+ chan.send(ConstellationControlMsg::SendEvent(pipeline.id,
+ event)).unwrap();
+ }
+ None => {
+ let event = CompositorMsg::KeyEvent(key, state, mods);
+ self.compositor_proxy.clone_compositor_proxy().send(event);
+ }
}
-
}
fn handle_get_pipeline_title_msg(&mut self, pipeline_id: PipelineId) {
@@ -887,238 +612,241 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
}
+ fn add_or_replace_pipeline_in_frame_tree(&mut self, frame_change: FrameChange) {
+ let evicted_frames = match frame_change.old_pipeline_id {
+ Some(old_pipeline_id) => {
+ // The new pipeline is replacing an old one.
+ // Remove paint permissions for the pipeline being replaced.
+ self.revoke_paint_permission(old_pipeline_id);
+
+ // Add new pipeline to navigation frame, and return frames evicted from history.
+ let frame_id = *self.pipeline_to_frame_map.get(&old_pipeline_id).unwrap();
+ let evicted_frames = self.mut_frame(frame_id).load(frame_change.new_pipeline_id);
+ self.pipeline_to_frame_map.insert(frame_change.new_pipeline_id, frame_id);
+
+ Some(evicted_frames)
+ }
+ None => {
+ // The new pipeline is in a new frame with no history
+ let frame_id = self.new_frame(frame_change.new_pipeline_id);
+
+ // If a child frame, add it to the parent pipeline. Otherwise
+ // it must surely be the root frame being created!
+ match self.pipeline(frame_change.new_pipeline_id).parent_info {
+ Some((parent_id, _)) => {
+ self.mut_pipeline(parent_id).add_child(frame_id);
+ }
+ None => {
+ assert!(self.root_frame_id.is_none());
+ self.root_frame_id = Some(frame_id);
+ }
+ }
+
+ // No evicted frames if a new frame was created
+ None
+ }
+ };
+
+ // Build frame tree and send permission
+ self.send_frame_tree_and_grant_paint_permission();
+
+ // Remove any evicted frames
+ if let Some(evicted_frames) = evicted_frames {
+ for pipeline_id in evicted_frames.iter() {
+ self.close_pipeline(*pipeline_id, ExitPipelineMode::Normal);
+ }
+ }
+ }
+
fn handle_painter_ready_msg(&mut self, pipeline_id: PipelineId) {
debug!("Painter {:?} 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.
- // Messages originating in the current frame are not navigations;
- // they may come from a page load in a subframe.
+ // If this pipeline is already part of the current frame tree,
+ // we don't need to do anything.
if self.pipeline_is_in_current_frame(pipeline_id) {
- self.create_compositor_layer_for_iframe_if_necessary(pipeline_id);
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.
+ // If it is found, mark this pending frame as ready to be enabled.
let pending_index = self.pending_frames.iter().rposition(|frame_change| {
- frame_change.after.pipeline.borrow().id == pipeline_id
+ frame_change.new_pipeline_id == pipeline_id
});
- match pending_index {
- Some(pending_index) => {
- let frame_change = self.pending_frames.swap_remove(pending_index);
- let to_add = frame_change.after.clone();
-
- // Create the next frame tree that will be given to the compositor
- let next_frame_tree = if to_add.parent.borrow().is_some() {
- // NOTE: work around borrowchk issues
- self.current_frame().as_ref().unwrap().clone()
- } else {
- to_add.clone()
- };
-
- // If there are frames to revoke permission from, do so now.
- match frame_change.before {
- Some(revoke_id) if self.current_frame().is_some() => {
- debug!("Constellation: revoking permission from {:?}", revoke_id);
- let current_frame = self.current_frame().as_ref().unwrap();
-
- let to_revoke = current_frame.find(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.borrow().revoke_paint_permission();
- }
-
- // If to_add is not the root frame, then replace revoked_frame with it.
- // This conveniently keeps scissor rect size intact.
- // NOTE: work around borrowchk issue
- let mut flag = false;
- {
- if to_add.parent.borrow().is_some() {
- debug!("Constellation: replacing {:?} with {:?} in {:?}",
- revoke_id, to_add.pipeline.borrow().id,
- next_frame_tree.pipeline.borrow().id);
- flag = true;
- }
- }
- if flag {
- next_frame_tree.replace_child(revoke_id, to_add);
- }
- }
-
- _ => {
- // Add to_add to parent's children, if it is not the root
- let parent = &to_add.parent;
- for parent in parent.borrow().iter() {
- let subpage_id = to_add.pipeline.borrow().subpage_id()
- .expect("Constellation:
- Child frame's subpage id is None. This should be impossible.");
- let rect = self.pending_sizes.remove(&(parent.id, subpage_id));
- let parent = next_frame_tree.find(parent.id).expect(
- "Constellation: pending frame has a parent frame that is not
- active. This is a bug.");
- parent.add_child(ChildFrameTree::new(to_add.clone(), rect));
- }
- }
- }
+ if let Some(pending_index) = pending_index {
+ self.pending_frames[pending_index].painter_ready = true;
+ }
+
+ // This is a bit complex. We need to loop through pending frames and find
+ // ones that can be swapped. A frame can be swapped (enabled) once it is
+ // ready to paint (has painter_ready set), and also has no dependencies
+ // (i.e. the pipeline it is replacing has been enabled and now has a frame).
+ // The outer loop is required because any time a pipeline is enabled, that
+ // may affect whether other pending frames are now able to be enabled. On the
+ // other hand, if no frames can be enabled after looping through all pending
+ // frames, we can safely exit the loop, knowing that we will need to wait on
+ // a dependent pipeline to be ready to paint.
+ loop {
+ let valid_frame_change = self.pending_frames.iter().rposition(|frame_change| {
+ let waiting_on_dependency = frame_change.old_pipeline_id.map_or(false, |old_pipeline_id| {
+ self.pipeline_to_frame_map.get(&old_pipeline_id).is_none()
+ });
+ frame_change.painter_ready && !waiting_on_dependency
+ });
- self.send_frame_tree_and_grant_paint_permission(next_frame_tree.clone());
- self.handle_evicted_frames_for_load_navigation(next_frame_tree,
- frame_change.navigation_type);
- },
- None => (),
+ if let Some(valid_frame_change) = valid_frame_change {
+ let frame_change = self.pending_frames.swap_remove(valid_frame_change);
+ self.add_or_replace_pipeline_in_frame_tree(frame_change);
+ } else {
+ break;
+ }
}
}
/// Called when the window is resized.
fn handle_resized_window_msg(&mut self, new_size: WindowSizeData) {
- let mut already_seen = HashSet::new();
- for frame_tree in self.current_frame().iter() {
- debug!("constellation sending resize message to active frame");
- let pipeline = &*frame_tree.pipeline.borrow();;
+ debug!("handle_resized_window_msg: {:?} {:?}", new_size.initial_viewport.to_untyped(),
+ new_size.visible_viewport.to_untyped());
+
+ if let Some(root_frame_id) = self.root_frame_id {
+ // Send Resize (or ResizeInactive) messages to each
+ // pipeline in the frame tree.
+ let frame = self.frames.get(&root_frame_id).unwrap();
+
+ let pipeline = self.pipelines.get(&frame.current).unwrap();
let ScriptControlChan(ref chan) = pipeline.script_chan;
let _ = chan.send(ConstellationControlMsg::Resize(pipeline.id, new_size));
- already_seen.insert(pipeline.id);
- }
- for frame_tree in self.navigation_context.previous.iter()
- .chain(self.navigation_context.next.iter()) {
- let pipeline = &*frame_tree.pipeline.borrow();
- if !already_seen.contains(&pipeline.id) {
- debug!("constellation sending resize message to inactive frame");
+
+ for pipeline_id in frame.prev.iter().chain(frame.next.iter()) {
+ let pipeline = self.pipelines.get(pipeline_id).unwrap();
let ScriptControlChan(ref chan) = pipeline.script_chan;
let _ = chan.send(ConstellationControlMsg::ResizeInactive(pipeline.id, new_size));
- already_seen.insert(pipeline.id);
}
}
- // If there are any pending outermost frames, then tell them to resize. (This is how the
- // initial window size gets sent to the first page loaded, giving it permission to reflow.)
- for change in self.pending_frames.iter() {
- let frame_tree = &change.after;
- if frame_tree.parent.borrow().is_none() {
- debug!("constellation sending resize message to pending outer frame ({:?})",
- frame_tree.pipeline.borrow().id);
- let ScriptControlChan(ref chan) = frame_tree.pipeline.borrow().script_chan;
- let _ = chan.send(ConstellationControlMsg::Resize(
- frame_tree.pipeline.borrow().id, new_size));
+ // Send resize message to any pending pipelines that aren't loaded yet.
+ for pending_frame in self.pending_frames.iter() {
+ let pipeline = self.pipelines.get(&pending_frame.new_pipeline_id).unwrap();
+ if pipeline.parent_info.is_none() {
+ let ScriptControlChan(ref chan) = pipeline.script_chan;
+ let _ = chan.send(ConstellationControlMsg::Resize(pipeline.id, new_size));
}
}
self.window_size = new_size;
}
- // Close all pipelines at and beneath a given frame
- fn close_pipelines(&mut self, frame_tree: Rc<FrameTree>) {
- // TODO(tkuehn): should only exit once per unique script task,
- // and then that script task will handle sub-exits
- for frame_tree in frame_tree.iter() {
- frame_tree.pipeline.borrow().exit(PipelineExitType::PipelineOnly);
- self.compositor_proxy.send(CompositorMsg::PaintTaskExited(frame_tree.pipeline.borrow().id));
- self.pipelines.remove(&frame_tree.pipeline.borrow().id);
+ // Close a frame (and all children)
+ fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
+ let frame = self.frames.remove(&frame_id).unwrap();
+
+ self.close_pipeline(frame.current, exit_mode);
+
+ for pipeline_id in frame.prev.iter().chain(frame.next.iter()) {
+ self.close_pipeline(*pipeline_id, exit_mode);
}
}
- fn handle_evicted_frames(&mut self, evicted_frames: Vec<Rc<FrameTree>>) {
- for frame_tree in evicted_frames.into_iter() {
- if !self.navigation_context.contains(frame_tree.pipeline.borrow().id) {
- self.close_pipelines(frame_tree);
- } else {
- let frames = frame_tree.children.borrow().iter()
- .map(|child| child.frame_tree.clone()).collect();
- self.handle_evicted_frames(frames);
- }
+ // Close all pipelines at and beneath a given frame
+ fn close_pipeline(&mut self, pipeline_id: PipelineId, exit_mode: ExitPipelineMode) {
+ let pipeline = self.pipelines.remove(&pipeline_id).unwrap();
+
+ // Remove any child frames
+ for child in pipeline.children.iter() {
+ self.close_frame(*child, exit_mode);
}
- }
+ // If a child pipeline, remove from subpage map
+ if let Some(info) = pipeline.parent_info {
+ self.subpage_map.remove(&info);
+ }
- fn handle_evicted_frames_for_load_navigation(&mut self,
- frame_tree: Rc<FrameTree>,
- navigation_type: NavigationType) {
- // Don't call navigation_context.load() on a Navigate type (or None, as in the case of
- // parsed iframes that finish loading).
- match navigation_type {
- NavigationType::Load => {
- debug!("Evicting frames for NavigationType::Load");
- let evicted_frames = self.navigation_context.load(frame_tree,
- &mut *self.compositor_proxy);
- self.handle_evicted_frames(evicted_frames);
- }
- _ => {}
+ // Remove assocation between this pipeline and its holding frame
+ self.pipeline_to_frame_map.remove(&pipeline_id);
+
+ // Inform script, compositor that this pipeline has exited.
+ match exit_mode {
+ ExitPipelineMode::Normal => pipeline.exit(PipelineExitType::PipelineOnly),
+ ExitPipelineMode::Force => pipeline.force_exit(),
}
+ self.compositor_proxy.send(CompositorMsg::PaintTaskExited(pipeline_id));
}
- // Grants a frame tree permission to paint; optionally updates navigation to reflect a new page
- fn send_frame_tree_and_grant_paint_permission(&mut self, frame_tree: Rc<FrameTree>) {
- debug!("Constellation sending SetFrameTree");
- let (chan, port) = channel();
- self.compositor_proxy.send(CompositorMsg::SetFrameTree(frame_tree.to_sendable(),
- chan,
- self.chan.clone()));
- if port.recv().is_err() {
- debug!("Compositor has discarded SetFrameTree");
- return; // Our message has been discarded, probably shutting down.
+ // Convert a frame to a sendable form to pass to the compositor
+ fn frame_to_sendable(&self, frame_id: FrameId) -> SendableFrameTree {
+ let pipeline = self.pipeline(self.frame(frame_id).current);
+
+ let mut frame_tree = SendableFrameTree {
+ pipeline: pipeline.to_sendable(),
+ rect: pipeline.rect,
+ children: vec!(),
+ };
+
+ for child_frame_id in pipeline.children.iter() {
+ frame_tree.children.push(self.frame_to_sendable(*child_frame_id));
}
- let iter = frame_tree.iter();
- for frame in iter {
- frame.has_compositor_layer.set(true);
- frame.pipeline.borrow().grant_paint_permission();
+ frame_tree
+ }
+
+ // Revoke paint permission from a pipeline, and all children.
+ fn revoke_paint_permission(&self, pipeline_id: PipelineId) {
+ let frame_id = self.pipeline_to_frame_map.get(&pipeline_id).map(|frame_id| *frame_id);
+ for frame in self.current_frame_tree_iter(frame_id) {
+ self.pipeline(frame.current).revoke_paint_permission();
}
}
- fn find_child_parent_pair_in_frame_tree(&self,
- frame_tree: Rc<FrameTree>,
- child_pipeline_id: PipelineId)
- -> Option<(ChildFrameTree, Rc<FrameTree>)> {
- for child in frame_tree.children.borrow().iter() {
- let child_frame_tree = child.frame_tree.clone();
- if child.frame_tree.pipeline.borrow().id == child_pipeline_id {
- return Some((ChildFrameTree::new(child_frame_tree, child.rect),
- frame_tree.clone()));
- }
- let result = self.find_child_parent_pair_in_frame_tree(child_frame_tree,
- child_pipeline_id);
- if result.is_some() {
- return result;
+ // Send the current frame tree to compositor, and grant paint
+ // permission to each pipeline in the current frame tree.
+ fn send_frame_tree_and_grant_paint_permission(&mut self) {
+ if let Some(root_frame_id) = self.root_frame_id {
+ let frame_tree = self.frame_to_sendable(root_frame_id);
+
+ let (chan, port) = channel();
+ self.compositor_proxy.send(CompositorMsg::SetFrameTree(frame_tree,
+ chan,
+ self.chan.clone()));
+ if port.recv().is_err() {
+ debug!("Compositor has discarded SetFrameTree");
+ return; // Our message has been discarded, probably shutting down.
}
}
- None
+
+ for frame in self.current_frame_tree_iter(self.root_frame_id) {
+ self.pipeline(frame.current).grant_paint_permission();
+ }
}
- fn create_compositor_layer_for_iframe_if_necessary(&mut self, pipeline_id: PipelineId) {
- let current_frame_tree = match self.current_frame() {
- &Some(ref tree) => tree.clone(),
- &None => return,
- };
+ fn pipeline_is_in_current_frame(&self, pipeline_id: PipelineId) -> bool {
+ self.current_frame_tree_iter(self.root_frame_id)
+ .any(|current_frame| current_frame.current == pipeline_id)
+ }
- let pair = self.find_child_parent_pair_in_frame_tree(current_frame_tree,
- pipeline_id);
- let (child, parent) = match pair {
- Some(pair) => pair,
- None => return,
- };
+ #[inline(always)]
+ fn frame(&self, frame_id: FrameId) -> &Frame {
+ self.frames.get(&frame_id).expect("unable to find frame - this is a bug")
+ }
- if child.frame_tree.has_compositor_layer.get() {
- child.frame_tree.pipeline.borrow().grant_paint_permission();
- return;
- }
+ #[inline(always)]
+ fn mut_frame(&mut self, frame_id: FrameId) -> &mut Frame {
+ self.frames.get_mut(&frame_id).expect("unable to find frame - this is a bug")
+ }
- let (chan, port) = channel();
- self.compositor_proxy.send(CompositorMsg::CreateRootLayerForPipeline(
- parent.pipeline.borrow().to_sendable(),
- child.frame_tree.pipeline.borrow().to_sendable(),
- child.rect,
- chan));
- match port.recv() {
- Ok(()) => {
- child.frame_tree.has_compositor_layer.set(true);
- child.frame_tree.pipeline.borrow().grant_paint_permission();
- }
- Err(_) => {} // The message has been discarded, we are probably shutting down.
- }
+ #[inline(always)]
+ fn pipeline(&self, pipeline_id: PipelineId) -> &Pipeline {
+ self.pipelines.get(&pipeline_id).expect("unable to find pipeline - this is a bug")
+ }
+
+ #[inline(always)]
+ fn mut_pipeline(&mut self, pipeline_id: PipelineId) -> &mut Pipeline {
+ 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 {
+ let pipeline_id = *self.subpage_map.get(&(containing_pipeline_id, subpage_id)).expect("no subpage pipeline_id");
+ self.mut_pipeline(pipeline_id)
}
}
diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs
index 9957cb067cc..d25fc4f59e1 100644
--- a/components/compositing/headless.rs
+++ b/components/compositing/headless.rs
@@ -90,14 +90,6 @@ impl CompositorEventListener for NullCompositor {
response_chan.send(()).unwrap();
}
- Msg::ChangeLayerPipelineAndRemoveChildren(_, _, response_channel) => {
- response_channel.send(()).unwrap();
- }
-
- Msg::CreateRootLayerForPipeline(_, _, _, response_channel) => {
- response_channel.send(()).unwrap();
- }
-
// Explicitly list ignored messages so that when we add a new one,
// we'll notice and think about whether it needs a response, like
// SetFrameTree.
diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs
index c09f4ea06d5..edfbae48a2a 100644
--- a/components/compositing/pipeline.rs
+++ b/components/compositing/pipeline.rs
@@ -8,23 +8,24 @@ use script_traits::{ScriptControlChan, ScriptTaskFactory};
use script_traits::{NewLayoutInfo, ConstellationControlMsg};
use devtools_traits::DevtoolsControlChan;
+use geom::rect::{TypedRect};
use gfx::paint_task::Msg as PaintMsg;
use gfx::paint_task::{PaintChan, PaintTask};
use gfx::font_cache_task::FontCacheTask;
-use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
+use msg::constellation_msg::{ConstellationChan, Failure, FrameId, PipelineId, SubpageId};
use msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType};
use net::image_cache_task::ImageCacheTask;
use net::resource_task::ResourceTask;
use net::storage_task::StorageTask;
use url::Url;
+use util::geometry::{PagePx};
use util::time::TimeProfilerChan;
-use std::rc::Rc;
use std::sync::mpsc::{Receiver, channel};
/// A uniquely-identifiable pipeline of script task, layout task, and paint task.
pub struct Pipeline {
pub id: PipelineId,
- pub parent: Option<(PipelineId, SubpageId)>,
+ pub parent_info: Option<(PipelineId, SubpageId)>,
pub script_chan: ScriptControlChan,
pub layout_chan: LayoutControlChan,
pub paint_chan: PaintChan,
@@ -34,6 +35,8 @@ 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 children: Vec<FrameId>,
}
/// The subset of the pipeline that is needed for layer composition.
@@ -49,7 +52,7 @@ impl Pipeline {
/// Returns the channels wrapped in a struct.
/// If script_pipeline is not None, then subpage_id must also be not None.
pub fn create<LTF,STF>(id: PipelineId,
- parent: Option<(PipelineId, SubpageId)>,
+ parent_info: Option<(PipelineId, SubpageId)>,
constellation_chan: ConstellationChan,
compositor_proxy: Box<CompositorProxy+'static+Send>,
devtools_chan: Option<DevtoolsControlChan>,
@@ -58,8 +61,8 @@ impl Pipeline {
resource_task: ResourceTask,
storage_task: StorageTask,
time_profiler_chan: TimeProfilerChan,
- window_size: WindowSizeData,
- script_pipeline: Option<Rc<Pipeline>>,
+ window_size: Option<WindowSizeData>,
+ script_chan: Option<ScriptControlChan>,
load_data: LoadData)
-> Pipeline
where LTF: LayoutTaskFactory, STF:ScriptTaskFactory {
@@ -71,10 +74,10 @@ impl Pipeline {
let failure = Failure {
pipeline_id: id,
- parent: parent,
+ parent_info: parent_info,
};
- let script_chan = match script_pipeline {
+ let script_chan = match script_chan {
None => {
let (script_chan, script_port) = channel();
ScriptTaskFactory::create(None::<&mut STF>,
@@ -93,18 +96,19 @@ impl Pipeline {
load_data.clone());
ScriptControlChan(script_chan)
}
- Some(spipe) => {
+ Some(script_chan) => {
+ let (containing_pipeline_id, subpage_id) = parent_info.expect("script_pipeline != None but subpage_id == None");
let new_layout_info = NewLayoutInfo {
- old_pipeline_id: spipe.id.clone(),
+ containing_pipeline_id: containing_pipeline_id,
new_pipeline_id: id,
- subpage_id: parent.expect("script_pipeline != None but subpage_id == None").1,
+ subpage_id: subpage_id,
layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>, &layout_pair),
load_data: load_data.clone(),
};
- let ScriptControlChan(ref chan) = spipe.script_chan;
+ let ScriptControlChan(ref chan) = script_chan;
chan.send(ConstellationControlMsg::AttachLayout(new_layout_info)).unwrap();
- spipe.script_chan.clone()
+ script_chan.clone()
}
};
@@ -132,7 +136,7 @@ impl Pipeline {
layout_shutdown_chan);
Pipeline::new(id,
- parent,
+ parent_info,
script_chan,
LayoutControlChan(pipeline_chan),
paint_chan,
@@ -142,7 +146,7 @@ impl Pipeline {
}
pub fn new(id: PipelineId,
- parent: Option<(PipelineId, SubpageId)>,
+ parent_info: Option<(PipelineId, SubpageId)>,
script_chan: ScriptControlChan,
layout_chan: LayoutControlChan,
paint_chan: PaintChan,
@@ -152,7 +156,7 @@ impl Pipeline {
-> Pipeline {
Pipeline {
id: id,
- parent: parent,
+ parent_info: parent_info,
script_chan: script_chan,
layout_chan: layout_chan,
paint_chan: paint_chan,
@@ -160,14 +164,11 @@ impl Pipeline {
paint_shutdown_port: paint_shutdown_port,
url: url,
title: None,
+ children: vec!(),
+ rect: None,
}
}
- pub fn activate(&self) {
- let ScriptControlChan(ref chan) = self.script_chan;
- chan.send(ConstellationControlMsg::Activate(self.id)).unwrap();
- }
-
pub fn grant_paint_permission(&self) {
let _ = self.paint_chan.send(PaintMsg::PaintPermissionGranted);
}
@@ -221,7 +222,7 @@ impl Pipeline {
}
}
- pub fn subpage_id(&self) -> Option<SubpageId> {
- self.parent.map(|parent| parent.1)
+ pub fn add_child(&mut self, frame_id: FrameId) {
+ self.children.push(frame_id);
}
}
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 27a187edfde..d5ccb9efb6e 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -26,7 +26,7 @@ impl ConstellationChan {
}
}
-#[derive(PartialEq, Eq, Copy)]
+#[derive(PartialEq, Eq, Copy, Debug)]
pub enum IFrameSandboxState {
IFrameSandboxed,
IFrameUnsandboxed
@@ -36,7 +36,7 @@ pub enum IFrameSandboxState {
#[derive(Clone, Copy)]
pub struct Failure {
pub pipeline_id: PipelineId,
- pub parent: Option<(PipelineId, SubpageId)>,
+ pub parent_info: Option<(PipelineId, SubpageId)>,
}
#[derive(Copy)]
@@ -236,13 +236,6 @@ impl LoadData {
}
}
-/// Represents the two different ways to which a page can be navigated
-#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
-pub enum NavigationType {
- Load, // entered or clicked on a url
- Navigate, // browser forward/back buttons
-}
-
#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub enum NavigationDirection {
Forward,
@@ -250,6 +243,9 @@ pub enum NavigationDirection {
}
#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
+pub struct FrameId(pub uint);
+
+#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct PipelineId(pub uint);
#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 9c2148334e2..ebe033cea37 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -215,7 +215,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
}
// This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation
- win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().pipeline(), load_data)).unwrap();
+ win.r().script_chan().send(ScriptMsg::Navigate(win.r().pipeline(), load_data)).unwrap();
}
fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> {
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 1bd9071be01..61ce736bb7e 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -63,6 +63,7 @@ pub trait HTMLIFrameElementHelpers {
/// http://www.whatwg.org/html/#process-the-iframe-attributes
fn process_the_iframe_attributes(self);
fn generate_new_subpage_id(self) -> (SubpageId, Option<SubpageId>);
+ fn navigate_child_browsing_context(self, url: Url);
}
impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
@@ -92,12 +93,7 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
(subpage_id, old_subpage_id)
}
- fn process_the_iframe_attributes(self) {
- let url = match self.get_url() {
- Some(url) => url.clone(),
- None => Url::parse("about:blank").unwrap(),
- };
-
+ fn navigate_child_browsing_context(self, url: Url) {
let sandboxed = if self.is_sandboxed() {
IFrameSandboxed
} else {
@@ -111,11 +107,20 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
self.containing_page_pipeline_id.set(Some(window.pipeline()));
let ConstellationChan(ref chan) = window.constellation_chan();
- chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url,
- window.pipeline(),
- new_subpage_id,
- old_subpage_id,
- sandboxed)).unwrap();
+ chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url,
+ window.pipeline(),
+ new_subpage_id,
+ old_subpage_id,
+ sandboxed)).unwrap();
+ }
+
+ fn process_the_iframe_attributes(self) {
+ let url = match self.get_url() {
+ Some(url) => url.clone(),
+ None => Url::parse("about:blank").unwrap(),
+ };
+
+ self.navigate_child_browsing_context(url);
}
}
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 1e67ebaaa60..70c12abc9f3 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -135,7 +135,7 @@ pub struct Window {
layout_join_port: DOMRefCell<Option<Receiver<()>>>,
/// The current size of the window, in pixels.
- window_size: Cell<WindowSizeData>,
+ window_size: Cell<Option<WindowSizeData>>,
/// Associated resource task for use by DOM objects like XMLHttpRequest
resource_task: ResourceTask,
@@ -428,7 +428,7 @@ pub trait WindowHelpers {
fn set_fragment_name(self, fragment: Option<String>);
fn steal_fragment_name(self) -> Option<String>;
fn set_window_size(self, size: WindowSizeData);
- fn window_size(self) -> WindowSizeData;
+ fn window_size(self) -> Option<WindowSizeData>;
fn get_url(self) -> Url;
fn resource_task(self) -> ResourceTask;
fn devtools_chan(self) -> Option<DevtoolsControlChan>;
@@ -517,6 +517,11 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
return
}
+ let window_size = match self.window_size.get() {
+ Some(window_size) => window_size,
+ None => return,
+ };
+
// Layout will let us know when it's done.
let (join_chan, join_port) = channel();
@@ -528,8 +533,6 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
let last_reflow_id = &self.last_reflow_id;
last_reflow_id.set(last_reflow_id.get() + 1);
- let window_size = self.window_size.get();
-
// On debug mode, print the reflow event information.
if opts::get().relayout_event {
debug_reflow_events(&goal, &query_type, &reason);
@@ -607,7 +610,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
}
fn handle_resize_inactive_msg(self, new_size: WindowSizeData) {
- self.window_size.set(new_size);
+ self.window_size.set(Some(new_size));
}
fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>) {
@@ -626,7 +629,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
self.script_chan.send(ScriptMsg::TriggerFragment(self.id, fragment)).unwrap();
},
None => {
- self.script_chan.send(ScriptMsg::TriggerLoad(self.id, LoadData::new(url))).unwrap();
+ self.script_chan.send(ScriptMsg::Navigate(self.id, LoadData::new(url))).unwrap();
}
}
}
@@ -645,10 +648,10 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
}
fn set_window_size(self, size: WindowSizeData) {
- self.window_size.set(size);
+ self.window_size.set(Some(size));
}
- fn window_size(self) -> WindowSizeData {
+ fn window_size(self) -> Option<WindowSizeData> {
self.window_size.get()
}
@@ -755,7 +758,7 @@ impl Window {
layout_chan: LayoutChan,
id: PipelineId,
subpage_id: Option<SubpageId>,
- window_size: WindowSizeData)
+ window_size: Option<WindowSizeData>)
-> Temporary<Window> {
let layout_rpc: Box<LayoutRPC> = {
let (rpc_send, rpc_recv) = channel();
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 3041098e78c..4d319aa7527 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -33,6 +33,7 @@ use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource};
use dom::element::{Element, AttributeHandlers};
use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable};
+use dom::htmliframeelement::HTMLIFrameElementHelpers;
use dom::uievent::UIEvent;
use dom::eventtarget::EventTarget;
use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node};
@@ -116,7 +117,7 @@ struct InProgressLoad {
/// The parent pipeline and child subpage associated with this load, if any.
subpage_id: Option<(PipelineId, SubpageId)>,
/// The current window size associated with this pipeline.
- window_size: WindowSizeData,
+ window_size: Option<WindowSizeData>,
/// Channel to the layout task associated with this pipeline.
layout_chan: LayoutChan,
/// The current viewport clipping rectangle applying to this pipelie, if any.
@@ -130,7 +131,7 @@ impl InProgressLoad {
fn new(id: PipelineId,
subpage_id: Option<(PipelineId, SubpageId)>,
layout_chan: LayoutChan,
- window_size: WindowSizeData,
+ window_size: Option<WindowSizeData>,
url: Url) -> InProgressLoad {
InProgressLoad {
pipeline_id: id,
@@ -161,7 +162,7 @@ pub enum ScriptMsg {
TriggerFragment(PipelineId, String),
/// Begins a content-initiated load on the specified pipeline (only
/// dispatched to ScriptTask).
- TriggerLoad(PipelineId, LoadData),
+ Navigate(PipelineId, LoadData),
/// Fires a JavaScript timeout
/// TimerSource must be FromWindow when dispatched to ScriptTask and
/// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
@@ -340,7 +341,7 @@ impl ScriptTaskFactory for ScriptTask {
storage_task: StorageTask,
image_cache_task: ImageCacheTask,
devtools_chan: Option<DevtoolsControlChan>,
- window_size: WindowSizeData,
+ window_size: Option<WindowSizeData>,
load_data: LoadData)
where C: ScriptListener + Send + 'static {
let ConstellationChan(const_chan) = constellation_chan.clone();
@@ -620,8 +621,8 @@ impl ScriptTask {
match msg {
ConstellationControlMsg::AttachLayout(_) =>
panic!("should have handled AttachLayout already"),
- ConstellationControlMsg::Activate(id) =>
- self.handle_activate(id),
+ ConstellationControlMsg::Navigate(pipeline_id, subpage_id, load_data) =>
+ self.handle_navigate(pipeline_id, Some(subpage_id), load_data),
ConstellationControlMsg::SendEvent(id, event) =>
self.handle_event(id, event),
ConstellationControlMsg::ReflowComplete(id, reflow_id) =>
@@ -645,8 +646,8 @@ impl ScriptTask {
fn handle_msg_from_script(&self, msg: ScriptMsg) {
match msg {
- ScriptMsg::TriggerLoad(id, load_data) =>
- self.trigger_load(id, load_data),
+ ScriptMsg::Navigate(id, load_data) =>
+ self.handle_navigate(id, None, load_data),
ScriptMsg::TriggerFragment(id, fragment) =>
self.trigger_fragment(id, fragment),
ScriptMsg::FireTimer(TimerSource::FromWindow(id), timer_id) =>
@@ -697,7 +698,7 @@ impl ScriptTask {
}
let mut loads = self.incomplete_loads.borrow_mut();
if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
- load.window_size = size;
+ load.window_size = Some(size);
return;
}
panic!("resize sent to nonexistent pipeline");
@@ -726,7 +727,7 @@ impl ScriptTask {
/// Handle a request to load a page in a new child frame of an existing page.
fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
let NewLayoutInfo {
- old_pipeline_id,
+ containing_pipeline_id,
new_pipeline_id,
subpage_id,
layout_chan,
@@ -734,7 +735,7 @@ impl ScriptTask {
} = new_layout_info;
let page = self.root_page();
- let parent_page = page.find(old_pipeline_id).expect("ScriptTask: received a layout
+ let parent_page = page.find(containing_pipeline_id).expect("ScriptTask: received a layout
whose parent has a PipelineId which does not correspond to a pipeline in the script
task's page tree. This is a bug.");
@@ -742,7 +743,7 @@ impl ScriptTask {
let chan = layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap();
let layout_chan = LayoutChan(chan.clone());
// Kick off the fetch for the new resource.
- let new_load = InProgressLoad::new(new_pipeline_id, Some((old_pipeline_id, subpage_id)),
+ let new_load = InProgressLoad::new(new_pipeline_id, Some((containing_pipeline_id, subpage_id)),
layout_chan, parent_window.r().window_size(),
load_data.url.clone());
self.start_page_load(new_load, load_data);
@@ -768,24 +769,17 @@ impl ScriptTask {
/// Handles thaw message
fn handle_thaw_msg(&self, id: PipelineId) {
- let page = self.root_page();
- let page = page.find(id).expect("ScriptTask: received thaw msg for a
- pipeline ID not associated with this script task. This is a bug.");
- let window = page.window().root();
- window.r().thaw();
- }
-
- /// Handle a request to make a previously-created pipeline active.
- //TODO: unsuspend JS and timers, etc. when we support such things.
- fn handle_activate(&self, pipeline_id: PipelineId) {
// We should only get this message when moving in history, so all pages requested
// should exist.
- let page = self.root_page().find(pipeline_id).unwrap();
+ let page = self.root_page().find(id).unwrap();
let needed_reflow = page.set_reflow_status(false);
if needed_reflow {
self.force_reflow(&*page, ReflowReason::CachedPageNeededReflow);
}
+
+ let window = page.window().root();
+ window.r().thaw();
}
/// Handles a notification that reflow completed.
@@ -1138,11 +1132,29 @@ impl ScriptTask {
}
}
+ /// https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents
/// The entry point for content to notify that a new load has been requested
- /// for the given pipeline.
- fn trigger_load(&self, pipeline_id: PipelineId, load_data: LoadData) {
- let ConstellationChan(ref const_chan) = self.constellation_chan;
- const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap();
+ /// for the given pipeline (specifically the "navigate" algorithm).
+ fn handle_navigate(&self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>, load_data: LoadData) {
+ match subpage_id {
+ Some(subpage_id) => {
+ let borrowed_page = self.root_page();
+ let iframe = borrowed_page.find(pipeline_id).and_then(|page| {
+ let doc = page.document().root();
+ let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
+
+ doc.traverse_preorder()
+ .filter_map(HTMLIFrameElementCast::to_ref)
+ .find(|node| node.subpage_id() == Some(subpage_id))
+ .map(Temporary::from_rooted)
+ }).root();
+ iframe.r().unwrap().navigate_child_browsing_context(load_data.url);
+ }
+ None => {
+ let ConstellationChan(ref const_chan) = self.constellation_chan;
+ const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap();
+ }
+ }
}
/// The entry point for content to notify that a fragment url has been requested
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index deb5ba30770..05ed3f25097 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -44,7 +44,7 @@ pub struct UntrustedNodeAddress(pub *const c_void);
unsafe impl Send for UntrustedNodeAddress {}
pub struct NewLayoutInfo {
- pub old_pipeline_id: PipelineId,
+ pub containing_pipeline_id: PipelineId,
pub new_pipeline_id: PipelineId,
pub subpage_id: SubpageId,
pub layout_chan: Box<Any+Send>, // opaque reference to a LayoutChannel
@@ -53,8 +53,6 @@ pub struct NewLayoutInfo {
/// Messages sent from the constellation to the script task
pub enum ConstellationControlMsg {
- /// Reactivate an existing pipeline.
- Activate(PipelineId),
/// Gives a channel and ID to a layout task, as well as the ID of that layout's parent
AttachLayout(NewLayoutInfo),
/// Window resized. Sends a DOM event eventually, but first we combine events.
@@ -74,7 +72,9 @@ pub enum ConstellationControlMsg {
/// Notifies script task to suspend all its timers
Freeze(PipelineId),
/// Notifies script task to resume all its timers
- Thaw(PipelineId)
+ Thaw(PipelineId),
+ /// Notifies script task that a url should be loaded in this iframe.
+ Navigate(PipelineId, SubpageId, LoadData),
}
unsafe impl Send for ConstellationControlMsg {
@@ -112,7 +112,7 @@ pub trait ScriptTaskFactory {
storage_task: StorageTask,
image_cache_task: ImageCacheTask,
devtools_chan: Option<DevtoolsControlChan>,
- window_size: WindowSizeData,
+ window_size: Option<WindowSizeData>,
load_data: LoadData)
where C: ScriptListener + Send;
fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;