1use std::default::Default;
6
7use base::generic_channel::GenericSend;
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use net_traits::CoreResourceMsg;
11use net_traits::blob_url_store::parse_blob_url;
12use net_traits::filemanager_thread::FileManagerThreadMsg;
13use profile_traits::ipc;
14use script_bindings::cformat;
15use servo_url::{ImmutableOrigin, ServoUrl};
16use uuid::Uuid;
17
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
20use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
21use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
22use crate::dom::bindings::root::{DomRoot, MutNullableDom};
23use crate::dom::bindings::str::{DOMString, USVString};
24use crate::dom::blob::Blob;
25use crate::dom::globalscope::GlobalScope;
26use crate::dom::urlhelper::UrlHelper;
27use crate::dom::urlsearchparams::URLSearchParams;
28use crate::script_runtime::CanGc;
29
30#[dom_struct]
32#[expect(clippy::upper_case_acronyms)]
33pub(crate) struct URL {
34 reflector_: Reflector,
35
36 #[no_trace]
38 url: DomRefCell<ServoUrl>,
39
40 search_params: MutNullableDom<URLSearchParams>,
42}
43
44impl URL {
45 fn new_inherited(url: ServoUrl) -> URL {
46 URL {
47 reflector_: Reflector::new(),
48 url: DomRefCell::new(url),
49 search_params: Default::default(),
50 }
51 }
52
53 fn new(
54 global: &GlobalScope,
55 proto: Option<HandleObject>,
56 url: ServoUrl,
57 can_gc: CanGc,
58 ) -> DomRoot<URL> {
59 reflect_dom_object_with_proto(Box::new(URL::new_inherited(url)), global, proto, can_gc)
60 }
61
62 pub(crate) fn query_pairs(&self) -> Vec<(String, String)> {
63 self.url
64 .borrow()
65 .as_url()
66 .query_pairs()
67 .into_owned()
68 .collect()
69 }
70
71 pub(crate) fn origin(&self) -> ImmutableOrigin {
72 self.url.borrow().origin()
73 }
74
75 pub(crate) fn set_query_pairs(&self, pairs: &[(String, String)]) {
76 let mut url = self.url.borrow_mut();
77
78 if pairs.is_empty() {
79 url.as_mut_url().set_query(None);
80 } else {
81 url.as_mut_url()
82 .query_pairs_mut()
83 .clear()
84 .extend_pairs(pairs);
85 }
86 }
87
88 fn unicode_serialization_blob_url(origin: &ImmutableOrigin, id: &Uuid) -> String {
90 let mut result = "blob:".to_string();
93
94 result.push_str(&origin.ascii_serialization());
101
102 result.push('/');
104
105 result.push_str(&id.to_string());
107
108 result
110 }
111}
112
113impl URLMethods<crate::DomTypeHolder> for URL {
114 fn Constructor(
116 global: &GlobalScope,
117 proto: Option<HandleObject>,
118 can_gc: CanGc,
119 url: USVString,
120 base: Option<USVString>,
121 ) -> Fallible<DomRoot<URL>> {
122 let parsed_base = match base {
124 None => None,
125 Some(base) => {
126 match ServoUrl::parse(&base.0) {
127 Ok(base) => Some(base),
128 Err(error) => {
129 return Err(Error::Type(cformat!("could not parse base: {}", error)));
131 },
132 }
133 },
134 };
135 let parsed_url = match ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0) {
136 Ok(url) => url,
137 Err(error) => {
138 return Err(Error::Type(cformat!("could not parse URL: {}", error)));
140 },
141 };
142
143 Ok(URL::new(global, proto, parsed_url, can_gc))
154 }
155
156 fn CanParse(_global: &GlobalScope, url: USVString, base: Option<USVString>) -> bool {
158 let parsed_base = match base {
160 None => None,
161 Some(base) => match ServoUrl::parse(&base.0) {
162 Ok(base) => Some(base),
163 Err(_) => {
164 return false;
166 },
167 },
168 };
169 ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0).is_ok()
171 }
172
173 fn Parse(
175 global: &GlobalScope,
176 url: USVString,
177 base: Option<USVString>,
178 can_gc: CanGc,
179 ) -> Option<DomRoot<URL>> {
180 let parsed_base = base.and_then(|base| ServoUrl::parse(base.0.as_str()).ok());
183 let parsed_url = ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0);
184
185 Some(URL::new(global, None, parsed_url.ok()?, can_gc))
194 }
195
196 fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
198 let origin = global.origin().immutable();
201
202 let id = blob.get_blob_url_id();
203
204 DOMString::from(URL::unicode_serialization_blob_url(origin, &id))
205 }
206
207 fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
209 let origin = global.origin().immutable();
213
214 if let Ok(url) = ServoUrl::parse(&url.str()) {
215 if url.fragment().is_none() && *origin == url.origin() {
216 if let Ok((id, _)) = parse_blob_url(&url) {
217 let resource_threads = global.resource_threads();
218 let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
219 let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin.clone(), tx);
220 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
221
222 let _ = rx.recv().unwrap();
223 }
224 }
225 }
226 }
227
228 fn Hash(&self) -> USVString {
230 UrlHelper::Hash(&self.url.borrow())
231 }
232
233 fn SetHash(&self, value: USVString) {
235 UrlHelper::SetHash(&mut self.url.borrow_mut(), value);
236 }
237
238 fn Host(&self) -> USVString {
240 UrlHelper::Host(&self.url.borrow())
241 }
242
243 fn SetHost(&self, value: USVString) {
245 UrlHelper::SetHost(&mut self.url.borrow_mut(), value);
246 }
247
248 fn Hostname(&self) -> USVString {
250 UrlHelper::Hostname(&self.url.borrow())
251 }
252
253 fn SetHostname(&self, value: USVString) {
255 UrlHelper::SetHostname(&mut self.url.borrow_mut(), value);
256 }
257
258 fn Href(&self) -> USVString {
260 UrlHelper::Href(&self.url.borrow())
261 }
262
263 fn SetHref(&self, value: USVString) -> ErrorResult {
265 match ServoUrl::parse(&value.0) {
266 Ok(url) => {
267 *self.url.borrow_mut() = url;
268 self.search_params.set(None); Ok(())
270 },
271 Err(error) => Err(Error::Type(cformat!("could not parse URL: {}", error))),
272 }
273 }
274
275 fn Password(&self) -> USVString {
277 UrlHelper::Password(&self.url.borrow())
278 }
279
280 fn SetPassword(&self, value: USVString) {
282 UrlHelper::SetPassword(&mut self.url.borrow_mut(), value);
283 }
284
285 fn Pathname(&self) -> USVString {
287 UrlHelper::Pathname(&self.url.borrow())
288 }
289
290 fn SetPathname(&self, value: USVString) {
292 UrlHelper::SetPathname(&mut self.url.borrow_mut(), value);
293 }
294
295 fn Port(&self) -> USVString {
297 UrlHelper::Port(&self.url.borrow())
298 }
299
300 fn SetPort(&self, value: USVString) {
302 UrlHelper::SetPort(&mut self.url.borrow_mut(), value);
303 }
304
305 fn Protocol(&self) -> USVString {
307 UrlHelper::Protocol(&self.url.borrow())
308 }
309
310 fn SetProtocol(&self, value: USVString) {
312 UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value);
313 }
314
315 fn Origin(&self) -> USVString {
317 UrlHelper::Origin(&self.url.borrow())
318 }
319
320 fn Search(&self) -> USVString {
322 UrlHelper::Search(&self.url.borrow())
323 }
324
325 fn SetSearch(&self, value: USVString) {
327 UrlHelper::SetSearch(&mut self.url.borrow_mut(), value);
328 if let Some(search_params) = self.search_params.get() {
329 search_params.set_list(self.query_pairs());
330 }
331 }
332
333 fn SearchParams(&self, can_gc: CanGc) -> DomRoot<URLSearchParams> {
335 self.search_params
336 .or_init(|| URLSearchParams::new(&self.global(), Some(self), can_gc))
337 }
338
339 fn Username(&self) -> USVString {
341 UrlHelper::Username(&self.url.borrow())
342 }
343
344 fn SetUsername(&self, value: USVString) {
346 UrlHelper::SetUsername(&mut self.url.borrow_mut(), value);
347 }
348
349 fn ToJSON(&self) -> USVString {
351 self.Href()
352 }
353}