Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/HTML/Location.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020-2023, Andreas Kling <kling@serenityos.org>
3
 * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <AK/String.h>
9
#include <AK/StringBuilder.h>
10
#include <LibJS/Heap/MarkedVector.h>
11
#include <LibJS/Runtime/Completion.h>
12
#include <LibJS/Runtime/PropertyDescriptor.h>
13
#include <LibJS/Runtime/PropertyKey.h>
14
#include <LibURL/Parser.h>
15
#include <LibWeb/Bindings/LocationPrototype.h>
16
#include <LibWeb/DOM/Document.h>
17
#include <LibWeb/HTML/CrossOrigin/AbstractOperations.h>
18
#include <LibWeb/HTML/Location.h>
19
#include <LibWeb/HTML/Navigation.h>
20
#include <LibWeb/HTML/Window.h>
21
#include <LibWeb/WebIDL/DOMException.h>
22
23
namespace Web::HTML {
24
25
JS_DEFINE_ALLOCATOR(Location);
26
27
// https://html.spec.whatwg.org/multipage/history.html#the-location-interface
28
Location::Location(JS::Realm& realm)
29
0
    : PlatformObject(realm, MayInterfereWithIndexedPropertyAccess::Yes)
30
0
{
31
0
}
32
33
0
Location::~Location() = default;
34
35
void Location::visit_edges(Cell::Visitor& visitor)
36
0
{
37
0
    Base::visit_edges(visitor);
38
0
    visitor.visit(m_default_properties);
39
0
}
40
41
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-location-interface
42
void Location::initialize(JS::Realm& realm)
43
0
{
44
0
    Base::initialize(realm);
45
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(Location);
46
47
0
    auto& vm = this->vm();
48
49
    // Step 2: Let valueOf be location's relevant realm.[[Intrinsics]].[[%Object.prototype.valueOf%]].
50
0
    auto& intrinsics = realm.intrinsics();
51
0
    auto value_of_function = intrinsics.object_prototype()->get_without_side_effects(vm.names.valueOf);
52
53
    // Step 3: Perform ! location.[[DefineOwnProperty]]("valueOf", { [[Value]]: valueOf, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
54
0
    auto value_of_property_descriptor = JS::PropertyDescriptor {
55
0
        .value = value_of_function,
56
0
        .writable = false,
57
0
        .enumerable = false,
58
0
        .configurable = false,
59
0
    };
60
0
    MUST(internal_define_own_property(vm.names.valueOf, value_of_property_descriptor));
61
62
    // Step 4: Perform ! location.[[DefineOwnProperty]](%Symbol.toPrimitive%, { [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
63
0
    auto to_primitive_property_descriptor = JS::PropertyDescriptor {
64
0
        .value = JS::js_undefined(),
65
0
        .writable = false,
66
0
        .enumerable = false,
67
0
        .configurable = false,
68
0
    };
69
0
    MUST(internal_define_own_property(vm.well_known_symbol_to_primitive(), to_primitive_property_descriptor));
70
71
    // 5. Set the value of the [[DefaultProperties]] internal slot of location to location.[[OwnPropertyKeys]]().
72
    // NOTE: In LibWeb this happens before the ESO is set up, so we must avoid location's custom [[OwnPropertyKeys]].
73
0
    m_default_properties.extend(MUST(Object::internal_own_property_keys()));
74
0
}
75
76
// https://html.spec.whatwg.org/multipage/history.html#relevant-document
77
JS::GCPtr<DOM::Document> Location::relevant_document() const
78
0
{
79
    // A Location object has an associated relevant Document, which is this Location object's
80
    // relevant global object's browsing context's active document, if this Location object's
81
    // relevant global object's browsing context is non-null, and null otherwise.
82
0
    auto* browsing_context = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).browsing_context();
83
0
    return browsing_context ? browsing_context->active_document() : nullptr;
84
0
}
85
86
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#location-object-navigate
87
WebIDL::ExceptionOr<void> Location::navigate(URL::URL url, Bindings::NavigationHistoryBehavior history_handling)
88
0
{
89
    // 1. Let navigable be location's relevant global object's navigable.
90
0
    auto navigable = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).navigable();
91
92
    // 2. Let sourceDocument be the incumbent global object's associated Document.
93
0
    auto& source_document = verify_cast<HTML::Window>(incumbent_global_object()).associated_document();
94
95
    // 3. If location's relevant Document is not yet completely loaded, and the incumbent global object does not have transient activation, then set historyHandling to "replace".
96
0
    if (!relevant_document()->is_completely_loaded() && !verify_cast<HTML::Window>(incumbent_global_object()).has_transient_activation()) {
97
0
        history_handling = Bindings::NavigationHistoryBehavior::Replace;
98
0
    }
99
100
    // 4. Navigate navigable to url using sourceDocument, with exceptionsEnabled set to true and historyHandling set to historyHandling.
101
0
    TRY(navigable->navigate({ .url = url,
102
0
        .source_document = source_document,
103
0
        .exceptions_enabled = true,
104
0
        .history_handling = history_handling }));
105
106
0
    return {};
107
0
}
108
109
// https://html.spec.whatwg.org/multipage/history.html#concept-location-url
110
URL::URL Location::url() const
111
0
{
112
    // A Location object has an associated url, which is this Location object's relevant Document's URL,
113
    // if this Location object's relevant Document is non-null, and about:blank otherwise.
114
0
    auto const relevant_document = this->relevant_document();
115
0
    return relevant_document ? relevant_document->url() : "about:blank"sv;
116
0
}
117
118
// https://html.spec.whatwg.org/multipage/history.html#dom-location-href
119
WebIDL::ExceptionOr<String> Location::href() const
120
0
{
121
0
    auto& vm = this->vm();
122
123
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
124
0
    auto const relevant_document = this->relevant_document();
125
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
126
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
127
128
    // 2. Return this's url, serialized.
129
0
    return TRY_OR_THROW_OOM(vm, String::from_byte_string(url().serialize()));
130
0
}
131
132
// https://html.spec.whatwg.org/multipage/history.html#the-location-interface:dom-location-href-2
133
WebIDL::ExceptionOr<void> Location::set_href(String const& new_href)
134
0
{
135
0
    auto& realm = this->realm();
136
0
    auto& window = verify_cast<HTML::Window>(HTML::current_global_object());
137
138
    // 1. If this's relevant Document is null, then return.
139
0
    auto const relevant_document = this->relevant_document();
140
0
    if (!relevant_document)
141
0
        return {};
142
143
    // FIXME: 2. Let url be the result of encoding-parsing a URL given the given value, relative to the entry settings object.
144
0
    auto href_url = window.associated_document().parse_url(new_href.to_byte_string());
145
146
    // 3. If url is failure, then throw a "SyntaxError" DOMException.
147
0
    if (!href_url.is_valid())
148
0
        return WebIDL::SyntaxError::create(realm, MUST(String::formatted("Invalid URL '{}'", new_href)));
149
150
    // 4. Location-object navigate this to url.
151
0
    TRY(navigate(href_url));
152
153
0
    return {};
154
0
}
155
156
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location-origin
157
WebIDL::ExceptionOr<String> Location::origin() const
158
0
{
159
0
    auto& vm = this->vm();
160
161
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
162
0
    auto const relevant_document = this->relevant_document();
163
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
164
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
165
166
    // 2. Return the serialization of this's url's origin.
167
0
    return TRY_OR_THROW_OOM(vm, String::from_byte_string(url().origin().serialize()));
168
0
}
169
170
// https://html.spec.whatwg.org/multipage/history.html#dom-location-protocol
171
WebIDL::ExceptionOr<String> Location::protocol() const
172
0
{
173
0
    auto& vm = this->vm();
174
175
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
176
0
    auto const relevant_document = this->relevant_document();
177
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
178
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
179
180
    // 2. Return this's url's scheme, followed by ":".
181
0
    return TRY_OR_THROW_OOM(vm, String::formatted("{}:", url().scheme()));
182
0
}
183
184
// https://html.spec.whatwg.org/multipage/history.html#dom-location-protocol
185
WebIDL::ExceptionOr<void> Location::set_protocol(String const& value)
186
0
{
187
0
    auto relevant_document = this->relevant_document();
188
189
    // 1. If this's relevant Document is null, then return.
190
0
    if (!relevant_document)
191
0
        return {};
192
193
    // 2. If this's relevant Document's origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
194
0
    if (!relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
195
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
196
197
    // 3. Let copyURL be a copy of this's url.
198
0
    auto copy_url = this->url();
199
200
    // 4. Let possibleFailure be the result of basic URL parsing the given value, followed by ":", with copyURL as url and scheme start state as state override.
201
0
    auto possible_failure = URL::Parser::basic_parse(value, {}, &copy_url, URL::Parser::State::SchemeStart);
202
203
    // 5. If possibleFailure is failure, then throw a "SyntaxError" DOMException.
204
0
    if (!possible_failure.is_valid())
205
0
        return WebIDL::SyntaxError::create(realm(), MUST(String::formatted("Failed to set protocol. '{}' is an invalid protocol", value)));
206
207
    // 6. if copyURL's scheme is not an HTTP(S) scheme, then terminate these steps.
208
0
    if (!(copy_url.scheme() == "http"sv || copy_url.scheme() == "https"sv))
209
0
        return {};
210
211
    // 7. Location-object navigate this to copyURL.
212
0
    TRY(navigate(copy_url));
213
214
0
    return {};
215
0
}
216
217
// https://html.spec.whatwg.org/multipage/history.html#dom-location-host
218
WebIDL::ExceptionOr<String> Location::host() const
219
0
{
220
0
    auto& vm = this->vm();
221
222
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
223
0
    auto const relevant_document = this->relevant_document();
224
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
225
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
226
227
    // 2. Let url be this's url.
228
0
    auto url = this->url();
229
230
    // 3. If url's host is null, return the empty string.
231
0
    if (url.host().has<Empty>())
232
0
        return String {};
233
234
    // 4. If url's port is null, return url's host, serialized.
235
0
    if (!url.port().has_value())
236
0
        return TRY_OR_THROW_OOM(vm, url.serialized_host());
237
238
    // 5. Return url's host, serialized, followed by ":" and url's port, serialized.
239
0
    return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), *url.port()));
240
0
}
241
242
WebIDL::ExceptionOr<void> Location::set_host(String const&)
243
0
{
244
0
    auto& vm = this->vm();
245
0
    return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.host setter");
246
0
}
247
248
// https://html.spec.whatwg.org/multipage/history.html#dom-location-hostname
249
WebIDL::ExceptionOr<String> Location::hostname() const
250
0
{
251
0
    auto& vm = this->vm();
252
253
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
254
0
    auto const relevant_document = this->relevant_document();
255
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
256
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
257
258
0
    auto url = this->url();
259
260
    // 2. If this's url's host is null, return the empty string.
261
0
    if (url.host().has<Empty>())
262
0
        return String {};
263
264
    // 3. Return this's url's host, serialized.
265
0
    return TRY_OR_THROW_OOM(vm, url.serialized_host());
266
0
}
267
268
WebIDL::ExceptionOr<void> Location::set_hostname(String const&)
269
0
{
270
0
    auto& vm = this->vm();
271
0
    return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.hostname setter");
272
0
}
273
274
// https://html.spec.whatwg.org/multipage/history.html#dom-location-port
275
WebIDL::ExceptionOr<String> Location::port() const
276
0
{
277
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
278
0
    auto const relevant_document = this->relevant_document();
279
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
280
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
281
282
0
    auto url = this->url();
283
284
    // 2. If this's url's port is null, return the empty string.
285
0
    if (!url.port().has_value())
286
0
        return String {};
287
288
    // 3. Return this's url's port, serialized.
289
0
    return String::number(*url.port());
290
0
}
291
292
WebIDL::ExceptionOr<void> Location::set_port(String const&)
293
0
{
294
0
    auto& vm = this->vm();
295
0
    return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.port setter");
296
0
}
297
298
// https://html.spec.whatwg.org/multipage/history.html#dom-location-pathname
299
WebIDL::ExceptionOr<String> Location::pathname() const
300
0
{
301
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
302
0
    auto const relevant_document = this->relevant_document();
303
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
304
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
305
306
    // 2. Return the result of URL path serializing this Location object's url.
307
0
    return url().serialize_path();
308
0
}
309
310
WebIDL::ExceptionOr<void> Location::set_pathname(String const&)
311
0
{
312
0
    auto& vm = this->vm();
313
0
    return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.pathname setter");
314
0
}
315
316
// https://html.spec.whatwg.org/multipage/history.html#dom-location-search
317
WebIDL::ExceptionOr<String> Location::search() const
318
0
{
319
0
    auto& vm = this->vm();
320
321
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
322
0
    auto const relevant_document = this->relevant_document();
323
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
324
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
325
326
0
    auto url = this->url();
327
328
    // 2. If this's url's query is either null or the empty string, return the empty string.
329
0
    if (!url.query().has_value() || url.query()->is_empty())
330
0
        return String {};
331
332
    // 3. Return "?", followed by this's url's query.
333
0
    return TRY_OR_THROW_OOM(vm, String::formatted("?{}", url.query()));
334
0
}
335
336
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location-search
337
WebIDL::ExceptionOr<void> Location::set_search(String const& value)
338
0
{
339
    // The search setter steps are:
340
0
    auto const relevant_document = this->relevant_document();
341
342
    // 1. If this's relevant Document is null, then return.
343
0
    if (!relevant_document)
344
0
        return {};
345
346
    // 2. If this's relevant Document's origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
347
0
    if (!relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
348
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
349
350
    // 3. Let copyURL be a copy of this's url.
351
0
    auto copy_url = this->url();
352
353
    // 4. If the given value is the empty string, set copyURL's query to null.
354
0
    if (value.is_empty()) {
355
0
        copy_url.set_query({});
356
0
    }
357
    // 5. Otherwise, run these substeps:
358
0
    else {
359
        // 5.1. Let input be the given value with a single leading "?" removed, if any.
360
0
        auto value_as_string_view = value.bytes_as_string_view();
361
0
        auto input = value_as_string_view.substring_view(value_as_string_view.starts_with('?'));
362
363
        // 5.2. Set copyURL's query to the empty string.
364
0
        copy_url.set_query(String {});
365
366
        // 5.3. Basic URL parse input, with null, the relevant Document's document's character encoding, copyURL as url, and query state as state override.
367
0
        (void)URL::Parser::basic_parse(input, {}, &copy_url, URL::Parser::State::Query);
368
0
    }
369
370
    // 6. Location-object navigate this to copyURL.
371
0
    TRY(navigate(copy_url));
372
373
0
    return {};
374
0
}
375
376
// https://html.spec.whatwg.org/multipage/history.html#dom-location-hash
377
WebIDL::ExceptionOr<String> Location::hash() const
378
0
{
379
0
    auto& vm = this->vm();
380
381
    // 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
382
0
    auto const relevant_document = this->relevant_document();
383
0
    if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
384
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
385
386
0
    auto url = this->url();
387
388
    // 2. If this's url's fragment is either null or the empty string, return the empty string.
389
0
    if (!url.fragment().has_value() || url.fragment()->is_empty())
390
0
        return String {};
391
392
    // 3. Return "#", followed by this's url's fragment.
393
0
    return TRY_OR_THROW_OOM(vm, String::formatted("#{}", *url.fragment()));
394
0
}
395
396
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location-hash
397
WebIDL::ExceptionOr<void> Location::set_hash(String const& value)
398
0
{
399
    // The hash setter steps are:
400
0
    auto const relevant_document = this->relevant_document();
401
402
    // 1. If this's relevant Document is null, then return.
403
0
    if (!relevant_document)
404
0
        return {};
405
406
    // 2. If this's relevant Document's origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
407
0
    if (!relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
408
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
409
410
    // 3. Let copyURL be a copy of this's url.
411
0
    auto copy_url = this->url();
412
413
    // 4. Let input be the given value with a single leading "#" removed, if any.
414
0
    auto input = value.bytes_as_string_view().trim("#"sv, TrimMode::Left);
415
416
    // 5. Set copyURL's fragment to the empty string.
417
0
    copy_url.set_fragment(String {});
418
419
    // 6. Basic URL parse input, with copyURL as url and fragment state as state override.
420
0
    (void)URL::Parser::basic_parse(input, {}, &copy_url, URL::Parser::State::Fragment);
421
422
    // 7. If copyURL's fragment is this's url's fragment, then return.
423
0
    if (copy_url.fragment() == this->url().fragment())
424
0
        return {};
425
426
    // 8. Location-object navigate this to copyURL.
427
0
    TRY(navigate(copy_url));
428
429
0
    return {};
430
0
}
431
432
// https://html.spec.whatwg.org/multipage/history.html#dom-location-reload
433
void Location::reload() const
434
0
{
435
    // 1. Let document be this's relevant Document.
436
0
    auto document = relevant_document();
437
438
    // 2. If document is null, then return.
439
0
    if (!document)
440
0
        return;
441
442
    // FIXME: 3. If document's origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
443
444
    // 4. Reload document's node navigable.
445
0
    document->navigable()->reload();
446
0
}
447
448
// https://html.spec.whatwg.org/multipage/history.html#dom-location-replace
449
WebIDL::ExceptionOr<void> Location::replace(String const& url)
450
0
{
451
    // 1. If this's relevant Document is null, then return.
452
0
    if (!relevant_document())
453
0
        return {};
454
455
    // 2. Parse url relative to the entry settings object. If that failed, throw a "SyntaxError" DOMException.
456
0
    auto replace_url = entry_settings_object().parse_url(url);
457
0
    if (!replace_url.is_valid())
458
0
        return WebIDL::SyntaxError::create(realm(), MUST(String::formatted("Invalid URL '{}'", url)));
459
460
    // 3. Location-object navigate this to the resulting URL record given "replace".
461
0
    TRY(navigate(replace_url, Bindings::NavigationHistoryBehavior::Replace));
462
463
0
    return {};
464
0
}
465
466
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location-assign
467
WebIDL::ExceptionOr<void> Location::assign(String const& url)
468
0
{
469
    // 1. If this's relevant Document is null, then return.
470
0
    auto const relevant_document = this->relevant_document();
471
0
    if (!relevant_document)
472
0
        return {};
473
474
    // 2. If this's relevant Document's origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
475
0
    if (!relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
476
0
        return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_string);
477
478
    // 3. Parse url relative to the entry settings object. If that failed, throw a "SyntaxError" DOMException.
479
0
    auto assign_url = entry_settings_object().parse_url(url);
480
0
    if (!assign_url.is_valid())
481
0
        return WebIDL::SyntaxError::create(realm(), MUST(String::formatted("Invalid URL '{}'", url)));
482
483
    // 4. Location-object navigate this to the resulting URL record.
484
0
    TRY(navigate(assign_url));
485
486
0
    return {};
487
0
}
488
489
// 7.10.5.1 [[GetPrototypeOf]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-getprototypeof
490
JS::ThrowCompletionOr<JS::Object*> Location::internal_get_prototype_of() const
491
0
{
492
    // 1. If IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this).
493
0
    if (HTML::is_platform_object_same_origin(*this))
494
0
        return MUST(JS::Object::internal_get_prototype_of());
495
496
    // 2. Return null.
497
0
    return nullptr;
498
0
}
499
500
// 7.10.5.2 [[SetPrototypeOf]] ( V ), https://html.spec.whatwg.org/multipage/history.html#location-setprototypeof
501
JS::ThrowCompletionOr<bool> Location::internal_set_prototype_of(Object* prototype)
502
0
{
503
    // 1. Return ! SetImmutablePrototype(this, V).
504
0
    return MUST(set_immutable_prototype(prototype));
505
0
}
506
507
// 7.10.5.3 [[IsExtensible]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-isextensible
508
JS::ThrowCompletionOr<bool> Location::internal_is_extensible() const
509
0
{
510
    // 1. Return true.
511
0
    return true;
512
0
}
513
514
// 7.10.5.4 [[PreventExtensions]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-preventextensions
515
JS::ThrowCompletionOr<bool> Location::internal_prevent_extensions()
516
0
{
517
    // 1. Return false.
518
0
    return false;
519
0
}
520
521
// 7.10.5.5 [[GetOwnProperty]] ( P ), https://html.spec.whatwg.org/multipage/history.html#location-getownproperty
522
JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> Location::internal_get_own_property(JS::PropertyKey const& property_key) const
523
0
{
524
0
    auto& vm = this->vm();
525
526
    // 1. If IsPlatformObjectSameOrigin(this) is true, then:
527
0
    if (HTML::is_platform_object_same_origin(*this)) {
528
        // 1. Let desc be OrdinaryGetOwnProperty(this, P).
529
0
        auto descriptor = MUST(Object::internal_get_own_property(property_key));
530
531
        // 2. If the value of the [[DefaultProperties]] internal slot of this contains P, then set desc.[[Configurable]] to true.
532
        // FIXME: This doesn't align with what the other browsers do. Spec issue: https://github.com/whatwg/html/issues/4157
533
0
        auto property_key_value = property_key.is_symbol()
534
0
            ? JS::Value { property_key.as_symbol() }
535
0
            : JS::PrimitiveString::create(vm, property_key.to_string());
536
0
        if (m_default_properties.contains_slow(property_key_value))
537
0
            descriptor->configurable = true;
538
539
        // 3. Return desc.
540
0
        return descriptor;
541
0
    }
542
543
    // 2. Let property be CrossOriginGetOwnPropertyHelper(this, P).
544
0
    auto property = HTML::cross_origin_get_own_property_helper(const_cast<Location*>(this), property_key);
545
546
    // 3. If property is not undefined, then return property.
547
0
    if (property.has_value())
548
0
        return property;
549
550
    // 4. Return ? CrossOriginPropertyFallback(P).
551
0
    return TRY(HTML::cross_origin_property_fallback(vm, property_key));
552
0
}
553
554
// 7.10.5.6 [[DefineOwnProperty]] ( P, Desc ), https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty
555
JS::ThrowCompletionOr<bool> Location::internal_define_own_property(JS::PropertyKey const& property_key, JS::PropertyDescriptor const& descriptor, Optional<JS::PropertyDescriptor>* precomputed_get_own_property)
556
0
{
557
    // 1. If IsPlatformObjectSameOrigin(this) is true, then:
558
0
    if (HTML::is_platform_object_same_origin(*this)) {
559
        // 1. If the value of the [[DefaultProperties]] internal slot of this contains P, then return false.
560
        // 2. Return ? OrdinaryDefineOwnProperty(this, P, Desc).
561
0
        return JS::Object::internal_define_own_property(property_key, descriptor, precomputed_get_own_property);
562
0
    }
563
564
    // 2. Throw a "SecurityError" DOMException.
565
0
    return throw_completion(WebIDL::SecurityError::create(realm(), MUST(String::formatted("Can't define property '{}' on cross-origin object", property_key))));
566
0
}
567
568
// 7.10.5.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-get
569
JS::ThrowCompletionOr<JS::Value> Location::internal_get(JS::PropertyKey const& property_key, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
570
0
{
571
0
    auto& vm = this->vm();
572
573
    // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinaryGet(this, P, Receiver).
574
0
    if (HTML::is_platform_object_same_origin(*this))
575
0
        return JS::Object::internal_get(property_key, receiver, cacheable_metadata, phase);
576
577
    // 2. Return ? CrossOriginGet(this, P, Receiver).
578
0
    return HTML::cross_origin_get(vm, static_cast<JS::Object const&>(*this), property_key, receiver);
579
0
}
580
581
// 7.10.5.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-set
582
JS::ThrowCompletionOr<bool> Location::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata)
583
0
{
584
0
    auto& vm = this->vm();
585
586
    // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinarySet(this, P, V, Receiver).
587
0
    if (HTML::is_platform_object_same_origin(*this))
588
0
        return JS::Object::internal_set(property_key, value, receiver, cacheable_metadata);
589
590
    // 2. Return ? CrossOriginSet(this, P, V, Receiver).
591
0
    return HTML::cross_origin_set(vm, static_cast<JS::Object&>(*this), property_key, value, receiver);
592
0
}
593
594
// 7.10.5.9 [[Delete]] ( P ), https://html.spec.whatwg.org/multipage/history.html#location-delete
595
JS::ThrowCompletionOr<bool> Location::internal_delete(JS::PropertyKey const& property_key)
596
0
{
597
    // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinaryDelete(this, P).
598
0
    if (HTML::is_platform_object_same_origin(*this))
599
0
        return JS::Object::internal_delete(property_key);
600
601
    // 2. Throw a "SecurityError" DOMException.
602
0
    return throw_completion(WebIDL::SecurityError::create(realm(), MUST(String::formatted("Can't delete property '{}' on cross-origin object", property_key))));
603
0
}
604
605
// 7.10.5.10 [[OwnPropertyKeys]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-ownpropertykeys
606
JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> Location::internal_own_property_keys() const
607
0
{
608
    // 1. If IsPlatformObjectSameOrigin(this) is true, then return OrdinaryOwnPropertyKeys(this).
609
0
    if (HTML::is_platform_object_same_origin(*this))
610
0
        return JS::Object::internal_own_property_keys();
611
612
    // 2. Return CrossOriginOwnPropertyKeys(this).
613
0
    return HTML::cross_origin_own_property_keys(this);
614
0
}
615
616
}