script/
fetch.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::rc::Rc;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10use base::id::WebViewId;
11use ipc_channel::ipc;
12use js::jsapi::{ExceptionStackBehavior, JS_IsExceptionPending};
13use js::jsval::UndefinedValue;
14use js::rust::HandleValue;
15use js::rust::wrappers::JS_SetPendingException;
16use net_traits::policy_container::PolicyContainer;
17use net_traits::request::{
18    CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
19    Request as NetTraitsRequest, RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
20};
21use net_traits::{
22    CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseListener,
23    FetchResponseMsg, FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming,
24    ResourceTimingType, cancel_async_fetch,
25};
26use servo_url::ServoUrl;
27use timers::TimerEventRequest;
28
29use crate::body::BodyMixin;
30use crate::dom::abortsignal::AbortAlgorithm;
31use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
32use crate::dom::bindings::codegen::Bindings::RequestBinding::{
33    RequestInfo, RequestInit, RequestMethods,
34};
35use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
36use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
37use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
38use crate::dom::bindings::error::{Error, Fallible};
39use crate::dom::bindings::import::module::SafeJSContext;
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::num::Finite;
42use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
43use crate::dom::bindings::reflector::DomGlobal;
44use crate::dom::bindings::root::DomRoot;
45use crate::dom::bindings::trace::RootedTraceableBox;
46use crate::dom::csp::{GlobalCspReporting, Violation};
47use crate::dom::fetchlaterresult::FetchLaterResult;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::headers::Guard;
50use crate::dom::performance::performanceresourcetiming::InitiatorType;
51use crate::dom::promise::Promise;
52use crate::dom::request::Request;
53use crate::dom::response::Response;
54use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
55use crate::dom::window::Window;
56use crate::network_listener::{self, PreInvoke, ResourceTimingListener, submit_timing_data};
57use crate::realms::{InRealm, enter_realm};
58use crate::script_runtime::CanGc;
59
60/// RAII fetch canceller object.
61/// By default initialized to having a
62/// request associated with it, which can be manually cancelled with `cancel`,
63/// or automatically cancelled on drop.
64/// Calling `ignore` will sever the relationship with the request,
65/// meaning it cannot be cancelled through this canceller from that point on.
66#[derive(Default, JSTraceable, MallocSizeOf)]
67pub(crate) struct FetchCanceller {
68    #[no_trace]
69    request_id: Option<RequestId>,
70    #[no_trace]
71    core_resource_thread: Option<CoreResourceThread>,
72}
73
74impl FetchCanceller {
75    /// Create a FetchCanceller associated with a request,
76    // and a particular(public vs private) resource thread.
77    pub(crate) fn new(request_id: RequestId, core_resource_thread: CoreResourceThread) -> Self {
78        Self {
79            request_id: Some(request_id),
80            core_resource_thread: Some(core_resource_thread),
81        }
82    }
83
84    /// Cancel a fetch if it is ongoing
85    pub(crate) fn cancel(&mut self) {
86        if let Some(request_id) = self.request_id.take() {
87            // stop trying to make fetch happen
88            // it's not going to happen
89
90            if let Some(ref core_resource_thread) = self.core_resource_thread {
91                // No error handling here. Cancellation is a courtesy call,
92                // we don't actually care if the other side heard.
93                cancel_async_fetch(vec![request_id], core_resource_thread);
94            }
95        }
96    }
97
98    /// Use this if you don't want it to send a cancellation request
99    /// on drop (e.g. if the fetch completes)
100    pub(crate) fn ignore(&mut self) {
101        let _ = self.request_id.take();
102    }
103}
104
105impl Drop for FetchCanceller {
106    fn drop(&mut self) {
107        self.cancel()
108    }
109}
110
111fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
112    let mut builder =
113        RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
114            .method(request.method)
115            .headers(request.headers)
116            .unsafe_request(request.unsafe_request)
117            .body(request.body)
118            .destination(request.destination)
119            .synchronous(request.synchronous)
120            .mode(request.mode)
121            .cache_mode(request.cache_mode)
122            .use_cors_preflight(request.use_cors_preflight)
123            .credentials_mode(request.credentials_mode)
124            .use_url_credentials(request.use_url_credentials)
125            .referrer_policy(request.referrer_policy)
126            .pipeline_id(request.pipeline_id)
127            .redirect_mode(request.redirect_mode)
128            .integrity_metadata(request.integrity_metadata)
129            .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
130            .parser_metadata(request.parser_metadata)
131            .initiator(request.initiator)
132            .client(global.request_client())
133            .insecure_requests_policy(request.insecure_requests_policy)
134            .has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
135            .https_state(request.https_state)
136            .response_tainting(request.response_tainting);
137    builder.id = request.id;
138    builder
139}
140
141/// <https://fetch.spec.whatwg.org/#abort-fetch>
142fn abort_fetch_call(
143    promise: Rc<Promise>,
144    request: &Request,
145    response_object: Option<&Response>,
146    abort_reason: HandleValue,
147    global: &GlobalScope,
148    cx: SafeJSContext,
149    can_gc: CanGc,
150) {
151    // Step 1. Reject promise with error.
152    promise.reject(cx, abort_reason, can_gc);
153    // Step 2. If request’s body is non-null and is readable, then cancel request’s body with error.
154    if let Some(body) = request.body() {
155        if body.is_readable() {
156            body.cancel(cx, global, abort_reason, can_gc);
157        }
158    }
159    // Step 3. If responseObject is null, then return.
160    // Step 4. Let response be responseObject’s response.
161    let Some(response) = response_object else {
162        return;
163    };
164    // Step 5. If response’s body is non-null and is readable, then error response’s body with error.
165    if let Some(body) = response.body() {
166        if body.is_readable() {
167            body.error(abort_reason, can_gc);
168        }
169    }
170}
171
172/// <https://fetch.spec.whatwg.org/#dom-global-fetch>
173#[allow(non_snake_case)]
174#[cfg_attr(crown, allow(crown::unrooted_must_root))]
175pub(crate) fn Fetch(
176    global: &GlobalScope,
177    input: RequestInfo,
178    init: RootedTraceableBox<RequestInit>,
179    comp: InRealm,
180    can_gc: CanGc,
181) -> Rc<Promise> {
182    // Step 1. Let p be a new promise.
183    let promise = Promise::new_in_current_realm(comp, can_gc);
184    let cx = GlobalScope::get_cx();
185
186    // Step 7. Let responseObject be null.
187    // NOTE: We do initialize the object earlier earlier so we can use it to track errors
188    let response = Response::new(global, can_gc);
189    response.Headers(can_gc).set_guard(Guard::Immutable);
190
191    // Step 2. Let requestObject be the result of invoking the initial value of Request as constructor
192    //         with input and init as arguments. If this throws an exception, reject p with it and return p.
193    let request_object = match Request::Constructor(global, None, can_gc, input, init) {
194        Err(e) => {
195            response.error_stream(e.clone(), can_gc);
196            promise.reject_error(e, can_gc);
197            return promise;
198        },
199        Ok(r) => r,
200    };
201    // Step 3. Let request be requestObject’s request.
202    let request = request_object.get_request();
203    let timing_type = request.timing_type();
204    let request_id = request.id;
205
206    // Step 4. If requestObject’s signal is aborted, then:
207    let signal = request_object.Signal();
208    if signal.aborted() {
209        // Step 4.1. Abort the fetch() call with p, request, null, and requestObject’s signal’s abort reason.
210        rooted!(in(*cx) let mut abort_reason = UndefinedValue());
211        signal.Reason(cx, abort_reason.handle_mut());
212        abort_fetch_call(
213            promise.clone(),
214            &request_object,
215            None,
216            abort_reason.handle(),
217            global,
218            cx,
219            can_gc,
220        );
221        // Step 4.2. Return p.
222        return promise;
223    }
224
225    // Step 5. Let globalObject be request’s client’s global object.
226    // NOTE:   We already get the global object as an argument
227    let mut request_init = request_init_from_request(request, global);
228
229    // Step 6. If globalObject is a ServiceWorkerGlobalScope object, then set request’s
230    //         service-workers mode to "none".
231    if global.is::<ServiceWorkerGlobalScope>() {
232        request_init.service_workers_mode = ServiceWorkersMode::None;
233    }
234
235    // Step 8. Let relevantRealm be this’s relevant realm.
236    //
237    // Is `comp` as argument
238
239    // Step 9. Let locallyAborted be false.
240    // Step 10. Let controller be null.
241    let fetch_context = Arc::new(Mutex::new(FetchContext {
242        fetch_promise: Some(TrustedPromise::new(promise.clone())),
243        response_object: Trusted::new(&*response),
244        request: Trusted::new(&*request_object),
245        global: Trusted::new(global),
246        resource_timing: ResourceFetchTiming::new(timing_type),
247        locally_aborted: false,
248        canceller: FetchCanceller::new(request_id, global.core_resource_thread()),
249    }));
250
251    // Step 11. Add the following abort steps to requestObject’s signal:
252    signal.add(&AbortAlgorithm::Fetch(fetch_context.clone()));
253
254    // Step 12. Set controller to the result of calling fetch given request and
255    // processResponse given response being these steps:
256    global.fetch(
257        request_init,
258        fetch_context,
259        global.task_manager().networking_task_source().to_sendable(),
260    );
261
262    // Step 13. Return p.
263    promise
264}
265
266/// <https://fetch.spec.whatwg.org/#queue-a-deferred-fetch>
267fn queue_deferred_fetch(
268    request: NetTraitsRequest,
269    activate_after: Finite<f64>,
270    global: &GlobalScope,
271) -> Arc<Mutex<DeferredFetchRecord>> {
272    let trusted_global = Trusted::new(global);
273    // Step 1. Populate request from client given request.
274    // TODO
275    // Step 2. Set request’s service-workers mode to "none".
276    // TODO
277    // Step 3. Set request’s keepalive to true.
278    // TODO
279    // Step 4. Let deferredRecord be a new deferred fetch record whose request is request, and whose notify invoked is onActivatedWithoutTermination.
280    let deferred_record = Arc::new(Mutex::new(DeferredFetchRecord {
281        request,
282        global: trusted_global.clone(),
283        invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
284        activated: Cell::new(false),
285    }));
286    // Step 5. Append deferredRecord to request’s client’s fetch group’s deferred fetch records.
287    // TODO
288    // Step 6. If activateAfter is non-null, then run the following steps in parallel:
289    let deferred_record_clone = deferred_record.clone();
290    global.schedule_timer(TimerEventRequest {
291        callback: Box::new(move || {
292            // Step 6.2. Process deferredRecord.
293            deferred_record_clone.lock().unwrap().process();
294
295            // Last step of https://fetch.spec.whatwg.org/#process-a-deferred-fetch
296            //
297            // Step 4. Queue a global task on the deferred fetch task source with
298            // deferredRecord’s request’s client’s global object to run deferredRecord’s notify invoked.
299            let deferred_record_clone = deferred_record_clone.clone();
300            trusted_global
301                .root()
302                .task_manager()
303                .deferred_fetch_task_source()
304                .queue(task!(notify_deferred_record: move || {
305                    deferred_record_clone.lock().unwrap().activate();
306                }));
307        }),
308        // Step 6.1. The user agent should wait until any of the following conditions is met:
309        duration: Duration::from_millis(*activate_after as u64),
310    });
311    // Step 7. Return deferredRecord.
312    deferred_record
313}
314
315/// <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
316#[allow(non_snake_case, unsafe_code)]
317pub(crate) fn FetchLater(
318    window: &Window,
319    input: RequestInfo,
320    init: RootedTraceableBox<DeferredRequestInit>,
321    can_gc: CanGc,
322) -> Fallible<DomRoot<FetchLaterResult>> {
323    let global_scope = window.upcast();
324    // Step 1. Let requestObject be the result of invoking the initial value
325    // of Request as constructor with input and init as arguments.
326    let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
327    // Step 2. If requestObject’s signal is aborted, then throw signal’s abort reason.
328    let signal = request_object.Signal();
329    if signal.aborted() {
330        let cx = GlobalScope::get_cx();
331        rooted!(in(*cx) let mut abort_reason = UndefinedValue());
332        signal.Reason(cx, abort_reason.handle_mut());
333        unsafe {
334            assert!(!JS_IsExceptionPending(*cx));
335            JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
336        }
337        return Err(Error::JSFailed);
338    }
339    // Step 3. Let request be requestObject’s request.
340    let request = request_object.get_request();
341    // Step 4. Let activateAfter be null.
342    let mut activate_after = Finite::wrap(0_f64);
343    // Step 5. If init is given and init["activateAfter"] exists, then set
344    // activateAfter to init["activateAfter"].
345    if let Some(init_activate_after) = init.activateAfter.as_ref() {
346        activate_after = *init_activate_after;
347    }
348    // Step 6. If activateAfter is less than 0, then throw a RangeError.
349    if *activate_after < 0.0 {
350        return Err(Error::Range("activateAfter must be at least 0".to_owned()));
351    }
352    // Step 7. If this’s relevant global object’s associated document is not fully active, then throw a TypeError.
353    if !window.Document().is_fully_active() {
354        return Err(Error::Type("Document is not fully active".to_owned()));
355    }
356    let url = request.url();
357    // Step 8. If request’s URL’s scheme is not an HTTP(S) scheme, then throw a TypeError.
358    if !matches!(url.scheme(), "http" | "https") {
359        return Err(Error::Type("URL is not http(s)".to_owned()));
360    }
361    // Step 9. If request’s URL is not a potentially trustworthy URL, then throw a SecurityError.
362    if !url.is_potentially_trustworthy() {
363        return Err(Error::Type("URL is not trustworthy".to_owned()));
364    }
365    // Step 10. If request’s body is not null, and request’s body length is null, then throw a TypeError.
366    if let Some(body) = request.body.as_ref() {
367        if body.len().is_none() {
368            return Err(Error::Type("Body is null".to_owned()));
369        }
370    }
371    // Step 11. If the available deferred-fetch quota given request’s client and request’s URL’s
372    // origin is less than request’s total request length, then throw a "QuotaExceededError" DOMException.
373    // TODO
374    // Step 12. Let activated be false.
375    // Step 13. Let deferredRecord be the result of calling queue a deferred fetch given request,
376    // activateAfter, and the following step: set activated to true.
377    let deferred_record = queue_deferred_fetch(request, activate_after, global_scope);
378    // Step 14. Add the following abort steps to requestObject’s signal: Set deferredRecord’s invoke state to "aborted".
379    signal.add(&AbortAlgorithm::FetchLater(deferred_record.clone()));
380    // Step 15. Return a new FetchLaterResult whose activated getter steps are to return activated.
381    Ok(FetchLaterResult::new(window, deferred_record, can_gc))
382}
383
384/// <https://fetch.spec.whatwg.org/#deferred-fetch-record-invoke-state>
385#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
386enum DeferredFetchRecordInvokeState {
387    Pending,
388    Sent,
389    Aborted,
390}
391
392/// <https://fetch.spec.whatwg.org/#deferred-fetch-record>
393#[derive(MallocSizeOf)]
394pub(crate) struct DeferredFetchRecord {
395    /// <https://fetch.spec.whatwg.org/#deferred-fetch-record-request>
396    request: NetTraitsRequest,
397    /// <https://fetch.spec.whatwg.org/#deferred-fetch-record-invoke-state>
398    invoke_state: Cell<DeferredFetchRecordInvokeState>,
399    global: Trusted<GlobalScope>,
400    activated: Cell<bool>,
401}
402
403impl DeferredFetchRecord {
404    /// Part of step 13 of <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
405    fn activate(&self) {
406        // and the following step: set activated to true.
407        self.activated.set(true);
408    }
409    /// Part of step 14 of <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
410    pub(crate) fn abort(&self) {
411        // Set deferredRecord’s invoke state to "aborted".
412        self.invoke_state
413            .set(DeferredFetchRecordInvokeState::Aborted);
414    }
415    /// Part of step 15 of <https://fetch.spec.whatwg.org/#dom-window-fetchlater>
416    pub(crate) fn activated_getter_steps(&self) -> bool {
417        // whose activated getter steps are to return activated.
418        self.activated.get()
419    }
420    /// <https://fetch.spec.whatwg.org/#process-a-deferred-fetch>
421    fn process(&self) {
422        // Step 1. If deferredRecord’s invoke state is not "pending", then return.
423        if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
424            return;
425        }
426        // Step 2. Set deferredRecord’s invoke state to "sent".
427        self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
428        // Step 3. Fetch deferredRecord’s request.
429        let url = self.request.url().clone();
430        let fetch_later_listener = Arc::new(Mutex::new(FetchLaterListener {
431            url,
432            resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
433            global: self.global.clone(),
434        }));
435        let global = self.global.root();
436        let request_init = request_init_from_request(self.request.clone(), &global);
437        global.fetch(
438            request_init,
439            fetch_later_listener,
440            global.task_manager().networking_task_source().to_sendable(),
441        );
442        // Step 4 is handled by caller
443    }
444}
445
446#[derive(JSTraceable, MallocSizeOf)]
447pub(crate) struct FetchContext {
448    #[ignore_malloc_size_of = "unclear ownership semantics"]
449    fetch_promise: Option<TrustedPromise>,
450    response_object: Trusted<Response>,
451    request: Trusted<Request>,
452    global: Trusted<GlobalScope>,
453    #[no_trace]
454    resource_timing: ResourceFetchTiming,
455    locally_aborted: bool,
456    canceller: FetchCanceller,
457}
458
459impl PreInvoke for FetchContext {}
460
461impl FetchContext {
462    /// Step 11 of <https://fetch.spec.whatwg.org/#dom-global-fetch>
463    pub(crate) fn abort_fetch(
464        &mut self,
465        abort_reason: HandleValue,
466        cx: SafeJSContext,
467        can_gc: CanGc,
468    ) {
469        // Step 11.1. Set locallyAborted to true.
470        self.locally_aborted = true;
471        // Step 11.2. Assert: controller is non-null.
472        //
473        // N/a, that's self
474
475        // Step 11.3. Abort controller with requestObject’s signal’s abort reason.
476        self.canceller.cancel();
477
478        // Step 11.4. Abort the fetch() call with p, request, responseObject,
479        // and requestObject’s signal’s abort reason.
480        let promise = self
481            .fetch_promise
482            .take()
483            .expect("fetch promise is missing")
484            .root();
485        abort_fetch_call(
486            promise,
487            &self.request.root(),
488            Some(&self.response_object.root()),
489            abort_reason,
490            &self.global.root(),
491            cx,
492            can_gc,
493        );
494    }
495}
496
497/// Step 12 of <https://fetch.spec.whatwg.org/#dom-global-fetch>
498impl FetchResponseListener for FetchContext {
499    fn process_request_body(&mut self, _: RequestId) {
500        // TODO
501    }
502
503    fn process_request_eof(&mut self, _: RequestId) {
504        // TODO
505    }
506
507    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
508    fn process_response(
509        &mut self,
510        _: RequestId,
511        fetch_metadata: Result<FetchMetadata, NetworkError>,
512    ) {
513        // Step 12.1. If locallyAborted is true, then abort these steps.
514        if self.locally_aborted {
515            return;
516        }
517        let promise = self
518            .fetch_promise
519            .take()
520            .expect("fetch promise is missing")
521            .root();
522
523        let _ac = enter_realm(&*promise);
524        match fetch_metadata {
525            // Step 12.3. If response is a network error, then reject
526            // p with a TypeError and abort these steps.
527            Err(_) => {
528                promise.reject_error(
529                    Error::Type("Network error occurred".to_string()),
530                    CanGc::note(),
531                );
532                self.fetch_promise = Some(TrustedPromise::new(promise));
533                let response = self.response_object.root();
534                response.set_type(DOMResponseType::Error, CanGc::note());
535                response.error_stream(
536                    Error::Type("Network error occurred".to_string()),
537                    CanGc::note(),
538                );
539                return;
540            },
541            // Step 12.4. Set responseObject to the result of creating a Response object,
542            // given response, "immutable", and relevantRealm.
543            Ok(metadata) => match metadata {
544                FetchMetadata::Unfiltered(m) => {
545                    fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
546                    self.response_object
547                        .root()
548                        .set_type(DOMResponseType::Default, CanGc::note());
549                },
550                FetchMetadata::Filtered { filtered, .. } => match filtered {
551                    FilteredMetadata::Basic(m) => {
552                        fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
553                        self.response_object
554                            .root()
555                            .set_type(DOMResponseType::Basic, CanGc::note());
556                    },
557                    FilteredMetadata::Cors(m) => {
558                        fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
559                        self.response_object
560                            .root()
561                            .set_type(DOMResponseType::Cors, CanGc::note());
562                    },
563                    FilteredMetadata::Opaque => {
564                        self.response_object
565                            .root()
566                            .set_type(DOMResponseType::Opaque, CanGc::note());
567                    },
568                    FilteredMetadata::OpaqueRedirect(url) => {
569                        let r = self.response_object.root();
570                        r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
571                        r.set_final_url(url);
572                    },
573                },
574            },
575        }
576
577        // Step 12.5. Resolve p with responseObject.
578        promise.resolve_native(&self.response_object.root(), CanGc::note());
579        self.fetch_promise = Some(TrustedPromise::new(promise));
580    }
581
582    fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
583        let response = self.response_object.root();
584        response.stream_chunk(chunk, CanGc::note());
585    }
586
587    fn process_response_eof(
588        &mut self,
589        _: RequestId,
590        _response: Result<ResourceFetchTiming, NetworkError>,
591    ) {
592        let response = self.response_object.root();
593        let _ac = enter_realm(&*response);
594        response.finish(CanGc::note());
595        // TODO
596        // ... trailerObject is not supported in Servo yet.
597    }
598
599    fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
600        &mut self.resource_timing
601    }
602
603    fn resource_timing(&self) -> &ResourceFetchTiming {
604        &self.resource_timing
605    }
606
607    fn submit_resource_timing(&mut self) {
608        // navigation submission is handled in servoparser/mod.rs
609        if self.resource_timing.timing_type == ResourceTimingType::Resource {
610            network_listener::submit_timing(self, CanGc::note())
611        }
612    }
613
614    fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
615        let global = &self.resource_timing_global();
616        global.report_csp_violations(violations, None, None);
617    }
618}
619
620impl ResourceTimingListener for FetchContext {
621    fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
622        (
623            InitiatorType::Fetch,
624            self.resource_timing_global().get_url().clone(),
625        )
626    }
627
628    fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
629        self.response_object.root().global()
630    }
631}
632
633struct FetchLaterListener {
634    /// URL of this request.
635    url: ServoUrl,
636    /// Timing data for this resource.
637    resource_timing: ResourceFetchTiming,
638    /// The global object fetching the report uri violation
639    global: Trusted<GlobalScope>,
640}
641
642impl FetchResponseListener for FetchLaterListener {
643    fn process_request_body(&mut self, _: RequestId) {}
644
645    fn process_request_eof(&mut self, _: RequestId) {}
646
647    fn process_response(
648        &mut self,
649        _: RequestId,
650        fetch_metadata: Result<FetchMetadata, NetworkError>,
651    ) {
652        _ = fetch_metadata;
653    }
654
655    fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
656        _ = chunk;
657    }
658
659    fn process_response_eof(
660        &mut self,
661        _: RequestId,
662        response: Result<ResourceFetchTiming, NetworkError>,
663    ) {
664        _ = response;
665    }
666
667    fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
668        &mut self.resource_timing
669    }
670
671    fn resource_timing(&self) -> &ResourceFetchTiming {
672        &self.resource_timing
673    }
674
675    fn submit_resource_timing(&mut self) {
676        network_listener::submit_timing(self, CanGc::note())
677    }
678
679    fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
680        let global = self.resource_timing_global();
681        global.report_csp_violations(violations, None, None);
682    }
683}
684
685impl ResourceTimingListener for FetchLaterListener {
686    fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
687        (InitiatorType::Fetch, self.url.clone())
688    }
689
690    fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
691        self.global.root()
692    }
693}
694
695impl PreInvoke for FetchLaterListener {
696    fn should_invoke(&self) -> bool {
697        true
698    }
699}
700
701fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
702    r.set_headers(m.headers, can_gc);
703    r.set_status(&m.status);
704    r.set_final_url(m.final_url);
705    r.set_redirected(m.redirected);
706}
707
708pub(crate) trait CspViolationsProcessor {
709    fn process_csp_violations(&self, violations: Vec<Violation>);
710}
711
712/// Convenience function for synchronously loading a whole resource.
713pub(crate) fn load_whole_resource(
714    request: RequestBuilder,
715    core_resource_thread: &CoreResourceThread,
716    global: &GlobalScope,
717    csp_violations_processor: &dyn CspViolationsProcessor,
718    can_gc: CanGc,
719) -> Result<(Metadata, Vec<u8>), NetworkError> {
720    let request = request.https_state(global.get_https_state());
721    let (action_sender, action_receiver) = ipc::channel().unwrap();
722    let url = request.url.clone();
723    core_resource_thread
724        .send(CoreResourceMsg::Fetch(
725            request,
726            FetchChannels::ResponseMsg(action_sender),
727        ))
728        .unwrap();
729
730    let mut buf = vec![];
731    let mut metadata = None;
732    loop {
733        match action_receiver.recv().unwrap() {
734            FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
735            },
736            FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
737                metadata = Some(match m {
738                    FetchMetadata::Unfiltered(m) => m,
739                    FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
740                })
741            },
742            FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
743            FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => {
744                let metadata = metadata.unwrap();
745                if let Some(timing) = &metadata.timing {
746                    submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
747                }
748                return Ok((metadata, buf));
749            },
750            FetchResponseMsg::ProcessResponse(_, Err(e)) |
751            FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e),
752            FetchResponseMsg::ProcessCspViolations(_, violations) => {
753                csp_violations_processor.process_csp_violations(violations);
754            },
755        }
756    }
757}
758
759/// <https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request>
760#[allow(clippy::too_many_arguments)]
761pub(crate) fn create_a_potential_cors_request(
762    webview_id: Option<WebViewId>,
763    url: ServoUrl,
764    destination: Destination,
765    cors_setting: Option<CorsSettings>,
766    same_origin_fallback: Option<bool>,
767    referrer: Referrer,
768    insecure_requests_policy: InsecureRequestsPolicy,
769    has_trustworthy_ancestor_origin: bool,
770    policy_container: PolicyContainer,
771) -> RequestBuilder {
772    RequestBuilder::new(webview_id, url, referrer)
773        // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
774        // Step 1
775        .mode(match cors_setting {
776            Some(_) => RequestMode::CorsMode,
777            None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
778            None => RequestMode::NoCors,
779        })
780        // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
781        // Step 3-4
782        .credentials_mode(match cors_setting {
783            Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
784            _ => CredentialsMode::Include,
785        })
786        // Step 5
787        .destination(destination)
788        .use_url_credentials(true)
789        .insecure_requests_policy(insecure_requests_policy)
790        .has_trustworthy_ancestor_origin(has_trustworthy_ancestor_origin)
791        .policy_container(policy_container)
792}