mirror of
https://github.com/servo/servo.git
synced 2025-10-02 17:49:16 +01:00
Implement fetchLater
(#39547)
Allows fetches to be deferred, only in a secure context. It does not yet implement quota computation, since we don't have a concept of document quota yet. Also update the `fetch/api/idlharness` test to run in a secure context, since this API is only available there. Positive Mozilla position: https://github.com/mozilla/standards-positions/issues/703 Positive WebKit position: https://github.com/WebKit/standards-positions/issues/85 Closes whatwg/fetch#1858 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
parent
19c498af16
commit
680a780552
39 changed files with 403 additions and 228 deletions
|
@ -346,11 +346,15 @@ impl Callback for TransmitBodyPromiseRejectionHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of <https://fetch.spec.whatwg.org/#concept-bodyinit-extract>
|
/// <https://fetch.spec.whatwg.org/#body-with-type>
|
||||||
pub(crate) struct ExtractedBody {
|
pub(crate) struct ExtractedBody {
|
||||||
|
/// <https://fetch.spec.whatwg.org/#concept-body-stream>
|
||||||
pub(crate) stream: DomRoot<ReadableStream>,
|
pub(crate) stream: DomRoot<ReadableStream>,
|
||||||
|
/// <https://fetch.spec.whatwg.org/#concept-body-source>
|
||||||
pub(crate) source: BodySource,
|
pub(crate) source: BodySource,
|
||||||
|
/// <https://fetch.spec.whatwg.org/#concept-body-total-bytes>
|
||||||
pub(crate) total_bytes: Option<usize>,
|
pub(crate) total_bytes: Option<usize>,
|
||||||
|
/// <https://fetch.spec.whatwg.org/#body-with-type-type>
|
||||||
pub(crate) content_type: Option<DOMString>,
|
pub(crate) content_type: Option<DOMString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ use crate::dom::bindings::str::DOMString;
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::readablestream::PipeTo;
|
use crate::dom::readablestream::PipeTo;
|
||||||
use crate::fetch::FetchContext;
|
use crate::fetch::{DeferredFetchRecord, FetchContext};
|
||||||
use crate::realms::InRealm;
|
use crate::realms::InRealm;
|
||||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||||
|
|
||||||
|
@ -49,6 +49,12 @@ pub(crate) enum AbortAlgorithm {
|
||||||
#[conditional_malloc_size_of]
|
#[conditional_malloc_size_of]
|
||||||
Arc<Mutex<FetchContext>>,
|
Arc<Mutex<FetchContext>>,
|
||||||
),
|
),
|
||||||
|
/// <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
|
||||||
|
FetchLater(
|
||||||
|
#[no_trace]
|
||||||
|
#[conditional_malloc_size_of]
|
||||||
|
Arc<Mutex<DeferredFetchRecord>>,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
|
@ -190,6 +196,9 @@ impl AbortSignal {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.abort_fetch(reason.handle(), cx, can_gc);
|
.abort_fetch(reason.handle(), cx, can_gc);
|
||||||
},
|
},
|
||||||
|
AbortAlgorithm::FetchLater(deferred_fetch_record) => {
|
||||||
|
deferred_fetch_record.lock().unwrap().abort();
|
||||||
|
},
|
||||||
AbortAlgorithm::DomEventListener(removable_listener) => {
|
AbortAlgorithm::DomEventListener(removable_listener) => {
|
||||||
removable_listener
|
removable_listener
|
||||||
.event_target
|
.event_target
|
||||||
|
|
59
components/script/dom/fetchlaterresult.rs
Normal file
59
components/script/dom/fetchlaterresult.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
|
||||||
|
use crate::dom::bindings::codegen::Bindings::FetchLaterResultBinding::FetchLaterResultMethods;
|
||||||
|
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||||
|
use crate::dom::bindings::root::DomRoot;
|
||||||
|
use crate::dom::window::Window;
|
||||||
|
use crate::fetch::DeferredFetchRecord;
|
||||||
|
use crate::script_runtime::CanGc;
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#fetchlaterresult>
|
||||||
|
#[dom_struct]
|
||||||
|
pub(crate) struct FetchLaterResult {
|
||||||
|
reflector_: Reflector,
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#fetchlaterresult-activated-getter-steps>
|
||||||
|
#[conditional_malloc_size_of]
|
||||||
|
#[no_trace]
|
||||||
|
activated_getter_steps: Arc<Mutex<DeferredFetchRecord>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchLaterResult {
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
|
fn new_inherited(activated_getter_steps: Arc<Mutex<DeferredFetchRecord>>) -> FetchLaterResult {
|
||||||
|
FetchLaterResult {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
activated_getter_steps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
|
pub(crate) fn new(
|
||||||
|
window: &Window,
|
||||||
|
activated_getter_steps: Arc<Mutex<DeferredFetchRecord>>,
|
||||||
|
can_gc: CanGc,
|
||||||
|
) -> DomRoot<FetchLaterResult> {
|
||||||
|
reflect_dom_object(
|
||||||
|
Box::new(FetchLaterResult::new_inherited(activated_getter_steps)),
|
||||||
|
window,
|
||||||
|
can_gc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchLaterResultMethods<crate::DomTypeHolder> for FetchLaterResult {
|
||||||
|
/// <https://fetch.spec.whatwg.org/#dom-fetchlaterresult-activated>
|
||||||
|
fn Activated(&self) -> bool {
|
||||||
|
// The activated getter steps are to return the result of running this’s activated getter steps.
|
||||||
|
self.activated_getter_steps
|
||||||
|
.lock()
|
||||||
|
.expect("Activated getter not accessible")
|
||||||
|
.activated_getter_steps()
|
||||||
|
}
|
||||||
|
}
|
|
@ -314,6 +314,7 @@ pub(crate) mod eventsource;
|
||||||
pub(crate) mod eventtarget;
|
pub(crate) mod eventtarget;
|
||||||
pub(crate) mod extendableevent;
|
pub(crate) mod extendableevent;
|
||||||
pub(crate) mod extendablemessageevent;
|
pub(crate) mod extendablemessageevent;
|
||||||
|
pub(crate) mod fetchlaterresult;
|
||||||
pub(crate) mod file;
|
pub(crate) mod file;
|
||||||
pub(crate) mod filelist;
|
pub(crate) mod filelist;
|
||||||
pub(crate) mod filereader;
|
pub(crate) mod filereader;
|
||||||
|
|
|
@ -92,7 +92,7 @@ impl Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://fetch.spec.whatwg.org/#dom-request
|
// https://fetch.spec.whatwg.org/#dom-request
|
||||||
fn constructor(
|
pub(crate) fn constructor(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
proto: Option<HandleObject>,
|
proto: Option<HandleObject>,
|
||||||
can_gc: CanGc,
|
can_gc: CanGc,
|
||||||
|
|
|
@ -111,10 +111,11 @@ use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
|
||||||
};
|
};
|
||||||
use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
|
use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
|
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
|
||||||
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
use crate::dom::bindings::codegen::Bindings::RequestBinding::{RequestInfo, RequestInit};
|
||||||
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
||||||
self, FrameRequestCallback, ScrollBehavior, WindowMethods, WindowPostMessageOptions,
|
self, DeferredRequestInit, FrameRequestCallback, ScrollBehavior, WindowMethods,
|
||||||
|
WindowPostMessageOptions,
|
||||||
};
|
};
|
||||||
use crate::dom::bindings::codegen::UnionTypes::{
|
use crate::dom::bindings::codegen::UnionTypes::{
|
||||||
RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
|
RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
|
||||||
|
@ -140,6 +141,7 @@ use crate::dom::document::{AnimationFrameCallback, Document};
|
||||||
use crate::dom::element::Element;
|
use crate::dom::element::Element;
|
||||||
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
|
use crate::dom::fetchlaterresult::FetchLaterResult;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::hashchangeevent::HashChangeEvent;
|
use crate::dom::hashchangeevent::HashChangeEvent;
|
||||||
use crate::dom::history::History;
|
use crate::dom::history::History;
|
||||||
|
@ -1821,6 +1823,16 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
|
||||||
fetch::Fetch(self.upcast(), input, init, comp, can_gc)
|
fetch::Fetch(self.upcast(), input, init, comp, can_gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
|
||||||
|
fn FetchLater(
|
||||||
|
&self,
|
||||||
|
input: RequestInfo,
|
||||||
|
init: RootedTraceableBox<DeferredRequestInit>,
|
||||||
|
can_gc: CanGc,
|
||||||
|
) -> Fallible<DomRoot<FetchLaterResult>> {
|
||||||
|
fetch::FetchLater(self, input, init, can_gc)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bluetooth")]
|
#[cfg(feature = "bluetooth")]
|
||||||
fn TestRunner(&self) -> DomRoot<TestRunner> {
|
fn TestRunner(&self) -> DomRoot<TestRunner> {
|
||||||
self.test_runner
|
self.test_runner
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use base::id::WebViewId;
|
use base::id::WebViewId;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
|
use js::jsapi::{ExceptionStackBehavior, JS_IsExceptionPending};
|
||||||
use js::jsval::UndefinedValue;
|
use js::jsval::UndefinedValue;
|
||||||
use js::rust::HandleValue;
|
use js::rust::HandleValue;
|
||||||
|
use js::rust::wrappers::JS_SetPendingException;
|
||||||
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
|
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
|
||||||
use net_traits::request::{
|
use net_traits::request::{
|
||||||
CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
|
CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
|
||||||
|
@ -20,6 +24,7 @@ use net_traits::{
|
||||||
ResourceTimingType, cancel_async_fetch,
|
ResourceTimingType, cancel_async_fetch,
|
||||||
};
|
};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
use timers::TimerEventRequest;
|
||||||
|
|
||||||
use crate::body::BodyMixin;
|
use crate::body::BodyMixin;
|
||||||
use crate::dom::abortsignal::AbortAlgorithm;
|
use crate::dom::abortsignal::AbortAlgorithm;
|
||||||
|
@ -29,14 +34,17 @@ use crate::dom::bindings::codegen::Bindings::RequestBinding::{
|
||||||
};
|
};
|
||||||
use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
|
use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
|
use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
|
||||||
use crate::dom::bindings::error::Error;
|
use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
|
||||||
|
use crate::dom::bindings::error::{Error, Fallible};
|
||||||
use crate::dom::bindings::import::module::SafeJSContext;
|
use crate::dom::bindings::import::module::SafeJSContext;
|
||||||
use crate::dom::bindings::inheritance::Castable;
|
use crate::dom::bindings::inheritance::Castable;
|
||||||
|
use crate::dom::bindings::num::Finite;
|
||||||
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||||
use crate::dom::bindings::reflector::DomGlobal;
|
use crate::dom::bindings::reflector::DomGlobal;
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||||
use crate::dom::csp::{GlobalCspReporting, Violation};
|
use crate::dom::csp::{GlobalCspReporting, Violation};
|
||||||
|
use crate::dom::fetchlaterresult::FetchLaterResult;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::headers::Guard;
|
use crate::dom::headers::Guard;
|
||||||
use crate::dom::performanceresourcetiming::InitiatorType;
|
use crate::dom::performanceresourcetiming::InitiatorType;
|
||||||
|
@ -44,6 +52,7 @@ use crate::dom::promise::Promise;
|
||||||
use crate::dom::request::Request;
|
use crate::dom::request::Request;
|
||||||
use crate::dom::response::Response;
|
use crate::dom::response::Response;
|
||||||
use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
|
use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
|
||||||
|
use crate::dom::window::Window;
|
||||||
use crate::network_listener::{self, PreInvoke, ResourceTimingListener, submit_timing_data};
|
use crate::network_listener::{self, PreInvoke, ResourceTimingListener, submit_timing_data};
|
||||||
use crate::realms::{InRealm, enter_realm};
|
use crate::realms::{InRealm, enter_realm};
|
||||||
use crate::script_runtime::CanGc;
|
use crate::script_runtime::CanGc;
|
||||||
|
@ -266,6 +275,189 @@ pub(crate) fn Fetch(
|
||||||
promise
|
promise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#queue-a-deferred-fetch>
|
||||||
|
fn queue_deferred_fetch(
|
||||||
|
request: NetTraitsRequest,
|
||||||
|
activate_after: Finite<f64>,
|
||||||
|
global: &GlobalScope,
|
||||||
|
) -> Arc<Mutex<DeferredFetchRecord>> {
|
||||||
|
let trusted_global = Trusted::new(global);
|
||||||
|
// Step 1. Populate request from client given request.
|
||||||
|
// TODO
|
||||||
|
// Step 2. Set request’s service-workers mode to "none".
|
||||||
|
// TODO
|
||||||
|
// Step 3. Set request’s keepalive to true.
|
||||||
|
// TODO
|
||||||
|
// Step 4. Let deferredRecord be a new deferred fetch record whose request is request, and whose notify invoked is onActivatedWithoutTermination.
|
||||||
|
let deferred_record = Arc::new(Mutex::new(DeferredFetchRecord {
|
||||||
|
request,
|
||||||
|
global: trusted_global.clone(),
|
||||||
|
invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
|
||||||
|
activated: Cell::new(false),
|
||||||
|
}));
|
||||||
|
// Step 5. Append deferredRecord to request’s client’s fetch group’s deferred fetch records.
|
||||||
|
// TODO
|
||||||
|
// Step 6. If activateAfter is non-null, then run the following steps in parallel:
|
||||||
|
let deferred_record_clone = deferred_record.clone();
|
||||||
|
global.schedule_timer(TimerEventRequest {
|
||||||
|
callback: Box::new(move || {
|
||||||
|
// Step 6.2. Process deferredRecord.
|
||||||
|
deferred_record_clone.lock().unwrap().process();
|
||||||
|
|
||||||
|
// Last step of https://fetch.spec.whatwg.org/#process-a-deferred-fetch
|
||||||
|
//
|
||||||
|
// Step 4. Queue a global task on the deferred fetch task source with
|
||||||
|
// deferredRecord’s request’s client’s global object to run deferredRecord’s notify invoked.
|
||||||
|
let deferred_record_clone = deferred_record_clone.clone();
|
||||||
|
trusted_global
|
||||||
|
.root()
|
||||||
|
.task_manager()
|
||||||
|
.deferred_fetch_task_source()
|
||||||
|
.queue(task!(notify_deferred_record: move || {
|
||||||
|
deferred_record_clone.lock().unwrap().activate();
|
||||||
|
}));
|
||||||
|
}),
|
||||||
|
// Step 6.1. The user agent should wait until any of the following conditions is met:
|
||||||
|
duration: Duration::from_millis(*activate_after as u64),
|
||||||
|
});
|
||||||
|
// Step 7. Return deferredRecord.
|
||||||
|
deferred_record
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
|
||||||
|
#[allow(non_snake_case, unsafe_code)]
|
||||||
|
pub(crate) fn FetchLater(
|
||||||
|
window: &Window,
|
||||||
|
input: RequestInfo,
|
||||||
|
init: RootedTraceableBox<DeferredRequestInit>,
|
||||||
|
can_gc: CanGc,
|
||||||
|
) -> Fallible<DomRoot<FetchLaterResult>> {
|
||||||
|
let global_scope = window.upcast();
|
||||||
|
// Step 1. Let requestObject be the result of invoking the initial value
|
||||||
|
// of Request as constructor with input and init as arguments.
|
||||||
|
let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
|
||||||
|
// Step 2. If requestObject’s signal is aborted, then throw signal’s abort reason.
|
||||||
|
let signal = request_object.Signal();
|
||||||
|
if signal.aborted() {
|
||||||
|
let cx = GlobalScope::get_cx();
|
||||||
|
rooted!(in(*cx) let mut abort_reason = UndefinedValue());
|
||||||
|
signal.Reason(cx, abort_reason.handle_mut());
|
||||||
|
unsafe {
|
||||||
|
assert!(!JS_IsExceptionPending(*cx));
|
||||||
|
JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
|
||||||
|
}
|
||||||
|
return Err(Error::JSFailed);
|
||||||
|
}
|
||||||
|
// Step 3. Let request be requestObject’s request.
|
||||||
|
let request = request_object.get_request();
|
||||||
|
// Step 4. Let activateAfter be null.
|
||||||
|
let mut activate_after = Finite::wrap(0_f64);
|
||||||
|
// Step 5. If init is given and init["activateAfter"] exists, then set
|
||||||
|
// activateAfter to init["activateAfter"].
|
||||||
|
if let Some(init_activate_after) = init.activateAfter.as_ref() {
|
||||||
|
activate_after = *init_activate_after;
|
||||||
|
}
|
||||||
|
// Step 6. If activateAfter is less than 0, then throw a RangeError.
|
||||||
|
if *activate_after < 0.0 {
|
||||||
|
return Err(Error::Range("activateAfter must be at least 0".to_owned()));
|
||||||
|
}
|
||||||
|
// Step 7. If this’s relevant global object’s associated document is not fully active, then throw a TypeError.
|
||||||
|
if !window.Document().is_fully_active() {
|
||||||
|
return Err(Error::Type("Document is not fully active".to_owned()));
|
||||||
|
}
|
||||||
|
let url = request.url();
|
||||||
|
// Step 8. If request’s URL’s scheme is not an HTTP(S) scheme, then throw a TypeError.
|
||||||
|
if !matches!(url.scheme(), "http" | "https") {
|
||||||
|
return Err(Error::Type("URL is not http(s)".to_owned()));
|
||||||
|
}
|
||||||
|
// Step 9. If request’s URL is not a potentially trustworthy URL, then throw a SecurityError.
|
||||||
|
if !url.is_potentially_trustworthy() {
|
||||||
|
return Err(Error::Type("URL is not trustworthy".to_owned()));
|
||||||
|
}
|
||||||
|
// Step 10. If request’s body is not null, and request’s body length is null, then throw a TypeError.
|
||||||
|
if let Some(body) = request.body.as_ref() {
|
||||||
|
if body.len().is_none() {
|
||||||
|
return Err(Error::Type("Body is null".to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 11. If the available deferred-fetch quota given request’s client and request’s URL’s
|
||||||
|
// origin is less than request’s total request length, then throw a "QuotaExceededError" DOMException.
|
||||||
|
// TODO
|
||||||
|
// Step 12. Let activated be false.
|
||||||
|
// Step 13. Let deferredRecord be the result of calling queue a deferred fetch given request,
|
||||||
|
// activateAfter, and the following step: set activated to true.
|
||||||
|
let deferred_record = queue_deferred_fetch(request, activate_after, global_scope);
|
||||||
|
// Step 14. Add the following abort steps to requestObject’s signal: Set deferredRecord’s invoke state to "aborted".
|
||||||
|
signal.add(&AbortAlgorithm::FetchLater(deferred_record.clone()));
|
||||||
|
// Step 15. Return a new FetchLaterResult whose activated getter steps are to return activated.
|
||||||
|
Ok(FetchLaterResult::new(window, deferred_record, can_gc))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#deferred-fetch-record-invoke-state>
|
||||||
|
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
|
||||||
|
enum DeferredFetchRecordInvokeState {
|
||||||
|
Pending,
|
||||||
|
Sent,
|
||||||
|
Aborted,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://fetch.spec.whatwg.org/#deferred-fetch-record>
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
|
pub(crate) struct DeferredFetchRecord {
|
||||||
|
/// <https://fetch.spec.whatwg.org/#deferred-fetch-record-request>
|
||||||
|
request: NetTraitsRequest,
|
||||||
|
/// <https://fetch.spec.whatwg.org/#deferred-fetch-record-invoke-state>
|
||||||
|
invoke_state: Cell<DeferredFetchRecordInvokeState>,
|
||||||
|
global: Trusted<GlobalScope>,
|
||||||
|
activated: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeferredFetchRecord {
|
||||||
|
/// Part of step 13 of <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
|
||||||
|
fn activate(&self) {
|
||||||
|
// and the following step: set activated to true.
|
||||||
|
self.activated.set(true);
|
||||||
|
}
|
||||||
|
/// Part of step 14 of <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
|
||||||
|
pub(crate) fn abort(&self) {
|
||||||
|
// Set deferredRecord’s invoke state to "aborted".
|
||||||
|
self.invoke_state
|
||||||
|
.set(DeferredFetchRecordInvokeState::Aborted);
|
||||||
|
}
|
||||||
|
/// Part of step 15 of <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
|
||||||
|
pub(crate) fn activated_getter_steps(&self) -> bool {
|
||||||
|
// whose activated getter steps are to return activated.
|
||||||
|
self.activated.get()
|
||||||
|
}
|
||||||
|
/// <https://fetch.spec.whatwg.org/#process-a-deferred-fetch>
|
||||||
|
fn process(&self) {
|
||||||
|
// Step 1. If deferredRecord’s invoke state is not "pending", then return.
|
||||||
|
if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Step 2. Set deferredRecord’s invoke state to "sent".
|
||||||
|
self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
|
||||||
|
// Step 3. Fetch deferredRecord’s request.
|
||||||
|
let url = self.request.url().clone();
|
||||||
|
let fetch_later_listener = Arc::new(Mutex::new(FetchLaterListener {
|
||||||
|
url,
|
||||||
|
resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
|
||||||
|
global: self.global.clone(),
|
||||||
|
}));
|
||||||
|
let global = self.global.root();
|
||||||
|
let _realm = enter_realm(&*global);
|
||||||
|
let mut request_init = request_init_from_request(self.request.clone());
|
||||||
|
request_init.policy_container =
|
||||||
|
RequestPolicyContainer::PolicyContainer(global.policy_container());
|
||||||
|
global.fetch(
|
||||||
|
request_init,
|
||||||
|
fetch_later_listener,
|
||||||
|
global.task_manager().networking_task_source().to_sendable(),
|
||||||
|
);
|
||||||
|
// Step 4 is handled by caller
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
pub(crate) struct FetchContext {
|
pub(crate) struct FetchContext {
|
||||||
#[ignore_malloc_size_of = "unclear ownership semantics"]
|
#[ignore_malloc_size_of = "unclear ownership semantics"]
|
||||||
|
@ -453,6 +645,74 @@ impl ResourceTimingListener for FetchContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FetchLaterListener {
|
||||||
|
/// URL of this request.
|
||||||
|
url: ServoUrl,
|
||||||
|
/// Timing data for this resource.
|
||||||
|
resource_timing: ResourceFetchTiming,
|
||||||
|
/// The global object fetching the report uri violation
|
||||||
|
global: Trusted<GlobalScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchResponseListener for FetchLaterListener {
|
||||||
|
fn process_request_body(&mut self, _: RequestId) {}
|
||||||
|
|
||||||
|
fn process_request_eof(&mut self, _: RequestId) {}
|
||||||
|
|
||||||
|
fn process_response(
|
||||||
|
&mut self,
|
||||||
|
_: RequestId,
|
||||||
|
fetch_metadata: Result<FetchMetadata, NetworkError>,
|
||||||
|
) {
|
||||||
|
_ = fetch_metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
|
||||||
|
_ = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_response_eof(
|
||||||
|
&mut self,
|
||||||
|
_: RequestId,
|
||||||
|
response: Result<ResourceFetchTiming, NetworkError>,
|
||||||
|
) {
|
||||||
|
_ = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
|
||||||
|
&mut self.resource_timing
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resource_timing(&self) -> &ResourceFetchTiming {
|
||||||
|
&self.resource_timing
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_resource_timing(&mut self) {
|
||||||
|
network_listener::submit_timing(self, CanGc::note())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
|
||||||
|
let global = self.resource_timing_global();
|
||||||
|
global.report_csp_violations(violations, None, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceTimingListener for FetchLaterListener {
|
||||||
|
fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
|
||||||
|
(InitiatorType::Fetch, self.url.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
|
||||||
|
self.global.root()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreInvoke for FetchLaterListener {
|
||||||
|
fn should_invoke(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
|
fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
|
||||||
r.set_headers(m.headers, can_gc);
|
r.set_headers(m.headers, can_gc);
|
||||||
r.set_status(&m.status);
|
r.set_status(&m.status);
|
||||||
|
|
|
@ -137,6 +137,7 @@ impl TaskManager {
|
||||||
task_source_functions!(self, clipboard_task_source, Clipboard);
|
task_source_functions!(self, clipboard_task_source, Clipboard);
|
||||||
task_source_functions!(self, crypto_task_source, Crypto);
|
task_source_functions!(self, crypto_task_source, Crypto);
|
||||||
task_source_functions!(self, database_access_task_source, DatabaseAccess);
|
task_source_functions!(self, database_access_task_source, DatabaseAccess);
|
||||||
|
task_source_functions!(self, deferred_fetch_task_source, DeferredFetch);
|
||||||
task_source_functions!(self, dom_manipulation_task_source, DOMManipulation);
|
task_source_functions!(self, dom_manipulation_task_source, DOMManipulation);
|
||||||
task_source_functions!(self, file_reading_task_source, FileReading);
|
task_source_functions!(self, file_reading_task_source, FileReading);
|
||||||
task_source_functions!(self, font_loading_task_source, FontLoading);
|
task_source_functions!(self, font_loading_task_source, FontLoading);
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub(crate) enum TaskSourceName {
|
||||||
/// <https://w3c.github.io/webcrypto/#dfn-crypto-task-source-0>
|
/// <https://w3c.github.io/webcrypto/#dfn-crypto-task-source-0>
|
||||||
Crypto,
|
Crypto,
|
||||||
DatabaseAccess,
|
DatabaseAccess,
|
||||||
|
/// <https://fetch.spec.whatwg.org/#deferred-fetch-task-source>
|
||||||
|
DeferredFetch,
|
||||||
DOMManipulation,
|
DOMManipulation,
|
||||||
FileReading,
|
FileReading,
|
||||||
/// <https://drafts.csswg.org/css-font-loading/#task-source>
|
/// <https://drafts.csswg.org/css-font-loading/#task-source>
|
||||||
|
@ -62,6 +64,7 @@ impl From<TaskSourceName> for ScriptThreadEventCategory {
|
||||||
TaskSourceName::Clipboard => ScriptThreadEventCategory::ScriptEvent,
|
TaskSourceName::Clipboard => ScriptThreadEventCategory::ScriptEvent,
|
||||||
TaskSourceName::Crypto => ScriptThreadEventCategory::ScriptEvent,
|
TaskSourceName::Crypto => ScriptThreadEventCategory::ScriptEvent,
|
||||||
TaskSourceName::DatabaseAccess => ScriptThreadEventCategory::ScriptEvent,
|
TaskSourceName::DatabaseAccess => ScriptThreadEventCategory::ScriptEvent,
|
||||||
|
TaskSourceName::DeferredFetch => ScriptThreadEventCategory::NetworkEvent,
|
||||||
TaskSourceName::DOMManipulation => ScriptThreadEventCategory::ScriptEvent,
|
TaskSourceName::DOMManipulation => ScriptThreadEventCategory::ScriptEvent,
|
||||||
TaskSourceName::FileReading => ScriptThreadEventCategory::FileRead,
|
TaskSourceName::FileReading => ScriptThreadEventCategory::FileRead,
|
||||||
TaskSourceName::FontLoading => ScriptThreadEventCategory::FontLoading,
|
TaskSourceName::FontLoading => ScriptThreadEventCategory::FontLoading,
|
||||||
|
|
|
@ -659,7 +659,7 @@ DOMInterfaces = {
|
||||||
},
|
},
|
||||||
|
|
||||||
'Window': {
|
'Window': {
|
||||||
'canGc': ['CreateImageBitmap', 'CreateImageBitmap_', 'CookieStore', 'Fetch', 'Open', 'SetInterval', 'SetTimeout', 'Stop', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'],
|
'canGc': ['CreateImageBitmap', 'CreateImageBitmap_', 'CookieStore', 'Fetch', 'FetchLater', 'Open', 'SetInterval', 'SetTimeout', 'Stop', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'],
|
||||||
'inRealms': ['Fetch', 'GetOpener', 'WebdriverCallback', 'WebdriverException'],
|
'inRealms': ['Fetch', 'GetOpener', 'WebdriverCallback', 'WebdriverException'],
|
||||||
'additionalTraits': ['crate::interfaces::WindowHelpers'],
|
'additionalTraits': ['crate::interfaces::WindowHelpers'],
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// https://fetch.spec.whatwg.org/#fetch-method
|
||||||
|
[Exposed=Window]
|
||||||
|
interface FetchLaterResult {
|
||||||
|
readonly attribute boolean activated;
|
||||||
|
};
|
|
@ -189,3 +189,12 @@ partial interface Window {
|
||||||
dictionary WindowPostMessageOptions : StructuredSerializeOptions {
|
dictionary WindowPostMessageOptions : StructuredSerializeOptions {
|
||||||
USVString targetOrigin = "/";
|
USVString targetOrigin = "/";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://fetch.spec.whatwg.org/#fetch-method
|
||||||
|
dictionary DeferredRequestInit : RequestInit {
|
||||||
|
DOMHighResTimeStamp activateAfter;
|
||||||
|
};
|
||||||
|
|
||||||
|
partial interface Window {
|
||||||
|
[NewObject, SecureContext, Throws] FetchLaterResult fetchLater(RequestInfo input, optional DeferredRequestInit init = {});
|
||||||
|
};
|
||||||
|
|
4
tests/wpt/include.ini
vendored
4
tests/wpt/include.ini
vendored
|
@ -140,6 +140,10 @@ skip: true
|
||||||
skip: false
|
skip: false
|
||||||
[fetch]
|
[fetch]
|
||||||
skip: false
|
skip: false
|
||||||
|
[fetch-later]
|
||||||
|
skip: false
|
||||||
|
[quota]
|
||||||
|
skip: true
|
||||||
[FileAPI]
|
[FileAPI]
|
||||||
skip: false
|
skip: false
|
||||||
[focus]
|
[focus]
|
||||||
|
|
12
tests/wpt/meta/MANIFEST.json
vendored
12
tests/wpt/meta/MANIFEST.json
vendored
|
@ -674692,10 +674692,10 @@
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"idlharness.any.js": [
|
"idlharness.https.any.js": [
|
||||||
"7b3c694e16ac3ec2776398067cf6ddbef949c969",
|
"7b3c694e16ac3ec2776398067cf6ddbef949c969",
|
||||||
[
|
[
|
||||||
"fetch/api/idlharness.any.html",
|
"fetch/api/idlharness.https.any.html",
|
||||||
{
|
{
|
||||||
"script_metadata": [
|
"script_metadata": [
|
||||||
[
|
[
|
||||||
|
@ -674719,7 +674719,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"fetch/api/idlharness.any.serviceworker.html",
|
"fetch/api/idlharness.https.any.serviceworker.html",
|
||||||
{
|
{
|
||||||
"script_metadata": [
|
"script_metadata": [
|
||||||
[
|
[
|
||||||
|
@ -674743,7 +674743,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"fetch/api/idlharness.any.sharedworker.html",
|
"fetch/api/idlharness.https.any.sharedworker.html",
|
||||||
{
|
{
|
||||||
"script_metadata": [
|
"script_metadata": [
|
||||||
[
|
[
|
||||||
|
@ -674767,7 +674767,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"fetch/api/idlharness.any.worker.html",
|
"fetch/api/idlharness.https.any.worker.html",
|
||||||
{
|
{
|
||||||
"script_metadata": [
|
"script_metadata": [
|
||||||
[
|
[
|
||||||
|
@ -680157,7 +680157,7 @@
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"basic.https.window.js": [
|
"basic.https.window.js": [
|
||||||
"f3ed42fe35a078942980ff44f66eee26a71f38cf",
|
"afedbf5d4c4fb6eb069e650911014a10e0d43b4a",
|
||||||
[
|
[
|
||||||
"fetch/fetch-later/basic.https.window.html",
|
"fetch/fetch-later/basic.https.window.html",
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[idlharness.any.worker.html]
|
[idlharness.https.any.html]
|
||||||
[Request interface: attribute keepalive]
|
[Request interface: attribute keepalive]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
||||||
[idlharness.any.sharedworker.html]
|
[idlharness.https.any.serviceworker.html]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
|
|
||||||
[idlharness.any.html]
|
[idlharness.https.any.worker.html]
|
||||||
[Request interface: attribute keepalive]
|
[Request interface: attribute keepalive]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -52,36 +52,6 @@
|
||||||
[Request interface: new Request('about:blank') must inherit property "duplex" with the proper type]
|
[Request interface: new Request('about:blank') must inherit property "duplex" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[FetchLaterResult interface: existence and properties of interface object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[FetchLaterResult interface object length]
|
[idlharness.https.any.sharedworker.html]
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[FetchLaterResult interface object name]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[FetchLaterResult interface: existence and properties of interface prototype object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[FetchLaterResult interface: existence and properties of interface prototype object's "constructor" property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[FetchLaterResult interface: existence and properties of interface prototype object's @@unscopables property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[FetchLaterResult interface: attribute activated]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Window interface: operation fetchLater(RequestInfo, optional DeferredRequestInit)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Window interface: window must inherit property "fetchLater(RequestInfo, optional DeferredRequestInit)" with the proper type]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Window interface: calling fetchLater(RequestInfo, optional DeferredRequestInit) on window with too few arguments must throw TypeError]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[idlharness.any.serviceworker.html]
|
|
||||||
expected: ERROR
|
expected: ERROR
|
3
tests/wpt/meta/fetch/fetch-later/__dir__.ini
vendored
Normal file
3
tests/wpt/meta/fetch/fetch-later/__dir__.ini
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
prefs: [
|
||||||
|
"dom_abort_controller_enabled:true",
|
||||||
|
]
|
|
@ -1,6 +1,4 @@
|
||||||
[activate-after.https.window.html]
|
[activate-after.https.window.html]
|
||||||
[fetchLater() sends out based on activateAfter.]
|
expected: TIMEOUT
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() sends out based on activateAfter, even if document is in BFCache.]
|
[fetchLater() sends out based on activateAfter, even if document is in BFCache.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
|
@ -1,69 +1,3 @@
|
||||||
[basic.https.window.html]
|
[basic.https.window.html]
|
||||||
[fetchLater() cannot be called without request.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with same-origin (https) URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with http://localhost URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with https://localhost URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with http://127.0.0.1 URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with https://127.0.0.1 URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with http://[::1\] URL does not throw.]
|
[fetchLater() with http://[::1\] URL does not throw.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[fetchLater() with https://[::1\] URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() with https://example.com URL does not throw.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws SecurityError on non-trustworthy http URL.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on file:// scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on ftp:// scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on ssh:// scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on wss:// scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on about: scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on javascript: scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on data: scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError on blob: scheme.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws RangeError on negative activateAfter.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater()'s return tells the deferred request is not yet sent.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws TypeError when mutating its returned state.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() throws AbortError when its initial abort signal is aborted.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[fetchLater() does not throw error when it is aborted before sending.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[header-referrer-no-referrer-when-downgrade.https.html]
|
|
||||||
[Test referer header https://web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[header-referrer-no-referrer.https.html]
|
|
||||||
[Test referer header ]
|
|
||||||
expected: FAIL
|
|
|
@ -1,6 +0,0 @@
|
||||||
[header-referrer-origin-when-cross-origin.https.html]
|
|
||||||
[Test referer header https://web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Test referer header https://www1.web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[header-referrer-origin.https.html]
|
|
||||||
[Test referer header https://www1.web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,6 +0,0 @@
|
||||||
[header-referrer-same-origin.https.html]
|
|
||||||
[Test referer header ]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Test referer header https://www1.web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[header-referrer-strict-origin-when-cross-origin.https.html]
|
|
||||||
[Test referer header https://www1.web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[header-referrer-strict-origin.https.html]
|
|
||||||
[Test referer header https://web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[header-referrer-unsafe-url.https.html]
|
|
||||||
[Test referer header https://web-platform.test:8443]
|
|
||||||
expected: FAIL
|
|
|
@ -1,4 +0,0 @@
|
||||||
[iframe.https.window.html]
|
|
||||||
expected: ERROR
|
|
||||||
[A blank iframe can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,36 +0,0 @@
|
||||||
[new-window.https.window.html]
|
|
||||||
[A blank window[target=''\][features=''\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A same-origin window[target=''\][features=''\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A cross-origin window[target=''\][features=''\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A blank window[target=''\][features='popup'\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A same-origin window[target=''\][features='popup'\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A cross-origin window[target=''\][features='popup'\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A blank window[target='_blank'\][features=''\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A same-origin window[target='_blank'\][features=''\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A cross-origin window[target='_blank'\][features=''\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A blank window[target='_blank'\][features='popup'\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A same-origin window[target='_blank'\][features='popup'\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A cross-origin window[target='_blank'\][features='popup'\] can trigger fetchLater.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,12 +1,3 @@
|
||||||
[deferred-fetch-allowed-by-permissions-policy.https.window.html]
|
[deferred-fetch-allowed-by-permissions-policy.https.window.html]
|
||||||
[Permissions policy header: "deferred-fetch=*" allows fetchLater() in the top-level document.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Permissions policy header: "deferred-fetch=*" allows fetchLater() in the same-origin iframe.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Permissions policy header: "deferred-fetch=*" allows fetchLater() in the cross-origin iframe.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Permissions policy header: "deferred-fetch=*" allow="deferred-fetch" allows fetchLater() in the cross-origin iframe.]
|
[Permissions policy header: "deferred-fetch=*" allow="deferred-fetch" allows fetchLater() in the cross-origin iframe.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
[deferred-fetch-default-permissions-policy.https.window.html]
|
|
||||||
[Default "deferred-fetch" permissions policy ["self"\] allows fetchLater() in the top-level document.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Default "deferred-fetch" permissions policy ["self"\] allows fetchLater() in the same-origin iframe.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Default "deferred-fetch-minimal" permissions policy ["*"\] allows fetchLater() in the cross-origin iframe.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[csp-allowed.https.window.html]
|
|
||||||
[FetchLater allowed by CSP should succeed]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[csp-blocked.https.window.html]
|
|
||||||
[FetchLater blocked by CSP should reject]
|
|
||||||
expected: FAIL
|
|
|
@ -1,15 +1,16 @@
|
||||||
[send-on-deactivate.https.window.html]
|
[send-on-deactivate.https.window.html]
|
||||||
|
expected: TIMEOUT
|
||||||
[fetchLater() sends on page entering BFCache if BackgroundSync is off.]
|
[fetchLater() sends on page entering BFCache if BackgroundSync is off.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Call fetchLater() when BFCached with activateAfter=0 sends immediately.]
|
[Call fetchLater() when BFCached with activateAfter=0 sends immediately.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[fetchLater() sends on navigating away a page w/o BFCache.]
|
[fetchLater() sends on navigating away a page w/o BFCache.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[fetchLater() does not send aborted request on navigating away a page w/o BFCache.]
|
[fetchLater() does not send aborted request on navigating away a page w/o BFCache.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[fetchLater() with activateAfter=1m sends on page entering BFCache if BackgroundSync is off.]
|
[fetchLater() with activateAfter=1m sends on page entering BFCache if BackgroundSync is off.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[not-send-after-abort.https.window.html]
|
|
||||||
[A discarded document does not send an already aborted fetchLater request.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[send-multiple.https.window.html]
|
|
||||||
[A discarded document sends all its fetchLater requests.]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/mozilla/meta/MANIFEST.json
vendored
2
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -13775,7 +13775,7 @@
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"interfaces.https.html": [
|
"interfaces.https.html": [
|
||||||
"1397b723a1cb001521ac1b2032c380f1e02cf1f0",
|
"027e138f4af5e2a13df9bf8c78765dcf57f6b611",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -92,6 +92,7 @@ test_interfaces([
|
||||||
"Event",
|
"Event",
|
||||||
"EventSource",
|
"EventSource",
|
||||||
"EventTarget",
|
"EventTarget",
|
||||||
|
"FetchLaterResult",
|
||||||
"File",
|
"File",
|
||||||
"FileList",
|
"FileList",
|
||||||
"FileReader",
|
"FileReader",
|
||||||
|
|
|
@ -52,11 +52,8 @@ test(() => {
|
||||||
}, `fetchLater() with https://example.com URL does not throw.`);
|
}, `fetchLater() with https://example.com URL does not throw.`);
|
||||||
|
|
||||||
test(() => {
|
test(() => {
|
||||||
const httpUrl = 'http://example.com';
|
assert_throws_js(TypeError, () => fetchLater('http://example.com'));
|
||||||
assert_throws_dom(
|
}, `fetchLater() throws TypeError on non-trustworthy http URL.`);
|
||||||
'SecurityError', () => fetchLater(httpUrl),
|
|
||||||
`should throw SecurityError for insecure http url ${httpUrl}`);
|
|
||||||
}, `fetchLater() throws SecurityError on non-trustworthy http URL.`);
|
|
||||||
|
|
||||||
test(() => {
|
test(() => {
|
||||||
assert_throws_js(TypeError, () => fetchLater('file://tmp'));
|
assert_throws_js(TypeError, () => fetchLater('file://tmp'));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue