docs
Loading...
Searching...
No Matches
URL.h
Go to the documentation of this file.
1///
2/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3/// A license is required for commercial use. https://ultralig.ht
4///
5#pragma once
7#include <Ultralight/String.h>
8#include <functional>
9
10namespace ultralight {
11
12///
13/// Parsed web address.
14///
15/// URL represents a parsed web address conforming to the WHATWG specification. You'll use it to
16/// inspect address components and resolve relative paths when loading pages into a View.
17///
18/// The object converts to String automatically, so you can pass it directly to View::LoadURL().
19///
20/// Construct a URL from a string and verify that parsing succeeded before loading the address:
21///
22/// ```
23/// URL link(input); // eg, "https://example.com/docs"
24/// if (link)
25/// view->LoadURL(link);
26/// ```
27///
28/// ## Parsing Addresses
29///
30/// Testing a URL in a conditional evaluates to `true` when parsing succeeds and `false` when
31/// parsing fails.
32///
33/// The library stores addresses in canonical form-- str() returns the lowercased scheme and host
34/// rather than the raw input text.
35///
36/// ## Reading Components
37///
38/// Inspect individual parts of an address with component accessors:
39///
40/// ```
41/// URL link("https://example.com/search?q=maps#top");
42///
43/// String site = link.host(); // "example.com"
44/// String route = link.path(); // "/search"
45/// String terms = link.query_parameter("q"); // "maps"
46/// ```
47///
48/// Accessors return an empty String when a component is absent.
49///
50/// Call query_parameter() with a key name to read a single parameter-- it returns the form-decoded
51/// value, or an empty String when the key isn't present.
52///
53/// ## Resolving Relative References
54///
55/// Resolve a relative path against an existing base URL using Resolve():
56///
57/// ```
58/// URL base(view->url());
59/// URL logo = base.Resolve("../images/logo.png");
60/// ```
61///
62/// Resolve() resolves references using the same rules a web browser applies to page links.
63///
64/// ## Building Query Strings
65///
66/// Append key-value pairs to an address using AppendQueryParameter():
67///
68/// ```
69/// URL search("https://www.google.com/search");
70/// search.AppendQueryParameter("q", "C++ URL & more");
71/// // https://www.google.com/search?q=C%2B%2B+URL+%26+more
72/// ```
73///
74/// You can add parameters individually or replace the entire query at once:
75///
76/// - **AppendQueryParameter() encodes both keys and values automatically.** This makes it safe to
77/// build addresses from user input.
78/// - **set_query() applies raw query text as-is.** You'll need to encode the string beforehand.
79///
80/// ## Converting File Paths
81///
82/// Convert a local file path to a `file:` URL with FromFilePath():
83///
84/// ```
85/// URL menu_url = URL::FromFilePath("C:\\Games\\ui\\menu.html"); // a Windows path
86/// String native_path = menu_url.ToFilePath(); // back to the native path
87/// ```
88///
89/// Both methods use the host OS's native path format, converting conventions such as Windows drive
90/// letters and backslashes automatically.
91///
92/// \parblock
93/// @note Parsing requires an absolute address with a scheme (input like `"example.com"` fails to
94/// parse).
95/// \endparblock
96///
97/// \parblock
98/// @note You must initialize the object directly because copy initialization from a string doesn't
99/// compile.
100/// \endparblock
101///
102/// @see View::LoadURL(), View::url()
103///
105 public:
106 ///
107 /// Create an empty, invalid URL.
108 ///
110
111 ///
112 /// Parse a string as an absolute URL (WHATWG-standard parsing).
113 ///
114 /// Input without a scheme (eg, "example.com") does not parse; the result is invalid. To
115 /// resolve a relative reference, use the two-argument constructor taking a base URL.
116 ///
117 /// @param url_string The string to parse.
118 ///
119 /// @warning A host with non-ASCII characters (an international domain name) needs the
120 /// library's Unicode data, which the Renderer loads. Parse such a URL only after
121 /// you've called Renderer::Create() or App::Create().
122 ///
123 explicit URL(const String& url_string);
124
125 ///
126 /// Resolve a (possibly relative) reference against a base URL.
127 ///
128 /// This follows the same rules a browser uses to resolve links on a page. An invalid base
129 /// yields an invalid result unless `relative` is itself an absolute URL.
130 ///
131 /// @param base The base URL to resolve against.
132 ///
133 /// @param relative The reference to resolve. This may be a path ("../images/logo.png"), an
134 /// absolute path ("/index.html"), a fragment ("#top"), or a full absolute
135 /// URL (which ignores the base).
136 ///
137 URL(const URL& base, const String& relative);
138
139 ///
140 /// Create a file: URL from a native file-system path.
141 ///
142 /// Platform path conventions are handled for you (eg, Windows drive letters and backslashes).
143 /// Use ToFilePath() for the reverse conversion.
144 ///
145 /// @param file_path An absolute native file-system path.
146 ///
147 /// @return Returns a file: URL, or an invalid URL if the path cannot be represented.
148 ///
149 static URL FromFilePath(const String& file_path);
150
151 ///
152 /// Percent-encode a string for safe embedding inside a URL component.
153 ///
154 /// This matches JavaScript's `encodeURIComponent`: every character except letters, digits, and
155 /// `- _ . ! ~ * ' ( )` is replaced by the percent-encoding of its UTF-8 bytes.
156 ///
157 /// @param component The text to encode.
158 ///
159 /// @return Returns the encoded text.
160 ///
161 static String EncodeComponent(const String& component);
162
163 ///
164 /// Decode a percent-encoded URL component.
165 ///
166 /// This matches JavaScript's `decodeURIComponent` for well-formed input: `%XX` escapes
167 /// (case-insensitive hex) are decoded as UTF-8 bytes.
168 ///
169 /// Unlike its JavaScript counterpart this function never fails. A malformed escape (truncated,
170 /// or with non-hex digits) is passed through unchanged.
171 ///
172 /// @param encoded The text to decode.
173 ///
174 /// @return Returns the decoded text.
175 ///
176 static String DecodeComponent(const String& encoded);
177
178 ///
179 /// Copy constructor.
180 ///
181 URL(const URL& other);
182
183 ///
184 /// Move constructor.
185 ///
186 URL(URL&& other);
187
188 ///
189 /// Destructor.
190 ///
192
193 ///
194 /// Assignment operator.
195 ///
196 URL& operator=(const URL& other);
197
198 ///
199 /// Move assignment operator.
200 ///
201 URL& operator=(URL&& other);
202
203 ///
204 /// Whether or not this URL parsed successfully.
205 ///
206 bool is_valid() const;
207
208 ///
209 /// Whether or not this URL parsed successfully (same as is_valid()).
210 ///
211 explicit operator bool() const { return is_valid(); }
212
213 ///
214 /// Whether or not this URL is empty (equivalent to !is_valid()).
215 ///
216 bool empty() const;
217
218 ///
219 /// Get the canonical serialization of the whole URL ("" if invalid).
220 ///
221 String str() const;
222
223 ///
224 /// Implicit conversion to the canonical serialization.
225 ///
226 operator String() const;
227
228 ///
229 /// Resolve a relative or absolute reference against this URL (same as the two-argument
230 /// constructor).
231 ///
232 /// @param relative The reference to resolve.
233 ///
234 /// @return Returns the resolved URL. The result is invalid when this URL is invalid, unless
235 /// `relative` is itself an absolute URL.
236 ///
237 URL Resolve(const String& relative) const;
238
239 ///
240 /// Get the origin serialization, like JavaScript's `URL.origin`.
241 ///
242 /// For hierarchical schemes this is "scheme://host" plus the port when one is explicitly
243 /// present (eg, "https://example.com:8080").
244 ///
245 /// @note Opaque origins (eg, data: URLs) serialize as "null".
246 ///
247 String origin() const;
248
249 ///
250 /// Get the scheme, lowercased, without the trailing colon (eg, "https"; "" if invalid).
251 ///
252 String scheme() const;
253
254 ///
255 /// Get the host, without the port (eg, "example.com"; "" if invalid or hostless).
256 ///
257 String host() const;
258
259 ///
260 /// Get the host with the explicit port appended, if one is present (eg, "example.com:8080").
261 ///
263
264 ///
265 /// Whether or not the URL carries an explicit port.
266 ///
267 /// A default port (eg, 443 for https) is elided during canonicalization and does not count as
268 /// explicit.
269 ///
270 bool has_port() const;
271
272 ///
273 /// Get the explicit port number (0 when has_port() is false).
274 ///
275 unsigned short port() const;
276
277 ///
278 /// Get the username portion of the URL's credentials, percent-decoded ("" if none).
279 ///
281
282 ///
283 /// Get the password portion of the URL's credentials, percent-decoded ("" if none).
284 ///
286
287 ///
288 /// Get the path (eg, "/a/b"; "" if invalid).
289 ///
290 String path() const;
291
292 ///
293 /// Get the query string, without the leading '?' ("" if none).
294 ///
295 /// Values inside the query are stored encoded. Use query_parameter() to read a decoded value.
296 ///
297 String query() const;
298
299 ///
300 /// Get the fragment, without the leading '#' ("" if none).
301 ///
303
304 ///
305 /// Set the scheme.
306 ///
307 /// The new scheme is validated and canonicalized (lowercased).
308 ///
309 /// @param scheme The new scheme, with or without the trailing colon.
310 ///
311 /// @return Returns whether or not the scheme was accepted. On failure the URL is unchanged.
312 ///
314
315 ///
316 /// Set the host (without port).
317 ///
318 /// Invalid input (eg, a stray colon) and empty input are ignored, leaving the URL unchanged.
319 /// This matches the JavaScript `URL.host` setter, which reports no acceptance signal either.
320 ///
321 /// @note This has no effect on non-hierarchical URLs (eg, data:).
322 ///
323 void set_host(const String& host);
324
325 ///
326 /// Set an explicit port.
327 ///
328 /// A scheme-default port (eg, 443 for https) is elided from the serialization.
329 ///
330 /// @note This has no effect on non-hierarchical URLs.
331 ///
332 void set_port(unsigned short port);
333
334 ///
335 /// Remove the explicit port, if any.
336 ///
338
339 ///
340 /// Set the username portion of the URL's credentials (percent-encoded as needed).
341 ///
342 /// Pass an empty string to remove it.
343 ///
344 /// @note This has no effect on non-hierarchical URLs.
345 ///
347
348 ///
349 /// Set the password portion of the URL's credentials (percent-encoded as needed).
350 ///
351 /// Pass an empty string to remove it.
352 ///
353 /// @note This has no effect on non-hierarchical URLs.
354 ///
356
357 ///
358 /// Set the path.
359 ///
360 /// The path is encoded as needed. On http(s) URLs an empty path becomes "/".
361 ///
362 void set_path(const String& path);
363
364 ///
365 /// Set the raw query string (without the leading '?').
366 ///
367 /// Pass an empty string to remove the query entirely.
368 ///
369 /// The value is used as-is, so you are responsible for encoding it. To build a query from
370 /// arbitrary text safely, use AppendQueryParameter() instead.
371 ///
372 void set_query(const String& query);
373
374 ///
375 /// Set the fragment (without the leading '#').
376 ///
377 /// Pass an empty string to remove the fragment entirely.
378 ///
380
381 ///
382 /// Whether or not the query contains a parameter with the given key (form-decoded match).
383 ///
384 bool has_query_parameter(const String& key) const;
385
386 ///
387 /// Get the first query-parameter value for the given key, form-decoded ("" if absent).
388 ///
389 String query_parameter(const String& key) const;
390
391 ///
392 /// Append a key-value pair to the query, form-encoding both.
393 ///
394 /// Spaces become `+` and reserved characters are percent-encoded.
395 ///
396 /// @param key The parameter name (unencoded).
397 ///
398 /// @param value The parameter value (unencoded).
399 ///
400 void AppendQueryParameter(const String& key, const String& value);
401
402 ///
403 /// Get the native file-system path for a file: URL.
404 ///
405 /// @return Returns the native path, or "" for any other scheme or if this URL is invalid.
406 ///
407 /// @see FromFilePath()
408 ///
410
411 ///
412 /// Equality over the canonical serialization.
413 ///
414 bool operator==(const URL& other) const;
415
416 ///
417 /// Inequality over the canonical serialization.
418 ///
419 bool operator!=(const URL& other) const;
420
421 ///
422 /// Lexicographic ordering over the canonical serialization (for ordered containers).
423 ///
424 bool operator<(const URL& other) const;
425
426 ///
427 /// Compare the canonical serialization against a raw string.
428 ///
429 /// @note The comparison is textual, so the string must already be in canonical form to match
430 /// (eg, `URL("HTTP://example.com") == "http://example.com/"`).
431 ///
432 bool operator==(const String& other) const;
433
434 ///
435 /// Inequality against a raw string (see the equality operator taking a String).
436 ///
437 bool operator!=(const String& other) const;
438
439 ///
440 /// Equality with the string on the left-hand side.
441 ///
442 inline friend bool operator==(const String& lhs, const URL& rhs) { return rhs == lhs; }
443
444 ///
445 /// Inequality with the string on the left-hand side.
446 ///
447 inline friend bool operator!=(const String& lhs, const URL& rhs) { return rhs != lhs; }
448
449 private:
450 String string_;
451};
452
453} // namespace ultralight
454
455namespace std {
456
457///
458/// Hash support so URL works as a key in unordered containers.
459///
460template <>
461struct hash<ultralight::URL> {
462 size_t operator()(const ultralight::URL& url) const { return url.str().Hash(); }
463};
464
465} // namespace std
#define UExport
Definition Exports.h:22
Unicode string container with conversions for UTF-8, UTF-16, and UTF-32.
Definition String.h:31
size_t Hash() const
Hash function.
Parsed web address.
Definition URL.h:104
bool has_port() const
Whether or not the URL carries an explicit port.
unsigned short port() const
Get the explicit port number (0 when has_port() is false).
URL()
Create an empty, invalid URL.
void clear_port()
Remove the explicit port, if any.
bool operator==(const String &other) const
Compare the canonical serialization against a raw string.
String host() const
Get the host, without the port (eg, "example.com"; "" if invalid or hostless).
~URL()
Destructor.
void set_username(const String &username)
Set the username portion of the URL's credentials (percent-encoded as needed).
bool operator<(const URL &other) const
Lexicographic ordering over the canonical serialization (for ordered containers).
bool has_query_parameter(const String &key) const
Whether or not the query contains a parameter with the given key (form-decoded match).
static String DecodeComponent(const String &encoded)
Decode a percent-encoded URL component.
String origin() const
Get the origin serialization, like JavaScript's URL.origin.
void set_password(const String &password)
Set the password portion of the URL's credentials (percent-encoded as needed).
bool operator!=(const String &other) const
Inequality against a raw string (see the equality operator taking a String).
void set_port(unsigned short port)
Set an explicit port.
friend bool operator==(const String &lhs, const URL &rhs)
Equality with the string on the left-hand side.
Definition URL.h:442
friend bool operator!=(const String &lhs, const URL &rhs)
Inequality with the string on the left-hand side.
Definition URL.h:447
bool empty() const
Whether or not this URL is empty (equivalent to !is_valid()).
String password() const
Get the password portion of the URL's credentials, percent-decoded ("" if none).
static String EncodeComponent(const String &component)
Percent-encode a string for safe embedding inside a URL component.
String query() const
Get the query string, without the leading '?
String ToFilePath() const
Get the native file-system path for a file: URL.
URL(const URL &base, const String &relative)
Resolve a (possibly relative) reference against a base URL.
String path() const
Get the path (eg, "/a/b"; "" if invalid).
void set_query(const String &query)
Set the raw query string (without the leading '?
bool is_valid() const
Whether or not this URL parsed successfully.
void set_path(const String &path)
Set the path.
bool operator==(const URL &other) const
Equality over the canonical serialization.
void set_fragment(const String &fragment)
Set the fragment (without the leading '#').
void AppendQueryParameter(const String &key, const String &value)
Append a key-value pair to the query, form-encoding both.
bool operator!=(const URL &other) const
Inequality over the canonical serialization.
bool set_scheme(const String &scheme)
Set the scheme.
String username() const
Get the username portion of the URL's credentials, percent-decoded ("" if none).
URL(const URL &other)
Copy constructor.
String scheme() const
Get the scheme, lowercased, without the trailing colon (eg, "https"; "" if invalid).
String host_with_port() const
Get the host with the explicit port appended, if one is present (eg, "example.com:8080").
String fragment() const
Get the fragment, without the leading '#' ("" if none).
static URL FromFilePath(const String &file_path)
Create a file: URL from a native file-system path.
String str() const
Get the canonical serialization of the whole URL ("" if invalid).
URL(const String &url_string)
Parse a string as an absolute URL (WHATWG-standard parsing).
void set_host(const String &host)
Set the host (without port).
URL & operator=(URL &&other)
Move assignment operator.
URL & operator=(const URL &other)
Assignment operator.
URL(URL &&other)
Move constructor.
URL Resolve(const String &relative) const
Resolve a relative or absolute reference against this URL (same as the two-argument constructor).
String query_parameter(const String &key) const
Get the first query-parameter value for the given key, form-decoded ("" if absent).
Definition StringSTL.h:166
Root namespace for every public Ultralight type, function, and enumeration.
size_t operator()(const ultralight::URL &url) const
Definition URL.h:462