Make non-initial about:blank loads asynchronous

Don't update iframe pipeline until load completes

To preserve the previous functionality of delaying load events when a
new navigation is triggered, pending pipeline id represents the
current pending load. The load event is only fired if the load message's
pipeline id matches the pending pipeline id.

Track frame size on Frame instead of Pipeline

Disabled matchMedia test

Track creator pipeline id
This commit is contained in:
Connor Brewster 2017-04-17 18:48:11 -05:00
parent f579405510
commit d004db95cf
18 changed files with 305 additions and 200 deletions

View file

@ -345,7 +345,7 @@ impl HTMLFormElement {
let _target = submitter.target();
// TODO: Handle browsing contexts, partially loaded documents (step 16-17)
let mut load_data = LoadData::new(action_components, doc.get_referrer_policy(), Some(doc.url()));
let mut load_data = LoadData::new(action_components, None, doc.get_referrer_policy(), Some(doc.url()));
// Step 18
match (&*scheme, method) {

View file

@ -44,7 +44,7 @@ use msg::constellation_msg::{FrameType, FrameId, PipelineId, TraversalDirection}
use net_traits::response::HttpsState;
use script_layout_interface::message::ReflowQueryType;
use script_thread::{ScriptThread, Runnable};
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, LoadData};
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, LoadData, UpdatePipelineIdReason};
use script_traits::{MozBrowserEvent, NewLayoutInfo, ScriptMsg as ConstellationMsg};
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
use servo_atoms::Atom;
@ -69,6 +69,12 @@ bitflags! {
}
}
#[derive(PartialEq)]
pub enum NavigationType {
InitialAboutBlank,
Regular,
}
#[derive(PartialEq)]
enum ProcessingMode {
FirstTime,
@ -80,6 +86,7 @@ pub struct HTMLIFrameElement {
htmlelement: HTMLElement,
frame_id: FrameId,
pipeline_id: Cell<Option<PipelineId>>,
pending_pipeline_id: Cell<Option<PipelineId>>,
sandbox: MutNullableJS<DOMTokenList>,
sandbox_allowance: Cell<Option<SandboxAllowance>>,
load_blocker: DOMRefCell<Option<LoadBlocker>>,
@ -108,12 +115,14 @@ impl HTMLIFrameElement {
pub fn generate_new_pipeline_id(&self) -> (Option<PipelineId>, PipelineId) {
let old_pipeline_id = self.pipeline_id.get();
let new_pipeline_id = PipelineId::new();
self.pipeline_id.set(Some(new_pipeline_id));
debug!("Frame {} created pipeline {}.", self.frame_id, new_pipeline_id);
(old_pipeline_id, new_pipeline_id)
}
pub fn navigate_or_reload_child_browsing_context(&self, load_data: Option<LoadData>, replace: bool) {
pub fn navigate_or_reload_child_browsing_context(&self,
load_data: Option<LoadData>,
nav_type: NavigationType,
replace: bool) {
let sandboxed = if self.is_sandboxed() {
IFrameSandboxed
} else {
@ -136,6 +145,7 @@ impl HTMLIFrameElement {
let window = window_from_node(self);
let (old_pipeline_id, new_pipeline_id) = self.generate_new_pipeline_id();
self.pending_pipeline_id.set(Some(new_pipeline_id));
let private_iframe = self.privatebrowsing();
let frame_type = if self.Mozbrowser() { FrameType::MozBrowserIFrame } else { FrameType::IFrame };
@ -149,37 +159,41 @@ impl HTMLIFrameElement {
replace: replace,
};
if load_data.as_ref().map_or(false, |d| d.url.as_str() == "about:blank") {
let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap();
match nav_type {
NavigationType::InitialAboutBlank => {
let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap();
global_scope
.constellation_chan()
.send(ConstellationMsg::ScriptLoadedAboutBlankInIFrame(load_info, pipeline_sender))
.unwrap();
global_scope
.constellation_chan()
.send(ConstellationMsg::ScriptNewIFrame(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,
};
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, document.origin().clone());
} else {
let load_info = IFrameLoadInfoWithData {
info: load_info,
load_data: load_data,
old_pipeline_id: old_pipeline_id,
sandbox: sandboxed,
};
global_scope
self.pipeline_id.set(Some(new_pipeline_id));
ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
},
NavigationType::Regular => {
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() {
@ -192,9 +206,10 @@ impl HTMLIFrameElement {
fn process_the_iframe_attributes(&self, mode: ProcessingMode) {
// TODO: srcdoc
let window = window_from_node(self);
// 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());
@ -205,9 +220,15 @@ impl HTMLIFrameElement {
// TODO: check ancestor browsing contexts for same URL
let creator_pipeline_id = if url.as_str() == "about:blank" {
Some(window.upcast::<GlobalScope>().pipeline_id())
} else {
None
};
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);
let load_data = LoadData::new(url, creator_pipeline_id, document.get_referrer_policy(), Some(document.url()));
self.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, false);
}
#[allow(unsafe_code)]
@ -225,19 +246,30 @@ impl HTMLIFrameElement {
// 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);
let pipeline_id = Some(window_from_node(self).upcast::<GlobalScope>().pipeline_id());
let load_data = LoadData::new(url, pipeline_id, document.get_referrer_policy(), Some(document.url().clone()));
self.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::InitialAboutBlank, false);
}
pub fn update_pipeline_id(&self, new_pipeline_id: PipelineId) {
pub fn update_pipeline_id(&self, new_pipeline_id: PipelineId, reason: UpdatePipelineIdReason) {
if self.pending_pipeline_id.get() != Some(new_pipeline_id) && reason == UpdatePipelineIdReason::Navigation {
return;
}
self.pipeline_id.set(Some(new_pipeline_id));
let mut blocker = self.load_blocker.borrow_mut();
LoadBlocker::terminate(&mut blocker);
// Only terminate the load blocker if the pipeline id was updated due to a traversal.
// The load blocker will be terminated for a navigation in iframe_load_event_steps.
if reason == UpdatePipelineIdReason::Traversal {
let mut blocker = self.load_blocker.borrow_mut();
LoadBlocker::terminate(&mut blocker);
}
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
let window = window_from_node(self);
window.reflow(ReflowGoal::ForDisplay,
ReflowQueryType::NoQuery,
ReflowReason::FramedContentChanged);
}
fn new_inherited(local_name: LocalName,
@ -247,6 +279,7 @@ impl HTMLIFrameElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
frame_id: FrameId::new(),
pipeline_id: Cell::new(None),
pending_pipeline_id: Cell::new(None),
sandbox: Default::default(),
sandbox_allowance: Cell::new(None),
load_blocker: DOMRefCell::new(None),
@ -296,7 +329,7 @@ impl HTMLIFrameElement {
pub fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId) {
// TODO(#9592): assert that the load blocker is present at all times when we
// can guarantee that it's created for the case of iframe.reload().
if Some(loaded_pipeline) != self.pipeline_id() { return; }
if Some(loaded_pipeline) != self.pending_pipeline_id.get() { return; }
// TODO A cross-origin child document would not be easily accessible
// from this script thread. It's unclear how to implement
@ -330,7 +363,8 @@ impl HTMLIFrameElement {
}
pub trait HTMLIFrameElementLayoutMethods {
fn pipeline_id(self) -> Option<PipelineId>;
fn pipeline_id(&self) -> Option<PipelineId>;
fn frame_id(&self) -> FrameId;
fn get_width(&self) -> LengthOrPercentageOrAuto;
fn get_height(&self) -> LengthOrPercentageOrAuto;
}
@ -338,12 +372,21 @@ pub trait HTMLIFrameElementLayoutMethods {
impl HTMLIFrameElementLayoutMethods for LayoutJS<HTMLIFrameElement> {
#[inline]
#[allow(unsafe_code)]
fn pipeline_id(self) -> Option<PipelineId> {
fn pipeline_id(&self) -> Option<PipelineId> {
unsafe {
(*self.unsafe_get()).pipeline_id.get()
}
}
#[inline]
#[allow(unsafe_code)]
fn frame_id(&self) -> FrameId {
unsafe {
(*self.unsafe_get()).frame_id
}
}
#[allow(unsafe_code)]
fn get_width(&self) -> LengthOrPercentageOrAuto {
unsafe {
@ -563,7 +606,7 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
fn Reload(&self, _hard_reload: bool) -> ErrorResult {
if self.Mozbrowser() {
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
self.navigate_or_reload_child_browsing_context(None, true);
self.navigate_or_reload_child_browsing_context(None, NavigationType::Regular, true);
}
Ok(())
} else {
@ -739,6 +782,7 @@ impl VirtualMethods for HTMLIFrameElement {
// a new iframe. Without this, the constellation gets very
// confused.
self.pipeline_id.set(None);
self.pending_pipeline_id.set(None);
}
}

View file

@ -61,7 +61,7 @@ use heapsize::{HeapSizeOf, heap_size_of};
use html5ever::{Prefix, Namespace, QualName};
use js::jsapi::{JSContext, JSObject, JSRuntime};
use libc::{self, c_void, uintptr_t};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{FrameId, PipelineId};
use ref_slice::ref_slice;
use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData};
use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress};
@ -970,6 +970,7 @@ pub trait LayoutNodeHelpers {
fn image_url(&self) -> Option<ServoUrl>;
fn canvas_data(&self) -> Option<HTMLCanvasData>;
fn svg_data(&self) -> Option<SVGSVGData>;
fn iframe_frame_id(&self) -> FrameId;
fn iframe_pipeline_id(&self) -> PipelineId;
fn opaque(&self) -> OpaqueNode;
}
@ -1120,6 +1121,12 @@ impl LayoutNodeHelpers for LayoutJS<Node> {
.map(|svg| svg.data())
}
fn iframe_frame_id(&self) -> FrameId {
let iframe_element = self.downcast::<HTMLIFrameElement>()
.expect("not an iframe element!");
iframe_element.frame_id()
}
fn iframe_pipeline_id(&self) -> PipelineId {
let iframe_element = self.downcast::<HTMLIFrameElement>()
.expect("not an iframe element!");

View file

@ -1537,10 +1537,10 @@ impl Window {
}
}
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
self.main_thread_script_chan().send(
MainThreadScriptMsg::Navigate(self.upcast::<GlobalScope>().pipeline_id(),
LoadData::new(url, referrer_policy, Some(doc.url())),
replace)).unwrap();
MainThreadScriptMsg::Navigate(pipeline_id,
LoadData::new(url, Some(pipeline_id), referrer_policy, Some(doc.url())), replace)).unwrap();
}
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {

View file

@ -44,7 +44,7 @@ use dom::node::{LayoutNodeHelpers, Node};
use dom::text::Text;
use gfx_traits::ByteIndex;
use html5ever::{LocalName, Namespace};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{FrameId, PipelineId};
use range::Range;
use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
@ -908,6 +908,11 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
this.svg_data()
}
fn iframe_frame_id(&self) -> FrameId {
let this = unsafe { self.get_jsmanaged() };
this.iframe_frame_id()
}
fn iframe_pipeline_id(&self) -> PipelineId {
let this = unsafe { self.get_jsmanaged() };
this.iframe_pipeline_id()

View file

@ -46,7 +46,7 @@ use dom::element::Element;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::globalscope::GlobalScope;
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmliframeelement::{HTMLIFrameElement, NavigationType};
use dom::mutationobserver::MutationObserver;
use dom::node::{Node, NodeDamage, window_from_node};
use dom::serviceworker::TrustedServiceWorkerAddress;
@ -87,7 +87,7 @@ use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg};
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg, UpdatePipelineIdReason};
use script_traits::{ScriptThreadFactory, TimerEvent, TimerSchedulerMsg, TimerSource};
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
@ -555,8 +555,8 @@ impl ScriptThreadFactory for ScriptThread {
let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
let origin = MutableOrigin::new(load_data.url.origin());
let new_load = InProgressLoad::new(id, frame_id, parent_info, layout_chan, window_size,
load_data.url.clone(), origin);
let new_load = InProgressLoad::new(id, frame_id, parent_info,
layout_chan, window_size, load_data.url.clone(), origin);
script_thread.start_page_load(new_load, load_data);
let reporter_name = format!("script-reporter-{}", id);
@ -827,7 +827,22 @@ impl ScriptThread {
FromConstellation(ConstellationControlMsg::AttachLayout(
new_layout_info)) => {
self.profile_event(ScriptThreadEventCategory::AttachLayout, || {
let origin = MutableOrigin::new(new_layout_info.load_data.url.origin());
// If this is an about:blank load, it must share the creator's origin.
// This must match the logic in the constellation when creating a new pipeline
let origin = if new_layout_info.load_data.url.as_str() != "about:blank" {
MutableOrigin::new(new_layout_info.load_data.url.origin())
} else if let Some(parent) = new_layout_info.parent_info
.and_then(|(pipeline_id, _)| self.documents.borrow()
.find_document(pipeline_id)) {
parent.origin().clone()
} else if let Some(creator) = new_layout_info.load_data.creator_pipeline_id
.and_then(|pipeline_id| self.documents.borrow()
.find_document(pipeline_id)) {
creator.origin().clone()
} else {
MutableOrigin::new(ImmutableOrigin::new_opaque())
};
self.handle_new_layout(new_layout_info, origin);
})
}
@ -1043,10 +1058,12 @@ impl ScriptThread {
event),
ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id,
frame_id,
new_pipeline_id) =>
new_pipeline_id,
reason) =>
self.handle_update_pipeline_id(parent_pipeline_id,
frame_id,
new_pipeline_id),
new_pipeline_id,
reason),
ConstellationControlMsg::FocusIFrame(parent_pipeline_id, frame_id) =>
self.handle_focus_iframe_msg(parent_pipeline_id, frame_id),
ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, msg) =>
@ -1062,8 +1079,6 @@ impl ScriptThread {
self.handle_frame_load_event(parent_id, frame_id, child_id),
ConstellationControlMsg::DispatchStorageEvent(pipeline_id, storage, url, key, old_value, new_value) =>
self.handle_storage_event(pipeline_id, storage, url, key, old_value, new_value),
ConstellationControlMsg::FramedContentChanged(parent_pipeline_id, frame_id) =>
self.handle_framed_content_changed(parent_pipeline_id, frame_id),
ConstellationControlMsg::ReportCSSError(pipeline_id, filename, line, column, msg) =>
self.handle_css_error_reporting(pipeline_id, filename, line, column, msg),
ConstellationControlMsg::Reload(pipeline_id) =>
@ -1399,20 +1414,6 @@ impl ScriptThread {
}
}
fn handle_framed_content_changed(&self,
parent_pipeline_id: PipelineId,
frame_id: FrameId) {
let doc = self.documents.borrow().find_document(parent_pipeline_id).unwrap();
let frame_element = doc.find_iframe(frame_id);
if let Some(ref frame_element) = frame_element {
frame_element.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
let window = doc.window();
window.reflow(ReflowGoal::ForDisplay,
ReflowQueryType::NoQuery,
ReflowReason::FramedContentChanged);
}
}
fn handle_post_message_msg(&self, pipeline_id: PipelineId, origin: Option<ImmutableOrigin>, data: Vec<u8>) {
match { self.documents.borrow().find_window(pipeline_id) } {
None => return warn!("postMessage after pipeline {} closed.", pipeline_id),
@ -1443,10 +1444,11 @@ impl ScriptThread {
fn handle_update_pipeline_id(&self,
parent_pipeline_id: PipelineId,
frame_id: FrameId,
new_pipeline_id: PipelineId) {
new_pipeline_id: PipelineId,
reason: UpdatePipelineIdReason) {
let frame_element = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id);
if let Some(frame_element) = frame_element {
frame_element.update_pipeline_id(new_pipeline_id);
frame_element.update_pipeline_id(new_pipeline_id, reason);
}
}
@ -2065,7 +2067,7 @@ impl ScriptThread {
Some(frame_id) => {
let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id);
if let Some(iframe) = iframe {
iframe.navigate_or_reload_child_browsing_context(Some(load_data), replace);
iframe.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, replace);
}
}
None => {