mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
parent
0888a3a16d
commit
939a89568e
11 changed files with 587 additions and 921 deletions
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue