Files
servo/components/script/dom/url.rs
Sam d5d400c7d6 script: Use CString for Error::Type and Error::Range (#42576)
Continuation of https://github.com/servo/servo/pull/42135, switch
Error::Type and Error::Range to also use CStrings internally, as they
are converted to CString for throwing JS exceptions (other get thrown as
DomException object, which uses rust string internally).

Changes in script crate are mechanical.

Testing: Should be covered by WPT tests.
Part of #42126

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2026-02-12 15:17:30 +00:00

354 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::default::Default;
use base::generic_channel::GenericSend;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use net_traits::CoreResourceMsg;
use net_traits::blob_url_store::parse_blob_url;
use net_traits::filemanager_thread::FileManagerThreadMsg;
use profile_traits::ipc;
use script_bindings::cformat;
use servo_url::{ImmutableOrigin, ServoUrl};
use uuid::Uuid;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::blob::Blob;
use crate::dom::globalscope::GlobalScope;
use crate::dom::urlhelper::UrlHelper;
use crate::dom::urlsearchparams::URLSearchParams;
use crate::script_runtime::CanGc;
/// <https://url.spec.whatwg.org/#url>
#[dom_struct]
#[expect(clippy::upper_case_acronyms)]
pub(crate) struct URL {
reflector_: Reflector,
/// <https://url.spec.whatwg.org/#concept-url-url>
#[no_trace]
url: DomRefCell<ServoUrl>,
/// <https://url.spec.whatwg.org/#dom-url-searchparams>
search_params: MutNullableDom<URLSearchParams>,
}
impl URL {
fn new_inherited(url: ServoUrl) -> URL {
URL {
reflector_: Reflector::new(),
url: DomRefCell::new(url),
search_params: Default::default(),
}
}
fn new(
global: &GlobalScope,
proto: Option<HandleObject>,
url: ServoUrl,
can_gc: CanGc,
) -> DomRoot<URL> {
reflect_dom_object_with_proto(Box::new(URL::new_inherited(url)), global, proto, can_gc)
}
pub(crate) fn query_pairs(&self) -> Vec<(String, String)> {
self.url
.borrow()
.as_url()
.query_pairs()
.into_owned()
.collect()
}
pub(crate) fn origin(&self) -> ImmutableOrigin {
self.url.borrow().origin()
}
pub(crate) fn set_query_pairs(&self, pairs: &[(String, String)]) {
let mut url = self.url.borrow_mut();
if pairs.is_empty() {
url.as_mut_url().set_query(None);
} else {
url.as_mut_url()
.query_pairs_mut()
.clear()
.extend_pairs(pairs);
}
}
/// <https://w3c.github.io/FileAPI/#unicodeBlobURL>
fn unicode_serialization_blob_url(origin: &ImmutableOrigin, id: &Uuid) -> String {
// Step 1. Let result be the empty string.
// Step 2. Append the string "blob:" to result.
let mut result = "blob:".to_string();
// Step 3. Let settings be the current settings object.
// Step 4. Let origin be settingss origin.
// Step 5. Let serialized be the ASCII serialization of origin.
// Step 6. If serialized is "null", set it to an implementation-defined value.
// Step 7. Append serialized to result.
// N.B. We leave it as "null" right now.
result.push_str(&origin.ascii_serialization());
// Step 8. Append U+0024 SOLIDUS (/) to result.
result.push('/');
// Step 9. Generate a UUID [RFC4122] as a string and append it to result.
result.push_str(&id.to_string());
// Step 10. Return result.
result
}
}
impl URLMethods<crate::DomTypeHolder> for URL {
/// <https://url.spec.whatwg.org/#constructors>
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
url: USVString,
base: Option<USVString>,
) -> Fallible<DomRoot<URL>> {
// Step 1. Parse url with base.
let parsed_base = match base {
None => None,
Some(base) => {
match ServoUrl::parse(&base.0) {
Ok(base) => Some(base),
Err(error) => {
// Step 2. Throw a TypeError if URL parsing fails.
return Err(Error::Type(cformat!("could not parse base: {}", error)));
},
}
},
};
let parsed_url = match ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0) {
Ok(url) => url,
Err(error) => {
// Step 2. Throw a TypeError if URL parsing fails.
return Err(Error::Type(cformat!("could not parse URL: {}", error)));
},
};
// Skip the steps below.
// Instead of constructing a new `URLSearchParams` object here, construct it
// on-demand inside `URL::SearchParams`.
//
// Step 3. Let query be parsedURLs query.
// Step 5. Set thiss query object to a new URLSearchParams object.
// Step 6. Initialize thiss query object with query.
// Step 7. Set thiss query objects URL object to this.
// Step 4. Set thiss URL to parsedURL.
Ok(URL::new(global, proto, parsed_url, can_gc))
}
/// <https://url.spec.whatwg.org/#dom-url-canparse>
fn CanParse(_global: &GlobalScope, url: USVString, base: Option<USVString>) -> bool {
// Step 1.
let parsed_base = match base {
None => None,
Some(base) => match ServoUrl::parse(&base.0) {
Ok(base) => Some(base),
Err(_) => {
// Step 2.1
return false;
},
},
};
// Step 2.2, 3
ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0).is_ok()
}
/// <https://url.spec.whatwg.org/#dom-url-parse>
fn Parse(
global: &GlobalScope,
url: USVString,
base: Option<USVString>,
can_gc: CanGc,
) -> Option<DomRoot<URL>> {
// Step 1: Let parsedURL be the result of running the API URL parser on url with base,
// if given.
let parsed_base = base.and_then(|base| ServoUrl::parse(base.0.as_str()).ok());
let parsed_url = ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0);
// Step 2: If parsedURL is failure, then return null.
// Step 3: Let url be a new URL object.
// Step 4: Initialize url with parsedURL.
// Step 5: Return url.
// These steps are all handled while mapping the Result to an Option<ServoUrl>.
// Regarding initialization, the same condition should apply here as stated in the comments
// in Self::Constructor above - construct it on-demand inside `URL::SearchParams`.
Some(URL::new(global, None, parsed_url.ok()?, can_gc))
}
/// <https://w3c.github.io/FileAPI/#dfn-createObjectURL>
fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
// XXX: Second field is an unicode-serialized Origin, it is a temporary workaround
// and should not be trusted. See issue https://github.com/servo/servo/issues/11722
let origin = global.origin().immutable();
let id = blob.get_blob_url_id();
DOMString::from(URL::unicode_serialization_blob_url(origin, &id))
}
/// <https://w3c.github.io/FileAPI/#dfn-revokeObjectURL>
fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
// If the value provided for the url argument is not a Blob URL OR
// if the value provided for the url argument does not have an entry in the Blob URL Store,
// this method call does nothing. User agents may display a message on the error console.
let origin = global.origin().immutable();
if let Ok(url) = ServoUrl::parse(&url.str()) {
if url.fragment().is_none() && *origin == url.origin() {
if let Ok((id, _)) = parse_blob_url(&url) {
let resource_threads = global.resource_threads();
let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin.clone(), tx);
let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
let _ = rx.recv().unwrap();
}
}
}
}
/// <https://url.spec.whatwg.org/#dom-url-hash>
fn Hash(&self) -> USVString {
UrlHelper::Hash(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-hash>
fn SetHash(&self, value: USVString) {
UrlHelper::SetHash(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-host>
fn Host(&self) -> USVString {
UrlHelper::Host(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-host>
fn SetHost(&self, value: USVString) {
UrlHelper::SetHost(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-hostname>
fn Hostname(&self) -> USVString {
UrlHelper::Hostname(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-hostname>
fn SetHostname(&self, value: USVString) {
UrlHelper::SetHostname(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-href>
fn Href(&self) -> USVString {
UrlHelper::Href(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-href>
fn SetHref(&self, value: USVString) -> ErrorResult {
match ServoUrl::parse(&value.0) {
Ok(url) => {
*self.url.borrow_mut() = url;
self.search_params.set(None); // To be re-initialized in the SearchParams getter.
Ok(())
},
Err(error) => Err(Error::Type(cformat!("could not parse URL: {}", error))),
}
}
/// <https://url.spec.whatwg.org/#dom-url-password>
fn Password(&self) -> USVString {
UrlHelper::Password(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-password>
fn SetPassword(&self, value: USVString) {
UrlHelper::SetPassword(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-pathname>
fn Pathname(&self) -> USVString {
UrlHelper::Pathname(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-pathname>
fn SetPathname(&self, value: USVString) {
UrlHelper::SetPathname(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-port>
fn Port(&self) -> USVString {
UrlHelper::Port(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-port>
fn SetPort(&self, value: USVString) {
UrlHelper::SetPort(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-protocol>
fn Protocol(&self) -> USVString {
UrlHelper::Protocol(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-protocol>
fn SetProtocol(&self, value: USVString) {
UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-origin>
fn Origin(&self) -> USVString {
UrlHelper::Origin(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-search>
fn Search(&self) -> USVString {
UrlHelper::Search(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-search>
fn SetSearch(&self, value: USVString) {
UrlHelper::SetSearch(&mut self.url.borrow_mut(), value);
if let Some(search_params) = self.search_params.get() {
search_params.set_list(self.query_pairs());
}
}
/// <https://url.spec.whatwg.org/#dom-url-searchparams>
fn SearchParams(&self, can_gc: CanGc) -> DomRoot<URLSearchParams> {
self.search_params
.or_init(|| URLSearchParams::new(&self.global(), Some(self), can_gc))
}
/// <https://url.spec.whatwg.org/#dom-url-username>
fn Username(&self) -> USVString {
UrlHelper::Username(&self.url.borrow())
}
/// <https://url.spec.whatwg.org/#dom-url-username>
fn SetUsername(&self, value: USVString) {
UrlHelper::SetUsername(&mut self.url.borrow_mut(), value);
}
/// <https://url.spec.whatwg.org/#dom-url-tojson>
fn ToJSON(&self) -> USVString {
self.Href()
}
}