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 servo_url::{ImmutableOrigin, ServoUrl};
15use uuid::Uuid;
16
17use crate::dom::bindings::cell::DomRefCell;
18use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
19use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
20use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
21use crate::dom::bindings::root::{DomRoot, MutNullableDom};
22use crate::dom::bindings::str::{DOMString, USVString};
23use crate::dom::blob::Blob;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::urlhelper::UrlHelper;
26use crate::dom::urlsearchparams::URLSearchParams;
27use crate::script_runtime::CanGc;
28
29#[dom_struct]
31#[expect(clippy::upper_case_acronyms)]
32pub(crate) struct URL {
33 reflector_: Reflector,
34
35 #[no_trace]
37 url: DomRefCell<ServoUrl>,
38
39 search_params: MutNullableDom<URLSearchParams>,
41}
42
43impl URL {
44 fn new_inherited(url: ServoUrl) -> URL {
45 URL {
46 reflector_: Reflector::new(),
47 url: DomRefCell::new(url),
48 search_params: Default::default(),
49 }
50 }
51
52 fn new(
53 global: &GlobalScope,
54 proto: Option<HandleObject>,
55 url: ServoUrl,
56 can_gc: CanGc,
57 ) -> DomRoot<URL> {
58 reflect_dom_object_with_proto(Box::new(URL::new_inherited(url)), global, proto, can_gc)
59 }
60
61 pub(crate) fn query_pairs(&self) -> Vec<(String, String)> {
62 self.url
63 .borrow()
64 .as_url()
65 .query_pairs()
66 .into_owned()
67 .collect()
68 }
69
70 pub(crate) fn origin(&self) -> ImmutableOrigin {
71 self.url.borrow().origin()
72 }
73
74 pub(crate) fn set_query_pairs(&self, pairs: &[(String, String)]) {
75 let mut url = self.url.borrow_mut();
76
77 if pairs.is_empty() {
78 url.as_mut_url().set_query(None);
79 } else {
80 url.as_mut_url()
81 .query_pairs_mut()
82 .clear()
83 .extend_pairs(pairs);
84 }
85 }
86
87 fn unicode_serialization_blob_url(origin: &ImmutableOrigin, id: &Uuid) -> String {
89 let mut result = "blob:".to_string();
92
93 result.push_str(&origin.ascii_serialization());
100
101 result.push('/');
103
104 result.push_str(&id.to_string());
106
107 result
109 }
110}
111
112impl URLMethods<crate::DomTypeHolder> for URL {
113 fn Constructor(
115 global: &GlobalScope,
116 proto: Option<HandleObject>,
117 can_gc: CanGc,
118 url: USVString,
119 base: Option<USVString>,
120 ) -> Fallible<DomRoot<URL>> {
121 let parsed_base = match base {
123 None => None,
124 Some(base) => {
125 match ServoUrl::parse(&base.0) {
126 Ok(base) => Some(base),
127 Err(error) => {
128 return Err(Error::Type(format!("could not parse base: {}", error)));
130 },
131 }
132 },
133 };
134 let parsed_url = match ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0) {
135 Ok(url) => url,
136 Err(error) => {
137 return Err(Error::Type(format!("could not parse URL: {}", error)));
139 },
140 };
141
142 Ok(URL::new(global, proto, parsed_url, can_gc))
153 }
154
155 fn CanParse(_global: &GlobalScope, url: USVString, base: Option<USVString>) -> bool {
157 let parsed_base = match base {
159 None => None,
160 Some(base) => match ServoUrl::parse(&base.0) {
161 Ok(base) => Some(base),
162 Err(_) => {
163 return false;
165 },
166 },
167 };
168 ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0).is_ok()
170 }
171
172 fn Parse(
174 global: &GlobalScope,
175 url: USVString,
176 base: Option<USVString>,
177 can_gc: CanGc,
178 ) -> Option<DomRoot<URL>> {
179 let parsed_base = base.and_then(|base| ServoUrl::parse(base.0.as_str()).ok());
182 let parsed_url = ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0);
183
184 Some(URL::new(global, None, parsed_url.ok()?, can_gc))
193 }
194
195 fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
197 let origin = global.origin().immutable();
200
201 let id = blob.get_blob_url_id();
202
203 DOMString::from(URL::unicode_serialization_blob_url(origin, &id))
204 }
205
206 fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
208 let origin = global.origin().immutable();
212
213 if let Ok(url) = ServoUrl::parse(&url.str()) {
214 if url.fragment().is_none() && *origin == url.origin() {
215 if let Ok((id, _)) = parse_blob_url(&url) {
216 let resource_threads = global.resource_threads();
217 let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
218 let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin.clone(), tx);
219 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
220
221 let _ = rx.recv().unwrap();
222 }
223 }
224 }
225 }
226
227 fn Hash(&self) -> USVString {
229 UrlHelper::Hash(&self.url.borrow())
230 }
231
232 fn SetHash(&self, value: USVString) {
234 UrlHelper::SetHash(&mut self.url.borrow_mut(), value);
235 }
236
237 fn Host(&self) -> USVString {
239 UrlHelper::Host(&self.url.borrow())
240 }
241
242 fn SetHost(&self, value: USVString) {
244 UrlHelper::SetHost(&mut self.url.borrow_mut(), value);
245 }
246
247 fn Hostname(&self) -> USVString {
249 UrlHelper::Hostname(&self.url.borrow())
250 }
251
252 fn SetHostname(&self, value: USVString) {
254 UrlHelper::SetHostname(&mut self.url.borrow_mut(), value);
255 }
256
257 fn Href(&self) -> USVString {
259 UrlHelper::Href(&self.url.borrow())
260 }
261
262 fn SetHref(&self, value: USVString) -> ErrorResult {
264 match ServoUrl::parse(&value.0) {
265 Ok(url) => {
266 *self.url.borrow_mut() = url;
267 self.search_params.set(None); Ok(())
269 },
270 Err(error) => Err(Error::Type(format!("could not parse URL: {}", error))),
271 }
272 }
273
274 fn Password(&self) -> USVString {
276 UrlHelper::Password(&self.url.borrow())
277 }
278
279 fn SetPassword(&self, value: USVString) {
281 UrlHelper::SetPassword(&mut self.url.borrow_mut(), value);
282 }
283
284 fn Pathname(&self) -> USVString {
286 UrlHelper::Pathname(&self.url.borrow())
287 }
288
289 fn SetPathname(&self, value: USVString) {
291 UrlHelper::SetPathname(&mut self.url.borrow_mut(), value);
292 }
293
294 fn Port(&self) -> USVString {
296 UrlHelper::Port(&self.url.borrow())
297 }
298
299 fn SetPort(&self, value: USVString) {
301 UrlHelper::SetPort(&mut self.url.borrow_mut(), value);
302 }
303
304 fn Protocol(&self) -> USVString {
306 UrlHelper::Protocol(&self.url.borrow())
307 }
308
309 fn SetProtocol(&self, value: USVString) {
311 UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value);
312 }
313
314 fn Origin(&self) -> USVString {
316 UrlHelper::Origin(&self.url.borrow())
317 }
318
319 fn Search(&self) -> USVString {
321 UrlHelper::Search(&self.url.borrow())
322 }
323
324 fn SetSearch(&self, value: USVString) {
326 UrlHelper::SetSearch(&mut self.url.borrow_mut(), value);
327 if let Some(search_params) = self.search_params.get() {
328 search_params.set_list(self.query_pairs());
329 }
330 }
331
332 fn SearchParams(&self, can_gc: CanGc) -> DomRoot<URLSearchParams> {
334 self.search_params
335 .or_init(|| URLSearchParams::new(&self.global(), Some(self), can_gc))
336 }
337
338 fn Username(&self) -> USVString {
340 UrlHelper::Username(&self.url.borrow())
341 }
342
343 fn SetUsername(&self, value: USVString) {
345 UrlHelper::SetUsername(&mut self.url.borrow_mut(), value);
346 }
347
348 fn ToJSON(&self) -> USVString {
350 self.Href()
351 }
352}