Compare commits

...

21 Commits

Author SHA1 Message Date
Josh Matthews
e37cacce2d Fix test which expected iframe load for initial about:blank. 2024-10-02 21:50:27 -04:00
Josh Matthews
d90ca0bc53 Ensure iframe load event occurs if initial about:blank is recreated with document.open. 2024-10-02 21:46:21 -04:00
Josh Matthews
6aad25dc25 Update expectations. 2024-09-28 00:09:07 -04:00
Josh Matthews
4352b4c7ff Synchronously mark iframes as having pending loads when navigating a window proxy with a frame element. 2024-09-27 23:57:07 -04:00
Josh Matthews
bff5123a99 clear timers when replacing a window. 2024-09-27 01:11:41 -04:00
Josh Matthews
52e1b19c5c Only discard documents when no window replacement has occurred. 2024-09-27 01:11:11 -04:00
Josh Matthews
4aef4d48be Only use window replacement when new document is same-origin. 2024-09-27 00:30:50 -04:00
Josh Matthews
8daf551654 Enter a realm when dispatching DOMContentLoaded. 2024-09-25 22:35:24 -04:00
Josh Matthews
225b581fe5 Treat a missing replaced document as a non-replaced window. 2024-09-25 22:35:08 -04:00
Josh Matthews
6aca6d30f7 Store a pipeline that's being replaced in the load data. 2024-09-25 22:03:25 -04:00
Josh Matthews
2d2e34167e Reset document state notification when replacing a window. 2024-09-25 21:34:48 -04:00
Josh Matthews
addcddf960 about:srcdoc loads are not synchronous. 2024-09-25 21:34:30 -04:00
Josh Matthews
e22b68349b Update python2 test helper to python3. 2024-09-25 20:45:44 -04:00
Josh Matthews
45bdc0f776 Don't suspend windows that have been replaced. 2024-09-25 20:43:26 -04:00
Josh Matthews
f6ba2a77c0 Use existing window when navigating away from initial about:blank document. 2024-09-25 17:41:01 -04:00
Josh Matthews
28c27ff835 Fix panic when about:blank iframe is removed from the document immediately after being added to it. 2024-09-25 09:56:30 -04:00
Josh Matthews
a25beae1f1 Formatting. 2024-09-25 09:56:29 -04:00
Josh Matthews
441266a000 Sync load event for about:blank iframe elements. 2024-09-25 09:56:00 -04:00
Josh Matthews
6a3b468623 Only suppress load event dispatch. 2024-09-25 09:55:06 -04:00
Josh Matthews
004aa169fd Formatting. 2024-09-25 09:55:06 -04:00
Josh Matthews
9976f0e65b Hack: normalize all about:blank load behaviour and prevent load/pageshow events on the content documents. 2024-09-25 09:55:04 -04:00
43 changed files with 461 additions and 337 deletions

View File

@@ -944,7 +944,6 @@ where
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn new_pipeline( fn new_pipeline(
&mut self, &mut self,
pipeline_id: PipelineId,
browsing_context_id: BrowsingContextId, browsing_context_id: BrowsingContextId,
top_level_browsing_context_id: TopLevelBrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId,
parent_pipeline_id: Option<PipelineId>, parent_pipeline_id: Option<PipelineId>,
@@ -962,6 +961,7 @@ where
if self.shutting_down { if self.shutting_down {
return; return;
} }
let pipeline_id = load_data.new_pipeline_id.clone();
debug!( debug!(
"{}: Creating new pipeline in {}", "{}: Creating new pipeline in {}",
pipeline_id, browsing_context_id pipeline_id, browsing_context_id
@@ -1431,6 +1431,8 @@ where
Referrer::NoReferrer, Referrer::NoReferrer,
None, None,
None, None,
None,
false,
); );
let ctx_id = BrowsingContextId::from(top_level_browsing_context_id); let ctx_id = BrowsingContextId::from(top_level_browsing_context_id);
let pipeline_id = match self.browsing_contexts.get(&ctx_id) { let pipeline_id = match self.browsing_contexts.get(&ctx_id) {
@@ -1676,8 +1678,8 @@ where
self.handle_abort_load_url_msg(source_pipeline_id); self.handle_abort_load_url_msg(source_pipeline_id);
}, },
// A page loaded has completed all parsing, script, and reflow messages have been sent. // A page loaded has completed all parsing, script, and reflow messages have been sent.
FromScriptMsg::LoadComplete => { FromScriptMsg::LoadComplete(is_initial_about_blank) => {
self.handle_load_complete_msg(source_top_ctx_id, source_pipeline_id) self.handle_load_complete_msg(source_top_ctx_id, source_pipeline_id, is_initial_about_blank)
}, },
// Handle navigating to a fragment // Handle navigating to a fragment
FromScriptMsg::NavigatedToFragment(new_url, replacement_enabled) => { FromScriptMsg::NavigatedToFragment(new_url, replacement_enabled) => {
@@ -2867,6 +2869,7 @@ where
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = PipelineId::new();
let new_load_data = LoadData { let new_load_data = LoadData {
new_pipeline_id,
crash: Some( crash: Some(
backtrace backtrace
.map(|b| format!("{}\n{}", reason, b)) .map(|b| format!("{}\n{}", reason, b))
@@ -2878,7 +2881,6 @@ where
let sandbox = IFrameSandboxState::IFrameSandboxed; let sandbox = IFrameSandboxState::IFrameSandboxed;
let is_private = false; let is_private = false;
self.new_pipeline( self.new_pipeline(
new_pipeline_id,
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
None, None,
@@ -2988,7 +2990,6 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId,
) { ) {
let window_size = self.window_size.initial_viewport; let window_size = self.window_size.initial_viewport;
let pipeline_id = PipelineId::new();
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Constellation, LoadOrigin::Constellation,
@@ -2997,7 +2998,10 @@ where
Referrer::NoReferrer, Referrer::NoReferrer,
None, None,
None, None,
None,
false,
); );
let pipeline_id = load_data.new_pipeline_id.clone();
let sandbox = IFrameSandboxState::IFrameUnsandboxed; let sandbox = IFrameSandboxState::IFrameUnsandboxed;
let is_private = false; let is_private = false;
let throttled = false; let throttled = false;
@@ -3022,7 +3026,6 @@ where
.insert(new_bc_group_id, new_bc_group); .insert(new_bc_group_id, new_bc_group);
self.new_pipeline( self.new_pipeline(
pipeline_id,
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
None, None,
@@ -3113,7 +3116,7 @@ where
} }
#[tracing::instrument(skip(self), fields(servo_profiling = true))] #[tracing::instrument(skip(self), fields(servo_profiling = true))]
fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) { fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId, is_initial_about_blank: bool) {
let browsing_context_id = match self.pipelines.get(&pipeline_id) { let browsing_context_id = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.browsing_context_id, Some(pipeline) => pipeline.browsing_context_id,
None => return warn!("{}: Subframe loaded after closure", pipeline_id), None => return warn!("{}: Subframe loaded after closure", pipeline_id),
@@ -3138,6 +3141,7 @@ where
target: browsing_context_id, target: browsing_context_id,
parent: parent_pipeline_id, parent: parent_pipeline_id,
child: pipeline_id, child: pipeline_id,
is_initial_about_blank,
}; };
let result = match self.pipelines.get(&parent_pipeline_id) { let result = match self.pipelines.get(&parent_pipeline_id) {
Some(parent) => parent.event_loop.send(msg), Some(parent) => parent.event_loop.send(msg),
@@ -3180,7 +3184,6 @@ where
parent_pipeline_id, parent_pipeline_id,
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
new_pipeline_id,
is_private, is_private,
mut replace, mut replace,
.. ..
@@ -3191,6 +3194,8 @@ where
.old_pipeline_id .old_pipeline_id
.and_then(|id| self.pipelines.get(&id)); .and_then(|id| self.pipelines.get(&id));
let new_pipeline_id = load_info.load_data.new_pipeline_id.clone();
// Replacement enabled also takes into account whether the document is "completely loaded", // Replacement enabled also takes into account whether the document is "completely loaded",
// see https://html.spec.whatwg.org/multipage/#the-iframe-element:completely-loaded // see https://html.spec.whatwg.org/multipage/#the-iframe-element:completely-loaded
if let Some(old_pipeline) = old_pipeline { if let Some(old_pipeline) = old_pipeline {
@@ -3259,7 +3264,6 @@ where
// Create the new pipeline, attached to the parent and push to pending changes // Create the new pipeline, attached to the parent and push to pending changes
self.new_pipeline( self.new_pipeline(
new_pipeline_id,
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
Some(parent_pipeline_id), Some(parent_pipeline_id),
@@ -3285,13 +3289,14 @@ where
fn handle_script_new_iframe(&mut self, load_info: IFrameLoadInfoWithData) { fn handle_script_new_iframe(&mut self, load_info: IFrameLoadInfoWithData) {
let IFrameLoadInfo { let IFrameLoadInfo {
parent_pipeline_id, parent_pipeline_id,
new_pipeline_id,
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
is_private, is_private,
.. ..
} = load_info.info; } = load_info.info;
let new_pipeline_id = load_info.load_data.new_pipeline_id.clone();
let (script_sender, parent_browsing_context_id) = let (script_sender, parent_browsing_context_id) =
match self.pipelines.get(&parent_pipeline_id) { match self.pipelines.get(&parent_pipeline_id) {
Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id), Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
@@ -3349,10 +3354,11 @@ where
opener_pipeline_id, opener_pipeline_id,
new_top_level_browsing_context_id, new_top_level_browsing_context_id,
new_browsing_context_id, new_browsing_context_id,
new_pipeline_id,
} = load_info; } = load_info;
let (script_sender, opener_browsing_context_id) = let new_pipeline_id = load_data.new_pipeline_id.clone();
let (_script_sender, opener_browsing_context_id) =
match self.pipelines.get(&opener_pipeline_id) { match self.pipelines.get(&opener_pipeline_id) {
Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id), Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
None => { None => {
@@ -3372,7 +3378,18 @@ where
); );
}, },
}; };
let pipeline = Pipeline::new( self.new_pipeline(
new_browsing_context_id,
new_top_level_browsing_context_id,
Some(opener_pipeline_id),
Some(opener_browsing_context_id),
self.window_size.initial_viewport,
load_data,
IFrameSandboxState::IFrameUnsandboxed, //XXXjdm
is_opener_private,
is_opener_throttled,
);
/*let pipeline = Pipeline::new(
new_pipeline_id, new_pipeline_id,
new_browsing_context_id, new_browsing_context_id,
new_top_level_browsing_context_id, new_top_level_browsing_context_id,
@@ -3384,7 +3401,7 @@ where
); );
assert!(!self.pipelines.contains_key(&new_pipeline_id)); assert!(!self.pipelines.contains_key(&new_pipeline_id));
self.pipelines.insert(new_pipeline_id, pipeline); self.pipelines.insert(new_pipeline_id, pipeline);*/
self.webviews.add( self.webviews.add(
new_top_level_browsing_context_id, new_top_level_browsing_context_id,
WebView { WebView {
@@ -3490,7 +3507,7 @@ where
); );
}, },
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let _ = entry.insert((load_data.clone(), replace)); let _ = entry.insert((load_data.clone(), if load_data.replacing_pipeline.is_some() { HistoryEntryReplacement::Enabled } else { replace }));
}, },
}; };
// Allow the embedder to handle the url itself // Allow the embedder to handle the url itself
@@ -3531,6 +3548,7 @@ where
return None; return None;
}, },
}; };
let new_pipeline_id = load_data.new_pipeline_id.clone();
let (window_size, pipeline_id, parent_pipeline_id, is_private, is_throttled) = let (window_size, pipeline_id, parent_pipeline_id, is_private, is_throttled) =
match self.browsing_contexts.get(&browsing_context_id) { match self.browsing_contexts.get(&browsing_context_id) {
Some(ctx) => ( Some(ctx) => (
@@ -3602,10 +3620,8 @@ where
HistoryEntryReplacement::Disabled => None, HistoryEntryReplacement::Disabled => None,
}; };
let new_pipeline_id = PipelineId::new();
let sandbox = IFrameSandboxState::IFrameUnsandboxed; let sandbox = IFrameSandboxState::IFrameUnsandboxed;
self.new_pipeline( self.new_pipeline(
new_pipeline_id,
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
None, None,
@@ -3653,6 +3669,7 @@ where
&mut self, &mut self,
top_level_browsing_context_id: TopLevelBrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId,
pipeline_id: PipelineId, pipeline_id: PipelineId,
is_initial_about_blank: bool,
) { ) {
let mut webdriver_reset = false; let mut webdriver_reset = false;
if let Some((expected_pipeline_id, ref reply_chan)) = self.webdriver.load_channel { if let Some((expected_pipeline_id, ref reply_chan)) = self.webdriver.load_channel {
@@ -3693,7 +3710,7 @@ where
.send(CompositorMsg::LoadComplete(top_level_browsing_context_id)); .send(CompositorMsg::LoadComplete(top_level_browsing_context_id));
} }
} else { } else {
self.handle_subframe_loaded(pipeline_id); self.handle_subframe_loaded(pipeline_id, is_initial_about_blank);
} }
} }
@@ -3909,9 +3926,8 @@ where
Some(pipeline) => pipeline.opener, Some(pipeline) => pipeline.opener,
None => None, None => None,
}; };
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = load_data.new_pipeline_id.clone();
self.new_pipeline( self.new_pipeline(
new_pipeline_id,
browsing_context_id, browsing_context_id,
top_level_id, top_level_id,
parent_pipeline_id, parent_pipeline_id,

View File

@@ -229,10 +229,12 @@ impl Pipeline {
window_size: state.window_size, window_size: state.window_size,
}; };
if let Err(e) = if !state.load_data.synchronously_loaded {
script_chan.send(ConstellationControlMsg::AttachLayout(new_layout_info)) if let Err(e) =
{ script_chan.send(ConstellationControlMsg::AttachLayout(new_layout_info))
warn!("Sending to script during pipeline creation failed ({})", e); {
warn!("Sending to script during pipeline creation failed ({})", e);
}
} }
(script_chan, None) (script_chan, None)
}, },

View File

@@ -1,3 +1,4 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
@@ -156,7 +157,7 @@ mod from_script {
Self::GetTopForBrowsingContext(..) => target!("GetTopForBrowsingContext"), Self::GetTopForBrowsingContext(..) => target!("GetTopForBrowsingContext"),
Self::GetBrowsingContextInfo(..) => target!("GetBrowsingContextInfo"), Self::GetBrowsingContextInfo(..) => target!("GetBrowsingContextInfo"),
Self::GetChildBrowsingContextId(..) => target!("GetChildBrowsingContextId"), Self::GetChildBrowsingContextId(..) => target!("GetChildBrowsingContextId"),
Self::LoadComplete => target!("LoadComplete"), Self::LoadComplete(..) => target!("LoadComplete"),
Self::LoadUrl(..) => target!("LoadUrl"), Self::LoadUrl(..) => target!("LoadUrl"),
Self::AbortLoadUrl => target!("AbortLoadUrl"), Self::AbortLoadUrl => target!("AbortLoadUrl"),
Self::PostMessage { .. } => target!("PostMessage"), Self::PostMessage { .. } => target!("PostMessage"),

View File

@@ -262,7 +262,7 @@ impl CanvasState {
CanvasImageSource::HTMLCanvasElement(canvas) => canvas.origin_is_clean(), CanvasImageSource::HTMLCanvasElement(canvas) => canvas.origin_is_clean(),
CanvasImageSource::OffscreenCanvas(canvas) => canvas.origin_is_clean(), CanvasImageSource::OffscreenCanvas(canvas) => canvas.origin_is_clean(),
CanvasImageSource::HTMLImageElement(image) => { CanvasImageSource::HTMLImageElement(image) => {
image.same_origin(GlobalScope::entry().origin()) image.same_origin(&GlobalScope::entry().origin())
}, },
CanvasImageSource::CSSStyleValue(_) => true, CanvasImageSource::CSSStyleValue(_) => true,
} }

View File

@@ -3020,7 +3020,7 @@ create_global_object(
raw.as_ptr() as *const libc::c_void, raw.as_ptr() as *const libc::c_void,
_trace, _trace,
obj.handle_mut(), obj.handle_mut(),
origin); &origin);
assert!(!obj.is_null()); assert!(!obj.is_null());
let root = raw.reflect_with(obj.get()); let root = raw.reflect_with(obj.get());

View File

@@ -162,8 +162,7 @@ pub unsafe fn create_global_object(
// Initialize the reserved slots before doing anything that can GC, to // Initialize the reserved slots before doing anything that can GC, to
// avoid getting trace hooks called on a partially initialized object. // avoid getting trace hooks called on a partially initialized object.
let private_val = PrivateValue(private); set_reflector_object(rval.handle(), private);
JS_SetReservedSlot(rval.get(), DOM_OBJECT_SLOT, &private_val);
let proto_array: Box<ProtoOrIfaceArray> = let proto_array: Box<ProtoOrIfaceArray> =
Box::new([ptr::null_mut::<JSObject>(); PrototypeList::PROTO_OR_IFACE_LENGTH]); Box::new([ptr::null_mut::<JSObject>(); PrototypeList::PROTO_OR_IFACE_LENGTH]);
let val = PrivateValue(Box::into_raw(proto_array) as *const libc::c_void); let val = PrivateValue(Box::into_raw(proto_array) as *const libc::c_void);
@@ -173,6 +172,11 @@ pub unsafe fn create_global_object(
JS_FireOnNewGlobalObject(*cx, rval.handle()); JS_FireOnNewGlobalObject(*cx, rval.handle());
} }
pub(crate) unsafe fn set_reflector_object(reflector: HandleObject, private: *const libc::c_void) {
let private_val = PrivateValue(private);
JS_SetReservedSlot(reflector.get(), DOM_OBJECT_SLOT, &private_val);
}
/// Choose the compartment to create a new global object in. /// Choose the compartment to create a new global object in.
fn select_compartment(cx: SafeJSContext, options: &mut RealmOptions) { fn select_compartment(cx: SafeJSContext, options: &mut RealmOptions) {
type Data = *mut Compartment; type Data = *mut Compartment;

View File

@@ -178,7 +178,7 @@ use crate::dom::wheelevent::WheelEvent;
use crate::dom::window::{ReflowReason, Window}; use crate::dom::window::{ReflowReason, Window};
use crate::dom::windowproxy::WindowProxy; use crate::dom::windowproxy::WindowProxy;
use crate::fetch::FetchCanceller; use crate::fetch::FetchCanceller;
use crate::realms::{AlreadyInRealm, InRealm}; use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptThreadEventCategory}; use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptThreadEventCategory};
use crate::script_thread::{MainThreadScriptMsg, ScriptThread}; use crate::script_thread::{MainThreadScriptMsg, ScriptThread};
use crate::stylesheet_set::StylesheetSetRef; use crate::stylesheet_set::StylesheetSetRef;
@@ -486,6 +486,9 @@ pub struct Document {
visibility_state: Cell<DocumentVisibilityState>, visibility_state: Cell<DocumentVisibilityState>,
/// <https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml> /// <https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml>
status_code: Option<u16>, status_code: Option<u16>,
inhibit_load_and_pageshow: Cell<bool>,
window_replaced: Cell<bool>,
is_initial_about_blank: Cell<bool>,
} }
#[derive(JSTraceable, MallocSizeOf)] #[derive(JSTraceable, MallocSizeOf)]
@@ -539,6 +542,10 @@ impl CollectionFilter for AnchorsFilter {
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl Document { impl Document {
pub(crate) fn is_initial_about_blank(&self) -> bool {
self.is_initial_about_blank.get()
}
pub fn note_node_with_dirty_descendants(&self, node: &Node) { pub fn note_node_with_dirty_descendants(&self, node: &Node) {
debug_assert!(*node.owner_doc() == *self); debug_assert!(*node.owner_doc() == *self);
if !node.is_connected() { if !node.is_connected() {
@@ -708,7 +715,9 @@ impl Document {
ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get()); ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
if activity != DocumentActivity::FullyActive { if activity != DocumentActivity::FullyActive {
self.window().suspend(); if self.is_window_relevant() {
self.window().suspend();
}
media.suspend(&client_context_id); media.suspend(&client_context_id);
return; return;
} }
@@ -2320,8 +2329,10 @@ impl Document {
if !self.salvageable.get() { if !self.salvageable.get() {
// Step 1 of clean-up steps. // Step 1 of clean-up steps.
global_scope.close_event_sources(); global_scope.close_event_sources();
let msg = ScriptMsg::DiscardDocument; if self.is_window_relevant() {
let _ = global_scope.script_to_constellation_chan().send(msg); let msg = ScriptMsg::DiscardDocument;
let _ = global_scope.script_to_constellation_chan().send(msg);
}
} }
// https://w3c.github.io/FileAPI/#lifeTime // https://w3c.github.io/FileAPI/#lifeTime
global_scope.clean_up_all_file_resources(); global_scope.clean_up_all_file_resources();
@@ -2358,17 +2369,20 @@ impl Document {
// Step 7. // Step 7.
debug!("Document loads are complete."); debug!("Document loads are complete.");
let document = Trusted::new(self); let document = Trusted::new(self);
let was_initial_about_blank = self.is_initial_about_blank.get();
self.window self.window
.task_manager() .task_manager()
.dom_manipulation_task_source() .dom_manipulation_task_source()
.queue( .queue(
task!(fire_load_event: move || { task!(prepare_for_post_load_tasks: move || {
let document = document.root(); let document = document.root();
let window = document.window(); let window = document.window();
if !window.is_alive() { if !window.is_alive() {
return; return;
} }
notify_constellation_load(&window, was_initial_about_blank);
// Step 7.1. // Step 7.1.
document.set_ready_state(DocumentReadyState::Complete); document.set_ready_state(DocumentReadyState::Complete);
@@ -2387,37 +2401,19 @@ impl Document {
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart
update_with_current_instant(&document.load_event_start); update_with_current_instant(&document.load_event_start);
debug!("About to dispatch load for {:?}", document.url()); if !document.inhibit_load_and_pageshow.get() {
// FIXME(nox): Why are errors silenced here? debug!("About to dispatch load for {:?}", document.url());
let _ = window.dispatch_event_with_target_override( // FIXME(nox): Why are errors silenced here?
&event, let _ = window.dispatch_event_with_target_override(
); &event,
);
}
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd
update_with_current_instant(&document.load_event_end); update_with_current_instant(&document.load_event_end);
if let Some(fragment) = document.url().fragment() { //assert!(!document.page_showing.get());
document.check_and_scroll_fragment(fragment); if !document.page_showing.get() {
}
}),
self.window.upcast(),
)
.unwrap();
// Step 8.
let document = Trusted::new(self);
if document.root().browsing_context().is_some() {
self.window
.task_manager()
.dom_manipulation_task_source()
.queue(
task!(fire_pageshow_event: move || {
let document = document.root();
let window = document.window();
if document.page_showing.get() || !window.is_alive() {
return;
}
document.page_showing.set(true); document.page_showing.set(true);
let event = PageTransitionEvent::new( let event = PageTransitionEvent::new(
@@ -2434,11 +2430,15 @@ impl Document {
let _ = window.dispatch_event_with_target_override( let _ = window.dispatch_event_with_target_override(
event, event,
); );
}), }
self.window.upcast(),
) if let Some(fragment) = document.url().fragment() {
.unwrap(); document.check_and_scroll_fragment(fragment);
} }
}),
self.window.upcast(),
)
.unwrap();
// Step 9. // Step 9.
// TODO: pending application cache download process tasks. // TODO: pending application cache download process tasks.
@@ -2482,10 +2482,10 @@ impl Document {
}), }),
Duration::from_secs(*time), Duration::from_secs(*time),
); );
} };
// Note: this will, among others, result in the "iframe-load-event-steps" being run. // Note: this will, among others, result in the "iframe-load-event-steps" being run.
// https://html.spec.whatwg.org/multipage/#iframe-load-event-steps // https://html.spec.whatwg.org/multipage/#iframe-load-event-steps
document.notify_constellation_load(); //document.notify_constellation_load();
}), }),
self.window.upcast(), self.window.upcast(),
) )
@@ -2641,9 +2641,10 @@ impl Document {
.dom_manipulation_task_source() .dom_manipulation_task_source()
.queue( .queue(
task!(fire_dom_content_loaded_event: move || { task!(fire_dom_content_loaded_event: move || {
let document = document.root(); let document = document.root();
document.upcast::<EventTarget>().fire_bubbling_event(atom!("DOMContentLoaded")); let _realm = enter_realm(&*document);
update_with_current_instant(&document.dom_content_loaded_event_end); document.upcast::<EventTarget>().fire_bubbling_event(atom!("DOMContentLoaded"));
update_with_current_instant(&document.dom_content_loaded_event_end);
}), }),
window.upcast(), window.upcast(),
) )
@@ -2701,10 +2702,6 @@ impl Document {
} }
} }
pub fn notify_constellation_load(&self) {
self.window().send_to_constellation(ScriptMsg::LoadComplete);
}
pub fn set_current_parser(&self, script: Option<&ServoParser>) { pub fn set_current_parser(&self, script: Option<&ServoParser>) {
self.current_parser.set(script); self.current_parser.set(script);
} }
@@ -3180,6 +3177,7 @@ impl Document {
referrer_policy: Option<ReferrerPolicy>, referrer_policy: Option<ReferrerPolicy>,
status_code: Option<u16>, status_code: Option<u16>,
canceller: FetchCanceller, canceller: FetchCanceller,
inhibit_load_and_pageshow: bool,
) -> Document { ) -> Document {
let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap()); let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
@@ -3206,6 +3204,7 @@ impl Document {
.and_then(|charset| Encoding::for_label(charset.as_str().as_bytes())) .and_then(|charset| Encoding::for_label(charset.as_str().as_bytes()))
.unwrap_or(UTF_8); .unwrap_or(UTF_8);
let is_initial_about_blank = url.as_str() == "about:blank";
let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes; let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
Document { Document {
node: Node::new_document_node(), node: Node::new_document_node(),
@@ -3327,9 +3326,20 @@ impl Document {
fonts: Default::default(), fonts: Default::default(),
visibility_state: Cell::new(DocumentVisibilityState::Hidden), visibility_state: Cell::new(DocumentVisibilityState::Hidden),
status_code, status_code,
inhibit_load_and_pageshow: Cell::new(inhibit_load_and_pageshow),
window_replaced: Cell::new(false),
is_initial_about_blank: Cell::new(is_initial_about_blank),
} }
} }
pub(crate) fn disown_window(&self) {
self.window_replaced.set(true);
}
pub(crate) fn is_window_relevant(&self) -> bool {
!self.window_replaced.get()
}
/// Note a pending animation tick, to be processed at the next `update_the_rendering` task. /// Note a pending animation tick, to be processed at the next `update_the_rendering` task.
pub fn note_pending_animation_tick(&self, tick_type: AnimationTickType) { pub fn note_pending_animation_tick(&self, tick_type: AnimationTickType) {
self.pending_animation_ticks.borrow_mut().extend(tick_type); self.pending_animation_ticks.borrow_mut().extend(tick_type);
@@ -3475,6 +3485,7 @@ impl Document {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
)) ))
} }
@@ -3495,6 +3506,7 @@ impl Document {
status_code: Option<u16>, status_code: Option<u16>,
canceller: FetchCanceller, canceller: FetchCanceller,
can_gc: CanGc, can_gc: CanGc,
inhibit_load_and_pageshow: bool,
) -> DomRoot<Document> { ) -> DomRoot<Document> {
Self::new_with_proto( Self::new_with_proto(
window, window,
@@ -3513,6 +3525,7 @@ impl Document {
status_code, status_code,
canceller, canceller,
can_gc, can_gc,
inhibit_load_and_pageshow,
) )
} }
@@ -3534,6 +3547,7 @@ impl Document {
status_code: Option<u16>, status_code: Option<u16>,
canceller: FetchCanceller, canceller: FetchCanceller,
can_gc: CanGc, can_gc: CanGc,
inhibit_load_and_pageshow: bool,
) -> DomRoot<Document> { ) -> DomRoot<Document> {
let document = reflect_dom_object_with_proto( let document = reflect_dom_object_with_proto(
Box::new(Document::new_inherited( Box::new(Document::new_inherited(
@@ -3551,6 +3565,7 @@ impl Document {
referrer_policy, referrer_policy,
status_code, status_code,
canceller, canceller,
inhibit_load_and_pageshow,
)), )),
window, window,
proto, proto,
@@ -3676,6 +3691,7 @@ impl Document {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
); );
new_doc new_doc
.appropriate_template_contents_owner_document .appropriate_template_contents_owner_document
@@ -5277,12 +5293,16 @@ impl DocumentMethods for Document {
self.set_url(new_url); self.set_url(new_url);
} }
self.inhibit_load_and_pageshow.set(false);
// Step 13 // Step 13
// TODO: https://github.com/servo/servo/issues/21938 // TODO: https://github.com/servo/servo/issues/21938
// Step 14 // Step 14
self.set_quirks_mode(QuirksMode::NoQuirks); self.set_quirks_mode(QuirksMode::NoQuirks);
self.is_initial_about_blank.set(false);
// Step 15 // Step 15
let resource_threads = self let resource_threads = self
.window .window
@@ -5652,3 +5672,8 @@ fn is_named_element_with_id_attribute(elem: &Element) -> bool {
// behaviour is actually implemented // behaviour is actually implemented
elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty()) elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
} }
fn notify_constellation_load(window: &Window, is_initial_about_blank: bool) {
window.send_to_constellation(ScriptMsg::LoadComplete(is_initial_about_blank));
}

View File

@@ -160,6 +160,7 @@ impl DOMImplementationMethods for DOMImplementation {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
); );
{ {

View File

@@ -89,6 +89,7 @@ impl DOMParserMethods for DOMParser {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
); );
ServoParser::parse_html_document(&document, Some(s), url, CanGc::note()); ServoParser::parse_html_document(&document, Some(s), url, CanGc::note());
document.set_ready_state(DocumentReadyState::Complete); document.set_ready_state(DocumentReadyState::Complete);
@@ -111,6 +112,7 @@ impl DOMParserMethods for DOMParser {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
); );
ServoParser::parse_xml_document(&document, Some(s), url, CanGc::note()); ServoParser::parse_xml_document(&document, Some(s), url, CanGc::note());
document.set_ready_state(DocumentReadyState::Complete); document.set_ready_state(DocumentReadyState::Complete);

View File

@@ -3,10 +3,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::Cell; use std::cell::{Cell, RefCell, Ref};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::ops::Index; use std::ops::{Deref, Index};
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@@ -208,7 +208,7 @@ pub struct GlobalScope {
/// Pipeline id associated with this global. /// Pipeline id associated with this global.
#[no_trace] #[no_trace]
pipeline_id: PipelineId, pipeline_id: Cell<PipelineId>,
/// A flag to indicate whether the developer tools has requested /// A flag to indicate whether the developer tools has requested
/// live updates from the worker. /// live updates from the worker.
@@ -243,7 +243,7 @@ pub struct GlobalScope {
/// A handle for communicating messages to the constellation thread. /// A handle for communicating messages to the constellation thread.
#[ignore_malloc_size_of = "channels are hard"] #[ignore_malloc_size_of = "channels are hard"]
#[no_trace] #[no_trace]
script_to_constellation_chan: ScriptToConstellationChan, script_to_constellation_chan: RefCell<ScriptToConstellationChan>,
#[ignore_malloc_size_of = "channels are hard"] #[ignore_malloc_size_of = "channels are hard"]
#[no_trace] #[no_trace]
@@ -266,11 +266,11 @@ pub struct GlobalScope {
/// The origin of the globalscope /// The origin of the globalscope
#[no_trace] #[no_trace]
origin: MutableOrigin, origin: RefCell<MutableOrigin>,
/// <https://html.spec.whatwg.org/multipage/#concept-environment-creation-url> /// <https://html.spec.whatwg.org/multipage/#concept-environment-creation-url>
#[no_trace] #[no_trace]
creation_url: Option<ServoUrl>, creation_url: RefCell<Option<ServoUrl>>,
/// A map for storing the previous permission state read results. /// A map for storing the previous permission state read results.
permission_state_invocation_results: DomRefCell<HashMap<String, PermissionState>>, permission_state_invocation_results: DomRefCell<HashMap<String, PermissionState>>,
@@ -779,7 +779,7 @@ impl GlobalScope {
crypto: Default::default(), crypto: Default::default(),
registration_map: DomRefCell::new(HashMapTracedValues::new()), registration_map: DomRefCell::new(HashMapTracedValues::new()),
worker_map: DomRefCell::new(HashMapTracedValues::new()), worker_map: DomRefCell::new(HashMapTracedValues::new()),
pipeline_id, pipeline_id: Cell::new(pipeline_id),
devtools_wants_updates: Default::default(), devtools_wants_updates: Default::default(),
console_timers: DomRefCell::new(Default::default()), console_timers: DomRefCell::new(Default::default()),
module_map: DomRefCell::new(Default::default()), module_map: DomRefCell::new(Default::default()),
@@ -787,14 +787,14 @@ impl GlobalScope {
devtools_chan, devtools_chan,
mem_profiler_chan, mem_profiler_chan,
time_profiler_chan, time_profiler_chan,
script_to_constellation_chan, script_to_constellation_chan: RefCell::new(script_to_constellation_chan),
scheduler_chan: scheduler_chan.clone(), scheduler_chan: scheduler_chan.clone(),
in_error_reporting_mode: Default::default(), in_error_reporting_mode: Default::default(),
resource_threads, resource_threads,
timers: OneshotTimers::new(scheduler_chan), timers: OneshotTimers::new(scheduler_chan),
init_timers: Default::default(), init_timers: Default::default(),
origin, origin: RefCell::new(origin),
creation_url, creation_url: RefCell::new(creation_url),
permission_state_invocation_results: Default::default(), permission_state_invocation_results: Default::default(),
microtask_queue, microtask_queue,
list_auto_close_worker: Default::default(), list_auto_close_worker: Default::default(),
@@ -2318,7 +2318,7 @@ impl GlobalScope {
pub fn issue_page_warning(&self, warning: &str) { pub fn issue_page_warning(&self, warning: &str) {
if let Some(ref chan) = self.devtools_chan { if let Some(ref chan) = self.devtools_chan {
let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError( let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError(
self.pipeline_id, self.pipeline_id.get(),
PageError { PageError {
type_: "PageError".to_string(), type_: "PageError".to_string(),
error_message: warning.to_string(), error_message: warning.to_string(),
@@ -2352,8 +2352,8 @@ impl GlobalScope {
} }
/// Get a sender to the constellation thread. /// Get a sender to the constellation thread.
pub fn script_to_constellation_chan(&self) -> &ScriptToConstellationChan { pub fn script_to_constellation_chan(&self) -> Ref<ScriptToConstellationChan> {
&self.script_to_constellation_chan self.script_to_constellation_chan.borrow()
} }
pub fn send_to_embedder(&self, msg: EmbedderMsg) { pub fn send_to_embedder(&self, msg: EmbedderMsg) {
@@ -2370,17 +2370,17 @@ impl GlobalScope {
/// Get the `PipelineId` for this global scope. /// Get the `PipelineId` for this global scope.
pub fn pipeline_id(&self) -> PipelineId { pub fn pipeline_id(&self) -> PipelineId {
self.pipeline_id self.pipeline_id.get()
} }
/// Get the origin for this global scope /// Get the origin for this global scope
pub fn origin(&self) -> &MutableOrigin { pub fn origin(&self) -> Ref<MutableOrigin> {
&self.origin self.origin.borrow()
} }
/// Get the creation_url for this global scope /// Get the creation_url for this global scope
pub fn creation_url(&self) -> &Option<ServoUrl> { pub fn creation_url(&self) -> Ref<Option<ServoUrl>> {
&self.creation_url self.creation_url.borrow()
} }
pub fn image_cache(&self) -> Arc<dyn ImageCache> { pub fn image_cache(&self) -> Arc<dyn ImageCache> {
@@ -2513,7 +2513,7 @@ impl GlobalScope {
} else if self.is::<Window>() { } else if self.is::<Window>() {
if let Some(ref chan) = self.devtools_chan { if let Some(ref chan) = self.devtools_chan {
let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError( let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError(
self.pipeline_id, self.pipeline_id.get(),
PageError { PageError {
type_: "PageError".to_string(), type_: "PageError".to_string(),
error_message: error_info.message.clone(), error_message: error_info.message.clone(),
@@ -3129,7 +3129,7 @@ impl GlobalScope {
if Some(false) == self.inherited_secure_context { if Some(false) == self.inherited_secure_context {
return false; return false;
} }
if let Some(creation_url) = self.creation_url() { if let Some(creation_url) = self.creation_url().deref() {
if creation_url.scheme() == "blob" && Some(true) == self.inherited_secure_context { if creation_url.scheme() == "blob" && Some(true) == self.inherited_secure_context {
return true; return true;
} }
@@ -3400,6 +3400,27 @@ impl GlobalScope {
Ok(message_clone.get()) Ok(message_clone.get())
} }
pub(crate) fn replace_contents(&self, data: ReplaceData) {
let ReplaceData {
pipeline_id,
script_to_constellation_chan,
creator_url,
origin,
} = data;
self.pipeline_id.set(pipeline_id);
*self.script_to_constellation_chan.borrow_mut() = script_to_constellation_chan;
*self.creation_url.borrow_mut() = Some(creator_url);
*self.origin.borrow_mut() = origin;
self.timers.reset();
}
}
pub(crate) struct ReplaceData {
pub pipeline_id: PipelineId,
pub script_to_constellation_chan: ScriptToConstellationChan,
pub creator_url: ServoUrl,
pub origin: MutableOrigin,
} }
/// Returns the Rust global scope from a JS global object. /// Returns the Rust global scope from a JS global object.

View File

@@ -63,6 +63,7 @@ use crate::dom::htmldatalistelement::HTMLDataListElement;
use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlfieldsetelement::HTMLFieldSetElement; use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection; use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection;
use crate::dom::htmliframeelement::HTMLIFrameElement;
use crate::dom::htmlimageelement::HTMLImageElement; use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlinputelement::{HTMLInputElement, InputType}; use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
use crate::dom::htmllabelelement::HTMLLabelElement; use crate::dom::htmllabelelement::HTMLLabelElement;
@@ -831,6 +832,8 @@ impl HTMLFormElement {
target_window.upcast::<GlobalScope>().get_referrer(), target_window.upcast::<GlobalScope>().get_referrer(),
target_document.get_referrer_policy(), target_document.get_referrer_policy(),
Some(target_window.upcast::<GlobalScope>().is_secure_context()), Some(target_window.upcast::<GlobalScope>().is_secure_context()),
(doc.url().as_str() == "about:blank").then(|| doc.window().pipeline_id()),
false,
); );
// Step 22 // Step 22
@@ -988,6 +991,14 @@ impl HTMLFormElement {
load_data.referrer = referrer; load_data.referrer = referrer;
load_data.referrer_policy = referrer_policy; load_data.referrer_policy = referrer_policy;
let new_pipeline_id = load_data.new_pipeline_id.clone();
let proxy = target.window_proxy();
if let Some(frame) = proxy.frame_element() {
if let Some(frame) = frame.downcast::<HTMLIFrameElement>() {
frame.update_pending_pipeline_id(new_pipeline_id);
}
}
// Step 4. // Step 4.
let this = Trusted::new(self); let this = Trusted::new(self);
let window = Trusted::new(target); let window = Trusted::new(target);

View File

@@ -8,6 +8,7 @@ use base::id::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
use bitflags::bitflags; use bitflags::bitflags;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
use js::jsapi::JSAutoRealm;
use js::rust::HandleObject; use js::rust::HandleObject;
use profile_traits::ipc as ProfiledIpc; use profile_traits::ipc as ProfiledIpc;
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
@@ -40,6 +41,8 @@ use crate::dom::node::{
}; };
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::windowproxy::WindowProxy; use crate::dom::windowproxy::WindowProxy;
use crate::microtask::{Microtask, MicrotaskRunnable};
use crate::realms::enter_realm;
use crate::script_runtime::CanGc; use crate::script_runtime::CanGc;
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
@@ -177,7 +180,7 @@ impl HTMLIFrameElement {
let window = window_from_node(self); let window = window_from_node(self);
let old_pipeline_id = self.pipeline_id(); let old_pipeline_id = self.pipeline_id();
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = load_data.new_pipeline_id.clone();
self.pending_pipeline_id.set(Some(new_pipeline_id)); self.pending_pipeline_id.set(Some(new_pipeline_id));
let global_scope = window.upcast::<GlobalScope>(); let global_scope = window.upcast::<GlobalScope>();
@@ -185,7 +188,6 @@ impl HTMLIFrameElement {
parent_pipeline_id: global_scope.pipeline_id(), parent_pipeline_id: global_scope.pipeline_id(),
browsing_context_id, browsing_context_id,
top_level_browsing_context_id, top_level_browsing_context_id,
new_pipeline_id,
is_private: false, // FIXME is_private: false, // FIXME
inherited_secure_context: load_data.inherited_secure_context, inherited_secure_context: load_data.inherited_secure_context,
replace, replace,
@@ -261,6 +263,8 @@ impl HTMLIFrameElement {
window.upcast::<GlobalScope>().get_referrer(), window.upcast::<GlobalScope>().get_referrer(),
document.get_referrer_policy(), document.get_referrer_policy(),
Some(window.upcast::<GlobalScope>().is_secure_context()), Some(window.upcast::<GlobalScope>().is_secure_context()),
None, //XXXjdm
false,
); );
let element = self.upcast::<Element>(); let element = self.upcast::<Element>();
load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc"))); load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc")));
@@ -288,9 +292,14 @@ impl HTMLIFrameElement {
} }
} }
if mode == ProcessingMode::FirstTime && let element = self.upcast::<Element>();
!self.upcast::<Element>().has_attribute(&local_name!("src")) let src = element.get_string_attribute(&local_name!("src"));
{ if mode == ProcessingMode::FirstTime && (src.is_empty() || src == "about:blank") {
let task = IframeElementMicrotask {
elem: DomRoot::from_ref(self),
about_blank_pipeline: self.about_blank_pipeline_id.get().unwrap(),
};
ScriptThread::await_stable_state(Microtask::IframeElement(task));
return; return;
} }
@@ -332,6 +341,9 @@ impl HTMLIFrameElement {
}; };
let document = document_from_node(self); let document = document_from_node(self);
let pipeline_id = self.pipeline_id();
let is_about_blank = pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
let replaced_pipeline = is_about_blank.then(|| self.about_blank_pipeline_id.get().unwrap());
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()), LoadOrigin::Script(document.origin().immutable().clone()),
url, url,
@@ -339,13 +351,12 @@ impl HTMLIFrameElement {
window.upcast::<GlobalScope>().get_referrer(), window.upcast::<GlobalScope>().get_referrer(),
document.get_referrer_policy(), document.get_referrer_policy(),
Some(window.upcast::<GlobalScope>().is_secure_context()), Some(window.upcast::<GlobalScope>().is_secure_context()),
replaced_pipeline,
false,
); );
let pipeline_id = self.pipeline_id();
// If the initial `about:blank` page is the current page, load with replacement enabled, // If the initial `about:blank` page is the current page, load with replacement enabled,
// see https://html.spec.whatwg.org/multipage/#the-iframe-element:about:blank-3 // see https://html.spec.whatwg.org/multipage/#the-iframe-element:about:blank-3
let is_about_blank =
pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
let replace = if is_about_blank { let replace = if is_about_blank {
HistoryEntryReplacement::Enabled HistoryEntryReplacement::Enabled
} else { } else {
@@ -381,6 +392,8 @@ impl HTMLIFrameElement {
window.upcast::<GlobalScope>().get_referrer(), window.upcast::<GlobalScope>().get_referrer(),
document.get_referrer_policy(), document.get_referrer_policy(),
Some(window.upcast::<GlobalScope>().is_secure_context()), Some(window.upcast::<GlobalScope>().is_secure_context()),
None,
true,
); );
let browsing_context_id = BrowsingContextId::new(); let browsing_context_id = BrowsingContextId::new();
let top_level_browsing_context_id = window.window_proxy().top_level_browsing_context_id(); let top_level_browsing_context_id = window.window_proxy().top_level_browsing_context_id();
@@ -405,6 +418,10 @@ impl HTMLIFrameElement {
self.browsing_context_id.set(None); self.browsing_context_id.set(None);
} }
pub(crate) fn update_pending_pipeline_id(&self, pending: PipelineId) {
self.pending_pipeline_id.set(Some(pending));
}
pub fn update_pipeline_id( pub fn update_pipeline_id(
&self, &self,
new_pipeline_id: PipelineId, new_pipeline_id: PipelineId,
@@ -488,13 +505,24 @@ impl HTMLIFrameElement {
} }
/// <https://html.spec.whatwg.org/multipage/#iframe-load-event-steps> steps 1-4 /// <https://html.spec.whatwg.org/multipage/#iframe-load-event-steps> steps 1-4
pub fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, can_gc: CanGc) { pub fn iframe_load_event_steps(
&self,
loaded_pipeline: PipelineId,
can_gc: CanGc,
dispatch_load_for_about_blank: bool,
) {
// TODO(#9592): assert that the load blocker is present at all times when we // 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(). // can guarantee that it's created for the case of iframe.reload().
if Some(loaded_pipeline) != self.pending_pipeline_id.get() { if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
return; return;
} }
if Some(loaded_pipeline) == self.about_blank_pipeline_id.get() &&
!dispatch_load_for_about_blank
{
return;
}
// TODO A cross-origin child document would not be easily accessible // TODO A cross-origin child document would not be easily accessible
// from this script thread. It's unclear how to implement // from this script thread. It's unclear how to implement
// steps 2, 3, and 5 efficiently in this case. // steps 2, 3, and 5 efficiently in this case.
@@ -788,3 +816,21 @@ impl VirtualMethods for HTMLIFrameElement {
self.destroy_nested_browsing_context(); self.destroy_nested_browsing_context();
} }
} }
#[derive(JSTraceable, MallocSizeOf)]
pub struct IframeElementMicrotask {
elem: DomRoot<HTMLIFrameElement>,
#[no_trace]
about_blank_pipeline: PipelineId,
}
impl MicrotaskRunnable for IframeElementMicrotask {
fn handler(&self, can_gc: CanGc) {
self.elem
.iframe_load_event_steps(self.about_blank_pipeline, can_gc, true);
}
fn enter_realm(&self) -> JSAutoRealm {
enter_realm(&*self.elem)
}
}

View File

@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Ref;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use net_traits::request::Referrer; use net_traits::request::Referrer;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin}; use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
@@ -123,6 +125,8 @@ impl Location {
referrer, referrer,
referrer_policy, referrer_policy,
None, // Top navigation doesn't inherit secure context None, // Top navigation doesn't inherit secure context
(self.window.Document().url().as_str() == "about:blank").then(|| self.window.pipeline_id()),
false,
); );
self.window self.window
.load_url(replacement_flag, reload_triggered, load_data); .load_url(replacement_flag, reload_triggered, load_data);
@@ -249,7 +253,7 @@ impl Location {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn origin(&self) -> &MutableOrigin { pub fn origin(&self) -> Ref<MutableOrigin> {
self.window.origin() self.window.origin()
} }
} }
@@ -389,11 +393,14 @@ impl LocationMethods for Location {
if self.has_document() { if self.has_document() {
// Note: no call to self.check_same_origin_domain() // Note: no call to self.check_same_origin_domain()
// Step 2: Parse the given value relative to the entry settings object. // Step 2: Parse the given value relative to the entry settings object.
// If that failed, throw a TypeError exception. // If that failed, throw a SyntaxError exception.
let base_url = self.entry_settings_object().api_base_url(); let base_url = self.entry_settings_object().api_base_url();
let url = match base_url.join(&value.0) { let url = match base_url.join(&value.0) {
Ok(url) => url, Ok(url) => url,
Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), Err(e) => {
info!("Couldn't parse URL: {}", e);
return Err(Error::Syntax);
}
}; };
// Step 3: Location-object navigate to the resulting URL record. // Step 3: Location-object navigate to the resulting URL record.
self.navigate( self.navigate(

View File

@@ -2299,6 +2299,7 @@ impl Node {
document.status_code(), document.status_code(),
Default::default(), Default::default(),
CanGc::note(), CanGc::note(),
false,
); );
DomRoot::upcast::<Node>(document) DomRoot::upcast::<Node>(document)
}, },

View File

@@ -216,6 +216,7 @@ impl ServoParser {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
); );
// Step 2. // Step 2.

View File

@@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::{Cow, ToOwned}; use std::borrow::{Cow, ToOwned};
use std::cell::{Cell, RefCell, RefMut}; use std::cell::{Cell, RefCell, RefMut, Ref as StdRef};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::default::Default; use std::default::Default;
@@ -185,7 +185,7 @@ pub struct Window {
globalscope: GlobalScope, globalscope: GlobalScope,
#[ignore_malloc_size_of = "trait objects are hard"] #[ignore_malloc_size_of = "trait objects are hard"]
script_chan: MainThreadScriptChan, script_chan: MainThreadScriptChan,
task_manager: TaskManager, task_manager: RefCell<TaskManager>,
#[no_trace] #[no_trace]
#[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"] #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
layout: RefCell<Box<dyn Layout>>, layout: RefCell<Box<dyn Layout>>,
@@ -357,8 +357,8 @@ pub struct Window {
} }
impl Window { impl Window {
pub fn task_manager(&self) -> &TaskManager { pub fn task_manager(&self) -> StdRef<TaskManager> {
&self.task_manager self.task_manager.borrow()
} }
pub fn layout(&self) -> Ref<Box<dyn Layout>> { pub fn layout(&self) -> Ref<Box<dyn Layout>> {
@@ -405,7 +405,8 @@ impl Window {
/// Cancel all current, and ignore all subsequently queued, tasks. /// Cancel all current, and ignore all subsequently queued, tasks.
pub fn ignore_all_tasks(&self) { pub fn ignore_all_tasks(&self) {
let mut ignore_flags = self.task_manager.task_cancellers.borrow_mut(); let task_manager = self.task_manager.borrow();
let mut ignore_flags = task_manager.task_cancellers.borrow_mut();
for task_source_name in TaskSourceName::all() { for task_source_name in TaskSourceName::all() {
let flag = ignore_flags.entry(*task_source_name).or_default(); let flag = ignore_flags.entry(*task_source_name).or_default();
flag.store(true, Ordering::SeqCst); flag.store(true, Ordering::SeqCst);
@@ -417,7 +418,7 @@ impl Window {
self.globalscope.time_profiler_chan() self.globalscope.time_profiler_chan()
} }
pub fn origin(&self) -> &MutableOrigin { pub fn origin(&self) -> Ref<MutableOrigin> {
self.globalscope.origin() self.globalscope.origin()
} }
@@ -1635,7 +1636,8 @@ impl Window {
/// This sets the current `task_manager.task_cancellers` sentinel value to /// This sets the current `task_manager.task_cancellers` sentinel value to
/// `true` and replaces it with a brand new one for future tasks. /// `true` and replaces it with a brand new one for future tasks.
pub fn cancel_all_tasks(&self) { pub fn cancel_all_tasks(&self) {
let mut ignore_flags = self.task_manager.task_cancellers.borrow_mut(); let task_manager = self.task_manager.borrow();
let mut ignore_flags = task_manager.task_cancellers.borrow_mut();
for task_source_name in TaskSourceName::all() { for task_source_name in TaskSourceName::all() {
let flag = ignore_flags.entry(*task_source_name).or_default(); let flag = ignore_flags.entry(*task_source_name).or_default();
let cancelled = std::mem::take(&mut *flag); let cancelled = std::mem::take(&mut *flag);
@@ -1647,7 +1649,8 @@ impl Window {
/// This sets the current sentinel value to /// This sets the current sentinel value to
/// `true` and replaces it with a brand new one for future tasks. /// `true` and replaces it with a brand new one for future tasks.
pub fn cancel_all_tasks_from_source(&self, task_source_name: TaskSourceName) { pub fn cancel_all_tasks_from_source(&self, task_source_name: TaskSourceName) {
let mut ignore_flags = self.task_manager.task_cancellers.borrow_mut(); let task_manager = self.task_manager.borrow();
let mut ignore_flags = task_manager.task_cancellers.borrow_mut();
let flag = ignore_flags.entry(task_source_name).or_default(); let flag = ignore_flags.entry(task_source_name).or_default();
let cancelled = std::mem::take(&mut *flag); let cancelled = std::mem::take(&mut *flag);
cancelled.store(true, Ordering::SeqCst); cancelled.store(true, Ordering::SeqCst);
@@ -2207,13 +2210,13 @@ impl Window {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn init_window_proxy(&self, window_proxy: &WindowProxy) { pub fn init_window_proxy(&self, window_proxy: &WindowProxy) {
assert!(self.window_proxy.get().is_none()); //assert!(self.window_proxy.get().is_none());
self.window_proxy.set(Some(window_proxy)); self.window_proxy.set(Some(window_proxy));
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn init_document(&self, document: &Document) { pub fn init_document(&self, document: &Document) {
assert!(self.document.get().is_none()); //assert!(self.document.get().is_none());
assert!(document.window() == self); assert!(document.window() == self);
self.document.set(Some(document)); self.document.set(Some(document));
if !self.unminify_js { if !self.unminify_js {
@@ -2267,6 +2270,7 @@ impl Window {
ScriptThreadEventCategory::DomEvent, ScriptThreadEventCategory::DomEvent,
Box::new( Box::new(
self.task_manager self.task_manager
.borrow()
.task_canceller(TaskSourceName::DOMManipulation) .task_canceller(TaskSourceName::DOMManipulation)
.wrap_task(task), .wrap_task(task),
), ),
@@ -2300,7 +2304,7 @@ impl Window {
// TODO: step 11, navigationType. // TODO: step 11, navigationType.
// Step 12, 13 // Step 12, 13
ScriptThread::navigate( ScriptThread::navigate(
window_proxy.browsing_context_id(), &window_proxy,
pipeline_id, pipeline_id,
load_data, load_data,
replace, replace,
@@ -2589,7 +2593,7 @@ impl Window {
inherited_secure_context, inherited_secure_context,
), ),
script_chan, script_chan,
task_manager, task_manager: RefCell::new(task_manager),
layout: RefCell::new(layout), layout: RefCell::new(layout),
image_cache_chan, image_cache_chan,
image_cache, image_cache,
@@ -2646,7 +2650,21 @@ impl Window {
current_event: DomRefCell::new(None), current_event: DomRefCell::new(None),
}); });
unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) } /*if let Some(reflector) = existing_window_reflector {
unsafe {
let raw = Root::new(MaybeUnreflectedDom::from_box(win));
let jsobject = reflector.get_jsobject();
let old_window: *const Window = crate::dom::bindings::conversions::native_from_object_static(jsobject.get()).unwrap();
set_reflector_object(jsobject, raw.as_ptr() as *const _);
let root = raw.reflect_with(jsobject.get());
let old_window_box = Box::from_raw(old_window as *mut _);
// create a new reflector for the replaced window object.
let _ = WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), old_window_box);
root
}
} else {*/
unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) }
//}
} }
pub fn pipeline_id(&self) -> PipelineId { pub fn pipeline_id(&self) -> PipelineId {
@@ -2660,6 +2678,27 @@ impl Window {
{ {
LayoutValue::new(self.layout_marker.borrow().clone(), value) LayoutValue::new(self.layout_marker.borrow().clone(), value)
} }
pub(crate) fn replace_contents(&self, data: ReplaceData) {
self.globalscope.replace_contents(crate::dom::globalscope::ReplaceData {
pipeline_id: data.pipeline_id,
script_to_constellation_chan: data.script_to_constellation_chan,
creator_url: data.creator_url,
origin: data.origin,
});
*self.task_manager.borrow_mut() = data.task_manager;
*self.layout.borrow_mut() = data.layout;
self.has_sent_idle_message.set(false);
}
}
pub(crate) struct ReplaceData {
pub script_to_constellation_chan: ScriptToConstellationChan,
pub task_manager: TaskManager,
pub layout: Box<dyn Layout>,
pub pipeline_id: PipelineId,
pub creator_url: ServoUrl,
pub origin: MutableOrigin,
} }
/// An instance of a value associated with a particular snapshot of layout. This stored /// An instance of a value associated with a particular snapshot of layout. This stored
@@ -2798,6 +2837,7 @@ impl Window {
ScriptThreadEventCategory::DomEvent, ScriptThreadEventCategory::DomEvent,
Box::new( Box::new(
self.task_manager self.task_manager
.borrow()
.task_canceller(TaskSourceName::DOMManipulation) .task_canceller(TaskSourceName::DOMManipulation)
.wrap_task(task), .wrap_task(task),
), ),

View File

@@ -298,7 +298,6 @@ impl WindowProxy {
let new_top_level_browsing_context_id = TopLevelBrowsingContextId::new(); let new_top_level_browsing_context_id = TopLevelBrowsingContextId::new();
let new_browsing_context_id = let new_browsing_context_id =
BrowsingContextId::from(new_top_level_browsing_context_id); BrowsingContextId::from(new_top_level_browsing_context_id);
let new_pipeline_id = PipelineId::new();
let document = self let document = self
.currently_active .currently_active
.get() .get()
@@ -313,13 +312,15 @@ impl WindowProxy {
document.global().get_referrer(), document.global().get_referrer(),
document.get_referrer_policy(), document.get_referrer_policy(),
None, // Doesn't inherit secure context None, // Doesn't inherit secure context
None,
true,
); );
let new_pipeline_id = load_data.new_pipeline_id.clone();
let load_info = AuxiliaryBrowsingContextLoadInfo { let load_info = AuxiliaryBrowsingContextLoadInfo {
load_data: load_data.clone(), load_data: load_data.clone(),
opener_pipeline_id: self.currently_active.get().unwrap(), opener_pipeline_id: self.currently_active.get().unwrap(),
new_browsing_context_id, new_browsing_context_id,
new_top_level_browsing_context_id, new_top_level_browsing_context_id,
new_pipeline_id,
}; };
let new_layout_info = NewLayoutInfo { let new_layout_info = NewLayoutInfo {
@@ -500,7 +501,7 @@ impl WindowProxy {
.and_then(ScriptThread::find_document) .and_then(ScriptThread::find_document)
.unwrap(); .unwrap();
// Step 14.1 // Step 14.1
let url = match existing_document.url().join(&url) { let url = match existing_document.base_url().join(&url) {
Ok(url) => url, Ok(url) => url,
Err(_) => return Err(Error::Syntax), Err(_) => return Err(Error::Syntax),
}; };
@@ -514,6 +515,7 @@ impl WindowProxy {
let referrer_policy = target_document.get_referrer_policy(); let referrer_policy = target_document.get_referrer_policy();
let pipeline_id = target_window.upcast::<GlobalScope>().pipeline_id(); let pipeline_id = target_window.upcast::<GlobalScope>().pipeline_id();
let secure = target_window.upcast::<GlobalScope>().is_secure_context(); let secure = target_window.upcast::<GlobalScope>().is_secure_context();
let is_about_blank = target_document.url().as_str() == "about:blank";
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Script(existing_document.origin().immutable().clone()), LoadOrigin::Script(existing_document.origin().immutable().clone()),
url, url,
@@ -521,6 +523,8 @@ impl WindowProxy {
referrer, referrer,
referrer_policy, referrer_policy,
Some(secure), Some(secure),
is_about_blank.then_some(pipeline_id),
false,
); );
let replacement_flag = if new { let replacement_flag = if new {
HistoryEntryReplacement::Enabled HistoryEntryReplacement::Enabled

View File

@@ -57,6 +57,7 @@ impl XMLDocument {
None, None,
None, None,
Default::default(), Default::default(),
false,
), ),
} }
} }

View File

@@ -1544,6 +1544,7 @@ impl XMLHttpRequest {
None, None,
Default::default(), Default::default(),
can_gc, can_gc,
false,
) )
} }

View File

@@ -20,7 +20,7 @@ use crate::dom::htmlareaelement::HTMLAreaElement;
use crate::dom::htmlformelement::HTMLFormElement; use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmllinkelement::HTMLLinkElement; use crate::dom::htmllinkelement::HTMLLinkElement;
use crate::dom::node::document_from_node; use crate::dom::node::document_from_node;
use crate::dom::types::{Element, GlobalScope}; use crate::dom::types::{Element, GlobalScope, HTMLIFrameElement};
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
bitflags::bitflags! { bitflags::bitflags! {
@@ -428,7 +428,17 @@ pub fn follow_hyperlink(
referrer, referrer,
referrer_policy, referrer_policy,
Some(secure), Some(secure),
(document.url().as_str() == "about:blank").then(|| document.window().pipeline_id()),
false,
); );
let new_pipeline_id = load_data.new_pipeline_id.clone();
if let Some(frame) = chosen.frame_element() {
if let Some(frame) = frame.downcast::<HTMLIFrameElement>() {
frame.update_pending_pipeline_id(new_pipeline_id);
}
}
let target = Trusted::new(target_window); let target = Trusted::new(target_window);
let task = task!(navigate_follow_hyperlink: move || { let task = task!(navigate_follow_hyperlink: move || {
debug!("following hyperlink to {}", load_data.url); debug!("following hyperlink to {}", load_data.url);

View File

@@ -19,6 +19,7 @@ use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::htmliframeelement::IframeElementMicrotask;
use crate::dom::htmlimageelement::ImageElementMicrotask; use crate::dom::htmlimageelement::ImageElementMicrotask;
use crate::dom::htmlmediaelement::MediaElementMicrotask; use crate::dom::htmlmediaelement::MediaElementMicrotask;
use crate::dom::mutationobserver::MutationObserver; use crate::dom::mutationobserver::MutationObserver;
@@ -41,6 +42,7 @@ pub enum Microtask {
User(UserMicrotask), User(UserMicrotask),
MediaElement(MediaElementMicrotask), MediaElement(MediaElementMicrotask),
ImageElement(ImageElementMicrotask), ImageElement(ImageElementMicrotask),
IframeElement(IframeElementMicrotask),
CustomElementReaction, CustomElementReaction,
NotifyMutationObservers, NotifyMutationObservers,
} }
@@ -134,6 +136,10 @@ impl MicrotaskQueue {
let _realm = task.enter_realm(); let _realm = task.enter_realm();
task.handler(can_gc); task.handler(can_gc);
}, },
Microtask::IframeElement(ref task) => {
let _realm = task.enter_realm();
task.handler(can_gc);
},
Microtask::CustomElementReaction => { Microtask::CustomElementReaction => {
ScriptThread::invoke_backup_element_queue(can_gc); ScriptThread::invoke_backup_element_queue(can_gc);
}, },

View File

@@ -221,6 +221,9 @@ struct InProgressLoad {
canceller: FetchCanceller, canceller: FetchCanceller,
/// If inheriting the security context /// If inheriting the security context
inherited_secure_context: Option<bool>, inherited_secure_context: Option<bool>,
///
#[no_trace]
replacing_pipeline: Option<PipelineId>,
} }
impl InProgressLoad { impl InProgressLoad {
@@ -236,6 +239,7 @@ impl InProgressLoad {
url: ServoUrl, url: ServoUrl,
origin: MutableOrigin, origin: MutableOrigin,
inherited_secure_context: Option<bool>, inherited_secure_context: Option<bool>,
replacing_pipeline: Option<PipelineId>,
) -> InProgressLoad { ) -> InProgressLoad {
let navigation_start = CrossProcessInstant::now(); let navigation_start = CrossProcessInstant::now();
InProgressLoad { InProgressLoad {
@@ -252,6 +256,7 @@ impl InProgressLoad {
navigation_start, navigation_start,
canceller: Default::default(), canceller: Default::default(),
inherited_secure_context, inherited_secure_context,
replacing_pipeline,
} }
} }
} }
@@ -834,6 +839,7 @@ impl ScriptThreadFactory for ScriptThread {
load_data.url.clone(), load_data.url.clone(),
origin, origin,
secure, secure,
None,
); );
script_thread.pre_page_load(new_load, load_data); script_thread.pre_page_load(new_load, load_data);
@@ -995,7 +1001,8 @@ impl ScriptThread {
/// Step 13 of <https://html.spec.whatwg.org/multipage/#navigate> /// Step 13 of <https://html.spec.whatwg.org/multipage/#navigate>
pub fn navigate( pub fn navigate(
browsing_context: BrowsingContextId, //browsing_context: BrowsingContextId,
window_proxy: &WindowProxy,
pipeline_id: PipelineId, pipeline_id: PipelineId,
mut load_data: LoadData, mut load_data: LoadData,
replace: HistoryEntryReplacement, replace: HistoryEntryReplacement,
@@ -1007,6 +1014,7 @@ impl ScriptThread {
}; };
let script_thread = unsafe { &*script_thread }; let script_thread = unsafe { &*script_thread };
let is_javascript = load_data.url.scheme() == "javascript"; let is_javascript = load_data.url.scheme() == "javascript";
let browsing_context = window_proxy.browsing_context_id();
// If resource is a request whose url's scheme is "javascript" // If resource is a request whose url's scheme is "javascript"
// https://html.spec.whatwg.org/multipage/#javascript-protocol // https://html.spec.whatwg.org/multipage/#javascript-protocol
if is_javascript { if is_javascript {
@@ -1040,6 +1048,14 @@ impl ScriptThread {
)); ));
} }
if let Some(frame) = window_proxy.frame_element() {
if let Some(frame) = frame.downcast::<HTMLIFrameElement>() {
frame.update_pending_pipeline_id(
load_data.new_pipeline_id.clone(),
);
}
}
script_thread script_thread
.script_sender .script_sender
.send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace))) .send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
@@ -1843,6 +1859,11 @@ impl ScriptThread {
ScriptThreadEventCategory::AttachLayout, ScriptThreadEventCategory::AttachLayout,
Some(pipeline_id), Some(pipeline_id),
|| { || {
if new_layout_info.load_data.synchronously_loaded {
assert_eq!(new_layout_info.load_data.url.as_str(), "about:blank");
return;
}
// If this is an about:blank or about:srcdoc load, it must share the // If this is an about:blank or about:srcdoc load, it must share the
// creator's origin. This must match the logic in the constellation // creator's origin. This must match the logic in the constellation
// when creating a new pipeline // when creating a new pipeline
@@ -2173,9 +2194,8 @@ impl ScriptThread {
TickAllAnimations(id, ..) => Some(id), TickAllAnimations(id, ..) => Some(id),
WebFontLoaded(id, ..) => Some(id), WebFontLoaded(id, ..) => Some(id),
DispatchIFrameLoadEvent { DispatchIFrameLoadEvent {
target: _,
parent: id, parent: id,
child: _, ..
} => Some(id), } => Some(id),
DispatchStorageEvent(id, ..) => Some(id), DispatchStorageEvent(id, ..) => Some(id),
ReportCSSError(id, ..) => Some(id), ReportCSSError(id, ..) => Some(id),
@@ -2380,7 +2400,8 @@ impl ScriptThread {
target: browsing_context_id, target: browsing_context_id,
parent: parent_id, parent: parent_id,
child: child_id, child: child_id,
} => self.handle_iframe_load_event(parent_id, browsing_context_id, child_id, can_gc), is_initial_about_blank,
} => self.handle_iframe_load_event(parent_id, browsing_context_id, child_id, is_initial_about_blank, can_gc),
ConstellationControlMsg::DispatchStorageEvent( ConstellationControlMsg::DispatchStorageEvent(
pipeline_id, pipeline_id,
storage, storage,
@@ -2919,6 +2940,7 @@ impl ScriptThread {
load_data.url.clone(), load_data.url.clone(),
origin, origin,
load_data.inherited_secure_context, load_data.inherited_secure_context,
load_data.replacing_pipeline,
); );
if load_data.url.as_str() == "about:blank" { if load_data.url.as_str() == "about:blank" {
self.start_page_load_about_blank(new_load, load_data.js_eval_result); self.start_page_load_about_blank(new_load, load_data.js_eval_result);
@@ -3306,8 +3328,10 @@ impl ScriptThread {
parser.abort(can_gc); parser.abort(can_gc);
} }
debug!("{id}: Shutting down layout"); if document.is_window_relevant() {
document.window().layout_mut().exit_now(); debug!("{id}: Shutting down layout");
document.window().layout_mut().exit_now();
}
debug!("{id}: Sending PipelineExited message to constellation"); debug!("{id}: Sending PipelineExited message to constellation");
self.script_sender self.script_sender
@@ -3325,15 +3349,17 @@ impl ScriptThread {
} }
} }
// We discard the browsing context after requesting layout shut down, if document.is_window_relevant() {
// to avoid running layout on detached iframes. // We discard the browsing context after requesting layout shut down,
let window = document.window(); // to avoid running layout on detached iframes.
if discard_bc == DiscardBrowsingContext::Yes { let window = document.window();
window.discard_browsing_context(); if discard_bc == DiscardBrowsingContext::Yes {
} window.discard_browsing_context();
}
debug!("{id}: Clearing JavaScript runtime"); debug!("{id}: Clearing JavaScript runtime");
window.clear_js_runtime(); window.clear_js_runtime();
}
} }
debug!("{id}: Finished pipeline exit"); debug!("{id}: Finished pipeline exit");
@@ -3441,6 +3467,7 @@ impl ScriptThread {
parent_id: PipelineId, parent_id: PipelineId,
browsing_context_id: BrowsingContextId, browsing_context_id: BrowsingContextId,
child_id: PipelineId, child_id: PipelineId,
is_initial_about_blank: bool,
can_gc: CanGc, can_gc: CanGc,
) { ) {
let iframe = self let iframe = self
@@ -3448,7 +3475,7 @@ impl ScriptThread {
.borrow() .borrow()
.find_iframe(parent_id, browsing_context_id); .find_iframe(parent_id, browsing_context_id);
match iframe { match iframe {
Some(iframe) => iframe.iframe_load_event_steps(child_id, can_gc), Some(iframe) => iframe.iframe_load_event_steps(child_id, can_gc, !is_initial_about_blank),
None => warn!("Message sent to closed pipeline {}.", parent_id), None => warn!("Message sent to closed pipeline {}.", parent_id),
} }
} }
@@ -3666,44 +3693,62 @@ impl ScriptThread {
}; };
// Create the window and document objects. // Create the window and document objects.
let window = Window::new( let old_document = incomplete.replacing_pipeline.and_then(ScriptThread::find_document);
self.js_runtime.clone(), let replacement_same_origin = old_document.as_ref().map_or(false, |doc| {
MainThreadScriptChan(sender.clone()), let old_origin = doc.origin();
task_manager, final_url.origin().same_origin(old_origin)
self.layout_factory.create(layout_config), });
self.image_cache_channel.clone(), let window = if let (Some(old_document), true) = (old_document, replacement_same_origin) {
self.image_cache.clone(), old_document.window().replace_contents(crate::dom::window::ReplaceData {
self.resource_threads.clone(), script_to_constellation_chan,
self.bluetooth_thread.clone(), task_manager,
self.mem_profiler_chan.clone(), layout: self.layout_factory.create(layout_config),
self.time_profiler_chan.clone(), pipeline_id: incomplete.pipeline_id,
self.devtools_chan.clone(), creator_url: final_url.clone(),
script_to_constellation_chan, origin: origin.clone(),
self.control_chan.clone(), });
self.scheduler_chan.clone(), old_document.disown_window();
incomplete.pipeline_id, DomRoot::from_ref(old_document.window())
incomplete.parent_info, } else {
incomplete.window_size, Window::new(
origin.clone(), self.js_runtime.clone(),
final_url.clone(), MainThreadScriptChan(sender.clone()),
incomplete.navigation_start, task_manager,
self.webgl_chan.as_ref().map(|chan| chan.channel()), self.layout_factory.create(layout_config),
self.webxr_registry.clone(), self.image_cache_channel.clone(),
self.microtask_queue.clone(), self.image_cache.clone(),
self.webrender_document, self.resource_threads.clone(),
self.webrender_api_sender.clone(), self.bluetooth_thread.clone(),
self.relayout_event, self.mem_profiler_chan.clone(),
self.prepare_for_screenshot, self.time_profiler_chan.clone(),
self.unminify_js, self.devtools_chan.clone(),
self.local_script_source.clone(), script_to_constellation_chan,
self.userscripts_path.clone(), self.control_chan.clone(),
self.headless, self.scheduler_chan.clone(),
self.replace_surrogates, incomplete.pipeline_id,
self.user_agent.clone(), incomplete.parent_info,
self.player_context.clone(), incomplete.window_size,
self.gpu_id_hub.clone(), origin.clone(),
incomplete.inherited_secure_context, final_url.clone(),
); incomplete.navigation_start,
self.webgl_chan.as_ref().map(|chan| chan.channel()),
self.webxr_registry.clone(),
self.microtask_queue.clone(),
self.webrender_document,
self.webrender_api_sender.clone(),
self.relayout_event,
self.prepare_for_screenshot,
self.unminify_js,
self.local_script_source.clone(),
self.userscripts_path.clone(),
self.headless,
self.replace_surrogates,
self.user_agent.clone(),
self.player_context.clone(),
self.gpu_id_hub.clone(),
incomplete.inherited_secure_context,
)
};
let _realm = enter_realm(&*window); let _realm = enter_realm(&*window);
@@ -3715,6 +3760,7 @@ impl ScriptThread {
incomplete.parent_info, incomplete.parent_info,
incomplete.opener, incomplete.opener,
); );
window_proxy.set_currently_active(&window);
if window_proxy.parent().is_some() { if window_proxy.parent().is_some() {
// https://html.spec.whatwg.org/multipage/#navigating-across-documents:delaying-load-events-mode-2 // https://html.spec.whatwg.org/multipage/#navigating-across-documents:delaying-load-events-mode-2
// The user agent must take this nested browsing context // The user agent must take this nested browsing context
@@ -3785,6 +3831,7 @@ impl ScriptThread {
Some(status_code), Some(status_code),
incomplete.canceller, incomplete.canceller,
can_gc, can_gc,
final_url.as_str() == "about:blank",
); );
document.set_ready_state(DocumentReadyState::Loading); document.set_ready_state(DocumentReadyState::Loading);

View File

@@ -136,6 +136,13 @@ impl OneshotTimers {
} }
} }
pub(crate) fn reset(&self) {
self.js_timers.reset();
self.next_timer_handle.set(OneshotTimerHandle(1));
self.timers.borrow_mut().clear();
self.expected_event_id.set(TimerEventId(0));
}
pub fn setup_scheduling(&self, timer_event_chan: IpcSender<TimerEvent>) { pub fn setup_scheduling(&self, timer_event_chan: IpcSender<TimerEvent>) {
let mut chan = self.timer_event_chan.borrow_mut(); let mut chan = self.timer_event_chan.borrow_mut();
assert!(chan.is_none()); assert!(chan.is_none());
@@ -415,6 +422,11 @@ impl Default for JsTimers {
} }
impl JsTimers { impl JsTimers {
pub(crate) fn reset(&self) {
self.next_timer_handle.set(JsTimerHandle(1));
self.active_timers.borrow_mut().clear();
}
// see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
pub fn set_timeout_or_interval( pub fn set_timeout_or_interval(
&self, &self,

View File

@@ -128,6 +128,8 @@ pub enum LoadOrigin {
/// parameters or headers /// parameters or headers
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LoadData { pub struct LoadData {
///
pub new_pipeline_id: PipelineId,
/// The origin where the load started. /// The origin where the load started.
pub load_origin: LoadOrigin, pub load_origin: LoadOrigin,
/// The URL. /// The URL.
@@ -162,6 +164,11 @@ pub struct LoadData {
/// Servo internal: if crash details are present, trigger a crash error page with these details. /// Servo internal: if crash details are present, trigger a crash error page with these details.
pub crash: Option<String>, pub crash: Option<String>,
///
pub replacing_pipeline: Option<PipelineId>,
///
pub synchronously_loaded: bool,
} }
/// The result of evaluating a javascript scheme url. /// The result of evaluating a javascript scheme url.
@@ -183,6 +190,8 @@ impl LoadData {
referrer: Referrer, referrer: Referrer,
referrer_policy: Option<ReferrerPolicy>, referrer_policy: Option<ReferrerPolicy>,
inherited_secure_context: Option<bool>, inherited_secure_context: Option<bool>,
replacing_pipeline: Option<PipelineId>,
synchronously_loaded: bool,
) -> LoadData { ) -> LoadData {
LoadData { LoadData {
load_origin, load_origin,
@@ -197,6 +206,9 @@ impl LoadData {
srcdoc: "".to_string(), srcdoc: "".to_string(),
inherited_secure_context, inherited_secure_context,
crash: None, crash: None,
replacing_pipeline,
synchronously_loaded,
new_pipeline_id: PipelineId::new(),
} }
} }
} }
@@ -358,6 +370,8 @@ pub enum ConstellationControlMsg {
parent: PipelineId, parent: PipelineId,
/// The pipeline that has completed loading. /// The pipeline that has completed loading.
child: PipelineId, child: PipelineId,
///
is_initial_about_blank: bool,
}, },
/// Cause a `storage` event to be dispatched at the appropriate window. /// Cause a `storage` event to be dispatched at the appropriate window.
/// The strings are key, old value and new value. /// The strings are key, old value and new value.
@@ -692,8 +706,6 @@ pub struct AuxiliaryBrowsingContextLoadInfo {
pub new_top_level_browsing_context_id: TopLevelBrowsingContextId, pub new_top_level_browsing_context_id: TopLevelBrowsingContextId,
/// The new browsing context ID. /// The new browsing context ID.
pub new_browsing_context_id: BrowsingContextId, pub new_browsing_context_id: BrowsingContextId,
/// The new pipeline ID for the auxiliary.
pub new_pipeline_id: PipelineId,
} }
/// Specifies the information required to load an iframe. /// Specifies the information required to load an iframe.
@@ -705,8 +717,6 @@ pub struct IFrameLoadInfo {
pub browsing_context_id: BrowsingContextId, pub browsing_context_id: BrowsingContextId,
/// The ID for the top-level ancestor browsing context of this iframe's nested browsing context. /// The ID for the top-level ancestor browsing context of this iframe's nested browsing context.
pub top_level_browsing_context_id: TopLevelBrowsingContextId, pub top_level_browsing_context_id: TopLevelBrowsingContextId,
/// The new pipeline ID that the iframe has generated.
pub new_pipeline_id: PipelineId,
/// Whether this iframe should be considered private /// Whether this iframe should be considered private
pub is_private: bool, pub is_private: bool,
/// Whether this iframe should be considered secure /// Whether this iframe should be considered secure

View File

@@ -181,7 +181,7 @@ pub enum ScriptMsg {
), ),
/// All pending loads are complete, and the `load` event for this pipeline /// All pending loads are complete, and the `load` event for this pipeline
/// has been dispatched. /// has been dispatched.
LoadComplete, LoadComplete(bool),
/// A new load has been requested, with an option to replace the current entry once loaded /// A new load has been requested, with an option to replace the current entry once loaded
/// instead of adding a new entry. /// instead of adding a new entry.
LoadUrl(LoadData, HistoryEntryReplacement), LoadUrl(LoadData, HistoryEntryReplacement),
@@ -293,7 +293,7 @@ impl fmt::Debug for ScriptMsg {
GetBrowsingContextInfo(..) => "GetBrowsingContextInfo", GetBrowsingContextInfo(..) => "GetBrowsingContextInfo",
GetTopForBrowsingContext(..) => "GetParentBrowsingContext", GetTopForBrowsingContext(..) => "GetParentBrowsingContext",
GetChildBrowsingContextId(..) => "GetChildBrowsingContextId", GetChildBrowsingContextId(..) => "GetChildBrowsingContextId",
LoadComplete => "LoadComplete", LoadComplete(..) => "LoadComplete",
LoadUrl(..) => "LoadUrl", LoadUrl(..) => "LoadUrl",
AbortLoadUrl => "AbortLoadUrl", AbortLoadUrl => "AbortLoadUrl",
PostMessage { .. } => "PostMessage", PostMessage { .. } => "PostMessage",

View File

@@ -658,6 +658,8 @@ impl Handler {
Referrer::NoReferrer, Referrer::NoReferrer,
None, None,
None, None,
None,
false,
); );
let cmd_msg = WebDriverCommandMsg::LoadUrl( let cmd_msg = WebDriverCommandMsg::LoadUrl(
top_level_browsing_context_id, top_level_browsing_context_id,

View File

@@ -1,9 +0,0 @@
[iframe-nosrc.html]
[window.open]
expected: FAIL
[link click]
expected: FAIL
[form submission]
expected: FAIL

View File

@@ -1,9 +0,0 @@
[iframe-src-204.html]
[Navigating to a different document with window.open]
expected: FAIL
[Navigating to a different document with link click]
expected: FAIL
[Navigating to a different document with form submission]
expected: FAIL

View File

@@ -1,9 +0,0 @@
[iframe-src-aboutblank-navigate-immediately.html]
[Navigating to a different document with window.open]
expected: FAIL
[Navigating to a different document with link click]
expected: FAIL
[Navigating to a different document with form submission]
expected: FAIL

View File

@@ -1,18 +0,0 @@
[iframe-src-aboutblank-wait-for-load.html]
[Navigating to a different document with src]
expected: FAIL
[Navigating to a different document with location.href]
expected: FAIL
[Navigating to a different document with location.assign]
expected: FAIL
[Navigating to a different document with window.open]
expected: FAIL
[Navigating to a different document with link click]
expected: FAIL
[Navigating to a different document with form submission]
expected: FAIL

View File

@@ -1,18 +0,0 @@
[initial-content-replacement.html]
[Content synchronously added to <iframe> with src='' won't get replaced]
expected: FAIL
[Content synchronously added to <iframe> with src='about:blank' won't get replaced]
expected: FAIL
[Content synchronously added to <iframe> with src='about:blank#foo' won't get replaced]
expected: FAIL
[Content synchronously added to <iframe> with src='about:blank?foo' won't get replaced]
expected: FAIL
[Content synchronously added to window.open('about:blank')-ed document won't get replaced]
expected: FAIL
[Content synchronously added to window.open('about:blank?foo')-ed document won't get replaced]
expected: FAIL

View File

@@ -1,21 +0,0 @@
[load-pageshow-events-iframe-contentWindow.html]
[load & pageshow event do not fire on contentWindow of <iframe> element created with no src]
expected: FAIL
[load & pageshow events do not fire on contentWindow of <iframe> element created with src='']
expected: FAIL
[load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank?foo']
expected: FAIL
[load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank#foo']
expected: FAIL
[load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank']
expected: FAIL
[load & pageshow events do not fire on contentWindow of <iframe> element created with src='']
expected: FAIL
[load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank']
expected: FAIL

View File

@@ -1,18 +0,0 @@
[load-pageshow-events-window-open.html]
[load event does not fire on window.open()]
expected: FAIL
[load event does not fire on window.open('about:blank')]
expected: FAIL
[load event does not fire on window.open('')]
expected: FAIL
[load event does not fire on window.open('about:blank#foo')]
expected: FAIL
[load event does not fire on window.open('about:blank?foo')]
expected: FAIL
[load event does not fire on window.open('about:blank')]
expected: FAIL

View File

@@ -1,6 +0,0 @@
[window-open-aboutblank.html]
[location.href]
expected: FAIL
[location.assign]
expected: FAIL

View File

@@ -1,6 +0,0 @@
[window-open-nourl.html]
[location.href]
expected: FAIL
[location.assign]
expected: FAIL

View File

@@ -1,4 +0,0 @@
[javascript-url-load-as-html.xhtml]
expected: TIMEOUT
[javascript: URL navigation to a string must create a HTML document using the correct properties]
expected: TIMEOUT

View File

@@ -1,10 +0,0 @@
[javascript-url-no-beforeunload.window.html]
expected: TIMEOUT
[Navigating an opened window with an iframe via location.href to a javascript: URL must not fire beforeunload on the iframe: string completion]
expected: NOTRUN
[Navigating an opened window via location.href to a javascript: URL must not fire beforeunload: string completion]
expected: TIMEOUT
[Navigating an opened window with an iframe via location.href to a javascript: URL must not fire beforeunload on the iframe: undefined completion]
expected: FAIL

View File

@@ -1,10 +0,0 @@
[javascript-url-referrer.window.html]
expected: TIMEOUT
[unsafe-url referrer policy used to create the starting page]
expected: FAIL
[origin referrer policy used to create the starting page]
expected: FAIL
[no-referrer referrer policy used to create the starting page]
expected: TIMEOUT

View File

@@ -1,6 +0,0 @@
[navigate-to-unparseable-url.html]
[location.href setter throws a SyntaxError DOMException for unparseable URLs]
expected: FAIL
[<a> tag navigate fails for unparseable URLs]
expected: FAIL

View File

@@ -1,3 +0,0 @@
[a-click.html]
[aElement.click() before the load event must NOT replace]
expected: FAIL

View File

@@ -10414,7 +10414,7 @@
], ],
"resource": { "resource": {
"file-submission.py": [ "file-submission.py": [
"5f65cebd05ce89b3a51c2382a39b6a6438133b43", "b03a4c9a5ddebc78af2bf8d0a35ebaca27bb4314",
[] []
], ],
"upload.txt": [ "upload.txt": [
@@ -13227,7 +13227,7 @@
] ]
], ],
"form_submit_about.html": [ "form_submit_about.html": [
"ec572ab0bc608c8cf5dd43f4159d3a67fc31a0de", "71342b9b6b3ae6f037829b2e472bdd2fd1e93692",
[ [
null, null,
{} {}

View File

@@ -6,17 +6,17 @@
def fail(msg): def fail(msg):
return ([("Content-Type", "text/plain")], "FAIL: " + msg) return ([("Content-Type", "text/plain")], b"FAIL: " + msg)
def main(request, response): def main(request, response):
content_type = request.headers.get(b'Content-Type').split(b"; ") content_type = request.headers.get(b'Content-Type').split(b"; ")
if len(content_type) != 2: if len(content_type) != 2:
return fail("content type length is incorrect") return fail(b"content type length is incorrect")
if content_type[0] != b'multipart/form-data': if content_type[0] != b'multipart/form-data':
return fail("content type first field is incorrect") return fail(b"content type first field is incorrect")
boundary = content_type[1].strip(b"boundary=") boundary = content_type[1].strip(b"boundary=")
@@ -24,6 +24,6 @@ def main(request, response):
body += b"\r\n" + b"content-type: text/plain\r\n\r\nHello\r\n--" + boundary + b"--\r\n" body += b"\r\n" + b"content-type: text/plain\r\n\r\nHello\r\n--" + boundary + b"--\r\n"
if body != request.body: if body != request.body:
return fail("request body doesn't match: " + body + "+++++++" + request.body) return fail(b"request body doesn't match: " + body + b"+++++++" + request.body)
return ([("Content-Type", "text/plain")], "OK") return ([("Content-Type", "text/plain")], "OK")

View File

@@ -9,7 +9,7 @@
var numOnLoads = 0 var numOnLoads = 0
var t = async_test("about:blank as form target") var t = async_test("about:blank as form target")
var iframe = document.getElementById('foo') var iframe = document.getElementById('foo')
iframe.onload = t.step_func(function(e) { if (++numOnLoads == 2) t.done() }) iframe.onload = t.step_func(function(e) { t.step_timeout(() => t.done(), 100); iframe.onload = t.unreached_func(); })
</script> </script>
</body> </body>
</html> </html>