Serenity Operating System
at master 656 lines 29 kB view raw
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}