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