First part of refactoring constellation to support iframe navigation.

The history is now recorded per frame, but needs to be exposed in a followup PR.

Also fixes a race condition that occurs loading iframes under heavy CPU load.

This ensures that iframes never do a reflow / layout until they have a valid
window size set from their parent frame.
This commit is contained in:
Glenn Watson 2015-03-13 15:08:02 +10:00
parent 0888a3a16d
commit 939a89568e
11 changed files with 587 additions and 921 deletions

View file

@ -291,18 +291,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.get_title_for_main_frame(); 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) => { (Msg::CreateOrUpdateBaseLayer(layer_properties), ShutdownState::NotShuttingDown) => {
self.create_or_update_base_layer(layer_properties); 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, let root_layer = self.create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline,
frame_rect); frame_rect);
for kid in frame_tree.children.iter() { 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; 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>>> { fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) -> Option<Rc<Layer<CompositorData>>> {
if !self.pipeline_details.contains_key(&pipeline_id) { if !self.pipeline_details.contains_key(&pipeline_id) {
panic!("Tried to create or update layer for unknown pipeline") panic!("Tried to create or update layer for unknown pipeline")

View file

@ -13,18 +13,16 @@ use windowing::{WindowEvent, WindowMethods};
use azure::azure_hl::{SourceSurfaceMethods, Color}; use azure::azure_hl::{SourceSurfaceMethods, Color};
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::{Rect, TypedRect}; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata}; use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
use layers::layers::LayerBufferSet; use layers::layers::LayerBufferSet;
use pipeline::CompositionPipeline;
use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState};
use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy};
use msg::constellation_msg::{ConstellationChan, PipelineId}; use msg::constellation_msg::{ConstellationChan, PipelineId};
use msg::constellation_msg::{Key, KeyState, KeyModifiers}; use msg::constellation_msg::{Key, KeyState, KeyModifiers};
use url::Url; use url::Url;
use util::cursor::Cursor; use util::cursor::Cursor;
use util::geometry::PagePx;
use util::memory::MemoryProfilerChan; use util::memory::MemoryProfilerChan;
use util::time::TimeProfilerChan; use util::time::TimeProfilerChan;
use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::mpsc::{channel, Sender, Receiver};
@ -207,10 +205,6 @@ pub enum Msg {
PaintMsgDiscarded, PaintMsgDiscarded,
/// Replaces the current frame tree, typically called during main frame navigation. /// Replaces the current frame tree, typically called during main frame navigation.
SetFrameTree(SendableFrameTree, Sender<()>, ConstellationChan), 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. /// The load of a page has completed.
LoadComplete, LoadComplete,
/// Indicates that the scrolling timeout with the given starting timestamp has happened and a /// 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::ChangePageUrl(..) => write!(f, "ChangePageUrl"),
Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"), Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),
Msg::SetFrameTree(..) => write!(f, "SetFrameTree"), Msg::SetFrameTree(..) => write!(f, "SetFrameTree"),
Msg::CreateRootLayerForPipeline(..) => write!(f, "CreateRootLayerForPipeline"),
Msg::ChangeLayerPipelineAndRemoveChildren(..) => write!(f, "ChangeLayerPipelineAndRemoveChildren"),
Msg::LoadComplete => write!(f, "LoadComplete"), Msg::LoadComplete => write!(f, "LoadComplete"),
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"), Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
Msg::KeyEvent(..) => write!(f, "KeyEvent"), Msg::KeyEvent(..) => write!(f, "KeyEvent"),

File diff suppressed because it is too large Load diff

View file

@ -90,14 +90,6 @@ impl CompositorEventListener for NullCompositor {
response_chan.send(()).unwrap(); 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, // Explicitly list ignored messages so that when we add a new one,
// we'll notice and think about whether it needs a response, like // we'll notice and think about whether it needs a response, like
// SetFrameTree. // SetFrameTree.

View file

@ -8,23 +8,24 @@ use script_traits::{ScriptControlChan, ScriptTaskFactory};
use script_traits::{NewLayoutInfo, ConstellationControlMsg}; use script_traits::{NewLayoutInfo, ConstellationControlMsg};
use devtools_traits::DevtoolsControlChan; use devtools_traits::DevtoolsControlChan;
use geom::rect::{TypedRect};
use gfx::paint_task::Msg as PaintMsg; use gfx::paint_task::Msg as PaintMsg;
use gfx::paint_task::{PaintChan, PaintTask}; use gfx::paint_task::{PaintChan, PaintTask};
use gfx::font_cache_task::FontCacheTask; 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 msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType};
use net::image_cache_task::ImageCacheTask; use net::image_cache_task::ImageCacheTask;
use net::resource_task::ResourceTask; use net::resource_task::ResourceTask;
use net::storage_task::StorageTask; use net::storage_task::StorageTask;
use url::Url; use url::Url;
use util::geometry::{PagePx};
use util::time::TimeProfilerChan; use util::time::TimeProfilerChan;
use std::rc::Rc;
use std::sync::mpsc::{Receiver, channel}; use std::sync::mpsc::{Receiver, channel};
/// A uniquely-identifiable pipeline of script task, layout task, and paint task. /// A uniquely-identifiable pipeline of script task, layout task, and paint task.
pub struct Pipeline { pub struct Pipeline {
pub id: PipelineId, pub id: PipelineId,
pub parent: Option<(PipelineId, SubpageId)>, pub parent_info: Option<(PipelineId, SubpageId)>,
pub script_chan: ScriptControlChan, pub script_chan: ScriptControlChan,
pub layout_chan: LayoutControlChan, pub layout_chan: LayoutControlChan,
pub paint_chan: PaintChan, pub paint_chan: PaintChan,
@ -34,6 +35,8 @@ pub struct Pipeline {
pub url: Url, pub url: Url,
/// The title of the most recently-loaded page. /// The title of the most recently-loaded page.
pub title: Option<String>, 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. /// The subset of the pipeline that is needed for layer composition.
@ -49,7 +52,7 @@ impl Pipeline {
/// Returns the channels wrapped in a struct. /// Returns the channels wrapped in a struct.
/// If script_pipeline is not None, then subpage_id must also be not None. /// If script_pipeline is not None, then subpage_id must also be not None.
pub fn create<LTF,STF>(id: PipelineId, pub fn create<LTF,STF>(id: PipelineId,
parent: Option<(PipelineId, SubpageId)>, parent_info: Option<(PipelineId, SubpageId)>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
compositor_proxy: Box<CompositorProxy+'static+Send>, compositor_proxy: Box<CompositorProxy+'static+Send>,
devtools_chan: Option<DevtoolsControlChan>, devtools_chan: Option<DevtoolsControlChan>,
@ -58,8 +61,8 @@ impl Pipeline {
resource_task: ResourceTask, resource_task: ResourceTask,
storage_task: StorageTask, storage_task: StorageTask,
time_profiler_chan: TimeProfilerChan, time_profiler_chan: TimeProfilerChan,
window_size: WindowSizeData, window_size: Option<WindowSizeData>,
script_pipeline: Option<Rc<Pipeline>>, script_chan: Option<ScriptControlChan>,
load_data: LoadData) load_data: LoadData)
-> Pipeline -> Pipeline
where LTF: LayoutTaskFactory, STF:ScriptTaskFactory { where LTF: LayoutTaskFactory, STF:ScriptTaskFactory {
@ -71,10 +74,10 @@ impl Pipeline {
let failure = Failure { let failure = Failure {
pipeline_id: id, pipeline_id: id,
parent: parent, parent_info: parent_info,
}; };
let script_chan = match script_pipeline { let script_chan = match script_chan {
None => { None => {
let (script_chan, script_port) = channel(); let (script_chan, script_port) = channel();
ScriptTaskFactory::create(None::<&mut STF>, ScriptTaskFactory::create(None::<&mut STF>,
@ -93,18 +96,19 @@ impl Pipeline {
load_data.clone()); load_data.clone());
ScriptControlChan(script_chan) 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 { let new_layout_info = NewLayoutInfo {
old_pipeline_id: spipe.id.clone(), containing_pipeline_id: containing_pipeline_id,
new_pipeline_id: 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), layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>, &layout_pair),
load_data: load_data.clone(), 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(); chan.send(ConstellationControlMsg::AttachLayout(new_layout_info)).unwrap();
spipe.script_chan.clone() script_chan.clone()
} }
}; };
@ -132,7 +136,7 @@ impl Pipeline {
layout_shutdown_chan); layout_shutdown_chan);
Pipeline::new(id, Pipeline::new(id,
parent, parent_info,
script_chan, script_chan,
LayoutControlChan(pipeline_chan), LayoutControlChan(pipeline_chan),
paint_chan, paint_chan,
@ -142,7 +146,7 @@ impl Pipeline {
} }
pub fn new(id: PipelineId, pub fn new(id: PipelineId,
parent: Option<(PipelineId, SubpageId)>, parent_info: Option<(PipelineId, SubpageId)>,
script_chan: ScriptControlChan, script_chan: ScriptControlChan,
layout_chan: LayoutControlChan, layout_chan: LayoutControlChan,
paint_chan: PaintChan, paint_chan: PaintChan,
@ -152,7 +156,7 @@ impl Pipeline {
-> Pipeline { -> Pipeline {
Pipeline { Pipeline {
id: id, id: id,
parent: parent, parent_info: parent_info,
script_chan: script_chan, script_chan: script_chan,
layout_chan: layout_chan, layout_chan: layout_chan,
paint_chan: paint_chan, paint_chan: paint_chan,
@ -160,14 +164,11 @@ impl Pipeline {
paint_shutdown_port: paint_shutdown_port, paint_shutdown_port: paint_shutdown_port,
url: url, url: url,
title: None, 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) { pub fn grant_paint_permission(&self) {
let _ = self.paint_chan.send(PaintMsg::PaintPermissionGranted); let _ = self.paint_chan.send(PaintMsg::PaintPermissionGranted);
} }
@ -221,7 +222,7 @@ impl Pipeline {
} }
} }
pub fn subpage_id(&self) -> Option<SubpageId> { pub fn add_child(&mut self, frame_id: FrameId) {
self.parent.map(|parent| parent.1) self.children.push(frame_id);
} }
} }

View file

@ -26,7 +26,7 @@ impl ConstellationChan {
} }
} }
#[derive(PartialEq, Eq, Copy)] #[derive(PartialEq, Eq, Copy, Debug)]
pub enum IFrameSandboxState { pub enum IFrameSandboxState {
IFrameSandboxed, IFrameSandboxed,
IFrameUnsandboxed IFrameUnsandboxed
@ -36,7 +36,7 @@ pub enum IFrameSandboxState {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Failure { pub struct Failure {
pub pipeline_id: PipelineId, pub pipeline_id: PipelineId,
pub parent: Option<(PipelineId, SubpageId)>, pub parent_info: Option<(PipelineId, SubpageId)>,
} }
#[derive(Copy)] #[derive(Copy)]
@ -236,19 +236,15 @@ 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)] #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub enum NavigationDirection { pub enum NavigationDirection {
Forward, Forward,
Back, Back,
} }
#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct FrameId(pub uint);
#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct PipelineId(pub uint); pub struct PipelineId(pub uint);

View file

@ -215,7 +215,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
} }
// This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation // 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> { fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> {

View file

@ -63,6 +63,7 @@ pub trait HTMLIFrameElementHelpers {
/// http://www.whatwg.org/html/#process-the-iframe-attributes /// http://www.whatwg.org/html/#process-the-iframe-attributes
fn process_the_iframe_attributes(self); fn process_the_iframe_attributes(self);
fn generate_new_subpage_id(self) -> (SubpageId, Option<SubpageId>); fn generate_new_subpage_id(self) -> (SubpageId, Option<SubpageId>);
fn navigate_child_browsing_context(self, url: Url);
} }
impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
@ -92,12 +93,7 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
(subpage_id, old_subpage_id) (subpage_id, old_subpage_id)
} }
fn process_the_iframe_attributes(self) { fn navigate_child_browsing_context(self, url: Url) {
let url = match self.get_url() {
Some(url) => url.clone(),
None => Url::parse("about:blank").unwrap(),
};
let sandboxed = if self.is_sandboxed() { let sandboxed = if self.is_sandboxed() {
IFrameSandboxed IFrameSandboxed
} else { } else {
@ -111,11 +107,20 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
self.containing_page_pipeline_id.set(Some(window.pipeline())); self.containing_page_pipeline_id.set(Some(window.pipeline()));
let ConstellationChan(ref chan) = window.constellation_chan(); let ConstellationChan(ref chan) = window.constellation_chan();
chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url, chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url,
window.pipeline(), window.pipeline(),
new_subpage_id, new_subpage_id,
old_subpage_id, old_subpage_id,
sandboxed)).unwrap(); 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);
} }
} }

View file

@ -135,7 +135,7 @@ pub struct Window {
layout_join_port: DOMRefCell<Option<Receiver<()>>>, layout_join_port: DOMRefCell<Option<Receiver<()>>>,
/// The current size of the window, in pixels. /// 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 /// Associated resource task for use by DOM objects like XMLHttpRequest
resource_task: ResourceTask, resource_task: ResourceTask,
@ -428,7 +428,7 @@ pub trait WindowHelpers {
fn set_fragment_name(self, fragment: Option<String>); fn set_fragment_name(self, fragment: Option<String>);
fn steal_fragment_name(self) -> Option<String>; fn steal_fragment_name(self) -> Option<String>;
fn set_window_size(self, size: WindowSizeData); fn set_window_size(self, size: WindowSizeData);
fn window_size(self) -> WindowSizeData; fn window_size(self) -> Option<WindowSizeData>;
fn get_url(self) -> Url; fn get_url(self) -> Url;
fn resource_task(self) -> ResourceTask; fn resource_task(self) -> ResourceTask;
fn devtools_chan(self) -> Option<DevtoolsControlChan>; fn devtools_chan(self) -> Option<DevtoolsControlChan>;
@ -517,6 +517,11 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
return 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. // Layout will let us know when it's done.
let (join_chan, join_port) = channel(); 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; let last_reflow_id = &self.last_reflow_id;
last_reflow_id.set(last_reflow_id.get() + 1); last_reflow_id.set(last_reflow_id.get() + 1);
let window_size = self.window_size.get();
// On debug mode, print the reflow event information. // On debug mode, print the reflow event information.
if opts::get().relayout_event { if opts::get().relayout_event {
debug_reflow_events(&goal, &query_type, &reason); 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) { 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>>) { 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(); self.script_chan.send(ScriptMsg::TriggerFragment(self.id, fragment)).unwrap();
}, },
None => { 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) { 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() self.window_size.get()
} }
@ -755,7 +758,7 @@ impl Window {
layout_chan: LayoutChan, layout_chan: LayoutChan,
id: PipelineId, id: PipelineId,
subpage_id: Option<SubpageId>, subpage_id: Option<SubpageId>,
window_size: WindowSizeData) window_size: Option<WindowSizeData>)
-> Temporary<Window> { -> Temporary<Window> {
let layout_rpc: Box<LayoutRPC> = { let layout_rpc: Box<LayoutRPC> = {
let (rpc_send, rpc_recv) = channel(); let (rpc_send, rpc_recv) = channel();

View file

@ -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::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource};
use dom::element::{Element, AttributeHandlers}; use dom::element::{Element, AttributeHandlers};
use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable}; use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable};
use dom::htmliframeelement::HTMLIFrameElementHelpers;
use dom::uievent::UIEvent; use dom::uievent::UIEvent;
use dom::eventtarget::EventTarget; use dom::eventtarget::EventTarget;
use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node}; 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. /// The parent pipeline and child subpage associated with this load, if any.
subpage_id: Option<(PipelineId, SubpageId)>, subpage_id: Option<(PipelineId, SubpageId)>,
/// The current window size associated with this pipeline. /// The current window size associated with this pipeline.
window_size: WindowSizeData, window_size: Option<WindowSizeData>,
/// Channel to the layout task associated with this pipeline. /// Channel to the layout task associated with this pipeline.
layout_chan: LayoutChan, layout_chan: LayoutChan,
/// The current viewport clipping rectangle applying to this pipelie, if any. /// The current viewport clipping rectangle applying to this pipelie, if any.
@ -130,7 +131,7 @@ impl InProgressLoad {
fn new(id: PipelineId, fn new(id: PipelineId,
subpage_id: Option<(PipelineId, SubpageId)>, subpage_id: Option<(PipelineId, SubpageId)>,
layout_chan: LayoutChan, layout_chan: LayoutChan,
window_size: WindowSizeData, window_size: Option<WindowSizeData>,
url: Url) -> InProgressLoad { url: Url) -> InProgressLoad {
InProgressLoad { InProgressLoad {
pipeline_id: id, pipeline_id: id,
@ -161,7 +162,7 @@ pub enum ScriptMsg {
TriggerFragment(PipelineId, String), TriggerFragment(PipelineId, String),
/// Begins a content-initiated load on the specified pipeline (only /// Begins a content-initiated load on the specified pipeline (only
/// dispatched to ScriptTask). /// dispatched to ScriptTask).
TriggerLoad(PipelineId, LoadData), Navigate(PipelineId, LoadData),
/// Fires a JavaScript timeout /// Fires a JavaScript timeout
/// TimerSource must be FromWindow when dispatched to ScriptTask and /// TimerSource must be FromWindow when dispatched to ScriptTask and
/// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope /// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
@ -340,7 +341,7 @@ impl ScriptTaskFactory for ScriptTask {
storage_task: StorageTask, storage_task: StorageTask,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
devtools_chan: Option<DevtoolsControlChan>, devtools_chan: Option<DevtoolsControlChan>,
window_size: WindowSizeData, window_size: Option<WindowSizeData>,
load_data: LoadData) load_data: LoadData)
where C: ScriptListener + Send + 'static { where C: ScriptListener + Send + 'static {
let ConstellationChan(const_chan) = constellation_chan.clone(); let ConstellationChan(const_chan) = constellation_chan.clone();
@ -620,8 +621,8 @@ impl ScriptTask {
match msg { match msg {
ConstellationControlMsg::AttachLayout(_) => ConstellationControlMsg::AttachLayout(_) =>
panic!("should have handled AttachLayout already"), panic!("should have handled AttachLayout already"),
ConstellationControlMsg::Activate(id) => ConstellationControlMsg::Navigate(pipeline_id, subpage_id, load_data) =>
self.handle_activate(id), self.handle_navigate(pipeline_id, Some(subpage_id), load_data),
ConstellationControlMsg::SendEvent(id, event) => ConstellationControlMsg::SendEvent(id, event) =>
self.handle_event(id, event), self.handle_event(id, event),
ConstellationControlMsg::ReflowComplete(id, reflow_id) => ConstellationControlMsg::ReflowComplete(id, reflow_id) =>
@ -645,8 +646,8 @@ impl ScriptTask {
fn handle_msg_from_script(&self, msg: ScriptMsg) { fn handle_msg_from_script(&self, msg: ScriptMsg) {
match msg { match msg {
ScriptMsg::TriggerLoad(id, load_data) => ScriptMsg::Navigate(id, load_data) =>
self.trigger_load(id, load_data), self.handle_navigate(id, None, load_data),
ScriptMsg::TriggerFragment(id, fragment) => ScriptMsg::TriggerFragment(id, fragment) =>
self.trigger_fragment(id, fragment), self.trigger_fragment(id, fragment),
ScriptMsg::FireTimer(TimerSource::FromWindow(id), timer_id) => ScriptMsg::FireTimer(TimerSource::FromWindow(id), timer_id) =>
@ -697,7 +698,7 @@ impl ScriptTask {
} }
let mut loads = self.incomplete_loads.borrow_mut(); let mut loads = self.incomplete_loads.borrow_mut();
if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { 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; return;
} }
panic!("resize sent to nonexistent pipeline"); 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. /// 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) { fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
let NewLayoutInfo { let NewLayoutInfo {
old_pipeline_id, containing_pipeline_id,
new_pipeline_id, new_pipeline_id,
subpage_id, subpage_id,
layout_chan, layout_chan,
@ -734,7 +735,7 @@ impl ScriptTask {
} = new_layout_info; } = new_layout_info;
let page = self.root_page(); 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 whose parent has a PipelineId which does not correspond to a pipeline in the script
task's page tree. This is a bug."); 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 chan = layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap();
let layout_chan = LayoutChan(chan.clone()); let layout_chan = LayoutChan(chan.clone());
// Kick off the fetch for the new resource. // 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(), layout_chan, parent_window.r().window_size(),
load_data.url.clone()); load_data.url.clone());
self.start_page_load(new_load, load_data); self.start_page_load(new_load, load_data);
@ -768,24 +769,17 @@ impl ScriptTask {
/// Handles thaw message /// Handles thaw message
fn handle_thaw_msg(&self, id: PipelineId) { 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 // We should only get this message when moving in history, so all pages requested
// should exist. // 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); let needed_reflow = page.set_reflow_status(false);
if needed_reflow { if needed_reflow {
self.force_reflow(&*page, ReflowReason::CachedPageNeededReflow); self.force_reflow(&*page, ReflowReason::CachedPageNeededReflow);
} }
let window = page.window().root();
window.r().thaw();
} }
/// Handles a notification that reflow completed. /// 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 /// The entry point for content to notify that a new load has been requested
/// for the given pipeline. /// for the given pipeline (specifically the "navigate" algorithm).
fn trigger_load(&self, pipeline_id: PipelineId, load_data: LoadData) { fn handle_navigate(&self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>, load_data: LoadData) {
let ConstellationChan(ref const_chan) = self.constellation_chan; match subpage_id {
const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap(); 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 /// The entry point for content to notify that a fragment url has been requested

View file

@ -44,7 +44,7 @@ pub struct UntrustedNodeAddress(pub *const c_void);
unsafe impl Send for UntrustedNodeAddress {} unsafe impl Send for UntrustedNodeAddress {}
pub struct NewLayoutInfo { pub struct NewLayoutInfo {
pub old_pipeline_id: PipelineId, pub containing_pipeline_id: PipelineId,
pub new_pipeline_id: PipelineId, pub new_pipeline_id: PipelineId,
pub subpage_id: SubpageId, pub subpage_id: SubpageId,
pub layout_chan: Box<Any+Send>, // opaque reference to a LayoutChannel 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 /// Messages sent from the constellation to the script task
pub enum ConstellationControlMsg { 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 /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent
AttachLayout(NewLayoutInfo), AttachLayout(NewLayoutInfo),
/// Window resized. Sends a DOM event eventually, but first we combine events. /// 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 /// Notifies script task to suspend all its timers
Freeze(PipelineId), Freeze(PipelineId),
/// Notifies script task to resume all its timers /// 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 { unsafe impl Send for ConstellationControlMsg {
@ -112,7 +112,7 @@ pub trait ScriptTaskFactory {
storage_task: StorageTask, storage_task: StorageTask,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
devtools_chan: Option<DevtoolsControlChan>, devtools_chan: Option<DevtoolsControlChan>,
window_size: WindowSizeData, window_size: Option<WindowSizeData>,
load_data: LoadData) load_data: LoadData)
where C: ScriptListener + Send; where C: ScriptListener + Send;
fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel; fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;