mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 17:25:36 +00:00
servo: Merge #13323 - Implement the Fetch method (from jeenalee:fetch); r=jdm
<!-- Please describe your changes on the following line: --> This PR implements the fetch method, which is described in [the Fetch Spec](https://fetch.spec.whatwg.org/#fetch-method). The expected wpt results are updated as well. A few comments about the current fetch implementation: - The fetch method manually calls `JSAutoCompartment` in order to prevent SpiderMonkey from crashing. This may have to change in the future. - Not all `FetchResponseListener` methods are implemented. - `net_traits::CoreResourceMsg::Fetch` message takes a `net_traits::request::RequestInit`. However, when the fetch method is called, a `RequestBinding::RequestInit` is given. The fetch method constructs a `dom::request::Request` with the given `RequestInit`, then creates `net_traits::request::RequestInit` from the dom Request object for the fetch message. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #11898 (github issue number if applicable). <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 5996e008a34dd92602acb7bbd3ea41a880053110
This commit is contained in:
parent
0c9d616de3
commit
b2281e107b
@ -229,6 +229,10 @@ impl Headers {
|
||||
*self.header_list.borrow_mut() = HyperHeaders::new();
|
||||
}
|
||||
|
||||
pub fn set_headers(&self, hyper_headers: HyperHeaders) {
|
||||
*self.header_list.borrow_mut() = hyper_headers;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
|
||||
pub fn extract_mime_type(&self) -> Vec<u8> {
|
||||
self.header_list.borrow().get_raw("content-type").map_or(vec![], |v| v[0].clone())
|
||||
|
@ -445,6 +445,10 @@ impl Request {
|
||||
r_clone.Headers().set_guard(headers_guard);
|
||||
r_clone
|
||||
}
|
||||
|
||||
pub fn get_request(&self) -> NetTraitsRequest {
|
||||
self.request.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn net_request_from_global(global: GlobalRef,
|
||||
|
@ -18,7 +18,9 @@ use dom::headers::{Headers, Guard};
|
||||
use dom::headers::{is_vchar, is_obs_text};
|
||||
use dom::promise::Promise;
|
||||
use dom::xmlhttprequest::Extractable;
|
||||
use hyper::header::Headers as HyperHeaders;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper_serde::Serde;
|
||||
use net_traits::response::{ResponseBody as NetTraitsResponseBody};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
@ -344,3 +346,24 @@ impl ResponseMethods for Response {
|
||||
fn serialize_without_fragment(url: &Url) -> &str {
|
||||
&url[..Position::AfterQuery]
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn set_type(&self, new_response_type: DOMResponseType) {
|
||||
*self.response_type.borrow_mut() = new_response_type;
|
||||
}
|
||||
|
||||
pub fn set_headers(&self, option_hyper_headers: Option<Serde<HyperHeaders>>) {
|
||||
self.Headers().set_headers(match option_hyper_headers {
|
||||
Some(hyper_headers) => hyper_headers.into_inner(),
|
||||
None => HyperHeaders::new(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_raw_status(&self, status: Option<(u16, Vec<u8>)>) {
|
||||
*self.raw_status.borrow_mut() = status;
|
||||
}
|
||||
|
||||
pub fn set_final_url(&self, final_url: Url) {
|
||||
*self.url.borrow_mut() = Some(final_url);
|
||||
}
|
||||
}
|
||||
|
11
servo/components/script/dom/webidls/Fetch.webidl
Normal file
11
servo/components/script/dom/webidls/Fetch.webidl
Normal file
@ -0,0 +1,11 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://fetch.spec.whatwg.org/#fetch-method
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
|
||||
partial interface WindowOrWorkerGlobalScope {
|
||||
[NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init);
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#windoworworkerglobalscope
|
||||
|
||||
// typedef (DOMString or Function) TimerHandler;
|
||||
|
||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
||||
interface WindowOrWorkerGlobalScope {
|
||||
// [Replaceable] readonly attribute USVString origin;
|
||||
|
||||
// base64 utility methods
|
||||
// DOMString btoa(DOMString data);
|
||||
// DOMString atob(DOMString data);
|
||||
|
||||
// timers
|
||||
// long setTimeout(TimerHandler handler, optional long timeout = 0, any... arguments);
|
||||
// void clearTimeout(optional long handle = 0);
|
||||
// long setInterval(TimerHandler handler, optional long timeout = 0, any... arguments);
|
||||
// void clearInterval(optional long handle = 0);
|
||||
|
||||
// ImageBitmap
|
||||
// Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options);
|
||||
// Promise<ImageBitmap> createImageBitmap(
|
||||
// ImageBitmapSource image, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options);
|
||||
};
|
||||
|
||||
Window implements WindowOrWorkerGlobalScope;
|
||||
WorkerGlobalScope implements WindowOrWorkerGlobalScope;
|
@ -12,8 +12,10 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHa
|
||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
|
||||
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::{self, FrameRequestCallback, WindowMethods};
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
|
||||
use dom::bindings::codegen::UnionTypes::RequestOrUSVString;
|
||||
use dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible, report_pending_exception};
|
||||
use dom::bindings::global::{GlobalRef, global_root_from_object};
|
||||
use dom::bindings::inheritance::Castable;
|
||||
@ -40,9 +42,11 @@ use dom::messageevent::MessageEvent;
|
||||
use dom::navigator::Navigator;
|
||||
use dom::node::{Node, from_untrusted_node_address, window_from_node};
|
||||
use dom::performance::Performance;
|
||||
use dom::promise::Promise;
|
||||
use dom::screen::Screen;
|
||||
use dom::storage::Storage;
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use fetch;
|
||||
use gfx_traits::LayerId;
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use js::jsapi::{Evaluate2, HandleObject, HandleValue, JSAutoCompartment, JSContext};
|
||||
@ -902,6 +906,12 @@ impl WindowMethods for Window {
|
||||
Err(e) => Err(Error::Type(format!("Couldn't open URL: {}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
// https://fetch.spec.whatwg.org/#fetch-method
|
||||
fn Fetch(&self, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> {
|
||||
fetch::Fetch(self.global().r(), input, init)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ScriptHelpers {
|
||||
|
@ -5,8 +5,10 @@
|
||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
|
||||
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||
use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
|
||||
use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception, ErrorInfo};
|
||||
use dom::bindings::codegen::UnionTypes::RequestOrUSVString;
|
||||
use dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible, report_pending_exception};
|
||||
use dom::bindings::global::{GlobalRef, GlobalRoot};
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::{JS, MutNullableHeap, Root};
|
||||
@ -17,10 +19,12 @@ use dom::console::TimerSet;
|
||||
use dom::crypto::Crypto;
|
||||
use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
|
||||
use dom::eventtarget::EventTarget;
|
||||
use dom::promise::Promise;
|
||||
use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
|
||||
use dom::window::{base64_atob, base64_btoa};
|
||||
use dom::workerlocation::WorkerLocation;
|
||||
use dom::workernavigator::WorkerNavigator;
|
||||
use fetch;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use js::jsapi::{HandleValue, JSAutoCompartment, JSContext, JSRuntime};
|
||||
use js::jsval::UndefinedValue;
|
||||
@ -393,6 +397,12 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
|
||||
fn ClearInterval(&self, handle: i32) {
|
||||
self.ClearTimeout(handle);
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
// https://fetch.spec.whatwg.org/#fetch-method
|
||||
fn Fetch(&self, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> {
|
||||
fetch::Fetch(self.global().r(), input, init)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
172
servo/components/script/fetch.rs
Normal file
172
servo/components/script/fetch.rs
Normal file
@ -0,0 +1,172 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||
use dom::bindings::codegen::Bindings::ResponseBinding::ResponseBinding::ResponseMethods;
|
||||
use dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
|
||||
use dom::bindings::codegen::UnionTypes::RequestOrUSVString;
|
||||
use dom::bindings::error::Error;
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||
use dom::bindings::reflector::Reflectable;
|
||||
use dom::headers::Guard;
|
||||
use dom::promise::Promise;
|
||||
use dom::request::Request;
|
||||
use dom::response::Response;
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::jsapi::JSAutoCompartment;
|
||||
use net_traits::{FetchResponseListener, NetworkError};
|
||||
use net_traits::{FilteredMetadata, FetchMetadata, Metadata};
|
||||
use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch;
|
||||
use net_traits::request::Request as NetTraitsRequest;
|
||||
use net_traits::request::RequestInit as NetTraitsRequestInit;
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use url::Url;
|
||||
|
||||
struct FetchContext {
|
||||
fetch_promise: Option<TrustedPromise>,
|
||||
response_object: Trusted<Response>,
|
||||
}
|
||||
|
||||
fn from_referrer_to_referrer_url(request: &NetTraitsRequest) -> Option<Url> {
|
||||
let referrer = request.referrer.borrow();
|
||||
referrer.to_url().map(|url| url.clone())
|
||||
}
|
||||
|
||||
fn request_init_from_request(request: NetTraitsRequest) -> NetTraitsRequestInit {
|
||||
NetTraitsRequestInit {
|
||||
method: request.method.borrow().clone(),
|
||||
url: request.url(),
|
||||
headers: request.headers.borrow().clone(),
|
||||
unsafe_request: request.unsafe_request,
|
||||
same_origin_data: request.same_origin_data.get(),
|
||||
body: request.body.borrow().clone(),
|
||||
type_: request.type_,
|
||||
destination: request.destination,
|
||||
synchronous: request.synchronous,
|
||||
mode: request.mode,
|
||||
use_cors_preflight: request.use_cors_preflight,
|
||||
credentials_mode: request.credentials_mode,
|
||||
use_url_credentials: request.use_url_credentials,
|
||||
// TODO: NetTraitsRequestInit and NetTraitsRequest have different "origin"
|
||||
// ... NetTraitsRequestInit.origin: Url
|
||||
// ... NetTraitsRequest.origin: RefCell<Origin>
|
||||
origin: request.url(),
|
||||
referrer_url: from_referrer_to_referrer_url(&request),
|
||||
referrer_policy: request.referrer_policy.get(),
|
||||
pipeline_id: request.pipeline_id.get(),
|
||||
}
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#fetch-method
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn Fetch(global: GlobalRef, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> {
|
||||
let core_resource_thread = global.core_resource_thread();
|
||||
|
||||
// Step 1
|
||||
let promise = Promise::new(global);
|
||||
let response = Response::new(global);
|
||||
|
||||
// Step 2
|
||||
let request = match Request::Constructor(global, input, init) {
|
||||
Err(e) => {
|
||||
promise.reject_error(promise.global().r().get_cx(), e);
|
||||
return promise;
|
||||
},
|
||||
Ok(r) => r.get_request(),
|
||||
};
|
||||
let request_init = request_init_from_request(request);
|
||||
|
||||
// Step 3
|
||||
response.Headers().set_guard(Guard::Immutable);
|
||||
|
||||
// Step 4
|
||||
let (action_sender, action_receiver) = ipc::channel().unwrap();
|
||||
let fetch_context = Arc::new(Mutex::new(FetchContext {
|
||||
fetch_promise: Some(TrustedPromise::new(promise.clone())),
|
||||
response_object: Trusted::new(&*response),
|
||||
}));
|
||||
let listener = NetworkListener {
|
||||
context: fetch_context,
|
||||
script_chan: global.networking_task_source(),
|
||||
wrapper: None,
|
||||
};
|
||||
|
||||
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
|
||||
listener.notify_fetch(message.to().unwrap());
|
||||
});
|
||||
core_resource_thread.send(NetTraitsFetch(request_init, action_sender)).unwrap();
|
||||
|
||||
promise
|
||||
}
|
||||
|
||||
impl PreInvoke for FetchContext {}
|
||||
|
||||
impl FetchResponseListener for FetchContext {
|
||||
fn process_request_body(&mut self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn process_request_eof(&mut self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
fn process_response(&mut self, fetch_metadata: Result<FetchMetadata, NetworkError>) {
|
||||
let promise = self.fetch_promise.take().expect("fetch promise is missing").root();
|
||||
|
||||
// JSAutoCompartment needs to be manually made.
|
||||
// Otherwise, Servo will crash.
|
||||
let promise_cx = promise.global().r().get_cx();
|
||||
let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
|
||||
match fetch_metadata {
|
||||
// Step 4.1
|
||||
Err(_) => {
|
||||
promise.reject_error(
|
||||
promise.global().r().get_cx(),
|
||||
Error::Type("Network error occurred".to_string()));
|
||||
self.fetch_promise = Some(TrustedPromise::new(promise));
|
||||
return;
|
||||
},
|
||||
// Step 4.2
|
||||
Ok(metadata) => {
|
||||
match metadata {
|
||||
FetchMetadata::Unfiltered(m) =>
|
||||
fill_headers_with_metadata(self.response_object.root(), m),
|
||||
FetchMetadata::Filtered { filtered, .. } => match filtered {
|
||||
FilteredMetadata::Transparent(m) =>
|
||||
fill_headers_with_metadata(self.response_object.root(), m),
|
||||
FilteredMetadata::Opaque =>
|
||||
self.response_object.root().set_type(DOMResponseType::Opaque),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 4.3
|
||||
promise.resolve_native(
|
||||
promise_cx,
|
||||
&self.response_object.root());
|
||||
self.fetch_promise = Some(TrustedPromise::new(promise));
|
||||
}
|
||||
|
||||
fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
|
||||
// TODO when body is implemented
|
||||
// ... this will append the chunk to Response's body.
|
||||
}
|
||||
|
||||
fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
|
||||
// TODO
|
||||
// ... trailerObject is not supported in Servo yet.
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_headers_with_metadata(r: Root<Response>, m: Metadata) {
|
||||
r.set_headers(m.headers);
|
||||
r.set_raw_status(m.status);
|
||||
r.set_final_url(m.final_url);
|
||||
}
|
@ -98,6 +98,7 @@ mod devtools;
|
||||
pub mod document_loader;
|
||||
#[macro_use]
|
||||
pub mod dom;
|
||||
pub mod fetch;
|
||||
pub mod layout_wrapper;
|
||||
mod mem;
|
||||
mod network_listener;
|
||||
|
Loading…
Reference in New Issue
Block a user