/src/serenity/Userland/Libraries/LibWeb/DOMURL/DOMURL.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> |
3 | | * Copyright (c) 2021, the SerenityOS developers. |
4 | | * Copyright (c) 2023, networkException <networkexception@serenityos.org> |
5 | | * Copyright (c) 2024, Shannon Booth <shannon@serenityos.org> |
6 | | * |
7 | | * SPDX-License-Identifier: BSD-2-Clause |
8 | | */ |
9 | | |
10 | | #include <AK/IPv4Address.h> |
11 | | #include <AK/IPv6Address.h> |
12 | | #include <LibURL/Parser.h> |
13 | | #include <LibWeb/Bindings/DOMURLPrototype.h> |
14 | | #include <LibWeb/Bindings/Intrinsics.h> |
15 | | #include <LibWeb/DOMURL/DOMURL.h> |
16 | | #include <LibWeb/FileAPI/Blob.h> |
17 | | #include <LibWeb/FileAPI/BlobURLStore.h> |
18 | | |
19 | | namespace Web::DOMURL { |
20 | | |
21 | | JS_DEFINE_ALLOCATOR(DOMURL); |
22 | | |
23 | | JS::NonnullGCPtr<DOMURL> DOMURL::create(JS::Realm& realm, URL::URL url, JS::NonnullGCPtr<URLSearchParams> query) |
24 | 0 | { |
25 | 0 | return realm.heap().allocate<DOMURL>(realm, realm, move(url), move(query)); |
26 | 0 | } |
27 | | |
28 | | // https://url.spec.whatwg.org/#api-url-parser |
29 | | static Optional<URL::URL> parse_api_url(String const& url, Optional<String> const& base) |
30 | 0 | { |
31 | | // FIXME: We somewhat awkwardly have two failure states encapsulated in the return type (and convert between them in the steps), |
32 | | // ideally we'd get rid of URL's valid flag |
33 | | |
34 | | // 1. Let parsedBase be null. |
35 | 0 | Optional<URL::URL> parsed_base; |
36 | | |
37 | | // 2. If base is non-null: |
38 | 0 | if (base.has_value()) { |
39 | | // 1. Set parsedBase to the result of running the basic URL parser on base. |
40 | 0 | auto parsed_base_url = URL::Parser::basic_parse(*base); |
41 | | |
42 | | // 2. If parsedBase is failure, then return failure. |
43 | 0 | if (!parsed_base_url.is_valid()) |
44 | 0 | return {}; |
45 | | |
46 | 0 | parsed_base = parsed_base_url; |
47 | 0 | } |
48 | | |
49 | | // 3. Return the result of running the basic URL parser on url with parsedBase. |
50 | 0 | auto parsed = URL::Parser::basic_parse(url, parsed_base); |
51 | 0 | return parsed.is_valid() ? parsed : Optional<URL::URL> {}; |
52 | 0 | } |
53 | | |
54 | | // https://url.spec.whatwg.org/#url-initialize |
55 | | JS::NonnullGCPtr<DOMURL> DOMURL::initialize_a_url(JS::Realm& realm, URL::URL const& url_record) |
56 | 0 | { |
57 | | // 1. Let query be urlRecord’s query, if that is non-null; otherwise the empty string. |
58 | 0 | auto query = url_record.query().value_or(String {}); |
59 | | |
60 | | // 2. Set url’s URL to urlRecord. |
61 | | // 3. Set url’s query object to a new URLSearchParams object. |
62 | 0 | auto query_object = URLSearchParams::create(realm, query); |
63 | | |
64 | | // 4. Initialize url’s query object with query. |
65 | 0 | auto result_url = DOMURL::create(realm, url_record, move(query_object)); |
66 | | |
67 | | // 5. Set url’s query object’s URL object to url. |
68 | 0 | result_url->m_query->m_url = result_url; |
69 | |
|
70 | 0 | return result_url; |
71 | 0 | } |
72 | | |
73 | | // https://url.spec.whatwg.org/#dom-url-parse |
74 | | JS::GCPtr<DOMURL> DOMURL::parse_for_bindings(JS::VM& vm, String const& url, Optional<String> const& base) |
75 | 0 | { |
76 | 0 | auto& realm = *vm.current_realm(); |
77 | | |
78 | | // 1. Let parsedURL be the result of running the API URL parser on url with base, if given. |
79 | 0 | auto parsed_url = parse_api_url(url, base); |
80 | | |
81 | | // 2. If parsedURL is failure, then return null. |
82 | 0 | if (!parsed_url.has_value()) |
83 | 0 | return nullptr; |
84 | | |
85 | | // 3. Let url be a new URL object. |
86 | | // 4. Initialize url with parsedURL. |
87 | | // 5. Return url. |
88 | 0 | return initialize_a_url(realm, parsed_url.value()); |
89 | 0 | } |
90 | | |
91 | | // https://url.spec.whatwg.org/#dom-url-url |
92 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMURL>> DOMURL::construct_impl(JS::Realm& realm, String const& url, Optional<String> const& base) |
93 | 0 | { |
94 | | // 1. Let parsedURL be the result of running the API URL parser on url with base, if given. |
95 | 0 | auto parsed_url = parse_api_url(url, base); |
96 | | |
97 | | // 2. If parsedURL is failure, then throw a TypeError. |
98 | 0 | if (!parsed_url.has_value()) |
99 | 0 | return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid URL"sv }; |
100 | | |
101 | | // 3. Initialize this with parsedURL. |
102 | 0 | return initialize_a_url(realm, parsed_url.value()); |
103 | 0 | } |
104 | | |
105 | | DOMURL::DOMURL(JS::Realm& realm, URL::URL url, JS::NonnullGCPtr<URLSearchParams> query) |
106 | 0 | : PlatformObject(realm) |
107 | 0 | , m_url(move(url)) |
108 | 0 | , m_query(move(query)) |
109 | 0 | { |
110 | 0 | } |
111 | | |
112 | 0 | DOMURL::~DOMURL() = default; |
113 | | |
114 | | void DOMURL::initialize(JS::Realm& realm) |
115 | 0 | { |
116 | 0 | Base::initialize(realm); |
117 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(DOMURL, URL); |
118 | 0 | } |
119 | | |
120 | | void DOMURL::visit_edges(Cell::Visitor& visitor) |
121 | 0 | { |
122 | 0 | Base::visit_edges(visitor); |
123 | 0 | visitor.visit(m_query); |
124 | 0 | } |
125 | | |
126 | | // https://w3c.github.io/FileAPI/#dfn-createObjectURL |
127 | | WebIDL::ExceptionOr<String> DOMURL::create_object_url(JS::VM& vm, JS::NonnullGCPtr<FileAPI::Blob> object) |
128 | 0 | { |
129 | | // The createObjectURL(obj) static method must return the result of adding an entry to the blob URL store for obj. |
130 | 0 | return TRY_OR_THROW_OOM(vm, FileAPI::add_entry_to_blob_url_store(object)); |
131 | 0 | } |
132 | | |
133 | | // https://w3c.github.io/FileAPI/#dfn-revokeObjectURL |
134 | | WebIDL::ExceptionOr<void> DOMURL::revoke_object_url(JS::VM& vm, StringView url) |
135 | 0 | { |
136 | | // 1. Let url record be the result of parsing url. |
137 | 0 | auto url_record = parse(url); |
138 | | |
139 | | // 2. If url record’s scheme is not "blob", return. |
140 | 0 | if (url_record.scheme() != "blob"sv) |
141 | 0 | return {}; |
142 | | |
143 | | // 3. Let origin be the origin of url record. |
144 | 0 | auto origin = url_record.origin(); |
145 | | |
146 | | // 4. Let settings be the current settings object. |
147 | 0 | auto& settings = HTML::current_settings_object(); |
148 | | |
149 | | // 5. If origin is not same origin with settings’s origin, return. |
150 | 0 | if (!origin.is_same_origin(settings.origin())) |
151 | 0 | return {}; |
152 | | |
153 | | // 6. Remove an entry from the Blob URL Store for url. |
154 | 0 | TRY_OR_THROW_OOM(vm, FileAPI::remove_entry_from_blob_url_store(url)); |
155 | 0 | return {}; |
156 | 0 | } |
157 | | |
158 | | // https://url.spec.whatwg.org/#dom-url-canparse |
159 | | bool DOMURL::can_parse(JS::VM&, String const& url, Optional<String> const& base) |
160 | 0 | { |
161 | | // 1. Let parsedURL be the result of running the API URL parser on url with base, if given. |
162 | 0 | auto parsed_url = parse_api_url(url, base); |
163 | | |
164 | | // 2. If parsedURL is failure, then return false. |
165 | 0 | if (!parsed_url.has_value()) |
166 | 0 | return false; |
167 | | |
168 | | // 3. Return true. |
169 | 0 | return true; |
170 | 0 | } |
171 | | |
172 | | // https://url.spec.whatwg.org/#dom-url-href |
173 | | WebIDL::ExceptionOr<String> DOMURL::href() const |
174 | 0 | { |
175 | 0 | auto& vm = realm().vm(); |
176 | | |
177 | | // The href getter steps and the toJSON() method steps are to return the serialization of this’s URL. |
178 | 0 | return TRY_OR_THROW_OOM(vm, String::from_byte_string(m_url.serialize())); |
179 | 0 | } |
180 | | |
181 | | // https://url.spec.whatwg.org/#dom-url-tojson |
182 | | WebIDL::ExceptionOr<String> DOMURL::to_json() const |
183 | 0 | { |
184 | 0 | auto& vm = realm().vm(); |
185 | | |
186 | | // The href getter steps and the toJSON() method steps are to return the serialization of this’s URL. |
187 | 0 | return TRY_OR_THROW_OOM(vm, String::from_byte_string(m_url.serialize())); |
188 | 0 | } |
189 | | |
190 | | // https://url.spec.whatwg.org/#ref-for-dom-url-href② |
191 | | WebIDL::ExceptionOr<void> DOMURL::set_href(String const& href) |
192 | 0 | { |
193 | | // 1. Let parsedURL be the result of running the basic URL parser on the given value. |
194 | 0 | URL::URL parsed_url = href; |
195 | | |
196 | | // 2. If parsedURL is failure, then throw a TypeError. |
197 | 0 | if (!parsed_url.is_valid()) |
198 | 0 | return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid URL"sv }; |
199 | | |
200 | | // 3. Set this’s URL to parsedURL. |
201 | 0 | m_url = move(parsed_url); |
202 | | |
203 | | // 4. Empty this’s query object’s list. |
204 | 0 | m_query->m_list.clear(); |
205 | | |
206 | | // 5. Let query be this’s URL’s query. |
207 | 0 | auto query = m_url.query(); |
208 | | |
209 | | // 6. If query is non-null, then set this’s query object’s list to the result of parsing query. |
210 | 0 | if (query.has_value()) |
211 | 0 | m_query->m_list = url_decode(*query); |
212 | 0 | return {}; |
213 | 0 | } |
214 | | |
215 | | // https://url.spec.whatwg.org/#dom-url-origin |
216 | | WebIDL::ExceptionOr<String> DOMURL::origin() const |
217 | 0 | { |
218 | 0 | auto& vm = realm().vm(); |
219 | | |
220 | | // The origin getter steps are to return the serialization of this’s URL’s origin. [HTML] |
221 | 0 | return TRY_OR_THROW_OOM(vm, String::from_byte_string(m_url.origin().serialize())); |
222 | 0 | } |
223 | | |
224 | | // https://url.spec.whatwg.org/#dom-url-protocol |
225 | | WebIDL::ExceptionOr<String> DOMURL::protocol() const |
226 | 0 | { |
227 | 0 | auto& vm = realm().vm(); |
228 | | |
229 | | // The protocol getter steps are to return this’s URL’s scheme, followed by U+003A (:). |
230 | 0 | return TRY_OR_THROW_OOM(vm, String::formatted("{}:", m_url.scheme())); |
231 | 0 | } |
232 | | |
233 | | // https://url.spec.whatwg.org/#ref-for-dom-url-protocol%E2%91%A0 |
234 | | WebIDL::ExceptionOr<void> DOMURL::set_protocol(String const& protocol) |
235 | 0 | { |
236 | 0 | auto& vm = realm().vm(); |
237 | | |
238 | | // The protocol setter steps are to basic URL parse the given value, followed by U+003A (:), with this’s URL as |
239 | | // url and scheme start state as state override. |
240 | 0 | (void)URL::Parser::basic_parse(TRY_OR_THROW_OOM(vm, String::formatted("{}:", protocol)), {}, &m_url, URL::Parser::State::SchemeStart); |
241 | 0 | return {}; |
242 | 0 | } |
243 | | |
244 | | // https://url.spec.whatwg.org/#dom-url-username |
245 | | String const& DOMURL::username() const |
246 | 0 | { |
247 | | // The username getter steps are to return this’s URL’s username. |
248 | 0 | return m_url.username(); |
249 | 0 | } |
250 | | |
251 | | // https://url.spec.whatwg.org/#ref-for-dom-url-username%E2%91%A0 |
252 | | void DOMURL::set_username(String const& username) |
253 | 0 | { |
254 | | // 1. If this’s URL cannot have a username/password/port, then return. |
255 | 0 | if (m_url.cannot_have_a_username_or_password_or_port()) |
256 | 0 | return; |
257 | | |
258 | | // 2. Set the username given this’s URL and the given value. |
259 | 0 | m_url.set_username(username); |
260 | 0 | } |
261 | | |
262 | | // https://url.spec.whatwg.org/#dom-url-password |
263 | | String const& DOMURL::password() const |
264 | 0 | { |
265 | | // The password getter steps are to return this’s URL’s password. |
266 | 0 | return m_url.password(); |
267 | 0 | } |
268 | | |
269 | | // https://url.spec.whatwg.org/#ref-for-dom-url-password%E2%91%A0 |
270 | | void DOMURL::set_password(String const& password) |
271 | 0 | { |
272 | | // 1. If this’s URL cannot have a username/password/port, then return. |
273 | 0 | if (m_url.cannot_have_a_username_or_password_or_port()) |
274 | 0 | return; |
275 | | |
276 | | // 2. Set the password given this’s URL and the given value. |
277 | 0 | m_url.set_password(password); |
278 | 0 | } |
279 | | |
280 | | // https://url.spec.whatwg.org/#dom-url-host |
281 | | WebIDL::ExceptionOr<String> DOMURL::host() const |
282 | 0 | { |
283 | 0 | auto& vm = realm().vm(); |
284 | | |
285 | | // 1. Let url be this’s URL. |
286 | 0 | auto& url = m_url; |
287 | | |
288 | | // 2. If url’s host is null, then return the empty string. |
289 | 0 | if (url.host().has<Empty>()) |
290 | 0 | return String {}; |
291 | | |
292 | | // 3. If url’s port is null, return url’s host, serialized. |
293 | 0 | if (!url.port().has_value()) |
294 | 0 | return TRY_OR_THROW_OOM(vm, url.serialized_host()); |
295 | | |
296 | | // 4. Return url’s host, serialized, followed by U+003A (:) and url’s port, serialized. |
297 | 0 | return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), *url.port())); |
298 | 0 | } |
299 | | |
300 | | // https://url.spec.whatwg.org/#dom-url-hostref-for-dom-url-host%E2%91%A0 |
301 | | void DOMURL::set_host(String const& host) |
302 | 0 | { |
303 | | // 1. If this’s URL’s cannot-be-a-base-URL is true, then return. |
304 | 0 | if (m_url.cannot_be_a_base_url()) |
305 | 0 | return; |
306 | | |
307 | | // 2. Basic URL parse the given value with this’s URL as url and host state as state override. |
308 | 0 | (void)URL::Parser::basic_parse(host, {}, &m_url, URL::Parser::State::Host); |
309 | 0 | } |
310 | | |
311 | | // https://url.spec.whatwg.org/#dom-url-hostname |
312 | | WebIDL::ExceptionOr<String> DOMURL::hostname() const |
313 | 0 | { |
314 | 0 | auto& vm = realm().vm(); |
315 | | |
316 | | // 1. If this’s URL’s host is null, then return the empty string. |
317 | 0 | if (m_url.host().has<Empty>()) |
318 | 0 | return String {}; |
319 | | |
320 | | // 2. Return this’s URL’s host, serialized. |
321 | 0 | return TRY_OR_THROW_OOM(vm, m_url.serialized_host()); |
322 | 0 | } |
323 | | |
324 | | // https://url.spec.whatwg.org/#ref-for-dom-url-hostname① |
325 | | void DOMURL::set_hostname(String const& hostname) |
326 | 0 | { |
327 | | // 1. If this’s URL’s cannot-be-a-base-URL is true, then return. |
328 | 0 | if (m_url.cannot_be_a_base_url()) |
329 | 0 | return; |
330 | | |
331 | | // 2. Basic URL parse the given value with this’s URL as url and hostname state as state override. |
332 | 0 | (void)URL::Parser::basic_parse(hostname, {}, &m_url, URL::Parser::State::Hostname); |
333 | 0 | } |
334 | | |
335 | | // https://url.spec.whatwg.org/#dom-url-port |
336 | | WebIDL::ExceptionOr<String> DOMURL::port() const |
337 | 0 | { |
338 | 0 | auto& vm = realm().vm(); |
339 | | |
340 | | // 1. If this’s URL’s port is null, then return the empty string. |
341 | 0 | if (!m_url.port().has_value()) |
342 | 0 | return String {}; |
343 | | |
344 | | // 2. Return this’s URL’s port, serialized. |
345 | 0 | return TRY_OR_THROW_OOM(vm, String::formatted("{}", *m_url.port())); |
346 | 0 | } |
347 | | |
348 | | // https://url.spec.whatwg.org/#ref-for-dom-url-port%E2%91%A0 |
349 | | void DOMURL::set_port(String const& port) |
350 | 0 | { |
351 | | // 1. If this’s URL cannot have a username/password/port, then return. |
352 | 0 | if (m_url.cannot_have_a_username_or_password_or_port()) |
353 | 0 | return; |
354 | | |
355 | | // 2. If the given value is the empty string, then set this’s URL’s port to null. |
356 | 0 | if (port.is_empty()) { |
357 | 0 | m_url.set_port({}); |
358 | 0 | } |
359 | | // 3. Otherwise, basic URL parse the given value with this’s URL as url and port state as state override. |
360 | 0 | else { |
361 | 0 | (void)URL::Parser::basic_parse(port, {}, &m_url, URL::Parser::State::Port); |
362 | 0 | } |
363 | 0 | } |
364 | | |
365 | | // https://url.spec.whatwg.org/#dom-url-pathname |
366 | | String DOMURL::pathname() const |
367 | 0 | { |
368 | | // The pathname getter steps are to return the result of URL path serializing this’s URL. |
369 | 0 | return m_url.serialize_path(); |
370 | 0 | } |
371 | | |
372 | | // https://url.spec.whatwg.org/#ref-for-dom-url-pathname%E2%91%A0 |
373 | | void DOMURL::set_pathname(String const& pathname) |
374 | 0 | { |
375 | | // FIXME: These steps no longer match the speci. |
376 | | // 1. If this’s URL’s cannot-be-a-base-URL is true, then return. |
377 | 0 | if (m_url.cannot_be_a_base_url()) |
378 | 0 | return; |
379 | | |
380 | | // 2. Empty this’s URL’s path. |
381 | 0 | m_url.set_paths({}); |
382 | | |
383 | | // 3. Basic URL parse the given value with this’s URL as url and path start state as state override. |
384 | 0 | (void)URL::Parser::basic_parse(pathname, {}, &m_url, URL::Parser::State::PathStart); |
385 | 0 | } |
386 | | |
387 | | // https://url.spec.whatwg.org/#dom-url-search |
388 | | WebIDL::ExceptionOr<String> DOMURL::search() const |
389 | 0 | { |
390 | 0 | auto& vm = realm().vm(); |
391 | | |
392 | | // 1. If this’s URL’s query is either null or the empty string, then return the empty string. |
393 | 0 | if (!m_url.query().has_value() || m_url.query()->is_empty()) |
394 | 0 | return String {}; |
395 | | |
396 | | // 2. Return U+003F (?), followed by this’s URL’s query. |
397 | 0 | return TRY_OR_THROW_OOM(vm, String::formatted("?{}", *m_url.query())); |
398 | 0 | } |
399 | | |
400 | | // https://url.spec.whatwg.org/#ref-for-dom-url-search%E2%91%A0 |
401 | | void DOMURL::set_search(String const& search) |
402 | 0 | { |
403 | | // 1. Let url be this’s URL. |
404 | 0 | auto& url = m_url; |
405 | | |
406 | | // 2. If the given value is the empty string: |
407 | 0 | if (search.is_empty()) { |
408 | | // 1. Set url’s query to null. |
409 | 0 | url.set_query({}); |
410 | | |
411 | | // 2. Empty this’s query object’s list. |
412 | 0 | m_query->m_list.clear(); |
413 | | |
414 | | // 3. Potentially strip trailing spaces from an opaque path with this. |
415 | 0 | strip_trailing_spaces_from_an_opaque_path(*this); |
416 | | |
417 | | // 4. Return. |
418 | 0 | return; |
419 | 0 | } |
420 | | |
421 | | // 3. Let input be the given value with a single leading U+003F (?) removed, if any. |
422 | 0 | auto search_as_string_view = search.bytes_as_string_view(); |
423 | 0 | auto input = search_as_string_view.substring_view(search_as_string_view.starts_with('?')); |
424 | | |
425 | | // 4. Set url’s query to the empty string. |
426 | 0 | url.set_query(String {}); |
427 | | |
428 | | // 5. Basic URL parse input with url as url and query state as state override. |
429 | 0 | (void)URL::Parser::basic_parse(input, {}, &url, URL::Parser::State::Query); |
430 | | |
431 | | // 6. Set this’s query object’s list to the result of parsing input. |
432 | 0 | m_query->m_list = url_decode(input); |
433 | 0 | } |
434 | | |
435 | | // https://url.spec.whatwg.org/#dom-url-searchparams |
436 | | JS::NonnullGCPtr<URLSearchParams const> DOMURL::search_params() const |
437 | 0 | { |
438 | | // The searchParams getter steps are to return this’s query object. |
439 | 0 | return m_query; |
440 | 0 | } |
441 | | |
442 | | // https://url.spec.whatwg.org/#dom-url-hash |
443 | | WebIDL::ExceptionOr<String> DOMURL::hash() const |
444 | 0 | { |
445 | 0 | auto& vm = realm().vm(); |
446 | | |
447 | | // 1. If this’s URL’s fragment is either null or the empty string, then return the empty string. |
448 | 0 | if (!m_url.fragment().has_value() || m_url.fragment()->is_empty()) |
449 | 0 | return String {}; |
450 | | |
451 | | // 2. Return U+0023 (#), followed by this’s URL’s fragment. |
452 | 0 | return TRY_OR_THROW_OOM(vm, String::formatted("#{}", m_url.fragment())); |
453 | 0 | } |
454 | | |
455 | | // https://url.spec.whatwg.org/#ref-for-dom-url-hash%E2%91%A0 |
456 | | void DOMURL::set_hash(String const& hash) |
457 | 0 | { |
458 | | // 1. If the given value is the empty string: |
459 | 0 | if (hash.is_empty()) { |
460 | | // 1. Set this’s URL’s fragment to null. |
461 | 0 | m_url.set_fragment({}); |
462 | | |
463 | | // 2. Potentially strip trailing spaces from an opaque path with this. |
464 | 0 | strip_trailing_spaces_from_an_opaque_path(*this); |
465 | | |
466 | | // 3. Return. |
467 | 0 | return; |
468 | 0 | } |
469 | | |
470 | | // 2. Let input be the given value with a single leading U+0023 (#) removed, if any. |
471 | 0 | auto hash_as_string_view = hash.bytes_as_string_view(); |
472 | 0 | auto input = hash_as_string_view.substring_view(hash_as_string_view.starts_with('#')); |
473 | | |
474 | | // 3. Set this’s URL’s fragment to the empty string. |
475 | 0 | m_url.set_fragment(String {}); |
476 | | |
477 | | // 4. Basic URL parse input with this’s URL as url and fragment state as state override. |
478 | 0 | (void)URL::Parser::basic_parse(input, {}, &m_url, URL::Parser::State::Fragment); |
479 | 0 | } |
480 | | |
481 | | // https://url.spec.whatwg.org/#concept-domain |
482 | | bool host_is_domain(URL::Host const& host) |
483 | 0 | { |
484 | | // A domain is a non-empty ASCII string that identifies a realm within a network. |
485 | 0 | return host.has<String>() && host.get<String>() != String {}; |
486 | 0 | } |
487 | | |
488 | | // https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path |
489 | | void strip_trailing_spaces_from_an_opaque_path(DOMURL& url) |
490 | 0 | { |
491 | | // 1. If url’s URL does not have an opaque path, then return. |
492 | | // FIXME: Reimplement this step once we modernize the URL implementation to meet the spec. |
493 | 0 | if (!url.cannot_be_a_base_url()) |
494 | 0 | return; |
495 | | |
496 | | // 2. If url’s URL’s fragment is non-null, then return. |
497 | 0 | if (url.fragment().has_value()) |
498 | 0 | return; |
499 | | |
500 | | // 3. If url’s URL’s query is non-null, then return. |
501 | 0 | if (url.query().has_value()) |
502 | 0 | return; |
503 | | |
504 | | // 4. Remove all trailing U+0020 SPACE code points from url’s URL’s path. |
505 | | // NOTE: At index 0 since the first step tells us that the URL only has one path segment. |
506 | 0 | auto opaque_path = url.path_segment_at_index(0); |
507 | 0 | auto trimmed_path = opaque_path.trim(" "sv, TrimMode::Right); |
508 | 0 | url.set_paths({ trimmed_path }); |
509 | 0 | } |
510 | | |
511 | | // https://url.spec.whatwg.org/#concept-url-parser |
512 | | URL::URL parse(StringView input, Optional<URL::URL> const& base_url, Optional<StringView> encoding) |
513 | 0 | { |
514 | | // FIXME: We should probably have an extended version of URL::URL for LibWeb instead of standalone functions like this. |
515 | | |
516 | | // 1. Let url be the result of running the basic URL parser on input with base and encoding. |
517 | 0 | auto url = URL::Parser::basic_parse(input, base_url, {}, {}, encoding); |
518 | | |
519 | | // 2. If url is failure, return failure. |
520 | 0 | if (!url.is_valid()) |
521 | 0 | return {}; |
522 | | |
523 | | // 3. If url’s scheme is not "blob", return url. |
524 | 0 | if (url.scheme() != "blob") |
525 | 0 | return url; |
526 | | |
527 | | // 4. Set url’s blob URL entry to the result of resolving the blob URL url, if that did not return failure, and null otherwise. |
528 | 0 | auto blob_url_entry = FileAPI::resolve_a_blob_url(url); |
529 | 0 | if (blob_url_entry.has_value()) { |
530 | 0 | url.set_blob_url_entry(URL::BlobURLEntry { |
531 | 0 | .type = blob_url_entry->object->type(), |
532 | 0 | .byte_buffer = MUST(ByteBuffer::copy(blob_url_entry->object->raw_bytes())), |
533 | 0 | .environment_origin = blob_url_entry->environment->origin(), |
534 | 0 | }); |
535 | 0 | } |
536 | | |
537 | | // 5. Return url |
538 | 0 | return url; |
539 | 0 | } |
540 | | |
541 | | } |