From f566a8d44f4f39b4cd085ce791c113ddf434dd22 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 21 May 2016 18:05:42 -0400 Subject: [PATCH 1/8] Update step annotations for prepare a script --- components/script/dom/document.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 15cb50aa466..50eeeeeb3d5 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1520,7 +1520,7 @@ impl Document { } /// https://html.spec.whatwg.org/multipage/#the-end step 5 and the latter parts of - /// https://html.spec.whatwg.org/multipage/#prepare-a-script 15.d and 15.e. + /// https://html.spec.whatwg.org/multipage/#prepare-a-script 20.d and 20.e. pub fn process_asap_scripts(&self) { // Execute the first in-order asap-executed script if it's ready, repeat as required. // Re-borrowing the list for each step because it can also be borrowed under execute. From 4c616dad9093b5320ee27629c89c57da038d6705 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 24 Jun 2016 14:31:00 +0800 Subject: [PATCH 2/8] Add crossorigin attribute and implement step 14 of prepare a script Add WPT test for HTMLScriptElement crossOrigin IDL attribute --- components/script/dom/htmlscriptelement.rs | 29 +++ .../dom/webidls/HTMLScriptElement.webidl | 2 +- tests/wpt/metadata/MANIFEST.json | 12 + .../wpt/metadata/html/dom/interfaces.html.ini | 6 - .../html/dom/reflection-misc.html.ini | 219 ------------------ .../resources/cross-origin.py | 10 + .../script-crossorigin-network.html | 49 ++++ .../script-crossorigin.html | 39 ++++ 8 files changed, 140 insertions(+), 226 deletions(-) create mode 100644 tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py create mode 100644 tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.html create mode 100644 tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 11f4451ad50..a583ddc2137 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -5,6 +5,7 @@ use document_loader::LoadType; use dom::attr::Attr; use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::HTMLScriptElementBinding; use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods; @@ -30,6 +31,7 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; +use net_traits::request::CORSSettings; use network_listener::{NetworkListener, PreInvoke}; use std::ascii::AsciiExt; use std::cell::Cell; @@ -358,6 +360,7 @@ impl HTMLScriptElement { true }, + // TODO: Step 19. None => false, }; @@ -652,6 +655,32 @@ impl HTMLScriptElementMethods for HTMLScriptElement { // https://html.spec.whatwg.org/multipage/#dom-script-htmlfor make_setter!(SetHtmlFor, "for"); + // https://html.spec.whatwg.org/multipage/#dom-script-crossorigin + fn GetCrossOrigin(&self) -> Option { + let element = self.upcast::(); + let attr = element.get_attribute(&ns!(), &atom!("crossorigin")); + + if let Some(mut val) = attr.map(|v| v.Value()) { + val.make_ascii_lowercase(); + if val == "anonymous" || val == "use-credentials" { + return Some(val); + } + return Some(DOMString::from("anonymous")); + } + None + } + + // https://html.spec.whatwg.org/multipage/#dom-script-crossorigin + fn SetCrossOrigin(&self, value: Option) { + let element = self.upcast::(); + match value { + Some(val) => element.set_string_attribute(&atom!("crossorigin"), val), + None => { + element.remove_attribute(&ns!(), &atom!("crossorigin")); + } + } + } + // https://html.spec.whatwg.org/multipage/#dom-script-text fn Text(&self) -> DOMString { Node::collect_text_contents(self.upcast::().children()) diff --git a/components/script/dom/webidls/HTMLScriptElement.webidl b/components/script/dom/webidls/HTMLScriptElement.webidl index 17df2e7e08b..1636df7a12b 100644 --- a/components/script/dom/webidls/HTMLScriptElement.webidl +++ b/components/script/dom/webidls/HTMLScriptElement.webidl @@ -10,7 +10,7 @@ interface HTMLScriptElement : HTMLElement { attribute DOMString charset; // attribute boolean async; attribute boolean defer; - // attribute DOMString crossOrigin; + attribute DOMString? crossOrigin; [Pure] attribute DOMString text; diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index ad6f50c06f5..93cdc730d5e 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -37505,6 +37505,18 @@ "path": "html/semantics/forms/the-input-element/minlength.html", "url": "/html/semantics/forms/the-input-element/minlength.html" } + ], + "html/semantics/scripting-1/the-script-element/script-crossorigin-network.html": [ + { + "path": "html/semantics/scripting-1/the-script-element/script-crossorigin-network.html", + "url": "/html/semantics/scripting-1/the-script-element/script-crossorigin-network.html" + } + ], + "html/semantics/scripting-1/the-script-element/script-crossorigin.html": [ + { + "path": "html/semantics/scripting-1/the-script-element/script-crossorigin.html", + "url": "/html/semantics/scripting-1/the-script-element/script-crossorigin.html" + } ] } }, diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 240d96a350b..6600612366f 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -4359,15 +4359,9 @@ [HTMLScriptElement interface: attribute async] expected: FAIL - [HTMLScriptElement interface: attribute crossOrigin] - expected: FAIL - [HTMLScriptElement interface: document.createElement("script") must inherit property "async" with the proper type (3)] expected: FAIL - [HTMLScriptElement interface: document.createElement("script") must inherit property "crossOrigin" with the proper type (5)] - expected: FAIL - [HTMLCanvasElement interface: operation probablySupportsContext(DOMString,any)] expected: FAIL diff --git a/tests/wpt/metadata/html/dom/reflection-misc.html.ini b/tests/wpt/metadata/html/dom/reflection-misc.html.ini index 953d81ccd68..bfe086b35fd 100644 --- a/tests/wpt/metadata/html/dom/reflection-misc.html.ini +++ b/tests/wpt/metadata/html/dom/reflection-misc.html.ini @@ -1317,222 +1317,6 @@ [script.tabIndex: IDL set to -2147483648 followed by getAttribute()] expected: FAIL - [script.crossOrigin: typeof IDL attribute] - expected: FAIL - - [script.crossOrigin: IDL get with DOM attribute unset] - expected: FAIL - - [script.crossOrigin: setAttribute() to "" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo " followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to undefined followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to 7 followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to 1.5 followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to true followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to false followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to object "[object Object\]" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to NaN followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to Infinity followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to -Infinity followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "\\0" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to null followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to object "test-toString" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to object "test-valueOf" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "anonymous" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "xanonymous" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "anonymous\\0" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "nonymous" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "ANONYMOUS" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "use-credentials" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "xuse-credentials" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "use-credentials\\0" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "se-credentials" followed by IDL get] - expected: FAIL - - [script.crossOrigin: setAttribute() to "USE-CREDENTIALS" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo " followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo " followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to undefined followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to undefined followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to 7 followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to 7 followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to 1.5 followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to 1.5 followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to true followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to true followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to false followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to false followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to object "[object Object\]" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to object "[object Object\]" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to NaN followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to NaN followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to Infinity followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to Infinity followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to -Infinity followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to -Infinity followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "\\0" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "\\0" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to object "test-toString" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to object "test-toString" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to object "test-valueOf" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to object "test-valueOf" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "anonymous" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "xanonymous" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "xanonymous" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "anonymous\\0" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "anonymous\\0" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "nonymous" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "nonymous" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "ANONYMOUS" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "ANONYMOUS" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "use-credentials" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "xuse-credentials" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "xuse-credentials" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "use-credentials\\0" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "use-credentials\\0" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "se-credentials" followed by getAttribute()] - expected: FAIL - - [script.crossOrigin: IDL set to "se-credentials" followed by IDL get] - expected: FAIL - - [script.crossOrigin: IDL set to "USE-CREDENTIALS" followed by IDL get] - expected: FAIL - [script.itemScope: typeof IDL attribute] expected: FAIL @@ -12447,6 +12231,3 @@ [dialog.itemId: IDL set to object "test-valueOf" followed by IDL get] expected: FAIL - [script.crossOrigin: IDL set to null followed by getAttribute()] - expected: FAIL - diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py new file mode 100644 index 00000000000..f8e05d96627 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py @@ -0,0 +1,10 @@ +def main(request, response): + headers = [("Content-Type", "text/javascript")] + milk = request.cookies.first("milk", None) + + if milk is None: + return headers, "var included = false;" + elif milk.value == "yes": + return headers, "var included = true;" + + return headers, "var included = false;" diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.html b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.html new file mode 100644 index 00000000000..488dd4488a4 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.html @@ -0,0 +1,49 @@ + + +HTMLScriptElement: crossorigin attribute network test + + + + + + + + diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html new file mode 100644 index 00000000000..52857a08ea0 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html @@ -0,0 +1,39 @@ + + +HTMLScriptElement: crossOrigin IDL attribute + + + + + + + + + + From 5729a4a3d42baadf6e903177658eea7324c39597 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 24 Jun 2016 23:38:30 +0800 Subject: [PATCH 3/8] Fix incorrect indentation --- components/script/dom/xmlhttprequest.rs | 60 ++++++++++++++----------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index d034b60aebd..e546b92b4f1 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -219,35 +219,39 @@ impl XMLHttpRequest { core_resource_thread: CoreResourceThread, init: RequestInit) { impl FetchResponseListener for XHRContext { - fn process_request_body(&mut self) { - // todo - } - fn process_request_eof(&mut self) { - // todo - } - fn process_response(&mut self, metadata: Result) { - let xhr = self.xhr.root(); - let rv = xhr.process_headers_available(self.gen_id, - metadata); - if rv.is_err() { - *self.sync_status.borrow_mut() = Some(rv); - } - } - fn process_response_chunk(&mut self, mut chunk: Vec) { - self.buf.borrow_mut().append(&mut chunk); - self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); - } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { - let rv = match response { - Ok(()) => { - self.xhr.root().process_response_complete(self.gen_id, Ok(())) - } - Err(e) => { - self.xhr.root().process_response_complete(self.gen_id, Err(e)) - } - }; + fn process_request_body(&mut self) { + // todo + } + + fn process_request_eof(&mut self) { + // todo + } + + fn process_response(&mut self, metadata: Result) { + let xhr = self.xhr.root(); + let rv = xhr.process_headers_available(self.gen_id, + metadata); + if rv.is_err() { *self.sync_status.borrow_mut() = Some(rv); } + } + + fn process_response_chunk(&mut self, mut chunk: Vec) { + self.buf.borrow_mut().append(&mut chunk); + self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); + } + + fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + let rv = match response { + Ok(()) => { + self.xhr.root().process_response_complete(self.gen_id, Ok(())) + } + Err(e) => { + self.xhr.root().process_response_complete(self.gen_id, Err(e)) + } + }; + *self.sync_status.borrow_mut() = Some(rv); + } } impl PreInvoke for XHRContext { @@ -273,9 +277,11 @@ impl LoadOrigin for XMLHttpRequest { fn referrer_url(&self) -> Option { return self.referrer_url.clone(); } + fn referrer_policy(&self) -> Option { return self.referrer_policy; } + fn pipeline_id(&self) -> Option { let global = self.global(); Some(global.r().pipeline_id()) From 2bb95989c52721a0260aa687d604733f0eb17af6 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 6 Jul 2016 22:23:22 +0800 Subject: [PATCH 4/8] Implement default values for RequestInit --- components/net_traits/request.rs | 81 +++++++++---------------- components/script/dom/xmlhttprequest.rs | 1 + 2 files changed, 30 insertions(+), 52 deletions(-) diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index 1ed8950bf2d..c31f821680d 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -6,6 +6,7 @@ use hyper::header::Headers; use hyper::method::Method; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use std::cell::{Cell, RefCell}; +use std::default::Default; use std::mem::swap; use url::{Origin as UrlOrigin, Url}; @@ -20,7 +21,7 @@ pub enum Initiator { } /// A request [type](https://fetch.spec.whatwg.org/#concept-request-type) -#[derive(Copy, Clone, PartialEq, HeapSizeOf)] +#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)] pub enum Type { None, Audio, Font, Image, Script, Style, Track, Video @@ -103,7 +104,7 @@ pub enum Window { } /// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous) -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum CORSSettings { Anonymous, UseCredentials @@ -122,6 +123,7 @@ pub struct RequestInit { pub same_origin_data: bool, pub body: Option>, // TODO: client object + pub type_: Type, pub destination: Destination, pub synchronous: bool, pub mode: RequestMode, @@ -137,6 +139,30 @@ pub struct RequestInit { pub pipeline_id: Option, } +impl Default for RequestInit { + fn default() -> RequestInit { + RequestInit { + method: Method::Get, + url: Url::parse("about:blank").unwrap(), + headers: Headers::new(), + unsafe_request: false, + same_origin_data: false, + body: None, + type_: Type::None, + destination: Destination::None, + synchronous: false, + mode: RequestMode::NoCORS, + use_cors_preflight: false, + credentials_mode: CredentialsMode::Omit, + use_url_credentials: false, + origin: Url::parse("about:blank").unwrap(), + referrer_url: None, + referrer_policy: None, + pipeline_id: None, + } + } +} + /// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec #[derive(Clone, HeapSizeOf)] pub struct Request { @@ -232,6 +258,7 @@ impl Request { req.unsafe_request = init.unsafe_request; req.same_origin_data.set(init.same_origin_data); *req.body.borrow_mut() = init.body; + req.type_ = init.type_; req.destination = init.destination; req.synchronous = init.synchronous; req.mode = init.mode; @@ -248,56 +275,6 @@ impl Request { req } - /// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request - pub fn potential_cors_request(url: Url, - cors_attribute_state: Option, - is_service_worker_global_scope: bool, - same_origin_fallback: bool, - pipeline_id: Option) -> Request { - Request { - method: RefCell::new(Method::Get), - local_urls_only: false, - sandboxed_storage_area_urls: false, - headers: RefCell::new(Headers::new()), - unsafe_request: false, - body: RefCell::new(None), - is_service_worker_global_scope: is_service_worker_global_scope, - window: Cell::new(Window::Client), - keep_alive: Cell::new(false), - skip_service_worker: Cell::new(false), - initiator: Initiator::None, - type_: Type::None, - destination: Destination::None, - origin: RefCell::new(Origin::Client), - omit_origin_header: Cell::new(false), - same_origin_data: Cell::new(false), - referrer: RefCell::new(Referrer::Client), - referrer_policy: Cell::new(None), - synchronous: false, - // Step 1-2 - mode: match cors_attribute_state { - Some(_) => RequestMode::CORSMode, - None if same_origin_fallback => RequestMode::SameOrigin, - None => RequestMode::NoCORS - }, - use_cors_preflight: false, - // Step 3-4 - credentials_mode: match cors_attribute_state { - Some(CORSSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin, - _ => CredentialsMode::Include, - }, - use_url_credentials: true, - cache_mode: Cell::new(CacheMode::Default), - redirect_mode: Cell::new(RedirectMode::Follow), - integrity_metadata: RefCell::new(String::new()), - url_list: RefCell::new(vec![url]), - redirect_count: Cell::new(0), - response_tainting: Cell::new(ResponseTainting::Basic), - pipeline_id: Cell::new(pipeline_id), - done: Cell::new(false) - } - } - pub fn url(&self) -> Url { self.url_list.borrow().first().unwrap().clone() } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index e546b92b4f1..66338dd9ef1 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -602,6 +602,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { referrer_url: self.referrer_url.clone(), referrer_policy: self.referrer_policy.clone(), pipeline_id: self.pipeline_id(), + .. RequestInit::default() }; if bypass_cross_origin_check { From d4816762fa9db76d56014caa50243f40f481265b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 16 Jul 2016 12:33:40 -0700 Subject: [PATCH 5/8] Add fetch_async to PendingAsyncLoad, DocumentLoader and Document --- components/net_traits/lib.rs | 7 +++++++ components/script/document_loader.rs | 17 +++++++++++++++-- components/script/dom/document.rs | 11 ++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 036fb54d940..bd3436d9f00 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -520,6 +520,13 @@ impl PendingAsyncLoad { let consumer = LoadConsumer::Listener(listener); self.core_resource_thread.send(CoreResourceMsg::Load(load_data, consumer, None)).unwrap(); } + + /// Initiate the fetch associated with this pending load. + pub fn fetch_async(mut self, request: RequestInit, fetch_target: IpcSender) { + self.guard.neuter(); + + self.core_resource_thread.send(CoreResourceMsg::Fetch(request, fetch_target)).unwrap(); + } } /// Message sent in response to `Load`. Contains metadata, and a port diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index 0fc1e5e06c5..c38a234b2c1 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -7,9 +7,11 @@ use dom::bindings::js::JS; use dom::document::Document; +use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::{PendingAsyncLoad, AsyncResponseTarget, LoadContext}; -use net_traits::{ResourceThreads, IpcSend}; +use net_traits::request::RequestInit; +use net_traits::{AsyncResponseTarget, PendingAsyncLoad, LoadContext}; +use net_traits::{FetchResponseMsg, ResourceThreads, IpcSend}; use std::thread; use url::Url; @@ -148,6 +150,17 @@ impl DocumentLoader { pending.load_async(listener) } + /// Initiate a new fetch. + pub fn fetch_async(&mut self, + load: LoadType, + request: RequestInit, + fetch_target: IpcSender, + referrer: &Document, + referrer_policy: Option) { + let pending = self.prepare_async_load(load, referrer, referrer_policy); + pending.fetch_async(request, fetch_target); + } + /// Mark an in-progress network request complete. pub fn finish_load(&mut self, load: &LoadType) { let idx = self.blocking_loads.iter().position(|unfinished| *unfinished == *load); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 50eeeeeb3d5..2cca84a625a 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -95,9 +95,10 @@ use js::jsapi::JS_GetRuntime; use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::{AsyncResponseTarget, IpcSend, PendingAsyncLoad}; +use net_traits::{AsyncResponseTarget, FetchResponseMsg, IpcSend, PendingAsyncLoad}; use net_traits::CookieSource::NonHTTP; use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl}; +use net_traits::request::RequestInit; use net_traits::response::HttpsState; use num_traits::ToPrimitive; use origin::Origin; @@ -1433,6 +1434,14 @@ impl Document { loader.load_async(load, listener, self, referrer_policy); } + pub fn fetch_async(&self, load: LoadType, + request: RequestInit, + fetch_target: IpcSender, + referrer_policy: Option) { + let mut loader = self.loader.borrow_mut(); + loader.fetch_async(load, request, fetch_target, self, referrer_policy); + } + pub fn finish_load(&self, load: LoadType) { debug!("Document got finish_load: {:?}", load); // The parser might need the loader, so restrict the lifetime of the borrow. From 4dcf693a751b0780a72a3e3058e12642a8082cfe Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 3 Jul 2016 21:24:53 +0800 Subject: [PATCH 6/8] Use fetch infrastructure to load external scripts --- components/script/document_loader.rs | 2 +- components/script/dom/htmlscriptelement.rs | 75 ++++++++++++++++------ 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index c38a234b2c1..5209b68b9ac 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -9,9 +9,9 @@ use dom::bindings::js::JS; use dom::document::Document; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::request::RequestInit; use net_traits::{AsyncResponseTarget, PendingAsyncLoad, LoadContext}; use net_traits::{FetchResponseMsg, ResourceThreads, IpcSend}; +use net_traits::request::RequestInit; use std::thread; use url::Url; diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index a583ddc2137..ea44dd81336 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -15,6 +15,7 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::js::RootedReference; use dom::bindings::refcounted::Trusted; +use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::document::Document; use dom::element::{AttributeMutation, Element, ElementCreator}; @@ -30,8 +31,8 @@ use html5ever::tree_builder::NextParserState; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; -use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; -use net_traits::request::CORSSettings; +use net_traits::{FetchResponseListener, Metadata, NetworkError}; +use net_traits::request::{CORSSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType}; use network_listener::{NetworkListener, PreInvoke}; use std::ascii::AsciiExt; use std::cell::Cell; @@ -153,8 +154,12 @@ struct ScriptContext { status: Result<(), NetworkError> } -impl AsyncResponseListener for ScriptContext { - fn headers_available(&mut self, metadata: Result) { +impl FetchResponseListener for ScriptContext { + fn process_request_body(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here? + + fn process_request_eof(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here? + + fn process_response(&mut self, metadata: Result) { self.metadata = metadata.ok(); let status_code = self.metadata.as_ref().and_then(|m| { @@ -171,18 +176,17 @@ impl AsyncResponseListener for ScriptContext { }; } - fn data_available(&mut self, payload: Vec) { + fn process_response_chunk(&mut self, mut chunk: Vec) { if self.status.is_ok() { - let mut payload = payload; - self.data.append(&mut payload); + self.data.append(&mut chunk); } } /// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script /// step 4-9 - fn response_complete(&mut self, status: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<(), NetworkError>) { // Step 5. - let load = status.and(self.status.clone()).map(|_| { + let load = response.and(self.status.clone()).map(|_| { let metadata = self.metadata.take().unwrap(); // Step 6. @@ -212,8 +216,38 @@ impl PreInvoke for ScriptContext {} /// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script fn fetch_a_classic_script(script: &HTMLScriptElement, url: Url, + cors_setting: Option, character_encoding: EncodingRef) { - // TODO(#9186): use the fetch infrastructure. + let doc = document_from_node(script); + + // Step 1, 2. + let request = RequestInit { + url: url.clone(), + type_: RequestType::Script, + destination: Destination::Script, + // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request + // Step 1 + mode: match cors_setting { + Some(_) => RequestMode::CORSMode, + None => RequestMode::NoCORS, + }, + // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request + // Step 3-4 + credentials_mode: match cors_setting { + Some(CORSSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin, + _ => CredentialsMode::Include, + }, + origin: doc.url().clone(), + pipeline_id: Some(script.global().r().pipeline_id()), + // FIXME: Set to true for now, discussion in https://github.com/whatwg/fetch/issues/381 + same_origin_data: true, + referrer_url: Some(doc.url().clone()), + referrer_policy: doc.get_referrer_policy(), + .. RequestInit::default() + }; + + // TODO: Step 3, Add custom steps to perform fetch + let context = Arc::new(Mutex::new(ScriptContext { elem: Trusted::new(script), character_encoding: character_encoding, @@ -231,14 +265,11 @@ fn fetch_a_classic_script(script: &HTMLScriptElement, script_chan: doc.window().networking_task_source(), wrapper: Some(doc.window().get_runnable_wrapper()), }; - let response_target = AsyncResponseTarget { - sender: action_sender, - }; - ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify_action(message.to().unwrap()); - }); - doc.load_async(LoadType::Script(url), response_target, None); + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify_fetch(message.to().unwrap()); + }); + doc.fetch_async(LoadType::Script(url), request, action_sender, None); } impl HTMLScriptElement { @@ -324,7 +355,13 @@ impl HTMLScriptElement { .and_then(|charset| encoding_from_whatwg_label(&charset.value())) .unwrap_or_else(|| doc.encoding()); - // TODO: Step 14: CORS. + // Step 14. + let cors_setting = match self.GetCrossOrigin() { + Some(ref s) if *s == "anonymous" => Some(CORSSettings::Anonymous), + Some(ref s) if *s == "use-credentials" => Some(CORSSettings::UseCredentials), + None => None, + _ => unreachable!() + }; // TODO: Step 15: Nonce. @@ -356,7 +393,7 @@ impl HTMLScriptElement { }; // Step 18.6. - fetch_a_classic_script(self, url, encoding); + fetch_a_classic_script(self, url, cors_setting, encoding); true }, From 07c9cfecec754805cf1276995dd99a5d27cec766 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 14 Sep 2016 00:30:59 -0700 Subject: [PATCH 7/8] Add FetchMetadata and update corresponding methods --- components/net/fetch/methods.rs | 7 +++- components/net_traits/lib.rs | 19 ++++++++- components/net_traits/response.rs | 48 +++++++++++++++------- components/script/dom/htmlscriptelement.rs | 10 +++-- components/script/dom/xmlhttprequest.rs | 20 +++++---- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index dd35ce06656..23fb67ce82b 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -22,7 +22,7 @@ use hyper::status::StatusCode; use hyper_serde::Serde; use mime_guess::guess_mime_type; use msg::constellation_msg::ReferrerPolicy; -use net_traits::FetchTaskTarget; +use net_traits::{FetchTaskTarget, FetchMetadata}; use net_traits::request::{CacheMode, CredentialsMode, Destination}; use net_traits::request::{RedirectMode, Referrer, Request, RequestMode, ResponseTainting}; use net_traits::request::{Type, Origin, Window}; @@ -981,7 +981,10 @@ fn http_network_fetch(request: Rc, // We're about to spawn a thread to be waited on here *done_chan = Some(channel()); - let meta = response.metadata().expect("Response metadata should exist at this stage"); + let meta = match response.metadata().expect("Response metadata should exist at this stage") { + FetchMetadata::Unfiltered(m) => m, + FetchMetadata::Filtered { unsafe_, .. } => unsafe_ + }; let done_sender = done_chan.as_ref().map(|ch| ch.0.clone()); let devtools_sender = devtools_chan.clone(); let meta_status = meta.status.clone(); diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index bd3436d9f00..2cbc198e0f7 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -170,7 +170,7 @@ pub enum FetchResponseMsg { ProcessRequestBody, ProcessRequestEOF, // todo: send more info about the response (or perhaps the entire Response) - ProcessResponse(Result), + ProcessResponse(Result), ProcessResponseChunk(Vec), ProcessResponseEOF(Result<(), NetworkError>), } @@ -200,10 +200,25 @@ pub trait FetchTaskTarget { fn process_response_eof(&mut self, response: &Response); } +#[derive(Serialize, Deserialize)] +pub enum FilteredMetadata { + Opaque, + Transparent(Metadata) +} + +#[derive(Serialize, Deserialize)] +pub enum FetchMetadata { + Unfiltered(Metadata), + Filtered { + filtered: FilteredMetadata, + unsafe_: Metadata + } +} + pub trait FetchResponseListener { fn process_request_body(&mut self); fn process_request_eof(&mut self); - fn process_response(&mut self, metadata: Result); + fn process_response(&mut self, metadata: Result); fn process_response_chunk(&mut self, chunk: Vec); fn process_response_eof(&mut self, response: Result<(), NetworkError>); } diff --git a/components/net_traits/response.rs b/components/net_traits/response.rs index 823c56c7002..c4b4a3fe8e3 100644 --- a/components/net_traits/response.rs +++ b/components/net_traits/response.rs @@ -4,7 +4,7 @@ //! The [Response](https://fetch.spec.whatwg.org/#responses) object //! resulting from a [fetch operation](https://fetch.spec.whatwg.org/#concept-fetch) -use {Metadata, NetworkError}; +use {FetchMetadata, FilteredMetadata, Metadata, NetworkError}; use hyper::header::{AccessControlExposeHeaders, ContentType, Headers}; use hyper::status::StatusCode; use hyper_serde::Serde; @@ -225,24 +225,42 @@ impl Response { response } - pub fn metadata(&self) -> Result { - let mut metadata = if let Some(ref url) = self.url { - Metadata::default(url.clone()) - } else { - return Err(NetworkError::Internal("No url found in response".to_string())); + pub fn metadata(&self) -> Result { + fn init_metadata(response: &Response, url: &Url) -> Metadata { + let mut metadata = Metadata::default(url.clone()); + metadata.set_content_type(match response.headers.get() { + Some(&ContentType(ref mime)) => Some(mime), + None => None + }); + metadata.headers = Some(Serde(response.headers.clone())); + metadata.status = response.raw_status.clone(); + metadata.https_state = response.https_state; + metadata }; if self.is_network_error() { - return Err(NetworkError::Internal("Cannot extract metadata from network error".to_string())); + return Err(NetworkError::Internal("Cannot extract metadata from network error".to_owned())); } - metadata.set_content_type(match self.headers.get() { - Some(&ContentType(ref mime)) => Some(mime), - None => None - }); - metadata.headers = Some(Serde(self.headers.clone())); - metadata.status = self.raw_status.clone(); - metadata.https_state = self.https_state; - return Ok(metadata); + let metadata = self.url.as_ref().map(|url| init_metadata(self, url)); + + if let Some(ref response) = self.internal_response { + match response.url { + Some(ref url) => { + let unsafe_metadata = init_metadata(response, url); + + Ok(FetchMetadata::Filtered { + filtered: match metadata { + Some(m) => FilteredMetadata::Transparent(m), + None => FilteredMetadata::Opaque + }, + unsafe_: unsafe_metadata + }) + } + None => Err(NetworkError::Internal("No url found in unsafe response".to_owned())) + } + } else { + Ok(FetchMetadata::Unfiltered(metadata.unwrap())) + } } } diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index ea44dd81336..566b69967d4 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -31,7 +31,7 @@ use html5ever::tree_builder::NextParserState; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; -use net_traits::{FetchResponseListener, Metadata, NetworkError}; +use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; use net_traits::request::{CORSSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType}; use network_listener::{NetworkListener, PreInvoke}; use std::ascii::AsciiExt; @@ -159,8 +159,12 @@ impl FetchResponseListener for ScriptContext { fn process_request_eof(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here? - fn process_response(&mut self, metadata: Result) { - self.metadata = metadata.ok(); + fn process_response(&mut self, + metadata: Result) { + self.metadata = metadata.ok().map(|meta| match meta { + FetchMetadata::Unfiltered(m) => m, + FetchMetadata::Filtered { unsafe_, .. } => unsafe_ + }); let status_code = self.metadata.as_ref().and_then(|m| { match m.status { diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 66338dd9ef1..24ee2c28d36 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -45,8 +45,8 @@ use js::jsapi::{JSContext, JS_ParseJSON}; use js::jsapi::JS_ClearPendingException; use js::jsval::{JSVal, NullValue, UndefinedValue}; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::{CoreResourceThread, LoadOrigin}; -use net_traits::{FetchResponseListener, Metadata, NetworkError}; +use net_traits::{CoreResourceThread, FetchMetadata, FilteredMetadata}; +use net_traits::{FetchResponseListener, LoadOrigin, NetworkError}; use net_traits::CoreResourceMsg::Fetch; use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::trim_http_whitespace; @@ -227,10 +227,10 @@ impl XMLHttpRequest { // todo } - fn process_response(&mut self, metadata: Result) { + fn process_response(&mut self, + metadata: Result) { let xhr = self.xhr.root(); - let rv = xhr.process_headers_available(self.gen_id, - metadata); + let rv = xhr.process_headers_available(self.gen_id, metadata); if rv.is_err() { *self.sync_status.borrow_mut() = Some(rv); } @@ -869,10 +869,16 @@ impl XMLHttpRequest { } fn process_headers_available(&self, - gen_id: GenerationId, metadata: Result) + gen_id: GenerationId, metadata: Result) -> Result<(), Error> { let metadata = match metadata { - Ok(meta) => meta, + Ok(meta) => match meta { + FetchMetadata::Unfiltered(m) => m, + FetchMetadata::Filtered { filtered, .. } => match filtered { + FilteredMetadata::Opaque => return Err(Error::Network), + FilteredMetadata::Transparent(m) => m + } + }, Err(_) => { self.process_partial_response(XHRProgress::Errored(gen_id, Error::Network)); return Err(Error::Network); From 6fbd2aa5b7628bd47971806ddf438cd350a60bee Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 14 Sep 2016 10:59:17 -0700 Subject: [PATCH 8/8] Avoid deadlock in main_fetch --- components/net/fetch/methods.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 23fb67ce82b..1f3eb0e5a24 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -361,13 +361,14 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, } } } else if let Some(ref mut target) = *target { - if let ResponseBody::Done(ref vec) = *response.body.lock().unwrap() { + let body = response.body.lock().unwrap(); + if let ResponseBody::Done(ref vec) = *body { // in case there was no channel to wait for, the body was // obtained synchronously via basic_fetch for data/file/about/etc // We should still send the body across as a chunk target.process_response_chunk(vec.clone()); } else { - assert!(*response.body.lock().unwrap() == ResponseBody::Empty) + assert!(*body == ResponseBody::Empty) } }