script/dom/
request.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::rc::Rc;
6use std::str::FromStr;
7
8use cssparser::match_ignore_ascii_case;
9use dom_struct::dom_struct;
10use http::Method as HttpMethod;
11use http::header::{HeaderName, HeaderValue};
12use http::method::InvalidMethod;
13use js::rust::HandleObject;
14use net_traits::ReferrerPolicy as MsgReferrerPolicy;
15use net_traits::fetch::headers::is_forbidden_method;
16use net_traits::request::{
17    CacheMode as NetTraitsRequestCache, CredentialsMode as NetTraitsRequestCredentials,
18    Destination as NetTraitsRequestDestination, Origin, RedirectMode as NetTraitsRequestRedirect,
19    Referrer as NetTraitsRequestReferrer, Request as NetTraitsRequest, RequestBuilder,
20    RequestMode as NetTraitsRequestMode, Window,
21};
22use servo_url::ServoUrl;
23
24use crate::body::{BodyMixin, BodyType, Extractable, consume_body};
25use crate::conversions::Convert;
26use crate::dom::bindings::cell::DomRefCell;
27use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
28use crate::dom::bindings::codegen::Bindings::RequestBinding::{
29    ReferrerPolicy, RequestCache, RequestCredentials, RequestDestination, RequestInfo, RequestInit,
30    RequestMethods, RequestMode, RequestRedirect,
31};
32use crate::dom::bindings::error::{Error, Fallible};
33use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
34use crate::dom::bindings::root::{DomRoot, MutNullableDom};
35use crate::dom::bindings::str::{ByteString, DOMString, USVString};
36use crate::dom::bindings::trace::RootedTraceableBox;
37use crate::dom::globalscope::GlobalScope;
38use crate::dom::headers::{Guard, Headers};
39use crate::dom::promise::Promise;
40use crate::dom::readablestream::ReadableStream;
41use crate::script_runtime::CanGc;
42
43#[dom_struct]
44pub(crate) struct Request {
45    reflector_: Reflector,
46    #[no_trace]
47    request: DomRefCell<NetTraitsRequest>,
48    body_stream: MutNullableDom<ReadableStream>,
49    headers: MutNullableDom<Headers>,
50}
51
52impl Request {
53    fn new_inherited(global: &GlobalScope, url: ServoUrl) -> Request {
54        Request {
55            reflector_: Reflector::new(),
56            request: DomRefCell::new(net_request_from_global(global, url)),
57            body_stream: MutNullableDom::new(None),
58            headers: Default::default(),
59        }
60    }
61
62    fn new(
63        global: &GlobalScope,
64        proto: Option<HandleObject>,
65        url: ServoUrl,
66        can_gc: CanGc,
67    ) -> DomRoot<Request> {
68        reflect_dom_object_with_proto(
69            Box::new(Request::new_inherited(global, url)),
70            global,
71            proto,
72            can_gc,
73        )
74    }
75
76    fn from_net_request(
77        global: &GlobalScope,
78        proto: Option<HandleObject>,
79        net_request: NetTraitsRequest,
80        can_gc: CanGc,
81    ) -> DomRoot<Request> {
82        let r = Request::new(global, proto, net_request.current_url(), can_gc);
83        *r.request.borrow_mut() = net_request;
84        r
85    }
86
87    fn clone_from(r: &Request, can_gc: CanGc) -> Fallible<DomRoot<Request>> {
88        let req = r.request.borrow();
89        let url = req.url();
90        let headers_guard = r.Headers(can_gc).get_guard();
91        let r_clone = Request::new(&r.global(), None, url, can_gc);
92        r_clone.request.borrow_mut().pipeline_id = req.pipeline_id;
93        {
94            let mut borrowed_r_request = r_clone.request.borrow_mut();
95            borrowed_r_request.origin = req.origin.clone();
96        }
97        *r_clone.request.borrow_mut() = req.clone();
98        r_clone
99            .Headers(can_gc)
100            .copy_from_headers(r.Headers(can_gc))?;
101        r_clone.Headers(can_gc).set_guard(headers_guard);
102        Ok(r_clone)
103    }
104
105    pub(crate) fn get_request(&self) -> NetTraitsRequest {
106        self.request.borrow().clone()
107    }
108}
109
110fn net_request_from_global(global: &GlobalScope, url: ServoUrl) -> NetTraitsRequest {
111    RequestBuilder::new(global.webview_id(), url, global.get_referrer())
112        .origin(global.get_url().origin())
113        .pipeline_id(Some(global.pipeline_id()))
114        .https_state(global.get_https_state())
115        .insecure_requests_policy(global.insecure_requests_policy())
116        .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_or_current_origin())
117        .build()
118}
119
120// https://fetch.spec.whatwg.org/#concept-method-normalize
121fn normalize_method(m: &str) -> Result<HttpMethod, InvalidMethod> {
122    match_ignore_ascii_case! { m,
123        "delete" => return Ok(HttpMethod::DELETE),
124        "get" => return Ok(HttpMethod::GET),
125        "head" => return Ok(HttpMethod::HEAD),
126        "options" => return Ok(HttpMethod::OPTIONS),
127        "post" => return Ok(HttpMethod::POST),
128        "put" => return Ok(HttpMethod::PUT),
129        _ => (),
130    }
131    debug!("Method: {:?}", m);
132    HttpMethod::from_str(m)
133}
134
135// https://fetch.spec.whatwg.org/#concept-method
136fn is_method(m: &ByteString) -> bool {
137    m.as_str().is_some()
138}
139
140// https://fetch.spec.whatwg.org/#cors-safelisted-method
141fn is_cors_safelisted_method(m: &HttpMethod) -> bool {
142    m == HttpMethod::GET || m == HttpMethod::HEAD || m == HttpMethod::POST
143}
144
145// https://url.spec.whatwg.org/#include-credentials
146fn includes_credentials(input: &ServoUrl) -> bool {
147    !input.username().is_empty() || input.password().is_some()
148}
149
150// https://fetch.spec.whatwg.org/#concept-body-disturbed
151fn request_is_disturbed(input: &Request) -> bool {
152    input.is_disturbed()
153}
154
155// https://fetch.spec.whatwg.org/#concept-body-locked
156fn request_is_locked(input: &Request) -> bool {
157    input.is_locked()
158}
159
160impl RequestMethods<crate::DomTypeHolder> for Request {
161    // https://fetch.spec.whatwg.org/#dom-request
162    fn Constructor(
163        global: &GlobalScope,
164        proto: Option<HandleObject>,
165        can_gc: CanGc,
166        mut input: RequestInfo,
167        init: RootedTraceableBox<RequestInit>,
168    ) -> Fallible<DomRoot<Request>> {
169        // Step 1
170        let temporary_request: NetTraitsRequest;
171
172        // Step 2
173        let mut fallback_mode: Option<NetTraitsRequestMode> = None;
174
175        // Step 3
176        let base_url = global.api_base_url();
177
178        // Step 4 TODO: "Let signal be null."
179
180        match input {
181            // Step 5
182            RequestInfo::USVString(USVString(ref usv_string)) => {
183                // Step 5.1
184                let parsed_url = base_url.join(usv_string);
185                // Step 5.2
186                if parsed_url.is_err() {
187                    return Err(Error::Type("Url could not be parsed".to_string()));
188                }
189                // Step 5.3
190                let url = parsed_url.unwrap();
191                if includes_credentials(&url) {
192                    return Err(Error::Type("Url includes credentials".to_string()));
193                }
194                // Step 5.4
195                temporary_request = net_request_from_global(global, url);
196                // Step 5.5
197                fallback_mode = Some(NetTraitsRequestMode::CorsMode);
198            },
199            // Step 6
200            RequestInfo::Request(ref input_request) => {
201                // This looks like Step 38
202                // TODO do this in the right place to not mask other errors
203                if request_is_disturbed(input_request) || request_is_locked(input_request) {
204                    return Err(Error::Type("Input is disturbed or locked".to_string()));
205                }
206                // Step 6.1
207                temporary_request = input_request.request.borrow().clone();
208                // Step 6.2 TODO: "Set signal to input's signal."
209            },
210        }
211
212        // Step 7
213        // TODO: `entry settings object` is not implemented yet.
214        let origin = base_url.origin();
215
216        // Step 8
217        let mut window = Window::Client;
218
219        // Step 9
220        // TODO: `environment settings object` is not implemented in Servo yet.
221
222        // Step 10
223        if !init.window.handle().is_null_or_undefined() {
224            return Err(Error::Type("Window is present and is not null".to_string()));
225        }
226
227        // Step 11
228        if !init.window.handle().is_undefined() {
229            window = Window::NoWindow;
230        }
231
232        // Step 12
233        let mut request: NetTraitsRequest;
234        request = net_request_from_global(global, temporary_request.current_url());
235        request.method = temporary_request.method;
236        request.headers = temporary_request.headers.clone();
237        request.unsafe_request = true;
238        request.window = window;
239        // TODO: `entry settings object` is not implemented in Servo yet.
240        request.origin = Origin::Client;
241        request.referrer = temporary_request.referrer;
242        request.referrer_policy = temporary_request.referrer_policy;
243        request.mode = temporary_request.mode;
244        request.credentials_mode = temporary_request.credentials_mode;
245        request.cache_mode = temporary_request.cache_mode;
246        request.redirect_mode = temporary_request.redirect_mode;
247        request.integrity_metadata = temporary_request.integrity_metadata;
248
249        // Step 13
250        if init.body.is_some() ||
251            init.cache.is_some() ||
252            init.credentials.is_some() ||
253            init.integrity.is_some() ||
254            init.headers.is_some() ||
255            init.method.is_some() ||
256            init.mode.is_some() ||
257            init.redirect.is_some() ||
258            init.referrer.is_some() ||
259            init.referrerPolicy.is_some() ||
260            !init.window.handle().is_undefined()
261        {
262            // Step 13.1
263            if request.mode == NetTraitsRequestMode::Navigate {
264                request.mode = NetTraitsRequestMode::SameOrigin;
265            }
266            // Step 13.2 TODO: "Unset request's reload-navigation flag."
267            // Step 13.3 TODO: "Unset request's history-navigation flag."
268            // Step 13.4
269            request.referrer = global.get_referrer();
270            // Step 13.5
271            request.referrer_policy = MsgReferrerPolicy::EmptyString;
272        }
273
274        // Step 14
275        if let Some(init_referrer) = init.referrer.as_ref() {
276            // Step 14.1
277            let referrer = &init_referrer.0;
278            // Step 14.2
279            if referrer.is_empty() {
280                request.referrer = NetTraitsRequestReferrer::NoReferrer;
281            } else {
282                // Step 14.3.1
283                let parsed_referrer = base_url.join(referrer);
284                // Step 14.3.2
285                if parsed_referrer.is_err() {
286                    return Err(Error::Type("Failed to parse referrer url".to_string()));
287                }
288                // Step 14.3.3
289                if let Ok(parsed_referrer) = parsed_referrer {
290                    if (parsed_referrer.cannot_be_a_base() &&
291                        parsed_referrer.scheme() == "about" &&
292                        parsed_referrer.path() == "client") ||
293                        parsed_referrer.origin() != origin
294                    {
295                        request.referrer = global.get_referrer();
296                    } else {
297                        // Step 14.3.4
298                        request.referrer = NetTraitsRequestReferrer::ReferrerUrl(parsed_referrer);
299                    }
300                }
301            }
302        }
303
304        // Step 15
305        if let Some(init_referrerpolicy) = init.referrerPolicy.as_ref() {
306            let init_referrer_policy = (*init_referrerpolicy).convert();
307            request.referrer_policy = init_referrer_policy;
308        }
309
310        // Step 16
311        let mode = init.mode.as_ref().map(|m| (*m).convert()).or(fallback_mode);
312
313        // Step 17
314        if let Some(NetTraitsRequestMode::Navigate) = mode {
315            return Err(Error::Type("Request mode is Navigate".to_string()));
316        }
317
318        // Step 18
319        if let Some(m) = mode {
320            request.mode = m;
321        }
322
323        // Step 19
324        if let Some(init_credentials) = init.credentials.as_ref() {
325            let credentials = (*init_credentials).convert();
326            request.credentials_mode = credentials;
327        }
328
329        // Step 20
330        if let Some(init_cache) = init.cache.as_ref() {
331            let cache = (*init_cache).convert();
332            request.cache_mode = cache;
333        }
334
335        // Step 21
336        if request.cache_mode == NetTraitsRequestCache::OnlyIfCached &&
337            request.mode != NetTraitsRequestMode::SameOrigin
338        {
339            return Err(Error::Type(
340                "Cache is 'only-if-cached' and mode is not 'same-origin'".to_string(),
341            ));
342        }
343
344        // Step 22
345        if let Some(init_redirect) = init.redirect.as_ref() {
346            let redirect = (*init_redirect).convert();
347            request.redirect_mode = redirect;
348        }
349
350        // Step 23
351        if let Some(init_integrity) = init.integrity.as_ref() {
352            let integrity = init_integrity.clone().to_string();
353            request.integrity_metadata = integrity;
354        }
355
356        // Step 24 TODO: "If init["keepalive"] exists..."
357
358        // Step 25.1
359        if let Some(init_method) = init.method.as_ref() {
360            if !is_method(init_method) {
361                return Err(Error::Type("Method is not a method".to_string()));
362            }
363            // Step 25.2
364            if is_forbidden_method(init_method) {
365                return Err(Error::Type("Method is forbidden".to_string()));
366            }
367            // Step 25.3
368            let method = match init_method.as_str() {
369                Some(s) => normalize_method(s)
370                    .map_err(|e| Error::Type(format!("Method is not valid: {:?}", e)))?,
371                None => return Err(Error::Type("Method is not a valid UTF8".to_string())),
372            };
373            // Step 25.4
374            request.method = method;
375        }
376
377        // Step 26 TODO: "If init["signal"] exists..."
378        // Step 27 TODO: "If init["priority"] exists..."
379
380        // Step 28
381        let r = Request::from_net_request(global, proto, request, can_gc);
382
383        // Step 29 TODO: "Set this's signal to new AbortSignal object..."
384        // Step 30 TODO: "If signal is not null..."
385
386        // Step 31
387        // "or_init" looks unclear here, but it always enters the block since r
388        // hasn't had any other way to initialize its headers
389        r.headers
390            .or_init(|| Headers::for_request(&r.global(), can_gc));
391
392        // Step 33 - but spec says this should only be when non-empty init?
393        let headers_copy = init
394            .headers
395            .as_ref()
396            .map(|possible_header| match possible_header {
397                HeadersInit::ByteStringSequenceSequence(init_sequence) => {
398                    HeadersInit::ByteStringSequenceSequence(init_sequence.clone())
399                },
400                HeadersInit::ByteStringByteStringRecord(init_map) => {
401                    HeadersInit::ByteStringByteStringRecord(init_map.clone())
402                },
403            });
404
405        // Step 33.3
406        // We cannot empty `r.Headers().header_list` because
407        // we would undo the Step 25 above.  One alternative is to set
408        // `headers_copy` as a deep copy of `r.Headers()`. However,
409        // `r.Headers()` is a `DomRoot<T>`, and therefore it is difficult
410        // to obtain a mutable reference to `r.Headers()`. Without the
411        // mutable reference, we cannot mutate `r.Headers()` to be the
412        // deep copied headers in Step 25.
413
414        // Step 32
415        if r.request.borrow().mode == NetTraitsRequestMode::NoCors {
416            let borrowed_request = r.request.borrow();
417            // Step 32.1
418            if !is_cors_safelisted_method(&borrowed_request.method) {
419                return Err(Error::Type(
420                    "The mode is 'no-cors' but the method is not a cors-safelisted method"
421                        .to_string(),
422                ));
423            }
424            // Step 32.2
425            r.Headers(can_gc).set_guard(Guard::RequestNoCors);
426        }
427
428        // Step 33.5
429        match headers_copy {
430            None => {
431                // This is equivalent to the specification's concept of
432                // "associated headers list". If an init headers is not given,
433                // but an input with headers is given, set request's
434                // headers as the input's Headers.
435                if let RequestInfo::Request(ref input_request) = input {
436                    r.Headers(can_gc)
437                        .copy_from_headers(input_request.Headers(can_gc))?;
438                }
439            },
440            Some(headers_copy) => r.Headers(can_gc).fill(Some(headers_copy))?,
441        }
442
443        // Step 33.5 depending on how we got here
444        // Copy the headers list onto the headers of net_traits::Request
445        r.request.borrow_mut().headers = r.Headers(can_gc).get_headers_list();
446
447        // Step 34
448        let mut input_body = if let RequestInfo::Request(ref mut input_request) = input {
449            let mut input_request_request = input_request.request.borrow_mut();
450            input_request_request.body.take()
451        } else {
452            None
453        };
454
455        // Step 35
456        if let Some(init_body_option) = init.body.as_ref() {
457            if init_body_option.is_some() || input_body.is_some() {
458                let req = r.request.borrow();
459                let req_method = &req.method;
460                match *req_method {
461                    HttpMethod::GET => {
462                        return Err(Error::Type(
463                            "Init's body is non-null, and request method is GET".to_string(),
464                        ));
465                    },
466                    HttpMethod::HEAD => {
467                        return Err(Error::Type(
468                            "Init's body is non-null, and request method is HEAD".to_string(),
469                        ));
470                    },
471                    _ => {},
472                }
473            }
474        }
475
476        // Step 36-37
477        if let Some(Some(ref init_body)) = init.body {
478            // Step 37.1 TODO "If init["keepalive"] exists and is true..."
479
480            // Step 37.2
481            let mut extracted_body = init_body.extract(global, can_gc)?;
482
483            // Step 37.3
484            if let Some(contents) = extracted_body.content_type.take() {
485                let ct_header_name = b"Content-Type";
486                if !r
487                    .Headers(can_gc)
488                    .Has(ByteString::new(ct_header_name.to_vec()))
489                    .unwrap()
490                {
491                    let ct_header_val = contents.as_bytes();
492                    r.Headers(can_gc).Append(
493                        ByteString::new(ct_header_name.to_vec()),
494                        ByteString::new(ct_header_val.to_vec()),
495                    )?;
496
497                    // Step 37.4
498                    // In Servo r.Headers's header list isn't a pointer to
499                    // the same actual list as r.request's, and so we need to
500                    // append to both lists to keep them in sync.
501                    if let Ok(v) = HeaderValue::from_bytes(ct_header_val) {
502                        r.request
503                            .borrow_mut()
504                            .headers
505                            .insert(HeaderName::from_bytes(ct_header_name).unwrap(), v);
506                    }
507                }
508            }
509
510            let (net_body, stream) = extracted_body.into_net_request_body();
511            r.body_stream.set(Some(&*stream));
512            input_body = Some(net_body);
513        }
514
515        // Step 38 is done earlier
516
517        // Step 39 "TODO if body is non-null and body's source is null..."
518        // This looks like where we need to set the use-preflight flag
519        // if the request has a body and nothing else has set the flag.
520
521        // Step 40 is done earlier
522
523        // Step 41
524        r.request.borrow_mut().body = input_body;
525
526        // Step 42
527        Ok(r)
528    }
529
530    // https://fetch.spec.whatwg.org/#dom-request-method
531    fn Method(&self) -> ByteString {
532        let r = self.request.borrow();
533        ByteString::new(r.method.as_ref().as_bytes().into())
534    }
535
536    // https://fetch.spec.whatwg.org/#dom-request-url
537    fn Url(&self) -> USVString {
538        let r = self.request.borrow();
539        USVString(r.url_list.first().map_or("", |u| u.as_str()).into())
540    }
541
542    // https://fetch.spec.whatwg.org/#dom-request-headers
543    fn Headers(&self, can_gc: CanGc) -> DomRoot<Headers> {
544        self.headers
545            .or_init(|| Headers::new(&self.global(), can_gc))
546    }
547
548    // https://fetch.spec.whatwg.org/#dom-request-destination
549    fn Destination(&self) -> RequestDestination {
550        self.request.borrow().destination.convert()
551    }
552
553    // https://fetch.spec.whatwg.org/#dom-request-referrer
554    fn Referrer(&self) -> USVString {
555        let r = self.request.borrow();
556        USVString(match r.referrer {
557            NetTraitsRequestReferrer::NoReferrer => String::from(""),
558            NetTraitsRequestReferrer::Client(_) => String::from("about:client"),
559            NetTraitsRequestReferrer::ReferrerUrl(ref u) => {
560                let u_c = u.clone();
561                u_c.into_string()
562            },
563        })
564    }
565
566    // https://fetch.spec.whatwg.org/#dom-request-referrerpolicy
567    fn ReferrerPolicy(&self) -> ReferrerPolicy {
568        self.request.borrow().referrer_policy.convert()
569    }
570
571    // https://fetch.spec.whatwg.org/#dom-request-mode
572    fn Mode(&self) -> RequestMode {
573        self.request.borrow().mode.clone().convert()
574    }
575
576    // https://fetch.spec.whatwg.org/#dom-request-credentials
577    fn Credentials(&self) -> RequestCredentials {
578        let r = self.request.borrow().clone();
579        r.credentials_mode.convert()
580    }
581
582    // https://fetch.spec.whatwg.org/#dom-request-cache
583    fn Cache(&self) -> RequestCache {
584        let r = self.request.borrow().clone();
585        r.cache_mode.convert()
586    }
587
588    // https://fetch.spec.whatwg.org/#dom-request-redirect
589    fn Redirect(&self) -> RequestRedirect {
590        let r = self.request.borrow().clone();
591        r.redirect_mode.convert()
592    }
593
594    // https://fetch.spec.whatwg.org/#dom-request-integrity
595    fn Integrity(&self) -> DOMString {
596        let r = self.request.borrow();
597        DOMString::from_string(r.integrity_metadata.clone())
598    }
599
600    /// <https://fetch.spec.whatwg.org/#dom-body-body>
601    fn GetBody(&self) -> Option<DomRoot<ReadableStream>> {
602        self.body()
603    }
604
605    // https://fetch.spec.whatwg.org/#dom-body-bodyused
606    fn BodyUsed(&self) -> bool {
607        self.is_disturbed()
608    }
609
610    // https://fetch.spec.whatwg.org/#dom-request-clone
611    fn Clone(&self, can_gc: CanGc) -> Fallible<DomRoot<Request>> {
612        // Step 1
613        if request_is_locked(self) {
614            return Err(Error::Type("Request is locked".to_string()));
615        }
616        if request_is_disturbed(self) {
617            return Err(Error::Type("Request is disturbed".to_string()));
618        }
619
620        // Step 2
621        Request::clone_from(self, can_gc)
622    }
623
624    // https://fetch.spec.whatwg.org/#dom-body-text
625    fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
626        consume_body(self, BodyType::Text, can_gc)
627    }
628
629    // https://fetch.spec.whatwg.org/#dom-body-blob
630    fn Blob(&self, can_gc: CanGc) -> Rc<Promise> {
631        consume_body(self, BodyType::Blob, can_gc)
632    }
633
634    // https://fetch.spec.whatwg.org/#dom-body-formdata
635    fn FormData(&self, can_gc: CanGc) -> Rc<Promise> {
636        consume_body(self, BodyType::FormData, can_gc)
637    }
638
639    // https://fetch.spec.whatwg.org/#dom-body-json
640    fn Json(&self, can_gc: CanGc) -> Rc<Promise> {
641        consume_body(self, BodyType::Json, can_gc)
642    }
643
644    // https://fetch.spec.whatwg.org/#dom-body-arraybuffer
645    fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
646        consume_body(self, BodyType::ArrayBuffer, can_gc)
647    }
648
649    /// <https://fetch.spec.whatwg.org/#dom-body-bytes>
650    fn Bytes(&self, can_gc: CanGc) -> std::rc::Rc<Promise> {
651        consume_body(self, BodyType::Bytes, can_gc)
652    }
653}
654
655impl BodyMixin for Request {
656    fn is_disturbed(&self) -> bool {
657        let body_stream = self.body_stream.get();
658        body_stream
659            .as_ref()
660            .is_some_and(|stream| stream.is_disturbed())
661    }
662
663    fn is_locked(&self) -> bool {
664        let body_stream = self.body_stream.get();
665        body_stream.is_some_and(|stream| stream.is_locked())
666    }
667
668    fn body(&self) -> Option<DomRoot<ReadableStream>> {
669        self.body_stream.get()
670    }
671
672    fn get_mime_type(&self, can_gc: CanGc) -> Vec<u8> {
673        let headers = self.Headers(can_gc);
674        headers.extract_mime_type()
675    }
676}
677
678impl Convert<NetTraitsRequestCache> for RequestCache {
679    fn convert(self) -> NetTraitsRequestCache {
680        match self {
681            RequestCache::Default => NetTraitsRequestCache::Default,
682            RequestCache::No_store => NetTraitsRequestCache::NoStore,
683            RequestCache::Reload => NetTraitsRequestCache::Reload,
684            RequestCache::No_cache => NetTraitsRequestCache::NoCache,
685            RequestCache::Force_cache => NetTraitsRequestCache::ForceCache,
686            RequestCache::Only_if_cached => NetTraitsRequestCache::OnlyIfCached,
687        }
688    }
689}
690
691impl Convert<RequestCache> for NetTraitsRequestCache {
692    fn convert(self) -> RequestCache {
693        match self {
694            NetTraitsRequestCache::Default => RequestCache::Default,
695            NetTraitsRequestCache::NoStore => RequestCache::No_store,
696            NetTraitsRequestCache::Reload => RequestCache::Reload,
697            NetTraitsRequestCache::NoCache => RequestCache::No_cache,
698            NetTraitsRequestCache::ForceCache => RequestCache::Force_cache,
699            NetTraitsRequestCache::OnlyIfCached => RequestCache::Only_if_cached,
700        }
701    }
702}
703
704impl Convert<NetTraitsRequestCredentials> for RequestCredentials {
705    fn convert(self) -> NetTraitsRequestCredentials {
706        match self {
707            RequestCredentials::Omit => NetTraitsRequestCredentials::Omit,
708            RequestCredentials::Same_origin => NetTraitsRequestCredentials::CredentialsSameOrigin,
709            RequestCredentials::Include => NetTraitsRequestCredentials::Include,
710        }
711    }
712}
713
714impl Convert<RequestCredentials> for NetTraitsRequestCredentials {
715    fn convert(self) -> RequestCredentials {
716        match self {
717            NetTraitsRequestCredentials::Omit => RequestCredentials::Omit,
718            NetTraitsRequestCredentials::CredentialsSameOrigin => RequestCredentials::Same_origin,
719            NetTraitsRequestCredentials::Include => RequestCredentials::Include,
720        }
721    }
722}
723
724impl Convert<NetTraitsRequestDestination> for RequestDestination {
725    fn convert(self) -> NetTraitsRequestDestination {
726        match self {
727            RequestDestination::_empty => NetTraitsRequestDestination::None,
728            RequestDestination::Audio => NetTraitsRequestDestination::Audio,
729            RequestDestination::Document => NetTraitsRequestDestination::Document,
730            RequestDestination::Embed => NetTraitsRequestDestination::Embed,
731            RequestDestination::Font => NetTraitsRequestDestination::Font,
732            RequestDestination::Frame => NetTraitsRequestDestination::Frame,
733            RequestDestination::Iframe => NetTraitsRequestDestination::IFrame,
734            RequestDestination::Image => NetTraitsRequestDestination::Image,
735            RequestDestination::Manifest => NetTraitsRequestDestination::Manifest,
736            RequestDestination::Json => NetTraitsRequestDestination::Json,
737            RequestDestination::Object => NetTraitsRequestDestination::Object,
738            RequestDestination::Report => NetTraitsRequestDestination::Report,
739            RequestDestination::Script => NetTraitsRequestDestination::Script,
740            RequestDestination::Sharedworker => NetTraitsRequestDestination::SharedWorker,
741            RequestDestination::Style => NetTraitsRequestDestination::Style,
742            RequestDestination::Track => NetTraitsRequestDestination::Track,
743            RequestDestination::Video => NetTraitsRequestDestination::Video,
744            RequestDestination::Worker => NetTraitsRequestDestination::Worker,
745            RequestDestination::Xslt => NetTraitsRequestDestination::Xslt,
746        }
747    }
748}
749
750impl Convert<RequestDestination> for NetTraitsRequestDestination {
751    fn convert(self) -> RequestDestination {
752        match self {
753            NetTraitsRequestDestination::None => RequestDestination::_empty,
754            NetTraitsRequestDestination::Audio => RequestDestination::Audio,
755            NetTraitsRequestDestination::Document => RequestDestination::Document,
756            NetTraitsRequestDestination::Embed => RequestDestination::Embed,
757            NetTraitsRequestDestination::Font => RequestDestination::Font,
758            NetTraitsRequestDestination::Frame => RequestDestination::Frame,
759            NetTraitsRequestDestination::IFrame => RequestDestination::Iframe,
760            NetTraitsRequestDestination::Image => RequestDestination::Image,
761            NetTraitsRequestDestination::Manifest => RequestDestination::Manifest,
762            NetTraitsRequestDestination::Json => RequestDestination::Json,
763            NetTraitsRequestDestination::Object => RequestDestination::Object,
764            NetTraitsRequestDestination::Report => RequestDestination::Report,
765            NetTraitsRequestDestination::Script => RequestDestination::Script,
766            NetTraitsRequestDestination::ServiceWorker |
767            NetTraitsRequestDestination::AudioWorklet |
768            NetTraitsRequestDestination::PaintWorklet => {
769                panic!("ServiceWorker request destination should not be exposed to DOM")
770            },
771            NetTraitsRequestDestination::SharedWorker => RequestDestination::Sharedworker,
772            NetTraitsRequestDestination::Style => RequestDestination::Style,
773            NetTraitsRequestDestination::Track => RequestDestination::Track,
774            NetTraitsRequestDestination::Video => RequestDestination::Video,
775            NetTraitsRequestDestination::Worker => RequestDestination::Worker,
776            NetTraitsRequestDestination::Xslt => RequestDestination::Xslt,
777            NetTraitsRequestDestination::WebIdentity => RequestDestination::_empty,
778        }
779    }
780}
781
782impl Convert<NetTraitsRequestMode> for RequestMode {
783    fn convert(self) -> NetTraitsRequestMode {
784        match self {
785            RequestMode::Navigate => NetTraitsRequestMode::Navigate,
786            RequestMode::Same_origin => NetTraitsRequestMode::SameOrigin,
787            RequestMode::No_cors => NetTraitsRequestMode::NoCors,
788            RequestMode::Cors => NetTraitsRequestMode::CorsMode,
789        }
790    }
791}
792
793impl Convert<RequestMode> for NetTraitsRequestMode {
794    fn convert(self) -> RequestMode {
795        match self {
796            NetTraitsRequestMode::Navigate => RequestMode::Navigate,
797            NetTraitsRequestMode::SameOrigin => RequestMode::Same_origin,
798            NetTraitsRequestMode::NoCors => RequestMode::No_cors,
799            NetTraitsRequestMode::CorsMode => RequestMode::Cors,
800            NetTraitsRequestMode::WebSocket { .. } => {
801                unreachable!("Websocket request mode should never be exposed to Dom")
802            },
803        }
804    }
805}
806
807impl Convert<MsgReferrerPolicy> for ReferrerPolicy {
808    fn convert(self) -> MsgReferrerPolicy {
809        match self {
810            ReferrerPolicy::_empty => MsgReferrerPolicy::EmptyString,
811            ReferrerPolicy::No_referrer => MsgReferrerPolicy::NoReferrer,
812            ReferrerPolicy::No_referrer_when_downgrade => {
813                MsgReferrerPolicy::NoReferrerWhenDowngrade
814            },
815            ReferrerPolicy::Origin => MsgReferrerPolicy::Origin,
816            ReferrerPolicy::Origin_when_cross_origin => MsgReferrerPolicy::OriginWhenCrossOrigin,
817            ReferrerPolicy::Unsafe_url => MsgReferrerPolicy::UnsafeUrl,
818            ReferrerPolicy::Same_origin => MsgReferrerPolicy::SameOrigin,
819            ReferrerPolicy::Strict_origin => MsgReferrerPolicy::StrictOrigin,
820            ReferrerPolicy::Strict_origin_when_cross_origin => {
821                MsgReferrerPolicy::StrictOriginWhenCrossOrigin
822            },
823        }
824    }
825}
826
827impl Convert<ReferrerPolicy> for MsgReferrerPolicy {
828    fn convert(self) -> ReferrerPolicy {
829        match self {
830            MsgReferrerPolicy::EmptyString => ReferrerPolicy::_empty,
831            MsgReferrerPolicy::NoReferrer => ReferrerPolicy::No_referrer,
832            MsgReferrerPolicy::NoReferrerWhenDowngrade => {
833                ReferrerPolicy::No_referrer_when_downgrade
834            },
835            MsgReferrerPolicy::Origin => ReferrerPolicy::Origin,
836            MsgReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicy::Origin_when_cross_origin,
837            MsgReferrerPolicy::UnsafeUrl => ReferrerPolicy::Unsafe_url,
838            MsgReferrerPolicy::SameOrigin => ReferrerPolicy::Same_origin,
839            MsgReferrerPolicy::StrictOrigin => ReferrerPolicy::Strict_origin,
840            MsgReferrerPolicy::StrictOriginWhenCrossOrigin => {
841                ReferrerPolicy::Strict_origin_when_cross_origin
842            },
843        }
844    }
845}
846
847impl Convert<NetTraitsRequestRedirect> for RequestRedirect {
848    fn convert(self) -> NetTraitsRequestRedirect {
849        match self {
850            RequestRedirect::Follow => NetTraitsRequestRedirect::Follow,
851            RequestRedirect::Error => NetTraitsRequestRedirect::Error,
852            RequestRedirect::Manual => NetTraitsRequestRedirect::Manual,
853        }
854    }
855}
856
857impl Convert<RequestRedirect> for NetTraitsRequestRedirect {
858    fn convert(self) -> RequestRedirect {
859        match self {
860            NetTraitsRequestRedirect::Follow => RequestRedirect::Follow,
861            NetTraitsRequestRedirect::Error => RequestRedirect::Error,
862            NetTraitsRequestRedirect::Manual => RequestRedirect::Manual,
863        }
864    }
865}