Implement synchronous about:blank loading.

Based on initial work by jdm in <https://github.com/servo/servo/pull/8600>.
This commit is contained in:
Ms2ger 2016-11-28 10:23:04 +01:00
parent 2677540cd0
commit b86965f394
34 changed files with 456 additions and 906 deletions

View file

@ -20,6 +20,7 @@ use dom::bindings::conversions::ToJSValConvertible;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::Reflectable;
use dom::bindings::str::DOMString;
use dom::browsingcontext::BrowsingContext;
@ -42,14 +43,16 @@ use js::jsval::{NullValue, UndefinedValue};
use msg::constellation_msg::{FrameType, FrameId, PipelineId, TraversalDirection};
use net_traits::response::HttpsState;
use script_layout_interface::message::ReflowQueryType;
use script_thread::ScriptThread;
use script_traits::{IFrameLoadInfo, LoadData, MozBrowserEvent, ScriptMsg as ConstellationMsg};
use script_thread::{ScriptThread, Runnable};
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, LoadData};
use script_traits::{MozBrowserEvent, NewLayoutInfo, ScriptMsg as ConstellationMsg};
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::cell::Cell;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::context::ReflowGoal;
use task_source::TaskSource;
use util::prefs::PREFS;
use util::servo_version;
@ -66,6 +69,12 @@ bitflags! {
}
}
#[derive(PartialEq)]
enum ProcessingMode {
FirstTime,
NotFirstTime,
}
#[dom_struct]
pub struct HTMLIFrameElement {
htmlelement: HTMLElement,
@ -131,20 +140,46 @@ impl HTMLIFrameElement {
let global_scope = window.upcast::<GlobalScope>();
let load_info = IFrameLoadInfo {
load_data: load_data,
parent_pipeline_id: global_scope.pipeline_id(),
frame_id: self.frame_id,
old_pipeline_id: old_pipeline_id,
new_pipeline_id: new_pipeline_id,
sandbox: sandboxed,
is_private: private_iframe,
frame_type: frame_type,
replace: replace,
};
global_scope
.constellation_chan()
.send(ConstellationMsg::ScriptLoadedURLInIFrame(load_info))
.unwrap();
if load_data.as_ref().map_or(false, |d| d.url.as_str() == "about:blank") {
let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap();
global_scope
.constellation_chan()
.send(ConstellationMsg::ScriptLoadedAboutBlankInIFrame(load_info, pipeline_sender))
.unwrap();
let new_layout_info = NewLayoutInfo {
parent_info: Some((global_scope.pipeline_id(), frame_type)),
new_pipeline_id: new_pipeline_id,
frame_id: self.frame_id,
load_data: load_data.unwrap(),
pipeline_port: pipeline_receiver,
content_process_shutdown_chan: None,
window_size: None,
layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
};
ScriptThread::process_attach_layout(new_layout_info);
} else {
let load_info = IFrameLoadInfoWithData {
info: load_info,
load_data: load_data,
old_pipeline_id: old_pipeline_id,
sandbox: sandboxed,
};
global_scope
.constellation_chan()
.send(ConstellationMsg::ScriptLoadedURLInIFrame(load_info))
.unwrap();
}
if PREFS.is_mozbrowser_enabled() {
// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart
@ -152,9 +187,23 @@ impl HTMLIFrameElement {
}
}
pub fn process_the_iframe_attributes(&self) {
/// https://html.spec.whatwg.org/multipage/#process-the-iframe-attributes
fn process_the_iframe_attributes(&self, mode: ProcessingMode) {
// TODO: srcdoc
// https://github.com/whatwg/html/issues/490
if mode == ProcessingMode::FirstTime && !self.upcast::<Element>().has_attribute(&local_name!("src")) {
let window = window_from_node(self);
let event_loop = window.dom_manipulation_task_source();
let _ = event_loop.queue(box IframeLoadEventSteps::new(self),
window.upcast());
return;
}
let url = self.get_url();
// TODO: check ancestor browsing contexts for same URL
let document = document_from_node(self);
self.navigate_or_reload_child_browsing_context(
Some(LoadData::new(url, document.get_referrer_policy(), Some(document.url()))), false);
@ -171,6 +220,16 @@ impl HTMLIFrameElement {
}
}
fn create_nested_browsing_context(&self) {
// Synchronously create a new context and navigate it to about:blank.
let url = ServoUrl::parse("about:blank").unwrap();
let document = document_from_node(self);
let load_data = LoadData::new(url,
document.get_referrer_policy(),
Some(document.url().clone()));
self.navigate_or_reload_child_browsing_context(Some(load_data), false);
}
pub fn update_pipeline_id(&self, new_pipeline_id: PipelineId) {
self.pipeline_id.set(Some(new_pipeline_id));
@ -272,7 +331,11 @@ impl HTMLIFrameElement {
self.pipeline_id.get()
.and_then(|pipeline_id| ScriptThread::find_document(pipeline_id))
.and_then(|document| {
if self.global().get_url().origin() == document.global().get_url().origin() {
// FIXME(#10964): this should use the Document's origin and the
// origin of the incumbent settings object.
let contained_url = document.global().get_url();
if self.global().get_url().origin() == contained_url.origin() ||
contained_url.as_str() == "about:blank" {
Some(Root::from_ref(document.window()))
} else {
None
@ -458,18 +521,7 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
// https://html.spec.whatwg.org/multipage/#dom-iframe-contentdocument
fn GetContentDocument(&self) -> Option<Root<Document>> {
self.get_content_window().and_then(|window| {
// FIXME(#10964): this should use the Document's origin and the
// origin of the incumbent settings object.
let self_url = self.get_url();
let win_url = window_from_node(self).get_url();
if UrlHelper::SameOrigin(&self_url, &win_url) {
Some(window.Document())
} else {
None
}
})
self.get_content_window().map(|window| window.Document())
}
// Experimental mozbrowser implementation is based on the webidl
@ -601,19 +653,17 @@ impl VirtualMethods for HTMLIFrameElement {
}));
},
&local_name!("src") => {
if let AttributeMutation::Set(_) = mutation {
// https://html.spec.whatwg.org/multipage/#the-iframe-element
// "Similarly, whenever an iframe element with a non-null nested browsing context
// but with no srcdoc attribute specified has its src attribute set, changed, or removed,
// the user agent must process the iframe attributes,"
// but we can't check that directly, since the child browsing context
// may be in a different script thread. Instread, we check to see if the parent
// is in a document tree and has a browsing context, which is what causes
// the child browsing context to be created.
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
debug!("iframe {} src set while in browsing context.", self.frame_id);
self.process_the_iframe_attributes();
}
// https://html.spec.whatwg.org/multipage/#the-iframe-element
// "Similarly, whenever an iframe element with a non-null nested browsing context
// but with no srcdoc attribute specified has its src attribute set, changed, or removed,
// the user agent must process the iframe attributes,"
// but we can't check that directly, since the child browsing context
// may be in a different script thread. Instread, we check to see if the parent
// is in a document tree and has a browsing context, which is what causes
// the child browsing context to be created.
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
debug!("iframe {} src set while in browsing context.", self.frame_id);
self.process_the_iframe_attributes(ProcessingMode::NotFirstTime);
}
},
_ => {},
@ -642,7 +692,8 @@ impl VirtualMethods for HTMLIFrameElement {
// iframe attributes for the "first time"."
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
debug!("iframe {} bound to browsing context.", self.frame_id);
self.process_the_iframe_attributes();
self.create_nested_browsing_context();
self.process_the_iframe_attributes(ProcessingMode::FirstTime);
}
}
@ -667,7 +718,7 @@ impl VirtualMethods for HTMLIFrameElement {
// HTMLIFrameElement::contentDocument.
let self_url = self.get_url();
let win_url = window_from_node(self).get_url();
UrlHelper::SameOrigin(&self_url, &win_url)
UrlHelper::SameOrigin(&self_url, &win_url) || self_url.as_str() == "about:blank"
};
let (sender, receiver) = if same_origin {
(None, None)
@ -690,3 +741,24 @@ impl VirtualMethods for HTMLIFrameElement {
}
}
}
struct IframeLoadEventSteps {
frame_element: Trusted<HTMLIFrameElement>,
pipeline_id: PipelineId,
}
impl IframeLoadEventSteps {
fn new(frame_element: &HTMLIFrameElement) -> IframeLoadEventSteps {
IframeLoadEventSteps {
frame_element: Trusted::new(frame_element),
pipeline_id: frame_element.pipeline_id().unwrap(),
}
}
}
impl Runnable for IframeLoadEventSteps {
fn handler(self: Box<IframeLoadEventSteps>) {
let this = self.frame_element.root();
this.iframe_load_event_steps(self.pipeline_id);
}
}