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::{
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::{
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
};
use script_traits::{IFrameSizeMsg, WindowSizeData, WindowSizeType};
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg};
use serde::{Deserialize, Serialize};
@ -171,7 +171,7 @@ use style_traits::viewport::ViewportConstraints;
use style_traits::CSSPixel;
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
/// 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;
// However, if the id is not encompassed by another change, it will be.
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 pipeline_id = match self.browsing_contexts.get(&ctx_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,
// 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) => {
let is_ready = self.handle_is_ready_to_save_image(pipeline_states);
@ -1911,7 +1916,7 @@ where
warn!("creating replacement pipeline for about:failure");
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 is_private = false;
self.new_pipeline(
@ -2030,7 +2035,7 @@ where
);
self.embedder_proxy.send(msg);
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 is_private = false;
let is_visible = true;
@ -2191,7 +2196,9 @@ where
// see https://html.spec.whatwg.org/multipage/#the-iframe-element:completely-loaded
debug!("checking old pipeline? {:?}", load_info.old_pipeline_id);
if let Some(old_pipeline) = old_pipeline {
replace |= !old_pipeline.completely_loaded;
if !old_pipeline.completely_loaded {
replace = HistoryEntryReplacement::Enabled;
}
debug!(
"old pipeline is {}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 parent_browsing_context_id = match self.pipelines.get(&parent_pipeline_id) {
Some(pipeline) => pipeline.browsing_context_id,
@ -2245,10 +2242,11 @@ where
},
};
let replace = if replace {
Some(NeedsToReload::No(browsing_context.pipeline_id))
} else {
None
let replace = match replace {
HistoryEntryReplacement::Enabled => {
Some(NeedsToReload::No(browsing_context.pipeline_id))
},
HistoryEntryReplacement::Disabled => None,
};
// https://github.com/rust-lang/rust/issues/59159
@ -2263,7 +2261,7 @@ where
Some(parent_pipeline_id),
None,
browsing_context_size,
load_data,
load_info.load_data,
load_info.sandbox,
is_private,
browsing_context_is_visible,
@ -2280,7 +2278,7 @@ where
fn handle_script_new_iframe(
&mut self,
load_info: IFrameLoadInfo,
load_info: IFrameLoadInfoWithData,
layout_sender: IpcSender<LayoutControlMsg>,
) {
let IFrameLoadInfo {
@ -2290,12 +2288,7 @@ where
top_level_browsing_context_id,
is_private,
..
} = load_info;
let url = ServoUrl::parse("about:blank").expect("infallible");
// TODO: Referrer?
let load_data = LoadData::new(url.clone(), Some(parent_pipeline_id), None, None);
} = load_info.info;
let (script_sender, parent_browsing_context_id) =
match self.pipelines.get(&parent_pipeline_id) {
@ -2321,9 +2314,8 @@ where
script_sender,
layout_sender,
self.compositor_proxy.clone(),
url,
is_parent_visible,
load_data,
load_info.load_data,
);
assert!(!self.pipelines.contains_key(&new_pipeline_id));
@ -2348,17 +2340,13 @@ where
layout_sender: IpcSender<LayoutControlMsg>,
) {
let AuxiliaryBrowsingContextLoadInfo {
load_data,
opener_pipeline_id,
new_top_level_browsing_context_id,
new_browsing_context_id,
new_pipeline_id,
} = 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) =
match self.pipelines.get(&opener_pipeline_id) {
Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
@ -2387,7 +2375,6 @@ where
script_sender,
layout_sender,
self.compositor_proxy.clone(),
url,
is_opener_visible,
load_data,
);
@ -2491,7 +2478,7 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId,
source_id: PipelineId,
load_data: LoadData,
replace: bool,
replace: HistoryEntryReplacement,
) {
match self.pending_approval_navigations.entry(source_id) {
Entry::Occupied(_) => {
@ -2517,13 +2504,15 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId,
source_id: PipelineId,
load_data: LoadData,
replace: bool,
replace: HistoryEntryReplacement,
) -> Option<PipelineId> {
let replace_debug = match replace {
HistoryEntryReplacement::Enabled => "",
HistoryEntryReplacement::Disabled => "not",
};
debug!(
"Loading {} in pipeline {}, {}replacing.",
load_data.url,
source_id,
if replace { "" } else { "not " }
load_data.url, source_id, replace_debug
);
// If this load targets an iframe, its framing element may exist
// in a separate script thread than the framed document that initiated
@ -2564,7 +2553,7 @@ where
Some(parent_pipeline_id) => {
// Find the script thread for the pipeline containing the iframe
// and issue an iframe load through there.
let msg = ConstellationControlMsg::Navigate(
let msg = ConstellationControlMsg::NavigateIframe(
parent_pipeline_id,
browsing_context_id,
load_data,
@ -2607,10 +2596,9 @@ where
// Create the new pipeline
let replace = if replace {
Some(NeedsToReload::No(pipeline_id))
} else {
None
let replace = match replace {
HistoryEntryReplacement::Enabled => Some(NeedsToReload::No(pipeline_id)),
HistoryEntryReplacement::Disabled => None,
};
let new_pipeline_id = PipelineId::new();
@ -2725,7 +2713,7 @@ where
&mut self,
pipeline_id: PipelineId,
new_url: ServoUrl,
replacement_enabled: bool,
replacement_enabled: HistoryEntryReplacement,
) {
let (top_level_browsing_context_id, old_url) = match self.pipelines.get_mut(&pipeline_id) {
Some(pipeline) => {
@ -2740,15 +2728,18 @@ where
},
};
if !replacement_enabled {
let diff = SessionHistoryDiff::HashDiff {
pipeline_reloader: NeedsToReload::No(pipeline_id),
new_url,
old_url,
};
self.get_joint_session_history(top_level_browsing_context_id)
.push_diff(diff);
self.notify_history_changed(top_level_browsing_context_id);
match replacement_enabled {
HistoryEntryReplacement::Disabled => {
let diff = SessionHistoryDiff::HashDiff {
pipeline_reloader: NeedsToReload::No(pipeline_id),
new_url,
old_url,
};
self.get_joint_session_history(top_level_browsing_context_id)
.push_diff(diff);
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) => {
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) => {
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
@ -3407,7 +3403,12 @@ where
Some(pipeline) => pipeline.load_data.clone(),
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) => {
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
@ -3590,7 +3591,7 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId,
load_data: LoadData,
reply: IpcSender<webdriver_msg::LoadStatus>,
replace: bool,
replace: HistoryEntryReplacement,
) {
let browsing_context_id = BrowsingContextId::from(top_level_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,
};
let url = state.load_data.url.clone();
let (script_chan, sampler_chan) = match state.event_loop {
Some(script_chan) => {
let new_layout_info = NewLayoutInfo {
@ -345,7 +343,6 @@ impl Pipeline {
script_chan,
pipeline_chan,
state.compositor_proxy,
url,
state.prev_visibility,
state.load_data,
);
@ -365,7 +362,6 @@ impl Pipeline {
event_loop: Rc<EventLoop>,
layout_chan: IpcSender<LayoutControlMsg>,
compositor_proxy: CompositorProxy,
url: ServoUrl,
is_visible: bool,
load_data: LoadData,
) -> Pipeline {
@ -377,7 +373,7 @@ impl Pipeline {
event_loop: event_loop,
layout_chan: layout_chan,
compositor_proxy: compositor_proxy,
url: url,
url: load_data.url.clone(),
children: vec![],
running_animations: false,
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::NodeBinding::NodeMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
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::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{document_from_node, Node};
use crate::dom::urlhelper::UrlHelper;
use crate::dom::virtualmethods::VirtualMethods;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use net_traits::request::Referrer;
use num_traits::ToPrimitive;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_url::ServoUrl;
use std::default::Default;
use style::attr::AttrValue;
@ -624,8 +628,19 @@ pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) {
// Step 7.
let (maybe_chosen, replace) = match target_attribute_value {
Some(name) => source.choose_browsing_context(name.Value(), noopener),
None => (Some(window.window_proxy()), false),
Some(name) => {
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.
@ -667,7 +682,23 @@ pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) {
};
// Step 14
debug!("following hyperlink to {}", url);
target_window.load_url(url, replace, false, referrer, referrer_policy);
let pipeline_id = target_window.upcast::<GlobalScope>().pipeline_id();
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/. */
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::DocumentBinding::DocumentMethods;
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::HTMLInputElementBinding::HTMLInputElementMethods;
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::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
@ -46,7 +48,6 @@ use crate::dom::node::{UnbindContext, VecPreOrderInsertionHelper};
use crate::dom::validitystate::ValidationFlags;
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window;
use crate::script_thread::MainThreadScriptMsg;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
use encoding_rs::{Encoding, UTF_8};
@ -56,7 +57,7 @@ use hyper::Method;
use mime::{self, Mime};
use net_traits::http_percent_encode;
use net_traits::request::Referrer;
use script_traits::LoadData;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_rand::random;
use std::borrow::ToOwned;
use std::cell::Cell;
@ -399,6 +400,7 @@ impl HTMLFormElement {
};
let target_window = target_document.window();
let mut load_data = LoadData::new(
LoadOrigin::Script(doc.origin().immutable().clone()),
action_components,
None,
Some(Referrer::ReferrerUrl(target_document.url())),
@ -529,7 +531,7 @@ impl HTMLFormElement {
}
/// [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
// Each planned navigation task is tagged with a generation ID, and
// 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);
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 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 window = Trusted::new(target);
let task = task!(navigate_to_form_planned_navigation: move || {
if generation_id != this.root().generation_id.get() {
return;
}
script_chan.send(MainThreadScriptMsg::Navigate(
pipeline_id,
load_data,
false,
)).unwrap();
window
.root()
.load_url(
HistoryEntryReplacement::Disabled,
false,
load_data,
);
});
// Step 3.

View file

@ -37,8 +37,8 @@ use profile_traits::ipc as ProfiledIpc;
use script_layout_interface::message::ReflowGoal;
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
use script_traits::{
IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData, UpdatePipelineIdReason,
WindowSizeData,
HistoryEntryReplacement, IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData,
LoadOrigin, UpdatePipelineIdReason, WindowSizeData,
};
use script_traits::{NewLayoutInfo, ScriptMsg};
use servo_url::ServoUrl;
@ -109,9 +109,9 @@ impl HTMLIFrameElement {
pub fn navigate_or_reload_child_browsing_context(
&self,
mut load_data: Option<LoadData>,
mut load_data: LoadData,
nav_type: NavigationType,
replace: bool,
replace: HistoryEntryReplacement,
) {
let sandboxed = if self.is_sandboxed() {
IFrameSandboxed
@ -136,30 +136,27 @@ impl HTMLIFrameElement {
// document; the new navigation will continue blocking it.
LoadBlocker::terminate(&mut load_blocker);
if let Some(ref mut load_data) = load_data {
let is_javascript = load_data.url.scheme() == "javascript";
if is_javascript {
let window_proxy = self.GetContentWindow();
if let Some(window_proxy) = window_proxy {
ScriptThread::eval_js_url(&window_proxy.global(), load_data);
if load_data.url.scheme() == "javascript" {
let window_proxy = self.GetContentWindow();
if let Some(window_proxy) = window_proxy {
// 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 {
Some(JsEvalResult::NoContent) => (),
_ => {
*load_blocker = Some(LoadBlocker::new(
&*document,
LoadType::Subframe(load_data.url.clone()),
));
},
};
}
match load_data.js_eval_result {
Some(JsEvalResult::NoContent) => (),
_ => {
*load_blocker = Some(LoadBlocker::new(
&*document,
LoadType::Subframe(load_data.url.clone()),
));
},
};
let window = window_from_node(self);
let old_pipeline_id = self.pipeline_id();
@ -182,6 +179,12 @@ impl HTMLIFrameElement {
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
.script_to_constellation_chan()
.send(ScriptMsg::ScriptNewIFrame(load_info, pipeline_sender))
@ -193,7 +196,7 @@ impl HTMLIFrameElement {
browsing_context_id: browsing_context_id,
top_level_browsing_context_id: top_level_browsing_context_id,
opener: None,
load_data: load_data.unwrap(),
load_data: load_data,
pipeline_port: pipeline_receiver,
content_process_shutdown_chan: None,
window_size: WindowSizeData {
@ -270,6 +273,7 @@ impl HTMLIFrameElement {
let document = document_from_node(self);
let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
url,
creator_pipeline_id,
Some(Referrer::ReferrerUrl(document.url())),
@ -281,12 +285,12 @@ impl HTMLIFrameElement {
// 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 = is_about_blank;
self.navigate_or_reload_child_browsing_context(
Some(load_data),
NavigationType::Regular,
replace,
);
let replace = if is_about_blank {
HistoryEntryReplacement::Enabled
} else {
HistoryEntryReplacement::Disabled
};
self.navigate_or_reload_child_browsing_context(load_data, NavigationType::Regular, replace);
}
fn create_nested_browsing_context(&self) {
@ -296,6 +300,7 @@ impl HTMLIFrameElement {
let window = window_from_node(self);
let pipeline_id = Some(window.upcast::<GlobalScope>().pipeline_id());
let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
url,
pipeline_id,
Some(Referrer::ReferrerUrl(document.url().clone())),
@ -309,9 +314,9 @@ impl HTMLIFrameElement {
.set(Some(top_level_browsing_context_id));
self.browsing_context_id.set(Some(browsing_context_id));
self.navigate_or_reload_child_browsing_context(
Some(load_data),
load_data,
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::WindowBinding::WindowBinding::WindowMethods;
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::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
@ -14,6 +15,7 @@ use crate::dom::urlhelper::UrlHelper;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use net_traits::request::Referrer;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_url::{MutableOrigin, ServoUrl};
#[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 {
self.window.get_url()
}
@ -46,7 +71,7 @@ impl Location {
let mut url = self.window.get_url();
let referrer = Referrer::ReferrerUrl(url.clone());
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 {
@ -66,7 +91,7 @@ impl Location {
pub fn reload_without_origin_check(&self) {
let url = self.get_url();
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)]
@ -84,7 +109,7 @@ impl LocationMethods for Location {
let base_url = self.window.get_url();
if let Ok(url) = base_url.join(&url.0) {
let referrer = Referrer::ReferrerUrl(base_url.clone());
self.window.load_url(url, false, false, referrer, None);
self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false);
Ok(())
} else {
Err(Error::Syntax)
@ -96,7 +121,7 @@ impl LocationMethods for Location {
self.check_same_origin_domain()?;
let url = self.get_url();
let referrer = Referrer::ReferrerUrl(url.clone());
self.window.load_url(url, true, true, referrer, None);
self.navigate(url, referrer, HistoryEntryReplacement::Enabled, true);
Ok(())
}
@ -108,7 +133,7 @@ impl LocationMethods for Location {
let base_url = self.window.get_url();
if let Ok(url) = base_url.join(&url.0) {
let referrer = Referrer::ReferrerUrl(base_url.clone());
self.window.load_url(url, true, false, referrer, None);
self.navigate(url, referrer, HistoryEntryReplacement::Enabled, false);
Ok(())
} else {
Err(Error::Syntax)
@ -178,7 +203,7 @@ impl LocationMethods for Location {
Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))),
};
let referrer = Referrer::ReferrerUrl(current_url.clone());
self.window.load_url(url, false, false, referrer, None);
self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false);
Ok(())
}

View file

@ -89,9 +89,8 @@ use media::WindowGLContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
use net_traits::image_cache::{PendingImageId, PendingImageResponse};
use net_traits::request::Referrer;
use net_traits::storage_thread::StorageType;
use net_traits::{ReferrerPolicy, ResourceThreads};
use net_traits::ResourceThreads;
use num_traits::ToPrimitive;
use profile_traits::ipc as ProfiledIpc;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
@ -103,7 +102,7 @@ use script_layout_interface::rpc::{
};
use script_layout_interface::{PendingImageState, TrustedNodeAddress};
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::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
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.
///
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
pub fn load_url(
&self,
url: ServoUrl,
replace: bool,
replace: HistoryEntryReplacement,
force_reload: bool,
referrer: Referrer,
referrer_policy: Option<ReferrerPolicy>,
load_data: LoadData,
) {
let doc = self.Document();
let referrer_policy = referrer_policy.or(doc.get_referrer_policy());
// https://html.spec.whatwg.org/multipage/#navigating-across-documents
// TODO: Important re security. See https://github.com/servo/servo/issues/23373
// Step 3: check that the source browsing-context is "allowed to navigate" this window.
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
if let Some(fragment) = url.fragment() {
self.send_to_constellation(ScriptMsg::NavigatedToFragment(url.clone(), replace));
if let Some(fragment) = load_data.url.fragment() {
self.send_to_constellation(ScriptMsg::NavigatedToFragment(
load_data.url.clone(),
replace,
));
doc.check_and_scroll_fragment(fragment);
let this = Trusted::new(self);
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 this = this.root();
let event = HashChangeEvent::new(
@ -1813,7 +1816,7 @@ impl Window {
self.pipeline_id(),
TaskSourceName::DOMManipulation,
));
doc.set_url(url.clone());
doc.set_url(load_data.url.clone());
return;
}
}
@ -1838,13 +1841,9 @@ impl Window {
// then put it in the delaying load events mode.
self.window_proxy().start_delaying_load_events_mode();
}
self.main_thread_script_chan()
.send(MainThreadScriptMsg::Navigate(
pipeline_id,
LoadData::new(url, Some(pipeline_id), Some(referrer), referrer_policy),
replace,
))
.unwrap();
// TODO: step 11, navigationType.
// Step 12, 13
ScriptThread::navigate(pipeline_id, load_data, replace);
};
}

View file

@ -47,7 +47,10 @@ use msg::constellation_msg::BrowsingContextId;
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::TopLevelBrowsingContextId;
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 std::cell::Cell;
use std::ptr;
@ -268,24 +271,28 @@ impl WindowProxy {
let new_browsing_context_id =
BrowsingContextId::from(new_top_level_browsing_context_id);
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
.currently_active
.get()
.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 load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()),
blank_url,
None,
Some(Referrer::ReferrerUrl(document.url().clone())),
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 new_layout_info = NewLayoutInfo {
parent_info: None,
@ -437,13 +444,21 @@ impl WindowProxy {
Referrer::Client
};
// 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,
new,
false,
referrer,
target_document.get_referrer_policy(),
Some(pipeline_id),
Some(referrer),
referrer_policy,
);
let replacement_flag = if new {
HistoryEntryReplacement::Enabled
} else {
HistoryEntryReplacement::Disabled
};
target_window.load_url(replacement_flag, false, load_data);
}
if noopener {
// 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::num::Finite;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::ThreadLocalStackRoots;
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::user_interaction::UserInteractionTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
use crate::task_source::TaskSource;
use crate::task_source::TaskSourceName;
use crate::webdriver_handlers;
use bluetooth_traits::BluetoothRequest;
@ -129,8 +131,10 @@ use script_traits::CompositorEvent::{
WheelEvent,
};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult};
use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData};
use script_traits::{
DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement,
};
use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData, LoadOrigin};
use script_traits::{MouseButton, MouseEventType, NewLayoutInfo};
use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory};
use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg};
@ -264,10 +268,6 @@ enum MixedMessage {
pub enum MainThreadScriptMsg {
/// Common variants associated with the script messages
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
/// reflowed.
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) {
SCRIPT_THREAD_ROOT.with(|root| {
if let Some(script_thread) = root.get() {
@ -1563,7 +1631,7 @@ impl ScriptThread {
SetDocumentActivity(id, ..) => Some(id),
ChangeFrameVisibilityStatus(id, ..) => Some(id),
NotifyVisibilityChange(id, ..) => Some(id),
Navigate(id, ..) => Some(id),
NavigateIframe(id, ..) => Some(id),
PostMessage { target: id, .. } => Some(id),
UpdatePipelineId(_, _, _, id, _) => Some(id),
UpdateHistoryState(id, ..) => Some(id),
@ -1593,7 +1661,6 @@ impl ScriptThread {
pipeline_id
},
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
MainThreadScriptMsg::Navigate(pipeline_id, ..) => Some(pipeline_id),
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
MainThreadScriptMsg::DispatchJobQueue { .. } => None,
@ -1703,14 +1770,14 @@ impl ScriptThread {
_ => unreachable!(),
};
},
ConstellationControlMsg::Navigate(
ConstellationControlMsg::NavigateIframe(
parent_pipeline_id,
browsing_context_id,
load_data,
replace,
) => self.handle_navigate(
) => self.handle_navigate_iframe(
parent_pipeline_id,
Some(browsing_context_id),
browsing_context_id,
load_data,
replace,
),
@ -1825,9 +1892,6 @@ impl ScriptThread {
fn handle_msg_from_script(&self, msg: MainThreadScriptMsg) {
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::CollectReports(chan)) => {
self.collect_reports(chan)
@ -3352,50 +3416,30 @@ impl ScriptThread {
document.handle_wheel_event(self.js_runtime.rt(), wheel_delta, point, node_address);
}
/// <https://html.spec.whatwg.org/multipage/#navigating-across-documents>
/// The entry point for content to notify that a new load has been requested
/// for the given pipeline (specifically the "navigate" algorithm).
fn handle_navigate(
/// Handle a "navigate an iframe" message from the constellation.
fn handle_navigate_iframe(
&self,
parent_pipeline_id: PipelineId,
browsing_context_id: Option<BrowsingContextId>,
mut load_data: LoadData,
replace: bool,
browsing_context_id: BrowsingContextId,
load_data: LoadData,
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
.documents
.borrow()
.find_iframe(parent_pipeline_id, browsing_context_id);
if let Some(iframe) = iframe {
iframe.navigate_or_reload_child_browsing_context(
Some(load_data),
NavigationType::Regular,
replace,
);
}
},
None => {
self.script_sender
.send((parent_pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
.unwrap();
},
let iframe = self
.documents
.borrow()
.find_iframe(parent_pipeline_id, browsing_context_id);
if let Some(iframe) = iframe {
iframe.navigate_or_reload_child_browsing_context(
load_data,
NavigationType::Regular,
replace,
);
}
}
/// 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) {
// 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.):
// Start with the scheme data of the parsed URL;
// 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};
pub use crate::script_msg::{
DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerMsg,
DOMMessage, HistoryEntryReplacement, SWManagerMsg, SWManagerSenders, ScopeThings,
ServiceWorkerMsg,
};
pub use crate::script_msg::{
EventResult, IFrameSize, IFrameSizeMsg, LayoutMsg, LogEntry, ScriptMsg,
@ -118,10 +119,24 @@ pub enum LayoutControlMsg {
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
/// parameters or headers
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LoadData {
/// The origin where the load started.
pub load_origin: LoadOrigin,
/// The URL.
pub url: ServoUrl,
/// The creator pipeline id if this is an about:blank load.
@ -161,12 +176,14 @@ pub enum JsEvalResult {
impl LoadData {
/// Create a new `LoadData` object.
pub fn new(
load_origin: LoadOrigin,
url: ServoUrl,
creator_pipeline_id: Option<PipelineId>,
referrer: Option<Referrer>,
referrer_policy: Option<ReferrerPolicy>,
) -> LoadData {
LoadData {
load_origin,
url: url,
creator_pipeline_id: creator_pipeline_id,
method: Method::GET,
@ -290,7 +307,12 @@ pub enum ConstellationControlMsg {
NotifyVisibilityChange(PipelineId, BrowsingContextId, bool),
/// Notifies script thread that a url should be loaded in this iframe.
/// 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.
PostMessage {
/// The target of the message.
@ -377,7 +399,7 @@ impl fmt::Debug for ConstellationControlMsg {
SetDocumentActivity(..) => "SetDocumentActivity",
ChangeFrameVisibilityStatus(..) => "ChangeFrameVisibilityStatus",
NotifyVisibilityChange(..) => "NotifyVisibilityChange",
Navigate(..) => "Navigate",
NavigateIframe(..) => "NavigateIframe",
PostMessage { .. } => "PostMessage",
UpdatePipelineId(..) => "UpdatePipelineId",
UpdateHistoryState(..) => "UpdateHistoryState",
@ -673,6 +695,8 @@ pub enum IFrameSandboxState {
/// Specifies the information required to load an auxiliary browsing context.
#[derive(Debug, Deserialize, Serialize)]
pub struct AuxiliaryBrowsingContextLoadInfo {
/// Load data containing the url to load
pub load_data: LoadData,
/// The pipeline opener browsing context.
pub opener_pipeline_id: PipelineId,
/// The new top-level ID for the auxiliary.
@ -698,7 +722,7 @@ pub struct IFrameLoadInfo {
pub is_private: bool,
/// Wether this load should replace the current entry (reload). If true, the current
/// 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.
@ -707,7 +731,7 @@ pub struct IFrameLoadInfoWithData {
/// The information required to load an iframe.
pub info: IFrameLoadInfo,
/// 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.
pub old_pipeline_id: Option<PipelineId>,
/// Sandbox type of this iframe

View file

@ -5,7 +5,6 @@
use crate::AnimationState;
use crate::AuxiliaryBrowsingContextLoadInfo;
use crate::DocumentState;
use crate::IFrameLoadInfo;
use crate::IFrameLoadInfoWithData;
use crate::LayoutControlMsg;
use crate::LoadData;
@ -97,6 +96,15 @@ pub enum LogEntry {
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.
#[derive(Deserialize, Serialize)]
pub enum ScriptMsg {
@ -143,7 +151,7 @@ pub enum ScriptMsg {
LoadComplete,
/// A new load has been requested, with an option to replace the current entry once loaded
/// instead of adding a new entry.
LoadUrl(LoadData, bool),
LoadUrl(LoadData, HistoryEntryReplacement),
/// Abort loading after sending a LoadUrl message.
AbortLoadUrl,
/// Post a message to the currently active window of a given browsing context.
@ -158,7 +166,7 @@ pub enum ScriptMsg {
data: Vec<u8>,
},
/// 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.
TraverseHistory(TraversalDirection),
/// Inform the constellation of a pushed history state.
@ -175,7 +183,7 @@ pub enum ScriptMsg {
/// A load has been requested in an IFrame.
ScriptLoadedURLInIFrame(IFrameLoadInfoWithData),
/// 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.
ScriptNewAuxiliary(
AuxiliaryBrowsingContextLoadInfo,

View file

@ -30,7 +30,7 @@ use script_traits::webdriver_msg::{LoadStatus, WebDriverCookieError, WebDriverFr
use script_traits::webdriver_msg::{
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::ser::{Serialize, Serializer};
use serde_json::{json, Value};
@ -593,7 +593,7 @@ impl Handler {
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 =
WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, sender.clone());
self.constellation_chan

View file

@ -631153,7 +631153,7 @@
"testharness"
],
"html/semantics/scripting-1/the-script-element/execution-timing/029.html": [
"c74665ec1e5f37d4e9ec0ecc65f626e79d4942d4",
"33548e566ac67823f9c19af7785a13e394c4964b",
"testharness"
],
"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>
<p><a href="javascript:log('JS URL')"></a></p>
<script>log('inline script #1');
window.addEventListener("beforeunload", function( event ) {
log('beforeunload event');
});
if(document.links[0].click){
document.links[0].click();
}else{
@ -25,13 +28,26 @@
log( 'inline script #2' );
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, [
['inline script #1', 'end script #1', 'JS URL', 'inline script #2'],
['inline script #1', 'end script #1', 'inline script #2', 'JS URL']]);
['inline script #1', 'end script #1', 'beforeunload event', 'inline script #2', 'JS URL'],
['inline script #1', 'end script #1', 'inline script #2', 'beforeunload event', 'JS URL']]);
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>
</body></html>