Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Fetch/Request.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibJS/Runtime/Completion.h>
8
#include <LibWeb/Bindings/Intrinsics.h>
9
#include <LibWeb/Bindings/RequestPrototype.h>
10
#include <LibWeb/DOM/AbortSignal.h>
11
#include <LibWeb/DOMURL/DOMURL.h>
12
#include <LibWeb/Fetch/Enums.h>
13
#include <LibWeb/Fetch/Headers.h>
14
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
15
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
16
#include <LibWeb/Fetch/Infrastructure/HTTP/Methods.h>
17
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
18
#include <LibWeb/Fetch/Request.h>
19
#include <LibWeb/HTML/Scripting/Environments.h>
20
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
21
22
namespace Web::Fetch {
23
24
JS_DEFINE_ALLOCATOR(Request);
25
26
Request::Request(JS::Realm& realm, JS::NonnullGCPtr<Infrastructure::Request> request)
27
0
    : PlatformObject(realm)
28
0
    , m_request(request)
29
0
{
30
0
}
31
32
0
Request::~Request() = default;
33
34
void Request::initialize(JS::Realm& realm)
35
0
{
36
0
    Base::initialize(realm);
37
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(Request);
38
0
}
39
40
void Request::visit_edges(Cell::Visitor& visitor)
41
0
{
42
0
    Base::visit_edges(visitor);
43
0
    visitor.visit(m_request);
44
0
    visitor.visit(m_headers);
45
0
    visitor.visit(m_signal);
46
0
}
47
48
// https://fetch.spec.whatwg.org/#concept-body-mime-type
49
// https://fetch.spec.whatwg.org/#ref-for-concept-body-mime-type%E2%91%A0
50
Optional<MimeSniff::MimeType> Request::mime_type_impl() const
51
0
{
52
    // Objects including the Body interface mixin need to define an associated MIME type algorithm which takes no arguments and returns failure or a MIME type.
53
    // A Request object’s MIME type is to return the result of extracting a MIME type from its request’s header list.
54
0
    return m_request->header_list()->extract_mime_type();
55
0
}
56
57
// https://fetch.spec.whatwg.org/#concept-body-body
58
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A7
59
JS::GCPtr<Infrastructure::Body const> Request::body_impl() const
60
0
{
61
    // Objects including the Body interface mixin have an associated body (null or a body).
62
    // A Request object’s body is its request’s body.
63
0
    return m_request->body().visit(
64
0
        [](JS::NonnullGCPtr<Infrastructure::Body> const& b) -> JS::GCPtr<Infrastructure::Body const> { return b; },
65
0
        [](Empty) -> JS::GCPtr<Infrastructure::Body const> { return nullptr; },
66
        // A byte sequence will be safely extracted into a body early on in fetch.
67
0
        [](ByteBuffer const&) -> JS::GCPtr<Infrastructure::Body const> { VERIFY_NOT_REACHED(); });
68
0
}
69
70
// https://fetch.spec.whatwg.org/#concept-body-body
71
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A7
72
JS::GCPtr<Infrastructure::Body> Request::body_impl()
73
0
{
74
    // Objects including the Body interface mixin have an associated body (null or a body).
75
    // A Request object’s body is its request’s body.
76
0
    return m_request->body().visit(
77
0
        [](JS::NonnullGCPtr<Infrastructure::Body>& b) -> JS::GCPtr<Infrastructure::Body> { return b; },
78
0
        [](Empty) -> JS::GCPtr<Infrastructure::Body> { return {}; },
79
        // A byte sequence will be safely extracted into a body early on in fetch.
80
0
        [](ByteBuffer&) -> JS::GCPtr<Infrastructure::Body> { VERIFY_NOT_REACHED(); });
81
0
}
82
83
// https://fetch.spec.whatwg.org/#request-create
84
JS::NonnullGCPtr<Request> Request::create(JS::Realm& realm, JS::NonnullGCPtr<Infrastructure::Request> request, Headers::Guard guard, JS::NonnullGCPtr<DOM::AbortSignal> signal)
85
0
{
86
    // 1. Let requestObject be a new Request object with realm.
87
    // 2. Set requestObject’s request to request.
88
0
    auto request_object = realm.heap().allocate<Request>(realm, realm, request);
89
90
    // 3. Set requestObject’s headers to a new Headers object with realm, whose headers list is request’s headers list and guard is guard.
91
0
    request_object->m_headers = realm.heap().allocate<Headers>(realm, realm, request->header_list());
92
0
    request_object->m_headers->set_guard(guard);
93
94
    // 4. Set requestObject’s signal to signal.
95
0
    request_object->m_signal = signal;
96
97
    // 5. Return requestObject.
98
0
    return request_object;
99
0
}
100
101
// https://fetch.spec.whatwg.org/#dom-request
102
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm& realm, RequestInfo const& input, RequestInit const& init)
103
0
{
104
0
    auto& vm = realm.vm();
105
106
    // Referred to as 'this' in the spec.
107
0
    auto request_object = realm.heap().allocate<Request>(realm, realm, Infrastructure::Request::create(vm));
108
109
    // 1. Let request be null.
110
0
    JS::GCPtr<Infrastructure::Request> input_request;
111
112
    // 2. Let fallbackMode be null.
113
0
    Optional<Infrastructure::Request::Mode> fallback_mode;
114
115
    // 3. Let baseURL be this’s relevant settings object’s API base URL.
116
0
    auto base_url = HTML::relevant_settings_object(*request_object).api_base_url();
117
118
    // 4. Let signal be null.
119
0
    DOM::AbortSignal* input_signal = nullptr;
120
121
    // 5. If input is a string, then:
122
0
    if (input.has<String>()) {
123
        // 1. Let parsedURL be the result of parsing input with baseURL.
124
0
        auto parsed_url = DOMURL::parse(input.get<String>(), base_url);
125
126
        // 2. If parsedURL is failure, then throw a TypeError.
127
0
        if (!parsed_url.is_valid())
128
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Input URL is not valid"sv };
129
130
        // 3. If parsedURL includes credentials, then throw a TypeError.
131
0
        if (parsed_url.includes_credentials())
132
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Input URL must not include credentials"sv };
133
134
        // 4. Set request to a new request whose URL is parsedURL.
135
0
        input_request = Infrastructure::Request::create(vm);
136
0
        input_request->set_url(move(parsed_url));
137
138
        // 5. Set fallbackMode to "cors".
139
0
        fallback_mode = Infrastructure::Request::Mode::CORS;
140
0
    }
141
    // 6. Otherwise:
142
0
    else {
143
        // 1. Assert: input is a Request object.
144
0
        VERIFY(input.has<JS::Handle<Request>>());
145
146
        // 2. Set request to input’s request.
147
0
        input_request = input.get<JS::Handle<Request>>()->request();
148
149
        // 3. Set signal to input’s signal.
150
0
        input_signal = input.get<JS::Handle<Request>>()->signal();
151
0
    }
152
153
    // 7. Let origin be this’s relevant settings object’s origin.
154
0
    auto const& origin = HTML::relevant_settings_object(*request_object).origin();
155
156
    // 8. Let window be "client".
157
0
    auto window = Infrastructure::Request::WindowType { Infrastructure::Request::Window::Client };
158
159
    // 9. If request’s window is an environment settings object and its origin is same origin with origin, then set window to request’s window.
160
0
    if (input_request->window().has<JS::GCPtr<HTML::EnvironmentSettingsObject>>()) {
161
0
        auto eso = input_request->window().get<JS::GCPtr<HTML::EnvironmentSettingsObject>>();
162
0
        if (eso->origin().is_same_origin(origin))
163
0
            window = input_request->window();
164
0
    }
165
166
    // 10. If init["window"] exists and is non-null, then throw a TypeError.
167
0
    if (init.window.has_value() && !init.window->is_null())
168
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "The 'window' property must be omitted or null"sv };
169
170
    // 11. If init["window"] exists, then set window to "no-window".
171
0
    if (init.window.has_value())
172
0
        window = Infrastructure::Request::Window::NoWindow;
173
174
    // 12. Set request to a new request with the following properties:
175
    // NOTE: This is done at the beginning as the 'this' value Request object
176
    //       cannot exist with a null Infrastructure::Request.
177
0
    auto request = request_object->request();
178
179
    // URL
180
    //     request’s URL.
181
0
    request->set_url(input_request->url());
182
183
    // method
184
    //     request’s method.
185
0
    request->set_method(MUST(ByteBuffer::copy(input_request->method())));
186
187
    // header list
188
    //     A copy of request’s header list.
189
0
    auto header_list_copy = Infrastructure::HeaderList::create(vm);
190
0
    for (auto& header : *input_request->header_list())
191
0
        header_list_copy->append(header);
192
0
    request->set_header_list(header_list_copy);
193
194
    // unsafe-request flag
195
    //     Set.
196
0
    request->set_unsafe_request(true);
197
198
    // client
199
    //     This’s relevant settings object.
200
0
    request->set_client(&HTML::relevant_settings_object(*request_object));
201
202
    // window
203
    //     window.
204
0
    request->set_window(window);
205
206
    // priority
207
    //     request’s priority.
208
0
    request->set_priority(input_request->priority());
209
210
    // origin
211
    //     request’s origin. The propagation of the origin is only significant for navigation requests being handled by a service worker. In this scenario a request can have an origin that is different from the current client.
212
0
    request->set_origin(input_request->origin());
213
214
    // referrer
215
    //     request’s referrer.
216
0
    request->set_referrer(input_request->referrer());
217
218
    // referrer policy
219
    //     request’s referrer policy.
220
0
    request->set_referrer_policy(input_request->referrer_policy());
221
222
    // mode
223
    //     request’s mode.
224
0
    request->set_mode(input_request->mode());
225
226
    // credentials mode
227
    //     request’s credentials mode.
228
0
    request->set_credentials_mode(input_request->credentials_mode());
229
230
    // cache mode
231
    //     request’s cache mode.
232
0
    request->set_cache_mode(input_request->cache_mode());
233
234
    // redirect mode
235
    //     request’s redirect mode.
236
0
    request->set_redirect_mode(input_request->redirect_mode());
237
238
    // integrity metadata
239
    //     request’s integrity metadata.
240
0
    request->set_integrity_metadata(input_request->integrity_metadata());
241
242
    // keepalive
243
    //     request’s keepalive.
244
0
    request->set_keepalive(input_request->keepalive());
245
246
    // reload-navigation flag
247
    //     request’s reload-navigation flag.
248
0
    request->set_reload_navigation(input_request->reload_navigation());
249
250
    // history-navigation flag
251
    //     request’s history-navigation flag.
252
0
    request->set_history_navigation(input_request->history_navigation());
253
254
    // URL list
255
    //     A clone of request’s URL list.
256
0
    request->set_url_list(input_request->url_list());
257
258
    // initiator type
259
    //     "fetch".
260
0
    request->set_initiator_type(Infrastructure::Request::InitiatorType::Fetch);
261
262
    // 13. If init is not empty, then:
263
0
    if (!init.is_empty()) {
264
        // 1. If request’s mode is "navigate", then set it to "same-origin".
265
0
        if (request->mode() == Infrastructure::Request::Mode::Navigate)
266
0
            request->set_mode(Infrastructure::Request::Mode::SameOrigin);
267
268
        // 2. Unset request’s reload-navigation flag.
269
0
        request->set_reload_navigation(false);
270
271
        // 3. Unset request’s history-navigation flag.
272
0
        request->set_history_navigation(false);
273
274
        // 4. Set request’s origin to "client".
275
0
        request->set_origin(Infrastructure::Request::Origin::Client);
276
277
        // 5. Set request’s referrer to "client".
278
0
        request->set_referrer(Infrastructure::Request::Referrer::Client);
279
280
        // 6. Set request’s referrer policy to the empty string.
281
0
        request->set_referrer_policy({});
282
283
        // 7. Set request’s URL to request’s current URL.
284
0
        request->set_url(request->current_url());
285
286
        // 8. Set request’s URL list to « request’s URL ».
287
        // NOTE: This is done implicitly by assigning the initial URL above.
288
0
    }
289
290
    // 14. If init["referrer"] exists, then:
291
0
    if (init.referrer.has_value()) {
292
        // 1. Let referrer be init["referrer"].
293
0
        auto const& referrer = *init.referrer;
294
295
        // 2. If referrer is the empty string, then set request’s referrer to "no-referrer".
296
0
        if (referrer.is_empty()) {
297
0
            request->set_referrer(Infrastructure::Request::Referrer::NoReferrer);
298
0
        }
299
        // 3. Otherwise:
300
0
        else {
301
            // 1. Let parsedReferrer be the result of parsing referrer with baseURL.
302
0
            auto parsed_referrer = DOMURL::parse(referrer, base_url);
303
304
            // 2. If parsedReferrer is failure, then throw a TypeError.
305
0
            if (!parsed_referrer.is_valid())
306
0
                return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Referrer must be a valid URL"sv };
307
308
            // 3. If one of the following is true
309
            // - parsedReferrer’s scheme is "about" and path is the string "client"
310
            // - parsedReferrer’s origin is not same origin with origin
311
            // then set request’s referrer to "client".
312
0
            auto parsed_referrer_origin = parsed_referrer.origin();
313
0
            if ((parsed_referrer.scheme() == "about"sv && parsed_referrer.paths().size() == 1 && parsed_referrer.paths()[0] == "client"sv)
314
0
                || !parsed_referrer_origin.is_same_origin(origin)) {
315
0
                request->set_referrer(Infrastructure::Request::Referrer::Client);
316
0
            }
317
            // 4. Otherwise, set request’s referrer to parsedReferrer.
318
0
            else {
319
0
                request->set_referrer(move(parsed_referrer));
320
0
            }
321
0
        }
322
0
    }
323
324
    // 15. If init["referrerPolicy"] exists, then set request’s referrer policy to it.
325
0
    if (init.referrer_policy.has_value())
326
0
        request->set_referrer_policy(from_bindings_enum(*init.referrer_policy));
327
328
    // 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise.
329
0
    auto mode = init.mode.has_value()
330
0
        ? from_bindings_enum(*init.mode)
331
0
        : fallback_mode;
332
333
    // 17. If mode is "navigate", then throw a TypeError.
334
0
    if (mode == Infrastructure::Request::Mode::Navigate)
335
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Mode must not be 'navigate"sv };
336
337
    // 18. If mode is non-null, set request’s mode to mode.
338
0
    if (mode.has_value())
339
0
        request->set_mode(*mode);
340
341
    // 19. If init["credentials"] exists, then set request’s credentials mode to it.
342
0
    if (init.credentials.has_value())
343
0
        request->set_credentials_mode(from_bindings_enum(*init.credentials));
344
345
    // 20. If init["cache"] exists, then set request’s cache mode to it.
346
0
    if (init.cache.has_value())
347
0
        request->set_cache_mode(from_bindings_enum(*init.cache));
348
349
    // 21. If request’s cache mode is "only-if-cached" and request’s mode is not "same-origin", then throw a TypeError.
350
0
    if (request->cache_mode() == Infrastructure::Request::CacheMode::OnlyIfCached && request->mode() != Infrastructure::Request::Mode::SameOrigin)
351
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Mode must be 'same-origin' when cache mode is 'only-if-cached'"sv };
352
353
    // 22. If init["redirect"] exists, then set request’s redirect mode to it.
354
0
    if (init.redirect.has_value())
355
0
        request->set_redirect_mode(from_bindings_enum(*init.redirect));
356
357
    // 23. If init["integrity"] exists, then set request’s integrity metadata to it.
358
0
    if (init.integrity.has_value())
359
0
        request->set_integrity_metadata(*init.integrity);
360
361
    // 24. If init["keepalive"] exists, then set request’s keepalive to it.
362
0
    if (init.keepalive.has_value())
363
0
        request->set_keepalive(*init.keepalive);
364
365
    // 25. If init["method"] exists, then:
366
0
    if (init.method.has_value()) {
367
        // 1. Let method be init["method"].
368
0
        auto method = *init.method;
369
370
        // 2. If method is not a method or method is a forbidden method, then throw a TypeError.
371
0
        if (!Infrastructure::is_method(method.bytes()))
372
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method has invalid value"sv };
373
0
        if (Infrastructure::is_forbidden_method(method.bytes()))
374
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must not be one of CONNECT, TRACE, or TRACK"sv };
375
376
        // 3. Normalize method.
377
0
        method = MUST(String::from_utf8(Infrastructure::normalize_method(method.bytes())));
378
379
        // 4. Set request’s method to method.
380
0
        request->set_method(MUST(ByteBuffer::copy(method.bytes())));
381
0
    }
382
383
    // 26. If init["signal"] exists, then set signal to it.
384
0
    if (init.signal.has_value())
385
0
        input_signal = *init.signal;
386
387
    // 27. If init["priority"] exists, then:
388
0
    if (init.priority.has_value())
389
0
        request->set_priority(from_bindings_enum(*init.priority));
390
391
    // 28. Set this’s request to request.
392
    // NOTE: This is done at the beginning as the 'this' value Request object
393
    //       cannot exist with a null Infrastructure::Request.
394
395
    // 29. Let signals be « signal » if signal is non-null; otherwise « ».
396
0
    auto& this_relevant_realm = HTML::relevant_realm(*request_object);
397
0
    Vector<JS::Handle<DOM::AbortSignal>> signals;
398
0
    if (input_signal != nullptr)
399
0
        signals.append(*input_signal);
400
401
    // 30. Set this’s signal to the result of creating a dependent abort signal from signals, using AbortSignal and this’s relevant realm.
402
0
    request_object->m_signal = TRY(DOM::AbortSignal::create_dependent_abort_signal(this_relevant_realm, signals));
403
404
    // 31. Set this’s headers to a new Headers object with this’s relevant Realm, whose header list is request’s header list and guard is "request".
405
0
    request_object->m_headers = realm.heap().allocate<Headers>(realm, realm, request->header_list());
406
0
    request_object->m_headers->set_guard(Headers::Guard::Request);
407
408
    // 32. If this’s request’s mode is "no-cors", then:
409
0
    if (request_object->request()->mode() == Infrastructure::Request::Mode::NoCORS) {
410
        // 1. If this’s request’s method is not a CORS-safelisted method, then throw a TypeError.
411
0
        if (!Infrastructure::is_cors_safelisted_method(request_object->request()->method()))
412
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must be one of GET, HEAD, or POST"sv };
413
414
        // 2. Set this’s headers’s guard to "request-no-cors".
415
0
        request_object->headers()->set_guard(Headers::Guard::RequestNoCORS);
416
0
    }
417
418
    // 33. If init is not empty, then:
419
0
    if (!init.is_empty()) {
420
        // 1. Let headers be a copy of this’s headers and its associated header list.
421
0
        auto headers = Variant<HeadersInit, JS::NonnullGCPtr<Infrastructure::HeaderList>> { request_object->headers()->header_list() };
422
423
        // 2. If init["headers"] exists, then set headers to init["headers"].
424
0
        if (init.headers.has_value())
425
0
            headers = *init.headers;
426
427
        // 3. Empty this’s headers’s header list.
428
0
        request_object->headers()->header_list()->clear();
429
430
        // 4. If headers is a Headers object, then for each header of its header list, append header to this’s headers.
431
0
        if (auto* header_list = headers.get_pointer<JS::NonnullGCPtr<Infrastructure::HeaderList>>()) {
432
0
            for (auto& header : *header_list->ptr())
433
0
                TRY(request_object->headers()->append(Infrastructure::Header::from_string_pair(header.name, header.value)));
434
0
        }
435
        // 5. Otherwise, fill this’s headers with headers.
436
0
        else {
437
0
            TRY(request_object->headers()->fill(headers.get<HeadersInit>()));
438
0
        }
439
0
    }
440
441
    // 34. Let inputBody be input’s request’s body if input is a Request object; otherwise null.
442
0
    Optional<Infrastructure::Request::BodyType const&> input_body;
443
0
    if (input.has<JS::Handle<Request>>())
444
0
        input_body = input.get<JS::Handle<Request>>()->request()->body();
445
446
    // 35. If either init["body"] exists and is non-null or inputBody is non-null, and request’s method is `GET` or `HEAD`, then throw a TypeError.
447
0
    if (((init.body.has_value() && (*init.body).has_value()) || (input_body.has_value() && !input_body.value().has<Empty>())) && StringView { request->method() }.is_one_of("GET"sv, "HEAD"sv))
448
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must not be GET or HEAD when body is provided"sv };
449
450
    // 36. Let initBody be null.
451
0
    JS::GCPtr<Infrastructure::Body> init_body;
452
453
    // 37. If init["body"] exists and is non-null, then:
454
0
    if (init.body.has_value() && (*init.body).has_value()) {
455
        // 1. Let bodyWithType be the result of extracting init["body"], with keepalive set to request’s keepalive.
456
0
        auto body_with_type = TRY(extract_body(realm, (*init.body).value(), request->keepalive()));
457
458
        // 2. Set initBody to bodyWithType’s body.
459
0
        init_body = body_with_type.body;
460
461
        // 3. Let type be bodyWithType’s type.
462
0
        auto const& type = body_with_type.type;
463
464
        // 4. If type is non-null and this’s headers’s header list does not contain `Content-Type`, then append (`Content-Type`, type) to this’s headers.
465
0
        if (type.has_value() && !request_object->headers()->header_list()->contains("Content-Type"sv.bytes()))
466
0
            TRY(request_object->headers()->append(Infrastructure::Header::from_string_pair("Content-Type"sv, type->span())));
467
0
    }
468
469
    // 38. Let inputOrInitBody be initBody if it is non-null; otherwise inputBody.
470
0
    Optional<Infrastructure::Request::BodyType> input_or_init_body = init_body
471
0
        ? Infrastructure::Request::BodyType { *init_body }
472
0
        : input_body.copy();
473
474
    // 39. If inputOrInitBody is non-null and inputOrInitBody’s source is null, then:
475
    // FIXME: The spec doesn't check if inputOrInitBody is a body before accessing source.
476
0
    if (input_or_init_body.has_value() && input_or_init_body->has<JS::NonnullGCPtr<Infrastructure::Body>>() && input_or_init_body->get<JS::NonnullGCPtr<Infrastructure::Body>>()->source().has<Empty>()) {
477
        // 1. If initBody is non-null and init["duplex"] does not exist, then throw a TypeError.
478
0
        if (init_body && !init.duplex.has_value())
479
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Body without source requires 'duplex' value to be set"sv };
480
481
        // 2. If this’s request’s mode is neither "same-origin" nor "cors", then throw a TypeError.
482
0
        if (request_object->request()->mode() != Infrastructure::Request::Mode::SameOrigin && request_object->request()->mode() != Infrastructure::Request::Mode::CORS)
483
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request mode must be 'same-origin' or 'cors'"sv };
484
485
        // 3. Set this’s request’s use-CORS-preflight flag.
486
0
        request_object->request()->set_use_cors_preflight(true);
487
0
    }
488
489
    // 40. Let finalBody be inputOrInitBody.
490
0
    auto const& final_body = input_or_init_body;
491
492
    // 41. If initBody is null and inputBody is non-null, then:
493
0
    if (!init_body && input_body.has_value()) {
494
        // 2. If input is unusable, then throw a TypeError.
495
0
        if (input.has<JS::Handle<Request>>() && input.get<JS::Handle<Request>>()->is_unusable())
496
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv };
497
498
        // FIXME: 2. Set finalBody to the result of creating a proxy for inputBody.
499
0
    }
500
501
    // 42. Set this’s request’s body to finalBody.
502
0
    if (final_body.has_value())
503
0
        request_object->request()->set_body(*final_body);
504
505
0
    return JS::NonnullGCPtr { *request_object };
506
0
}
507
508
// https://fetch.spec.whatwg.org/#dom-request-method
509
String Request::method() const
510
0
{
511
    // The method getter steps are to return this’s request’s method.
512
0
    return MUST(String::from_utf8(m_request->method()));
513
0
}
514
515
// https://fetch.spec.whatwg.org/#dom-request-url
516
String Request::url() const
517
0
{
518
    // The url getter steps are to return this’s request’s URL, serialized.
519
0
    return MUST(String::from_byte_string(m_request->url().serialize()));
520
0
}
521
522
// https://fetch.spec.whatwg.org/#dom-request-headers
523
JS::NonnullGCPtr<Headers> Request::headers() const
524
0
{
525
    // The headers getter steps are to return this’s headers.
526
0
    return *m_headers;
527
0
}
528
529
// https://fetch.spec.whatwg.org/#dom-request-destination
530
Bindings::RequestDestination Request::destination() const
531
0
{
532
    // The destination getter are to return this’s request’s destination.
533
0
    return to_bindings_enum(m_request->destination());
534
0
}
535
536
// https://fetch.spec.whatwg.org/#dom-request-referrer
537
String Request::referrer() const
538
0
{
539
0
    return m_request->referrer().visit(
540
0
        [&](Infrastructure::Request::Referrer const& referrer) {
541
0
            switch (referrer) {
542
            // 1. If this’s request’s referrer is "no-referrer", then return the empty string.
543
0
            case Infrastructure::Request::Referrer::NoReferrer:
544
0
                return String {};
545
            // 2. If this’s request’s referrer is "client", then return "about:client".
546
0
            case Infrastructure::Request::Referrer::Client:
547
0
                return "about:client"_string;
548
0
            default:
549
0
                VERIFY_NOT_REACHED();
550
0
            }
551
0
        },
552
0
        [&](URL::URL const& url) {
553
            // 3. Return this’s request’s referrer, serialized.
554
0
            return MUST(String::from_byte_string(url.serialize()));
555
0
        });
556
0
}
557
558
// https://fetch.spec.whatwg.org/#dom-request-referrerpolicy
559
Bindings::ReferrerPolicy Request::referrer_policy() const
560
0
{
561
    // The referrerPolicy getter steps are to return this’s request’s referrer policy.
562
0
    return to_bindings_enum(m_request->referrer_policy());
563
0
}
564
565
// https://fetch.spec.whatwg.org/#dom-request-mode
566
Bindings::RequestMode Request::mode() const
567
0
{
568
    // The mode getter steps are to return this’s request’s mode.
569
0
    return to_bindings_enum(m_request->mode());
570
0
}
571
572
// https://fetch.spec.whatwg.org/#dom-request-credentials
573
Bindings::RequestCredentials Request::credentials() const
574
0
{
575
    // The credentials getter steps are to return this’s request’s credentials mode.
576
0
    return to_bindings_enum(m_request->credentials_mode());
577
0
}
578
579
// https://fetch.spec.whatwg.org/#dom-request-cache
580
Bindings::RequestCache Request::cache() const
581
0
{
582
    // The cache getter steps are to return this’s request’s cache mode.
583
0
    return to_bindings_enum(m_request->cache_mode());
584
0
}
585
586
// https://fetch.spec.whatwg.org/#dom-request-redirect
587
Bindings::RequestRedirect Request::redirect() const
588
0
{
589
    // The redirect getter steps are to return this’s request’s redirect mode.
590
0
    return to_bindings_enum(m_request->redirect_mode());
591
0
}
592
593
// https://fetch.spec.whatwg.org/#dom-request-integrity
594
String Request::integrity() const
595
0
{
596
    // The integrity getter steps are to return this’s request’s integrity metadata.
597
0
    return m_request->integrity_metadata();
598
0
}
599
600
// https://fetch.spec.whatwg.org/#dom-request-keepalive
601
bool Request::keepalive() const
602
0
{
603
    // The keepalive getter steps are to return this’s request’s keepalive.
604
0
    return m_request->keepalive();
605
0
}
606
607
// https://fetch.spec.whatwg.org/#dom-request-isreloadnavigation
608
bool Request::is_reload_navigation() const
609
0
{
610
    // The isReloadNavigation getter steps are to return true if this’s request’s reload-navigation flag is set; otherwise false.
611
0
    return m_request->reload_navigation();
612
0
}
613
614
// https://fetch.spec.whatwg.org/#dom-request-ishistorynavigation
615
bool Request::is_history_navigation() const
616
0
{
617
    // The isHistoryNavigation getter steps are to return true if this’s request’s history-navigation flag is set; otherwise false.
618
0
    return m_request->history_navigation();
619
0
}
620
621
// https://fetch.spec.whatwg.org/#dom-request-signal
622
JS::NonnullGCPtr<DOM::AbortSignal> Request::signal() const
623
0
{
624
    // The signal getter steps are to return this’s signal.
625
0
    return *m_signal;
626
0
}
627
628
// https://fetch.spec.whatwg.org/#dom-request-duplex
629
Bindings::RequestDuplex Request::duplex() const
630
0
{
631
    // The duplex getter steps are to return "half".
632
0
    return Bindings::RequestDuplex::Half;
633
0
}
634
635
// https://fetch.spec.whatwg.org/#dom-request-clone
636
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::clone() const
637
0
{
638
0
    auto& realm = this->realm();
639
640
    // 1. If this is unusable, then throw a TypeError.
641
0
    if (is_unusable())
642
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv };
643
644
    // 2. Let clonedRequest be the result of cloning this’s request.
645
0
    auto cloned_request = m_request->clone(realm);
646
647
    // 3. Assert: this’s signal is non-null.
648
0
    VERIFY(m_signal);
649
650
    // 4. Let clonedSignal be the result of creating a dependent abort signal from « this’s signal », using AbortSignal and this’s relevant realm.
651
0
    auto& relevant_realm = HTML::relevant_realm(*this);
652
0
    auto cloned_signal = TRY(DOM::AbortSignal::create_dependent_abort_signal(relevant_realm, { m_signal }));
653
654
    // 5. Let clonedRequestObject be the result of creating a Request object, given clonedRequest, this’s headers’s guard, clonedSignal and this’s relevant realm.
655
0
    auto cloned_request_object = Request::create(relevant_realm, cloned_request, m_headers->guard(), cloned_signal);
656
657
    // 6. Return clonedRequestObject.
658
0
    return cloned_request_object;
659
0
}
660
661
}