Auto merge of #23368 - gterzian:clean_up_navigation, r=asajeffrey

Clean-up navigation

<!-- Please describe your changes on the following line: -->

1. Navigation as a result of following a hyperlink should be done in a task: https://html.spec.whatwg.org/multipage/links.html#following-hyperlinks:dom-manipulation-task-source
2. The javascript url navigation should also be done in a task: https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigating-across-documents:dom-manipulation-task-source
3. In `window.load_url`, it seems there is no need to send a message to the script-thread(the entirety of `load_url` should instead be done in a task when appropriate), so we can just do that last part "sync" by calling a method on the script, which will send a message to the constellation(for the parallel navigation steps), or queue task(for the JS navigation), as appropriate.
4. Separate the "normal" navigation flow from the handling of "navigate an iframe" message from constellation, since doing everything in one method as was previously done with `handle_navigate`, is confusing.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [ ] `./mach build -d` does not report any errors
- [ ] `./mach test-tidy` does not report any errors
- [ ] These changes fix #___ (GitHub issue number if applicable)

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23368)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-07-18 14:22:03 -04:00 committed by GitHub
commit 2dfbbd0ca2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 402 additions and 220 deletions

View file

@ -145,11 +145,11 @@ use script_traits::{
use script_traits::{ use script_traits::{
ConstellationControlMsg, ConstellationMsg as FromCompositorMsg, DiscardBrowsingContext, ConstellationControlMsg, ConstellationMsg as FromCompositorMsg, DiscardBrowsingContext,
}; };
use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData}; use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData, LoadOrigin};
use script_traits::{HistoryEntryReplacement, IFrameSizeMsg, WindowSizeData, WindowSizeType};
use script_traits::{ use script_traits::{
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
}; };
use script_traits::{IFrameSizeMsg, WindowSizeData, WindowSizeType};
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory}; use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg}; use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -171,7 +171,7 @@ use style_traits::viewport::ViewportConstraints;
use style_traits::CSSPixel; use style_traits::CSSPixel;
use webvr_traits::{WebVREvent, WebVRMsg}; use webvr_traits::{WebVREvent, WebVRMsg};
type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, bool)>; type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>;
/// Servo supports tabs (referred to as browsers), so `Constellation` needs to /// Servo supports tabs (referred to as browsers), so `Constellation` needs to
/// store browser specific data for bookkeeping. /// store browser specific data for bookkeeping.
@ -1332,7 +1332,7 @@ where
// If there is already a pending page (self.pending_changes), it will not be overridden; // If there is already a pending page (self.pending_changes), it will not be overridden;
// However, if the id is not encompassed by another change, it will be. // However, if the id is not encompassed by another change, it will be.
FromCompositorMsg::LoadUrl(top_level_browsing_context_id, url) => { FromCompositorMsg::LoadUrl(top_level_browsing_context_id, url) => {
let load_data = LoadData::new(url, None, None, None); let load_data = LoadData::new(LoadOrigin::Constellation, url, None, None, None);
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) {
Some(ctx) => ctx.pipeline_id, Some(ctx) => ctx.pipeline_id,
@ -1345,7 +1345,12 @@ where
}; };
// Since this is a top-level load, initiated by the embedder, go straight to load_url, // Since this is a top-level load, initiated by the embedder, go straight to load_url,
// bypassing schedule_navigation. // bypassing schedule_navigation.
self.load_url(top_level_browsing_context_id, pipeline_id, load_data, false); self.load_url(
top_level_browsing_context_id,
pipeline_id,
load_data,
HistoryEntryReplacement::Disabled,
);
}, },
FromCompositorMsg::IsReadyToSaveImage(pipeline_states) => { FromCompositorMsg::IsReadyToSaveImage(pipeline_states) => {
let is_ready = self.handle_is_ready_to_save_image(pipeline_states); let is_ready = self.handle_is_ready_to_save_image(pipeline_states);
@ -1911,7 +1916,7 @@ where
warn!("creating replacement pipeline for about:failure"); warn!("creating replacement pipeline for about:failure");
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = PipelineId::new();
let load_data = LoadData::new(failure_url, None, None, None); let load_data = LoadData::new(LoadOrigin::Constellation, failure_url, None, None, None);
let sandbox = IFrameSandboxState::IFrameSandboxed; let sandbox = IFrameSandboxState::IFrameSandboxed;
let is_private = false; let is_private = false;
self.new_pipeline( self.new_pipeline(
@ -2030,7 +2035,7 @@ where
); );
self.embedder_proxy.send(msg); self.embedder_proxy.send(msg);
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(url, None, None, None); let load_data = LoadData::new(LoadOrigin::Constellation, url, None, None, None);
let sandbox = IFrameSandboxState::IFrameUnsandboxed; let sandbox = IFrameSandboxState::IFrameUnsandboxed;
let is_private = false; let is_private = false;
let is_visible = true; let is_visible = true;
@ -2191,7 +2196,9 @@ where
// see https://html.spec.whatwg.org/multipage/#the-iframe-element:completely-loaded // see https://html.spec.whatwg.org/multipage/#the-iframe-element:completely-loaded
debug!("checking old pipeline? {:?}", load_info.old_pipeline_id); debug!("checking old pipeline? {:?}", load_info.old_pipeline_id);
if let Some(old_pipeline) = old_pipeline { if let Some(old_pipeline) = old_pipeline {
replace |= !old_pipeline.completely_loaded; if !old_pipeline.completely_loaded {
replace = HistoryEntryReplacement::Enabled;
}
debug!( debug!(
"old pipeline is {}completely loaded", "old pipeline is {}completely loaded",
if old_pipeline.completely_loaded { if old_pipeline.completely_loaded {
@ -2202,16 +2209,6 @@ where
); );
} }
let load_data = load_info.load_data.unwrap_or_else(|| {
let url = match old_pipeline {
Some(old_pipeline) => old_pipeline.url.clone(),
None => ServoUrl::parse("about:blank").expect("infallible"),
};
// TODO - loaddata here should have referrer info (not None, None)
LoadData::new(url, Some(parent_pipeline_id), None, None)
});
let is_parent_private = { let is_parent_private = {
let parent_browsing_context_id = match self.pipelines.get(&parent_pipeline_id) { let parent_browsing_context_id = match self.pipelines.get(&parent_pipeline_id) {
Some(pipeline) => pipeline.browsing_context_id, Some(pipeline) => pipeline.browsing_context_id,
@ -2245,10 +2242,11 @@ where
}, },
}; };
let replace = if replace { let replace = match replace {
HistoryEntryReplacement::Enabled => {
Some(NeedsToReload::No(browsing_context.pipeline_id)) Some(NeedsToReload::No(browsing_context.pipeline_id))
} else { },
None HistoryEntryReplacement::Disabled => None,
}; };
// https://github.com/rust-lang/rust/issues/59159 // https://github.com/rust-lang/rust/issues/59159
@ -2263,7 +2261,7 @@ where
Some(parent_pipeline_id), Some(parent_pipeline_id),
None, None,
browsing_context_size, browsing_context_size,
load_data, load_info.load_data,
load_info.sandbox, load_info.sandbox,
is_private, is_private,
browsing_context_is_visible, browsing_context_is_visible,
@ -2280,7 +2278,7 @@ where
fn handle_script_new_iframe( fn handle_script_new_iframe(
&mut self, &mut self,
load_info: IFrameLoadInfo, load_info: IFrameLoadInfoWithData,
layout_sender: IpcSender<LayoutControlMsg>, layout_sender: IpcSender<LayoutControlMsg>,
) { ) {
let IFrameLoadInfo { let IFrameLoadInfo {
@ -2290,12 +2288,7 @@ where
top_level_browsing_context_id, top_level_browsing_context_id,
is_private, is_private,
.. ..
} = load_info; } = load_info.info;
let url = ServoUrl::parse("about:blank").expect("infallible");
// TODO: Referrer?
let load_data = LoadData::new(url.clone(), Some(parent_pipeline_id), None, None);
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) {
@ -2321,9 +2314,8 @@ where
script_sender, script_sender,
layout_sender, layout_sender,
self.compositor_proxy.clone(), self.compositor_proxy.clone(),
url,
is_parent_visible, is_parent_visible,
load_data, load_info.load_data,
); );
assert!(!self.pipelines.contains_key(&new_pipeline_id)); assert!(!self.pipelines.contains_key(&new_pipeline_id));
@ -2348,17 +2340,13 @@ where
layout_sender: IpcSender<LayoutControlMsg>, layout_sender: IpcSender<LayoutControlMsg>,
) { ) {
let AuxiliaryBrowsingContextLoadInfo { let AuxiliaryBrowsingContextLoadInfo {
load_data,
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, new_pipeline_id,
} = load_info; } = load_info;
let url = ServoUrl::parse("about:blank").expect("infallible");
// TODO: Referrer?
let load_data = LoadData::new(url.clone(), None, None, None);
let (script_sender, opener_browsing_context_id) = 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),
@ -2387,7 +2375,6 @@ where
script_sender, script_sender,
layout_sender, layout_sender,
self.compositor_proxy.clone(), self.compositor_proxy.clone(),
url,
is_opener_visible, is_opener_visible,
load_data, load_data,
); );
@ -2491,7 +2478,7 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId,
source_id: PipelineId, source_id: PipelineId,
load_data: LoadData, load_data: LoadData,
replace: bool, replace: HistoryEntryReplacement,
) { ) {
match self.pending_approval_navigations.entry(source_id) { match self.pending_approval_navigations.entry(source_id) {
Entry::Occupied(_) => { Entry::Occupied(_) => {
@ -2517,13 +2504,15 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId,
source_id: PipelineId, source_id: PipelineId,
load_data: LoadData, load_data: LoadData,
replace: bool, replace: HistoryEntryReplacement,
) -> Option<PipelineId> { ) -> Option<PipelineId> {
let replace_debug = match replace {
HistoryEntryReplacement::Enabled => "",
HistoryEntryReplacement::Disabled => "not",
};
debug!( debug!(
"Loading {} in pipeline {}, {}replacing.", "Loading {} in pipeline {}, {}replacing.",
load_data.url, load_data.url, source_id, replace_debug
source_id,
if replace { "" } else { "not " }
); );
// If this load targets an iframe, its framing element may exist // If this load targets an iframe, its framing element may exist
// in a separate script thread than the framed document that initiated // in a separate script thread than the framed document that initiated
@ -2564,7 +2553,7 @@ where
Some(parent_pipeline_id) => { Some(parent_pipeline_id) => {
// Find the script thread for the pipeline containing the iframe // Find the script thread for the pipeline containing the iframe
// and issue an iframe load through there. // and issue an iframe load through there.
let msg = ConstellationControlMsg::Navigate( let msg = ConstellationControlMsg::NavigateIframe(
parent_pipeline_id, parent_pipeline_id,
browsing_context_id, browsing_context_id,
load_data, load_data,
@ -2607,10 +2596,9 @@ where
// Create the new pipeline // Create the new pipeline
let replace = if replace { let replace = match replace {
Some(NeedsToReload::No(pipeline_id)) HistoryEntryReplacement::Enabled => Some(NeedsToReload::No(pipeline_id)),
} else { HistoryEntryReplacement::Disabled => None,
None
}; };
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = PipelineId::new();
@ -2725,7 +2713,7 @@ where
&mut self, &mut self,
pipeline_id: PipelineId, pipeline_id: PipelineId,
new_url: ServoUrl, new_url: ServoUrl,
replacement_enabled: bool, replacement_enabled: HistoryEntryReplacement,
) { ) {
let (top_level_browsing_context_id, old_url) = match self.pipelines.get_mut(&pipeline_id) { let (top_level_browsing_context_id, old_url) = match self.pipelines.get_mut(&pipeline_id) {
Some(pipeline) => { Some(pipeline) => {
@ -2740,7 +2728,8 @@ where
}, },
}; };
if !replacement_enabled { match replacement_enabled {
HistoryEntryReplacement::Disabled => {
let diff = SessionHistoryDiff::HashDiff { let diff = SessionHistoryDiff::HashDiff {
pipeline_reloader: NeedsToReload::No(pipeline_id), pipeline_reloader: NeedsToReload::No(pipeline_id),
new_url, new_url,
@ -2749,6 +2738,8 @@ where
self.get_joint_session_history(top_level_browsing_context_id) self.get_joint_session_history(top_level_browsing_context_id)
.push_diff(diff); .push_diff(diff);
self.notify_history_changed(top_level_browsing_context_id); self.notify_history_changed(top_level_browsing_context_id);
},
HistoryEntryReplacement::Enabled => {},
} }
} }
@ -3390,7 +3381,12 @@ where
)); ));
}, },
WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, reply) => { WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, reply) => {
self.load_url_for_webdriver(top_level_browsing_context_id, load_data, reply, false); self.load_url_for_webdriver(
top_level_browsing_context_id,
load_data,
reply,
HistoryEntryReplacement::Disabled,
);
}, },
WebDriverCommandMsg::Refresh(top_level_browsing_context_id, reply) => { WebDriverCommandMsg::Refresh(top_level_browsing_context_id, reply) => {
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
@ -3407,7 +3403,12 @@ where
Some(pipeline) => pipeline.load_data.clone(), Some(pipeline) => pipeline.load_data.clone(),
None => return warn!("Pipeline {} refresh after closure.", pipeline_id), None => return warn!("Pipeline {} refresh after closure.", pipeline_id),
}; };
self.load_url_for_webdriver(top_level_browsing_context_id, load_data, reply, true); self.load_url_for_webdriver(
top_level_browsing_context_id,
load_data,
reply,
HistoryEntryReplacement::Enabled,
);
}, },
WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => { WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => {
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
@ -3590,7 +3591,7 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId,
load_data: LoadData, load_data: LoadData,
reply: IpcSender<webdriver_msg::LoadStatus>, reply: IpcSender<webdriver_msg::LoadStatus>,
replace: bool, replace: HistoryEntryReplacement,
) { ) {
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {

View file

@ -222,8 +222,6 @@ impl Pipeline {
device_pixel_ratio: state.device_pixel_ratio, device_pixel_ratio: state.device_pixel_ratio,
}; };
let url = state.load_data.url.clone();
let (script_chan, sampler_chan) = match state.event_loop { let (script_chan, sampler_chan) = match state.event_loop {
Some(script_chan) => { Some(script_chan) => {
let new_layout_info = NewLayoutInfo { let new_layout_info = NewLayoutInfo {
@ -345,7 +343,6 @@ impl Pipeline {
script_chan, script_chan,
pipeline_chan, pipeline_chan,
state.compositor_proxy, state.compositor_proxy,
url,
state.prev_visibility, state.prev_visibility,
state.load_data, state.load_data,
); );
@ -365,7 +362,6 @@ impl Pipeline {
event_loop: Rc<EventLoop>, event_loop: Rc<EventLoop>,
layout_chan: IpcSender<LayoutControlMsg>, layout_chan: IpcSender<LayoutControlMsg>,
compositor_proxy: CompositorProxy, compositor_proxy: CompositorProxy,
url: ServoUrl,
is_visible: bool, is_visible: bool,
load_data: LoadData, load_data: LoadData,
) -> Pipeline { ) -> Pipeline {
@ -377,7 +373,7 @@ impl Pipeline {
event_loop: event_loop, event_loop: event_loop,
layout_chan: layout_chan, layout_chan: layout_chan,
compositor_proxy: compositor_proxy, compositor_proxy: compositor_proxy,
url: url, url: load_data.url.clone(),
children: vec![], children: vec![],
running_animations: false, running_animations: false,
load_data: load_data, load_data: load_data,

View file

@ -11,6 +11,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAncho
use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods; use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::document::determine_policy_for_token; use crate::dom::document::determine_policy_for_token;
@ -19,16 +20,19 @@ use crate::dom::domtokenlist::DOMTokenList;
use crate::dom::element::Element; use crate::dom::element::Element;
use crate::dom::event::Event; use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlimageelement::HTMLImageElement; use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::mouseevent::MouseEvent; use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{document_from_node, Node}; use crate::dom::node::{document_from_node, Node};
use crate::dom::urlhelper::UrlHelper; use crate::dom::urlhelper::UrlHelper;
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::task_source::TaskSource;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix}; use html5ever::{LocalName, Prefix};
use net_traits::request::Referrer; use net_traits::request::Referrer;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::default::Default; use std::default::Default;
use style::attr::AttrValue; use style::attr::AttrValue;
@ -624,8 +628,19 @@ pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) {
// Step 7. // Step 7.
let (maybe_chosen, replace) = match target_attribute_value { let (maybe_chosen, replace) = match target_attribute_value {
Some(name) => source.choose_browsing_context(name.Value(), noopener), Some(name) => {
None => (Some(window.window_proxy()), false), let (maybe_chosen, new) = source.choose_browsing_context(name.Value(), noopener);
let replace = if new {
HistoryEntryReplacement::Enabled
} else {
HistoryEntryReplacement::Disabled
};
(maybe_chosen, replace)
},
None => (
Some(window.window_proxy()),
HistoryEntryReplacement::Disabled,
),
}; };
// Step 8. // Step 8.
@ -667,7 +682,23 @@ pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) {
}; };
// Step 14 // Step 14
debug!("following hyperlink to {}", url); let pipeline_id = target_window.upcast::<GlobalScope>().pipeline_id();
target_window.load_url(url, replace, false, referrer, referrer_policy); let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
url,
Some(pipeline_id),
Some(referrer),
referrer_policy,
);
let target = Trusted::new(target_window);
let task = task!(navigate_follow_hyperlink: move || {
debug!("following hyperlink to {}", load_data.url);
target.root().load_url(replace, false, load_data);
});
target_window
.task_manager()
.dom_manipulation_task_source()
.queue(task, target_window.upcast())
.unwrap();
}; };
} }

View file

@ -3,6 +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 crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrBinding::AttrMethods;
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
@ -12,6 +13,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding;
use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
@ -46,7 +48,6 @@ use crate::dom::node::{UnbindContext, VecPreOrderInsertionHelper};
use crate::dom::validitystate::ValidationFlags; use crate::dom::validitystate::ValidationFlags;
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::script_thread::MainThreadScriptMsg;
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use encoding_rs::{Encoding, UTF_8}; use encoding_rs::{Encoding, UTF_8};
@ -56,7 +57,7 @@ use hyper::Method;
use mime::{self, Mime}; use mime::{self, Mime};
use net_traits::http_percent_encode; use net_traits::http_percent_encode;
use net_traits::request::Referrer; use net_traits::request::Referrer;
use script_traits::LoadData; use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_rand::random; use servo_rand::random;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::Cell; use std::cell::Cell;
@ -399,6 +400,7 @@ impl HTMLFormElement {
}; };
let target_window = target_document.window(); let target_window = target_document.window();
let mut load_data = LoadData::new( let mut load_data = LoadData::new(
LoadOrigin::Script(doc.origin().immutable().clone()),
action_components, action_components,
None, None,
Some(Referrer::ReferrerUrl(target_document.url())), Some(Referrer::ReferrerUrl(target_document.url())),
@ -529,7 +531,7 @@ impl HTMLFormElement {
} }
/// [Planned navigation](https://html.spec.whatwg.org/multipage/#planned-navigation) /// [Planned navigation](https://html.spec.whatwg.org/multipage/#planned-navigation)
fn plan_to_navigate(&self, load_data: LoadData, target: &Window) { fn plan_to_navigate(&self, mut load_data: LoadData, target: &Window) {
// Step 1 // Step 1
// Each planned navigation task is tagged with a generation ID, and // Each planned navigation task is tagged with a generation ID, and
// before the task is handled, it first checks whether the HTMLFormElement's // before the task is handled, it first checks whether the HTMLFormElement's
@ -537,19 +539,35 @@ impl HTMLFormElement {
let generation_id = GenerationId(self.generation_id.get().0 + 1); let generation_id = GenerationId(self.generation_id.get().0 + 1);
self.generation_id.set(generation_id); self.generation_id.set(generation_id);
// Step 2. // Step 2
let elem = self.upcast::<Element>();
let referrer = match elem.get_attribute(&ns!(), &local_name!("rel")) {
Some(ref link_types) if link_types.Value().contains("noreferrer") => {
Referrer::NoReferrer
},
_ => Referrer::Client,
};
let referrer_policy = target.Document().get_referrer_policy();
let pipeline_id = target.upcast::<GlobalScope>().pipeline_id(); let pipeline_id = target.upcast::<GlobalScope>().pipeline_id();
let script_chan = target.main_thread_script_chan().clone(); load_data.creator_pipeline_id = Some(pipeline_id);
load_data.referrer = Some(referrer);
load_data.referrer_policy = referrer_policy;
// Step 4.
let this = Trusted::new(self); let this = Trusted::new(self);
let window = Trusted::new(target);
let task = task!(navigate_to_form_planned_navigation: move || { let task = task!(navigate_to_form_planned_navigation: move || {
if generation_id != this.root().generation_id.get() { if generation_id != this.root().generation_id.get() {
return; return;
} }
script_chan.send(MainThreadScriptMsg::Navigate( window
pipeline_id, .root()
load_data, .load_url(
HistoryEntryReplacement::Disabled,
false, false,
)).unwrap(); load_data,
);
}); });
// Step 3. // Step 3.

View file

@ -37,8 +37,8 @@ use profile_traits::ipc as ProfiledIpc;
use script_layout_interface::message::ReflowGoal; use script_layout_interface::message::ReflowGoal;
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
use script_traits::{ use script_traits::{
IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData, UpdatePipelineIdReason, HistoryEntryReplacement, IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData,
WindowSizeData, LoadOrigin, UpdatePipelineIdReason, WindowSizeData,
}; };
use script_traits::{NewLayoutInfo, ScriptMsg}; use script_traits::{NewLayoutInfo, ScriptMsg};
use servo_url::ServoUrl; use servo_url::ServoUrl;
@ -109,9 +109,9 @@ impl HTMLIFrameElement {
pub fn navigate_or_reload_child_browsing_context( pub fn navigate_or_reload_child_browsing_context(
&self, &self,
mut load_data: Option<LoadData>, mut load_data: LoadData,
nav_type: NavigationType, nav_type: NavigationType,
replace: bool, replace: HistoryEntryReplacement,
) { ) {
let sandboxed = if self.is_sandboxed() { let sandboxed = if self.is_sandboxed() {
IFrameSandboxed IFrameSandboxed
@ -136,20 +136,18 @@ impl HTMLIFrameElement {
// document; the new navigation will continue blocking it. // document; the new navigation will continue blocking it.
LoadBlocker::terminate(&mut load_blocker); LoadBlocker::terminate(&mut load_blocker);
if let Some(ref mut load_data) = load_data { if load_data.url.scheme() == "javascript" {
let is_javascript = load_data.url.scheme() == "javascript";
if is_javascript {
let window_proxy = self.GetContentWindow(); let window_proxy = self.GetContentWindow();
if let Some(window_proxy) = window_proxy { if let Some(window_proxy) = window_proxy {
ScriptThread::eval_js_url(&window_proxy.global(), load_data); // Important re security. See https://github.com/servo/servo/issues/23373
// TODO: check according to https://w3c.github.io/webappsec-csp/#should-block-navigation-request
if ScriptThread::check_load_origin(&load_data.load_origin, &document.url().origin())
{
ScriptThread::eval_js_url(&window_proxy.global(), &mut load_data);
} }
} }
} }
//TODO(#9592): Deal with the case where an iframe is being reloaded so url is None.
// The iframe should always have access to the nested context's active
// document URL through the browsing context.
if let Some(ref load_data) = load_data {
match load_data.js_eval_result { match load_data.js_eval_result {
Some(JsEvalResult::NoContent) => (), Some(JsEvalResult::NoContent) => (),
_ => { _ => {
@ -159,7 +157,6 @@ 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();
@ -182,6 +179,12 @@ impl HTMLIFrameElement {
self.about_blank_pipeline_id.set(Some(new_pipeline_id)); self.about_blank_pipeline_id.set(Some(new_pipeline_id));
let load_info = IFrameLoadInfoWithData {
info: load_info,
load_data: load_data.clone(),
old_pipeline_id: old_pipeline_id,
sandbox: sandboxed,
};
global_scope global_scope
.script_to_constellation_chan() .script_to_constellation_chan()
.send(ScriptMsg::ScriptNewIFrame(load_info, pipeline_sender)) .send(ScriptMsg::ScriptNewIFrame(load_info, pipeline_sender))
@ -193,7 +196,7 @@ impl HTMLIFrameElement {
browsing_context_id: browsing_context_id, browsing_context_id: browsing_context_id,
top_level_browsing_context_id: top_level_browsing_context_id, top_level_browsing_context_id: top_level_browsing_context_id,
opener: None, opener: None,
load_data: load_data.unwrap(), load_data: load_data,
pipeline_port: pipeline_receiver, pipeline_port: pipeline_receiver,
content_process_shutdown_chan: None, content_process_shutdown_chan: None,
window_size: WindowSizeData { window_size: WindowSizeData {
@ -270,6 +273,7 @@ impl HTMLIFrameElement {
let document = document_from_node(self); let document = document_from_node(self);
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
url, url,
creator_pipeline_id, creator_pipeline_id,
Some(Referrer::ReferrerUrl(document.url())), Some(Referrer::ReferrerUrl(document.url())),
@ -281,12 +285,12 @@ impl HTMLIFrameElement {
// 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 = let is_about_blank =
pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get(); pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
let replace = is_about_blank; let replace = if is_about_blank {
self.navigate_or_reload_child_browsing_context( HistoryEntryReplacement::Enabled
Some(load_data), } else {
NavigationType::Regular, HistoryEntryReplacement::Disabled
replace, };
); self.navigate_or_reload_child_browsing_context(load_data, NavigationType::Regular, replace);
} }
fn create_nested_browsing_context(&self) { fn create_nested_browsing_context(&self) {
@ -296,6 +300,7 @@ impl HTMLIFrameElement {
let window = window_from_node(self); let window = window_from_node(self);
let pipeline_id = Some(window.upcast::<GlobalScope>().pipeline_id()); let pipeline_id = Some(window.upcast::<GlobalScope>().pipeline_id());
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
url, url,
pipeline_id, pipeline_id,
Some(Referrer::ReferrerUrl(document.url().clone())), Some(Referrer::ReferrerUrl(document.url().clone())),
@ -309,9 +314,9 @@ impl HTMLIFrameElement {
.set(Some(top_level_browsing_context_id)); .set(Some(top_level_browsing_context_id));
self.browsing_context_id.set(Some(browsing_context_id)); self.browsing_context_id.set(Some(browsing_context_id));
self.navigate_or_reload_child_browsing_context( self.navigate_or_reload_child_browsing_context(
Some(load_data), load_data,
NavigationType::InitialAboutBlank, NavigationType::InitialAboutBlank,
false, HistoryEntryReplacement::Disabled,
); );
} }

View file

@ -6,6 +6,7 @@ use crate::dom::bindings::codegen::Bindings::LocationBinding;
use crate::dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use crate::dom::bindings::codegen::Bindings::LocationBinding::LocationMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::str::{DOMString, USVString};
@ -14,6 +15,7 @@ use crate::dom::urlhelper::UrlHelper;
use crate::dom::window::Window; use crate::dom::window::Window;
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 servo_url::{MutableOrigin, ServoUrl}; use servo_url::{MutableOrigin, ServoUrl};
#[dom_struct] #[dom_struct]
@ -38,6 +40,29 @@ impl Location {
) )
} }
/// https://html.spec.whatwg.org/multipage/#location-object-navigate
fn navigate(
&self,
url: ServoUrl,
referrer: Referrer,
replacement_flag: HistoryEntryReplacement,
reload_triggered: bool,
) {
let document = self.window.Document();
let referrer_policy = document.get_referrer_policy();
let pipeline_id = self.window.upcast::<GlobalScope>().pipeline_id();
let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
url,
Some(pipeline_id),
Some(referrer),
referrer_policy,
);
// TODO: rethrow exceptions, set exceptions enabled flag.
self.window
.load_url(replacement_flag, reload_triggered, load_data);
}
fn get_url(&self) -> ServoUrl { fn get_url(&self) -> ServoUrl {
self.window.get_url() self.window.get_url()
} }
@ -46,7 +71,7 @@ impl Location {
let mut url = self.window.get_url(); let mut url = self.window.get_url();
let referrer = Referrer::ReferrerUrl(url.clone()); let referrer = Referrer::ReferrerUrl(url.clone());
setter(&mut url, value); setter(&mut url, value);
self.window.load_url(url, false, false, referrer, None); self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false);
} }
fn check_same_origin_domain(&self) -> ErrorResult { fn check_same_origin_domain(&self) -> ErrorResult {
@ -66,7 +91,7 @@ impl Location {
pub fn reload_without_origin_check(&self) { pub fn reload_without_origin_check(&self) {
let url = self.get_url(); let url = self.get_url();
let referrer = Referrer::ReferrerUrl(url.clone()); let referrer = Referrer::ReferrerUrl(url.clone());
self.window.load_url(url, true, true, referrer, None); self.navigate(url, referrer, HistoryEntryReplacement::Enabled, true);
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -84,7 +109,7 @@ impl LocationMethods for Location {
let base_url = self.window.get_url(); let base_url = self.window.get_url();
if let Ok(url) = base_url.join(&url.0) { if let Ok(url) = base_url.join(&url.0) {
let referrer = Referrer::ReferrerUrl(base_url.clone()); let referrer = Referrer::ReferrerUrl(base_url.clone());
self.window.load_url(url, false, false, referrer, None); self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false);
Ok(()) Ok(())
} else { } else {
Err(Error::Syntax) Err(Error::Syntax)
@ -96,7 +121,7 @@ impl LocationMethods for Location {
self.check_same_origin_domain()?; self.check_same_origin_domain()?;
let url = self.get_url(); let url = self.get_url();
let referrer = Referrer::ReferrerUrl(url.clone()); let referrer = Referrer::ReferrerUrl(url.clone());
self.window.load_url(url, true, true, referrer, None); self.navigate(url, referrer, HistoryEntryReplacement::Enabled, true);
Ok(()) Ok(())
} }
@ -108,7 +133,7 @@ impl LocationMethods for Location {
let base_url = self.window.get_url(); let base_url = self.window.get_url();
if let Ok(url) = base_url.join(&url.0) { if let Ok(url) = base_url.join(&url.0) {
let referrer = Referrer::ReferrerUrl(base_url.clone()); let referrer = Referrer::ReferrerUrl(base_url.clone());
self.window.load_url(url, true, false, referrer, None); self.navigate(url, referrer, HistoryEntryReplacement::Enabled, false);
Ok(()) Ok(())
} else { } else {
Err(Error::Syntax) Err(Error::Syntax)
@ -178,7 +203,7 @@ impl LocationMethods for Location {
Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))),
}; };
let referrer = Referrer::ReferrerUrl(current_url.clone()); let referrer = Referrer::ReferrerUrl(current_url.clone());
self.window.load_url(url, false, false, referrer, None); self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false);
Ok(()) Ok(())
} }

View file

@ -89,9 +89,8 @@ use media::WindowGLContext;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse}; use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
use net_traits::image_cache::{PendingImageId, PendingImageResponse}; use net_traits::image_cache::{PendingImageId, PendingImageResponse};
use net_traits::request::Referrer;
use net_traits::storage_thread::StorageType; use net_traits::storage_thread::StorageType;
use net_traits::{ReferrerPolicy, ResourceThreads}; use net_traits::ResourceThreads;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use profile_traits::ipc as ProfiledIpc; use profile_traits::ipc as ProfiledIpc;
use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::mem::ProfilerChan as MemProfilerChan;
@ -103,7 +102,7 @@ use script_layout_interface::rpc::{
}; };
use script_layout_interface::{PendingImageState, TrustedNodeAddress}; use script_layout_interface::{PendingImageState, TrustedNodeAddress};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use script_traits::{ConstellationControlMsg, DocumentState, LoadData}; use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData};
use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId}; use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId};
use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType}; use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
use selectors::attr::CaseSensitivity; use selectors::attr::CaseSensitivity;
@ -1770,27 +1769,31 @@ impl Window {
} }
/// Commence a new URL load which will either replace this window or scroll to a fragment. /// Commence a new URL load which will either replace this window or scroll to a fragment.
///
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
pub fn load_url( pub fn load_url(
&self, &self,
url: ServoUrl, replace: HistoryEntryReplacement,
replace: bool,
force_reload: bool, force_reload: bool,
referrer: Referrer, load_data: LoadData,
referrer_policy: Option<ReferrerPolicy>,
) { ) {
let doc = self.Document(); let doc = self.Document();
let referrer_policy = referrer_policy.or(doc.get_referrer_policy()); // TODO: Important re security. See https://github.com/servo/servo/issues/23373
// https://html.spec.whatwg.org/multipage/#navigating-across-documents // Step 3: check that the source browsing-context is "allowed to navigate" this window.
if !force_reload && if !force_reload &&
url.as_url()[..Position::AfterQuery] == doc.url().as_url()[..Position::AfterQuery] load_data.url.as_url()[..Position::AfterQuery] ==
doc.url().as_url()[..Position::AfterQuery]
{ {
// Step 6 // Step 6
if let Some(fragment) = url.fragment() { if let Some(fragment) = load_data.url.fragment() {
self.send_to_constellation(ScriptMsg::NavigatedToFragment(url.clone(), replace)); self.send_to_constellation(ScriptMsg::NavigatedToFragment(
load_data.url.clone(),
replace,
));
doc.check_and_scroll_fragment(fragment); doc.check_and_scroll_fragment(fragment);
let this = Trusted::new(self); let this = Trusted::new(self);
let old_url = doc.url().into_string(); let old_url = doc.url().into_string();
let new_url = url.clone().into_string(); let new_url = load_data.url.clone().into_string();
let task = task!(hashchange_event: move || { let task = task!(hashchange_event: move || {
let this = this.root(); let this = this.root();
let event = HashChangeEvent::new( let event = HashChangeEvent::new(
@ -1813,7 +1816,7 @@ impl Window {
self.pipeline_id(), self.pipeline_id(),
TaskSourceName::DOMManipulation, TaskSourceName::DOMManipulation,
)); ));
doc.set_url(url.clone()); doc.set_url(load_data.url.clone());
return; return;
} }
} }
@ -1838,13 +1841,9 @@ impl Window {
// then put it in the delaying load events mode. // then put it in the delaying load events mode.
self.window_proxy().start_delaying_load_events_mode(); self.window_proxy().start_delaying_load_events_mode();
} }
self.main_thread_script_chan() // TODO: step 11, navigationType.
.send(MainThreadScriptMsg::Navigate( // Step 12, 13
pipeline_id, ScriptThread::navigate(pipeline_id, load_data, replace);
LoadData::new(url, Some(pipeline_id), Some(referrer), referrer_policy),
replace,
))
.unwrap();
}; };
} }

View file

@ -47,7 +47,10 @@ use msg::constellation_msg::BrowsingContextId;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use msg::constellation_msg::TopLevelBrowsingContextId; use msg::constellation_msg::TopLevelBrowsingContextId;
use net_traits::request::Referrer; use net_traits::request::Referrer;
use script_traits::{AuxiliaryBrowsingContextLoadInfo, LoadData, NewLayoutInfo, ScriptMsg}; use script_traits::{
AuxiliaryBrowsingContextLoadInfo, HistoryEntryReplacement, LoadData, LoadOrigin,
};
use script_traits::{NewLayoutInfo, ScriptMsg};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::cell::Cell; use std::cell::Cell;
use std::ptr; use std::ptr;
@ -268,24 +271,28 @@ impl WindowProxy {
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 new_pipeline_id = PipelineId::new();
let load_info = AuxiliaryBrowsingContextLoadInfo {
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: new_pipeline_id,
};
let document = self let document = self
.currently_active .currently_active
.get() .get()
.and_then(|id| ScriptThread::find_document(id)) .and_then(|id| ScriptThread::find_document(id))
.unwrap(); .expect("A WindowProxy creating an auxiliary to have an active document");
let blank_url = ServoUrl::parse("about:blank").ok().unwrap(); let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
blank_url, blank_url,
None, None,
Some(Referrer::ReferrerUrl(document.url().clone())), Some(Referrer::ReferrerUrl(document.url().clone())),
document.get_referrer_policy(), document.get_referrer_policy(),
); );
let load_info = AuxiliaryBrowsingContextLoadInfo {
load_data: load_data.clone(),
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: new_pipeline_id,
};
let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap(); let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap();
let new_layout_info = NewLayoutInfo { let new_layout_info = NewLayoutInfo {
parent_info: None, parent_info: None,
@ -437,13 +444,21 @@ impl WindowProxy {
Referrer::Client Referrer::Client
}; };
// Step 14.5 // Step 14.5
target_window.load_url( let referrer_policy = target_document.get_referrer_policy();
let pipeline_id = target_window.upcast::<GlobalScope>().pipeline_id();
let load_data = LoadData::new(
LoadOrigin::Script(existing_document.origin().immutable().clone()),
url, url,
new, Some(pipeline_id),
false, Some(referrer),
referrer, referrer_policy,
target_document.get_referrer_policy(),
); );
let replacement_flag = if new {
HistoryEntryReplacement::Enabled
} else {
HistoryEntryReplacement::Disabled
};
target_window.load_url(replacement_flag, false, load_data);
} }
if noopener { if noopener {
// Step 15 (Dis-owning has been done in create_auxiliary_browsing_context). // Step 15 (Dis-owning has been done in create_auxiliary_browsing_context).

View file

@ -33,6 +33,7 @@ use crate::dom::bindings::conversions::{
}; };
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite; use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::ThreadLocalStackRoots; use crate::dom::bindings::root::ThreadLocalStackRoots;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection};
@ -83,6 +84,7 @@ use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
use crate::task_source::remote_event::RemoteEventTaskSource; use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::user_interaction::UserInteractionTaskSource; use crate::task_source::user_interaction::UserInteractionTaskSource;
use crate::task_source::websocket::WebsocketTaskSource; use crate::task_source::websocket::WebsocketTaskSource;
use crate::task_source::TaskSource;
use crate::task_source::TaskSourceName; use crate::task_source::TaskSourceName;
use crate::webdriver_handlers; use crate::webdriver_handlers;
use bluetooth_traits::BluetoothRequest; use bluetooth_traits::BluetoothRequest;
@ -129,8 +131,10 @@ use script_traits::CompositorEvent::{
WheelEvent, WheelEvent,
}; };
use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult}; use script_traits::{
use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData}; DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement,
};
use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData, LoadOrigin};
use script_traits::{MouseButton, MouseEventType, NewLayoutInfo}; use script_traits::{MouseButton, MouseEventType, NewLayoutInfo};
use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory}; use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory};
use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg}; use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg};
@ -264,10 +268,6 @@ enum MixedMessage {
pub enum MainThreadScriptMsg { pub enum MainThreadScriptMsg {
/// Common variants associated with the script messages /// Common variants associated with the script messages
Common(CommonScriptMsg), Common(CommonScriptMsg),
/// Begins a content-initiated load on the specified pipeline (only
/// dispatched to ScriptThread). Allows for a replace bool to be passed. If true,
/// the current entry will be replaced instead of a new entry being added.
Navigate(PipelineId, LoadData, bool),
/// Notifies the script thread that a new worklet has been loaded, and thus the page should be /// Notifies the script thread that a new worklet has been loaded, and thus the page should be
/// reflowed. /// reflowed.
WorkletLoaded(PipelineId), WorkletLoaded(PipelineId),
@ -919,6 +919,74 @@ impl ScriptThread {
}); });
} }
/// Check that two origins are "similar enough",
/// for now only used to prevent cross-origin JS url evaluation.
///
/// https://github.com/whatwg/html/issues/2591
pub fn check_load_origin(source: &LoadOrigin, target: &ImmutableOrigin) -> bool {
match (source, target) {
(LoadOrigin::Constellation, _) | (LoadOrigin::WebDriver, _) => {
// Always allow loads initiated by the constellation or webdriver.
true
},
(_, ImmutableOrigin::Opaque(_)) => {
// If the target is opaque, allow.
// This covers newly created about:blank auxiliaries, and iframe with no src.
// TODO: https://github.com/servo/servo/issues/22879
true
},
(LoadOrigin::Script(source_origin), _) => source_origin == target,
}
}
/// Step 13 of https://html.spec.whatwg.org/multipage/#navigate
pub fn navigate(
pipeline_id: PipelineId,
mut load_data: LoadData,
replace: HistoryEntryReplacement,
) {
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = match root.get() {
None => return,
Some(script) => script,
};
let script_thread = unsafe { &*script_thread };
let is_javascript = load_data.url.scheme() == "javascript";
// If resource is a request whose url's scheme is "javascript"
// https://html.spec.whatwg.org/multipage/#javascript-protocol
if is_javascript {
let window = match script_thread.documents.borrow().find_window(pipeline_id) {
None => return,
Some(window) => window,
};
let global = window.upcast::<GlobalScope>();
let trusted_global = Trusted::new(global);
let sender = script_thread.script_sender.clone();
let task = task!(navigate_javascript: move || {
// Important re security. See https://github.com/servo/servo/issues/23373
// TODO: check according to https://w3c.github.io/webappsec-csp/#should-block-navigation-request
if let Some(window) = trusted_global.root().downcast::<Window>() {
if ScriptThread::check_load_origin(&load_data.load_origin, &window.get_url().origin()) {
ScriptThread::eval_js_url(&trusted_global.root(), &mut load_data);
sender
.send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
.unwrap();
}
}
});
global
.dom_manipulation_task_source()
.queue(task, global.upcast())
.expect("Enqueing navigate js task on the DOM manipulation task source failed");
} else {
script_thread
.script_sender
.send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
.expect("Sending a LoadUrl message to the constellation failed");
}
});
}
pub fn process_attach_layout(new_layout_info: NewLayoutInfo, origin: MutableOrigin) { pub fn process_attach_layout(new_layout_info: NewLayoutInfo, origin: MutableOrigin) {
SCRIPT_THREAD_ROOT.with(|root| { SCRIPT_THREAD_ROOT.with(|root| {
if let Some(script_thread) = root.get() { if let Some(script_thread) = root.get() {
@ -1563,7 +1631,7 @@ impl ScriptThread {
SetDocumentActivity(id, ..) => Some(id), SetDocumentActivity(id, ..) => Some(id),
ChangeFrameVisibilityStatus(id, ..) => Some(id), ChangeFrameVisibilityStatus(id, ..) => Some(id),
NotifyVisibilityChange(id, ..) => Some(id), NotifyVisibilityChange(id, ..) => Some(id),
Navigate(id, ..) => Some(id), NavigateIframe(id, ..) => Some(id),
PostMessage { target: id, .. } => Some(id), PostMessage { target: id, .. } => Some(id),
UpdatePipelineId(_, _, _, id, _) => Some(id), UpdatePipelineId(_, _, _, id, _) => Some(id),
UpdateHistoryState(id, ..) => Some(id), UpdateHistoryState(id, ..) => Some(id),
@ -1593,7 +1661,6 @@ impl ScriptThread {
pipeline_id pipeline_id
}, },
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None, MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
MainThreadScriptMsg::Navigate(pipeline_id, ..) => Some(pipeline_id),
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id), MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id), MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
MainThreadScriptMsg::DispatchJobQueue { .. } => None, MainThreadScriptMsg::DispatchJobQueue { .. } => None,
@ -1703,14 +1770,14 @@ impl ScriptThread {
_ => unreachable!(), _ => unreachable!(),
}; };
}, },
ConstellationControlMsg::Navigate( ConstellationControlMsg::NavigateIframe(
parent_pipeline_id, parent_pipeline_id,
browsing_context_id, browsing_context_id,
load_data, load_data,
replace, replace,
) => self.handle_navigate( ) => self.handle_navigate_iframe(
parent_pipeline_id, parent_pipeline_id,
Some(browsing_context_id), browsing_context_id,
load_data, load_data,
replace, replace,
), ),
@ -1825,9 +1892,6 @@ impl ScriptThread {
fn handle_msg_from_script(&self, msg: MainThreadScriptMsg) { fn handle_msg_from_script(&self, msg: MainThreadScriptMsg) {
match msg { match msg {
MainThreadScriptMsg::Navigate(parent_pipeline_id, load_data, replace) => {
self.handle_navigate(parent_pipeline_id, None, load_data, replace)
},
MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, task, _, _)) => task.run_box(), MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, task, _, _)) => task.run_box(),
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => { MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
self.collect_reports(chan) self.collect_reports(chan)
@ -3352,50 +3416,30 @@ impl ScriptThread {
document.handle_wheel_event(self.js_runtime.rt(), wheel_delta, point, node_address); document.handle_wheel_event(self.js_runtime.rt(), wheel_delta, point, node_address);
} }
/// <https://html.spec.whatwg.org/multipage/#navigating-across-documents> /// Handle a "navigate an iframe" message from the constellation.
/// The entry point for content to notify that a new load has been requested fn handle_navigate_iframe(
/// for the given pipeline (specifically the "navigate" algorithm).
fn handle_navigate(
&self, &self,
parent_pipeline_id: PipelineId, parent_pipeline_id: PipelineId,
browsing_context_id: Option<BrowsingContextId>, browsing_context_id: BrowsingContextId,
mut load_data: LoadData, load_data: LoadData,
replace: bool, replace: HistoryEntryReplacement,
) { ) {
let is_javascript = load_data.url.scheme() == "javascript";
if is_javascript {
let window = self.documents.borrow().find_window(parent_pipeline_id);
if let Some(window) = window {
ScriptThread::eval_js_url(window.upcast::<GlobalScope>(), &mut load_data);
}
}
match browsing_context_id {
Some(browsing_context_id) => {
let iframe = self let iframe = self
.documents .documents
.borrow() .borrow()
.find_iframe(parent_pipeline_id, browsing_context_id); .find_iframe(parent_pipeline_id, browsing_context_id);
if let Some(iframe) = iframe { if let Some(iframe) = iframe {
iframe.navigate_or_reload_child_browsing_context( iframe.navigate_or_reload_child_browsing_context(
Some(load_data), load_data,
NavigationType::Regular, NavigationType::Regular,
replace, replace,
); );
} }
},
None => {
self.script_sender
.send((parent_pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
.unwrap();
},
}
} }
/// Turn javascript: URL into JS code to eval, according to the steps in
/// https://html.spec.whatwg.org/multipage/#javascript-protocol
pub fn eval_js_url(global_scope: &GlobalScope, load_data: &mut LoadData) { pub fn eval_js_url(global_scope: &GlobalScope, load_data: &mut LoadData) {
// Turn javascript: URL into JS code to eval, according to the steps in
// https://html.spec.whatwg.org/multipage/#javascript-protocol
// This slice of the URLs serialization is equivalent to (5.) to (7.): // This slice of the URLs serialization is equivalent to (5.) to (7.):
// Start with the scheme data of the parsed URL; // Start with the scheme data of the parsed URL;
// append question mark and query component, if any; // append question mark and query component, if any;

View file

@ -63,7 +63,8 @@ use webrender_api::{DocumentId, ExternalScrollId, ImageKey, RenderApiSender};
use webvr_traits::{WebVREvent, WebVRMsg}; use webvr_traits::{WebVREvent, WebVRMsg};
pub use crate::script_msg::{ pub use crate::script_msg::{
DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerMsg, DOMMessage, HistoryEntryReplacement, SWManagerMsg, SWManagerSenders, ScopeThings,
ServiceWorkerMsg,
}; };
pub use crate::script_msg::{ pub use crate::script_msg::{
EventResult, IFrameSize, IFrameSizeMsg, LayoutMsg, LogEntry, ScriptMsg, EventResult, IFrameSize, IFrameSizeMsg, LayoutMsg, LogEntry, ScriptMsg,
@ -118,10 +119,24 @@ pub enum LayoutControlMsg {
PaintMetric(Epoch, u64), PaintMetric(Epoch, u64),
} }
/// The origin where a given load was initiated.
/// Useful for origin checks, for example before evaluation a JS URL.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum LoadOrigin {
/// A load originating in the constellation.
Constellation,
/// A load originating in webdriver.
WebDriver,
/// A load originating in script.
Script(ImmutableOrigin),
}
/// can be passed to `LoadUrl` to load a page with GET/POST /// can be passed to `LoadUrl` to load a page with GET/POST
/// parameters or headers /// parameters or headers
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LoadData { pub struct LoadData {
/// The origin where the load started.
pub load_origin: LoadOrigin,
/// The URL. /// The URL.
pub url: ServoUrl, pub url: ServoUrl,
/// The creator pipeline id if this is an about:blank load. /// The creator pipeline id if this is an about:blank load.
@ -161,12 +176,14 @@ pub enum JsEvalResult {
impl LoadData { impl LoadData {
/// Create a new `LoadData` object. /// Create a new `LoadData` object.
pub fn new( pub fn new(
load_origin: LoadOrigin,
url: ServoUrl, url: ServoUrl,
creator_pipeline_id: Option<PipelineId>, creator_pipeline_id: Option<PipelineId>,
referrer: Option<Referrer>, referrer: Option<Referrer>,
referrer_policy: Option<ReferrerPolicy>, referrer_policy: Option<ReferrerPolicy>,
) -> LoadData { ) -> LoadData {
LoadData { LoadData {
load_origin,
url: url, url: url,
creator_pipeline_id: creator_pipeline_id, creator_pipeline_id: creator_pipeline_id,
method: Method::GET, method: Method::GET,
@ -290,7 +307,12 @@ pub enum ConstellationControlMsg {
NotifyVisibilityChange(PipelineId, BrowsingContextId, bool), NotifyVisibilityChange(PipelineId, BrowsingContextId, bool),
/// Notifies script thread that a url should be loaded in this iframe. /// Notifies script thread that a url should be loaded in this iframe.
/// PipelineId is for the parent, BrowsingContextId is for the nested browsing context /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context
Navigate(PipelineId, BrowsingContextId, LoadData, bool), NavigateIframe(
PipelineId,
BrowsingContextId,
LoadData,
HistoryEntryReplacement,
),
/// Post a message to a given window. /// Post a message to a given window.
PostMessage { PostMessage {
/// The target of the message. /// The target of the message.
@ -377,7 +399,7 @@ impl fmt::Debug for ConstellationControlMsg {
SetDocumentActivity(..) => "SetDocumentActivity", SetDocumentActivity(..) => "SetDocumentActivity",
ChangeFrameVisibilityStatus(..) => "ChangeFrameVisibilityStatus", ChangeFrameVisibilityStatus(..) => "ChangeFrameVisibilityStatus",
NotifyVisibilityChange(..) => "NotifyVisibilityChange", NotifyVisibilityChange(..) => "NotifyVisibilityChange",
Navigate(..) => "Navigate", NavigateIframe(..) => "NavigateIframe",
PostMessage { .. } => "PostMessage", PostMessage { .. } => "PostMessage",
UpdatePipelineId(..) => "UpdatePipelineId", UpdatePipelineId(..) => "UpdatePipelineId",
UpdateHistoryState(..) => "UpdateHistoryState", UpdateHistoryState(..) => "UpdateHistoryState",
@ -673,6 +695,8 @@ pub enum IFrameSandboxState {
/// Specifies the information required to load an auxiliary browsing context. /// Specifies the information required to load an auxiliary browsing context.
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct AuxiliaryBrowsingContextLoadInfo { pub struct AuxiliaryBrowsingContextLoadInfo {
/// Load data containing the url to load
pub load_data: LoadData,
/// The pipeline opener browsing context. /// The pipeline opener browsing context.
pub opener_pipeline_id: PipelineId, pub opener_pipeline_id: PipelineId,
/// The new top-level ID for the auxiliary. /// The new top-level ID for the auxiliary.
@ -698,7 +722,7 @@ pub struct IFrameLoadInfo {
pub is_private: bool, pub is_private: bool,
/// Wether this load should replace the current entry (reload). If true, the current /// Wether this load should replace the current entry (reload). If true, the current
/// entry will be replaced instead of a new entry being added. /// entry will be replaced instead of a new entry being added.
pub replace: bool, pub replace: HistoryEntryReplacement,
} }
/// Specifies the information required to load a URL in an iframe. /// Specifies the information required to load a URL in an iframe.
@ -707,7 +731,7 @@ pub struct IFrameLoadInfoWithData {
/// The information required to load an iframe. /// The information required to load an iframe.
pub info: IFrameLoadInfo, pub info: IFrameLoadInfo,
/// Load data containing the url to load /// Load data containing the url to load
pub load_data: Option<LoadData>, pub load_data: LoadData,
/// The old pipeline ID for this iframe, if a page was previously loaded. /// The old pipeline ID for this iframe, if a page was previously loaded.
pub old_pipeline_id: Option<PipelineId>, pub old_pipeline_id: Option<PipelineId>,
/// Sandbox type of this iframe /// Sandbox type of this iframe

View file

@ -5,7 +5,6 @@
use crate::AnimationState; use crate::AnimationState;
use crate::AuxiliaryBrowsingContextLoadInfo; use crate::AuxiliaryBrowsingContextLoadInfo;
use crate::DocumentState; use crate::DocumentState;
use crate::IFrameLoadInfo;
use crate::IFrameLoadInfoWithData; use crate::IFrameLoadInfoWithData;
use crate::LayoutControlMsg; use crate::LayoutControlMsg;
use crate::LoadData; use crate::LoadData;
@ -97,6 +96,15 @@ pub enum LogEntry {
Warn(String), Warn(String),
} }
/// https://html.spec.whatwg.org/multipage/#replacement-enabled
#[derive(Debug, Deserialize, Serialize)]
pub enum HistoryEntryReplacement {
/// Traverse the history with replacement enabled.
Enabled,
/// Traverse the history with replacement disabled.
Disabled,
}
/// Messages from the script to the constellation. /// Messages from the script to the constellation.
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub enum ScriptMsg { pub enum ScriptMsg {
@ -143,7 +151,7 @@ pub enum ScriptMsg {
LoadComplete, LoadComplete,
/// 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, bool), LoadUrl(LoadData, HistoryEntryReplacement),
/// Abort loading after sending a LoadUrl message. /// Abort loading after sending a LoadUrl message.
AbortLoadUrl, AbortLoadUrl,
/// Post a message to the currently active window of a given browsing context. /// Post a message to the currently active window of a given browsing context.
@ -158,7 +166,7 @@ pub enum ScriptMsg {
data: Vec<u8>, data: Vec<u8>,
}, },
/// Inform the constellation that a fragment was navigated to and whether or not it was a replacement navigation. /// Inform the constellation that a fragment was navigated to and whether or not it was a replacement navigation.
NavigatedToFragment(ServoUrl, bool), NavigatedToFragment(ServoUrl, HistoryEntryReplacement),
/// HTMLIFrameElement Forward or Back traversal. /// HTMLIFrameElement Forward or Back traversal.
TraverseHistory(TraversalDirection), TraverseHistory(TraversalDirection),
/// Inform the constellation of a pushed history state. /// Inform the constellation of a pushed history state.
@ -175,7 +183,7 @@ pub enum ScriptMsg {
/// A load has been requested in an IFrame. /// A load has been requested in an IFrame.
ScriptLoadedURLInIFrame(IFrameLoadInfoWithData), ScriptLoadedURLInIFrame(IFrameLoadInfoWithData),
/// A load of the initial `about:blank` has been completed in an IFrame. /// A load of the initial `about:blank` has been completed in an IFrame.
ScriptNewIFrame(IFrameLoadInfo, IpcSender<LayoutControlMsg>), ScriptNewIFrame(IFrameLoadInfoWithData, IpcSender<LayoutControlMsg>),
/// Script has opened a new auxiliary browsing context. /// Script has opened a new auxiliary browsing context.
ScriptNewAuxiliary( ScriptNewAuxiliary(
AuxiliaryBrowsingContextLoadInfo, AuxiliaryBrowsingContextLoadInfo,

View file

@ -30,7 +30,7 @@ use script_traits::webdriver_msg::{LoadStatus, WebDriverCookieError, WebDriverFr
use script_traits::webdriver_msg::{ use script_traits::webdriver_msg::{
WebDriverJSError, WebDriverJSResult, WebDriverJSValue, WebDriverScriptCommand, WebDriverJSError, WebDriverJSResult, WebDriverJSValue, WebDriverScriptCommand,
}; };
use script_traits::{ConstellationMsg, LoadData, WebDriverCommandMsg}; use script_traits::{ConstellationMsg, LoadData, LoadOrigin, WebDriverCommandMsg};
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor}; use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_json::{json, Value}; use serde_json::{json, Value};
@ -593,7 +593,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let load_data = LoadData::new(url, None, None, None); let load_data = LoadData::new(LoadOrigin::WebDriver, url, None, None, None);
let cmd_msg = let cmd_msg =
WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, sender.clone()); WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, sender.clone());
self.constellation_chan self.constellation_chan

View file

@ -631153,7 +631153,7 @@
"testharness" "testharness"
], ],
"html/semantics/scripting-1/the-script-element/execution-timing/029.html": [ "html/semantics/scripting-1/the-script-element/execution-timing/029.html": [
"c74665ec1e5f37d4e9ec0ecc65f626e79d4942d4", "33548e566ac67823f9c19af7785a13e394c4964b",
"testharness" "testharness"
], ],
"html/semantics/scripting-1/the-script-element/execution-timing/030.html": [ "html/semantics/scripting-1/the-script-element/execution-timing/030.html": [

View file

@ -10,6 +10,9 @@
<div id="log">FAILED (This TC requires JavaScript enabled)</div> <div id="log">FAILED (This TC requires JavaScript enabled)</div>
<p><a href="javascript:log('JS URL')"></a></p> <p><a href="javascript:log('JS URL')"></a></p>
<script>log('inline script #1'); <script>log('inline script #1');
window.addEventListener("beforeunload", function( event ) {
log('beforeunload event');
});
if(document.links[0].click){ if(document.links[0].click){
document.links[0].click(); document.links[0].click();
}else{ }else{
@ -25,13 +28,26 @@
log( 'inline script #2' ); log( 'inline script #2' );
var t = async_test() var t = async_test()
function test() { function final_test() {
// The JS URL part is required to run in an additional task,
// altough that is not fully consistently implemented,
// see https://github.com/whatwg/html/issues/3730#issuecomment-492071447
assert_any(assert_array_equals, eventOrder, [ assert_any(assert_array_equals, eventOrder, [
['inline script #1', 'end script #1', 'JS URL', 'inline script #2'], ['inline script #1', 'end script #1', 'beforeunload event', 'inline script #2', 'JS URL'],
['inline script #1', 'end script #1', 'inline script #2', 'JS URL']]); ['inline script #1', 'end script #1', 'inline script #2', 'beforeunload event', 'JS URL']]);
t.done(); t.done();
} }
onload = t.step_func(test)
function test_on_load() {
// When the page loads, a task to run the navigate steps
// previously enqueued as part of following-hyperlinks,
// should have run, and have enqueued another task to execute the JS URL.
assert_any(assert_array_equals, eventOrder, [
['inline script #1', 'end script #1', 'beforeunload event', 'inline script #2'],
['inline script #1', 'end script #1', 'inline script #2', 'beforeunload event']]);
t.step_timeout(final_test, 1000)
}
onload = t.step_func(test_on_load);
</script> </script>
</body></html> </body></html>