mirror of
https://github.com/servo/servo.git
synced 2025-08-02 04:00:32 +01:00
"javascript:" urls: run in correct global
Make some changes to javascript scheme url evaluation to bring it closer to https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigating-across-documents - Evaluate the js before the page load so that it happens in the correct `window` global. - If the result is not a string, the response should be a 204. This required saving some data in load_data, since it's not possible to modify the response at the point where we're evaluating the js.
This commit is contained in:
parent
20c73e7f7d
commit
fa3e9ab244
2 changed files with 93 additions and 44 deletions
|
@ -87,8 +87,8 @@ use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
|
||||||
use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
|
use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
|
||||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||||
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
|
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
|
||||||
use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
|
use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData, MouseButton, MouseEventType};
|
||||||
use script_traits::{NewLayoutInfo, ScriptToConstellationChan, ScriptMsg, UpdatePipelineIdReason};
|
use script_traits::{MozBrowserEvent, NewLayoutInfo, ScriptToConstellationChan, ScriptMsg, UpdatePipelineIdReason};
|
||||||
use script_traits::{ScriptThreadFactory, TimerEvent, TimerSchedulerMsg, TimerSource};
|
use script_traits::{ScriptThreadFactory, TimerEvent, TimerSchedulerMsg, TimerSource};
|
||||||
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||||
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
|
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
|
||||||
|
@ -1506,7 +1506,7 @@ impl ScriptThread {
|
||||||
load_data.url.clone(),
|
load_data.url.clone(),
|
||||||
origin);
|
origin);
|
||||||
if load_data.url.as_str() == "about:blank" {
|
if load_data.url.as_str() == "about:blank" {
|
||||||
self.start_page_load_about_blank(new_load);
|
self.start_page_load_about_blank(new_load, load_data.js_eval_result);
|
||||||
} else {
|
} else {
|
||||||
self.pre_page_load(new_load, load_data);
|
self.pre_page_load(new_load, load_data);
|
||||||
}
|
}
|
||||||
|
@ -1677,6 +1677,18 @@ impl ScriptThread {
|
||||||
match idx {
|
match idx {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
let load = self.incomplete_loads.borrow_mut().remove(idx);
|
let load = self.incomplete_loads.borrow_mut().remove(idx);
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#process-a-navigate-response
|
||||||
|
// 2. If response's status is 204 or 205, then abort these steps.
|
||||||
|
match metadata {
|
||||||
|
Some(Metadata { status: Some((204 ... 205, _)), .. }) => {
|
||||||
|
// TODO: This leaves the page in a broken state where you can't follow
|
||||||
|
// other links. Fix this.
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
|
||||||
metadata.map(|meta| self.load(meta, load))
|
metadata.map(|meta| self.load(meta, load))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -2116,39 +2128,7 @@ impl ScriptThread {
|
||||||
// Notify devtools that a new script global exists.
|
// Notify devtools that a new script global exists.
|
||||||
self.notify_devtools(document.Title(), final_url.clone(), (incomplete.pipeline_id, None));
|
self.notify_devtools(document.Title(), final_url.clone(), (incomplete.pipeline_id, None));
|
||||||
|
|
||||||
let is_javascript = incomplete.url.scheme() == "javascript";
|
let parse_input = DOMString::new();
|
||||||
let parse_input = if is_javascript {
|
|
||||||
use url::percent_encoding::percent_decode;
|
|
||||||
|
|
||||||
// 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 URL’s serialization is equivalent to (5.) to (7.):
|
|
||||||
// Start with the scheme data of the parsed URL;
|
|
||||||
// append question mark and query component, if any;
|
|
||||||
// append number sign and fragment component if any.
|
|
||||||
let encoded = &incomplete.url[Position::BeforePath..];
|
|
||||||
|
|
||||||
// Percent-decode (8.) and UTF-8 decode (9.)
|
|
||||||
let script_source = percent_decode(encoded.as_bytes()).decode_utf8_lossy();
|
|
||||||
|
|
||||||
// Script source is ready to be evaluated (11.)
|
|
||||||
unsafe {
|
|
||||||
let _ac = JSAutoCompartment::new(self.get_cx(), window.reflector().get_jsobject().get());
|
|
||||||
rooted!(in(self.get_cx()) let mut jsval = UndefinedValue());
|
|
||||||
window.upcast::<GlobalScope>().evaluate_js_on_global_with_result(
|
|
||||||
&script_source, jsval.handle_mut());
|
|
||||||
let strval = DOMString::from_jsval(self.get_cx(),
|
|
||||||
jsval.handle(),
|
|
||||||
StringificationBehavior::Empty);
|
|
||||||
match strval {
|
|
||||||
Ok(ConversionResult::Success(s)) => s,
|
|
||||||
_ => DOMString::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DOMString::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
document.set_https_state(metadata.https_state);
|
document.set_https_state(metadata.https_state);
|
||||||
|
|
||||||
|
@ -2327,7 +2307,7 @@ impl ScriptThread {
|
||||||
/// for the given pipeline (specifically the "navigate" algorithm).
|
/// for the given pipeline (specifically the "navigate" algorithm).
|
||||||
fn handle_navigate(&self, parent_pipeline_id: PipelineId,
|
fn handle_navigate(&self, parent_pipeline_id: PipelineId,
|
||||||
browsing_context_id: Option<BrowsingContextId>,
|
browsing_context_id: Option<BrowsingContextId>,
|
||||||
load_data: LoadData,
|
mut load_data: LoadData,
|
||||||
replace: bool) {
|
replace: bool) {
|
||||||
match browsing_context_id {
|
match browsing_context_id {
|
||||||
Some(browsing_context_id) => {
|
Some(browsing_context_id) => {
|
||||||
|
@ -2335,8 +2315,56 @@ impl ScriptThread {
|
||||||
if let Some(iframe) = iframe {
|
if let Some(iframe) = iframe {
|
||||||
iframe.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, replace);
|
iframe.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Test javascript: urls in iframes
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
let is_javascript = load_data.url.scheme() == "javascript";
|
||||||
|
if is_javascript {
|
||||||
|
use url::percent_encoding::percent_decode;
|
||||||
|
|
||||||
|
// 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 URL’s serialization is equivalent to (5.) to (7.):
|
||||||
|
// Start with the scheme data of the parsed URL;
|
||||||
|
// append question mark and query component, if any;
|
||||||
|
// append number sign and fragment component if any.
|
||||||
|
let encoded = &load_data.url[Position::BeforePath..];
|
||||||
|
|
||||||
|
// Percent-decode (8.) and UTF-8 decode (9.)
|
||||||
|
let script_source = percent_decode(encoded.as_bytes()).decode_utf8_lossy();
|
||||||
|
|
||||||
|
// Script source is ready to be evaluated (11.)
|
||||||
|
let window = self.documents.borrow().find_window(parent_pipeline_id);
|
||||||
|
if let Some(window) = window {
|
||||||
|
let _ac = JSAutoCompartment::new(self.get_cx(), window.reflector().get_jsobject().get());
|
||||||
|
rooted!(in(self.get_cx()) let mut jsval = UndefinedValue());
|
||||||
|
window.upcast::<GlobalScope>().evaluate_js_on_global_with_result(
|
||||||
|
&script_source, jsval.handle_mut());
|
||||||
|
|
||||||
|
load_data.js_eval_result = if jsval.get().is_string() {
|
||||||
|
unsafe {
|
||||||
|
let strval = DOMString::from_jsval(self.get_cx(),
|
||||||
|
jsval.handle(),
|
||||||
|
StringificationBehavior::Empty);
|
||||||
|
match strval {
|
||||||
|
Ok(ConversionResult::Success(s)) => {
|
||||||
|
Some(JsEvalResult::Ok(String::from(s).as_bytes().to_vec()))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(JsEvalResult::NoContent)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_javascript {
|
||||||
|
load_data.url = ServoUrl::parse("about:blank").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
self.script_sender
|
self.script_sender
|
||||||
.send((parent_pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
|
.send((parent_pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -2375,7 +2403,7 @@ impl ScriptThread {
|
||||||
/// argument until a notification is received that the fetch is complete.
|
/// argument until a notification is received that the fetch is complete.
|
||||||
fn pre_page_load(&self, incomplete: InProgressLoad, load_data: LoadData) {
|
fn pre_page_load(&self, incomplete: InProgressLoad, load_data: LoadData) {
|
||||||
let id = incomplete.pipeline_id.clone();
|
let id = incomplete.pipeline_id.clone();
|
||||||
let mut req_init = RequestInit {
|
let req_init = RequestInit {
|
||||||
url: load_data.url.clone(),
|
url: load_data.url.clone(),
|
||||||
method: load_data.method,
|
method: load_data.method,
|
||||||
destination: Destination::Document,
|
destination: Destination::Document,
|
||||||
|
@ -2391,10 +2419,6 @@ impl ScriptThread {
|
||||||
.. RequestInit::default()
|
.. RequestInit::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
if req_init.url.scheme() == "javascript" {
|
|
||||||
req_init.url = ServoUrl::parse("about:blank").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let context = ParserContext::new(id, load_data.url);
|
let context = ParserContext::new(id, load_data.url);
|
||||||
self.incomplete_parser_contexts.borrow_mut().push((id, context));
|
self.incomplete_parser_contexts.borrow_mut().push((id, context));
|
||||||
|
|
||||||
|
@ -2434,7 +2458,7 @@ impl ScriptThread {
|
||||||
|
|
||||||
/// Synchronously fetch `about:blank`. Stores the `InProgressLoad`
|
/// Synchronously fetch `about:blank`. Stores the `InProgressLoad`
|
||||||
/// argument until a notification is received that the fetch is complete.
|
/// argument until a notification is received that the fetch is complete.
|
||||||
fn start_page_load_about_blank(&self, incomplete: InProgressLoad) {
|
fn start_page_load_about_blank(&self, incomplete: InProgressLoad, js_eval_result: Option<JsEvalResult>) {
|
||||||
let id = incomplete.pipeline_id;
|
let id = incomplete.pipeline_id;
|
||||||
|
|
||||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||||
|
@ -2444,8 +2468,20 @@ impl ScriptThread {
|
||||||
|
|
||||||
let mut meta = Metadata::default(url);
|
let mut meta = Metadata::default(url);
|
||||||
meta.set_content_type(Some(&mime!(Text / Html)));
|
meta.set_content_type(Some(&mime!(Text / Html)));
|
||||||
|
|
||||||
|
// If this page load is the result of a javascript scheme url, map
|
||||||
|
// the evaluation result into a response.
|
||||||
|
let chunk = match js_eval_result {
|
||||||
|
Some(JsEvalResult::Ok(content)) => content,
|
||||||
|
Some(JsEvalResult::NoContent) => {
|
||||||
|
meta.status = Some((204, b"No Content".to_vec()));
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
None => vec![]
|
||||||
|
};
|
||||||
|
|
||||||
context.process_response(Ok(FetchMetadata::Unfiltered(meta)));
|
context.process_response(Ok(FetchMetadata::Unfiltered(meta)));
|
||||||
context.process_response_chunk(vec![]);
|
context.process_response_chunk(chunk);
|
||||||
context.process_response_eof(Ok(()));
|
context.process_response_eof(Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,12 +146,24 @@ pub struct LoadData {
|
||||||
pub headers: Headers,
|
pub headers: Headers,
|
||||||
/// The data.
|
/// The data.
|
||||||
pub data: Option<Vec<u8>>,
|
pub data: Option<Vec<u8>>,
|
||||||
|
/// The result of evaluating a javascript scheme url.
|
||||||
|
pub js_eval_result: Option<JsEvalResult>,
|
||||||
/// The referrer policy.
|
/// The referrer policy.
|
||||||
pub referrer_policy: Option<ReferrerPolicy>,
|
pub referrer_policy: Option<ReferrerPolicy>,
|
||||||
/// The referrer URL.
|
/// The referrer URL.
|
||||||
pub referrer_url: Option<ServoUrl>,
|
pub referrer_url: Option<ServoUrl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The result of evaluating a javascript scheme url.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub enum JsEvalResult {
|
||||||
|
/// The js evaluation had a non-string result, 204 status code.
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#navigate 12.11
|
||||||
|
NoContent,
|
||||||
|
/// The js evaluation had a string result.
|
||||||
|
Ok(Vec<u8>)
|
||||||
|
}
|
||||||
|
|
||||||
impl LoadData {
|
impl LoadData {
|
||||||
/// Create a new `LoadData` object.
|
/// Create a new `LoadData` object.
|
||||||
pub fn new(url: ServoUrl,
|
pub fn new(url: ServoUrl,
|
||||||
|
@ -165,6 +177,7 @@ impl LoadData {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
headers: Headers::new(),
|
headers: Headers::new(),
|
||||||
data: None,
|
data: None,
|
||||||
|
js_eval_result: None,
|
||||||
referrer_policy: referrer_policy,
|
referrer_policy: referrer_policy,
|
||||||
referrer_url: referrer_url,
|
referrer_url: referrer_url,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue