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

@ -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;