1use std::default::Default;
6
7use dom_struct::dom_struct;
8use js::rust::HandleObject;
9use net_traits::blob_url_store::{get_blob_origin, parse_blob_url};
10use net_traits::filemanager_thread::FileManagerThreadMsg;
11use net_traits::{CoreResourceMsg, IpcSend};
12use profile_traits::ipc;
13use servo_url::ServoUrl;
14use uuid::Uuid;
15
16use crate::dom::bindings::cell::DomRefCell;
17use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
18use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
19use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
20use crate::dom::bindings::root::{DomRoot, MutNullableDom};
21use crate::dom::bindings::str::{DOMString, USVString};
22use crate::dom::blob::Blob;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::urlhelper::UrlHelper;
25use crate::dom::urlsearchparams::URLSearchParams;
26use crate::script_runtime::CanGc;
27
28#[dom_struct]
30#[allow(clippy::upper_case_acronyms)]
31pub(crate) struct URL {
32 reflector_: Reflector,
33
34 #[no_trace]
36 url: DomRefCell<ServoUrl>,
37
38 search_params: MutNullableDom<URLSearchParams>,
40}
41
42impl URL {
43 fn new_inherited(url: ServoUrl) -> URL {
44 URL {
45 reflector_: Reflector::new(),
46 url: DomRefCell::new(url),
47 search_params: Default::default(),
48 }
49 }
50
51 fn new(
52 global: &GlobalScope,
53 proto: Option<HandleObject>,
54 url: ServoUrl,
55 can_gc: CanGc,
56 ) -> DomRoot<URL> {
57 reflect_dom_object_with_proto(Box::new(URL::new_inherited(url)), global, proto, can_gc)
58 }
59
60 pub(crate) fn query_pairs(&self) -> Vec<(String, String)> {
61 self.url
62 .borrow()
63 .as_url()
64 .query_pairs()
65 .into_owned()
66 .collect()
67 }
68
69 pub(crate) fn set_query_pairs(&self, pairs: &[(String, String)]) {
70 let mut url = self.url.borrow_mut();
71
72 if pairs.is_empty() {
73 url.as_mut_url().set_query(None);
74 } else {
75 url.as_mut_url()
76 .query_pairs_mut()
77 .clear()
78 .extend_pairs(pairs);
79 }
80 }
81
82 fn unicode_serialization_blob_url(origin: &str, id: &Uuid) -> String {
84 let mut result = "blob:".to_string();
86
87 result.push_str(origin);
89
90 result.push('/');
92
93 result.push_str(&id.to_string());
95
96 result
97 }
98}
99
100impl URLMethods<crate::DomTypeHolder> for URL {
101 fn Constructor(
103 global: &GlobalScope,
104 proto: Option<HandleObject>,
105 can_gc: CanGc,
106 url: USVString,
107 base: Option<USVString>,
108 ) -> Fallible<DomRoot<URL>> {
109 let parsed_base = match base {
111 None => None,
112 Some(base) => {
113 match ServoUrl::parse(&base.0) {
114 Ok(base) => Some(base),
115 Err(error) => {
116 return Err(Error::Type(format!("could not parse base: {}", error)));
118 },
119 }
120 },
121 };
122 let parsed_url = match ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0) {
123 Ok(url) => url,
124 Err(error) => {
125 return Err(Error::Type(format!("could not parse URL: {}", error)));
127 },
128 };
129
130 Ok(URL::new(global, proto, parsed_url, can_gc))
141 }
142
143 fn CanParse(_global: &GlobalScope, url: USVString, base: Option<USVString>) -> bool {
145 let parsed_base = match base {
147 None => None,
148 Some(base) => match ServoUrl::parse(&base.0) {
149 Ok(base) => Some(base),
150 Err(_) => {
151 return false;
153 },
154 },
155 };
156 ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0).is_ok()
158 }
159
160 fn Parse(
162 global: &GlobalScope,
163 url: USVString,
164 base: Option<USVString>,
165 can_gc: CanGc,
166 ) -> Option<DomRoot<URL>> {
167 let parsed_base = base.and_then(|base| ServoUrl::parse(base.0.as_str()).ok());
170 let parsed_url = ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0);
171
172 Some(URL::new(global, None, parsed_url.ok()?, can_gc))
181 }
182
183 fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
185 let origin = get_blob_origin(&global.get_url());
188
189 let id = blob.get_blob_url_id();
190
191 DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
192 }
193
194 fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
196 let origin = get_blob_origin(&global.get_url());
200
201 if let Ok(url) = ServoUrl::parse(&url) {
202 if url.fragment().is_none() && origin == get_blob_origin(&url) {
203 if let Ok((id, _)) = parse_blob_url(&url) {
204 let resource_threads = global.resource_threads();
205 let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
206 let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin, tx);
207 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
208
209 let _ = rx.recv().unwrap();
210 }
211 }
212 }
213 }
214
215 fn Hash(&self) -> USVString {
217 UrlHelper::Hash(&self.url.borrow())
218 }
219
220 fn SetHash(&self, value: USVString) {
222 UrlHelper::SetHash(&mut self.url.borrow_mut(), value);
223 }
224
225 fn Host(&self) -> USVString {
227 UrlHelper::Host(&self.url.borrow())
228 }
229
230 fn SetHost(&self, value: USVString) {
232 UrlHelper::SetHost(&mut self.url.borrow_mut(), value);
233 }
234
235 fn Hostname(&self) -> USVString {
237 UrlHelper::Hostname(&self.url.borrow())
238 }
239
240 fn SetHostname(&self, value: USVString) {
242 UrlHelper::SetHostname(&mut self.url.borrow_mut(), value);
243 }
244
245 fn Href(&self) -> USVString {
247 UrlHelper::Href(&self.url.borrow())
248 }
249
250 fn SetHref(&self, value: USVString) -> ErrorResult {
252 match ServoUrl::parse(&value.0) {
253 Ok(url) => {
254 *self.url.borrow_mut() = url;
255 self.search_params.set(None); Ok(())
257 },
258 Err(error) => Err(Error::Type(format!("could not parse URL: {}", error))),
259 }
260 }
261
262 fn Password(&self) -> USVString {
264 UrlHelper::Password(&self.url.borrow())
265 }
266
267 fn SetPassword(&self, value: USVString) {
269 UrlHelper::SetPassword(&mut self.url.borrow_mut(), value);
270 }
271
272 fn Pathname(&self) -> USVString {
274 UrlHelper::Pathname(&self.url.borrow())
275 }
276
277 fn SetPathname(&self, value: USVString) {
279 UrlHelper::SetPathname(&mut self.url.borrow_mut(), value);
280 }
281
282 fn Port(&self) -> USVString {
284 UrlHelper::Port(&self.url.borrow())
285 }
286
287 fn SetPort(&self, value: USVString) {
289 UrlHelper::SetPort(&mut self.url.borrow_mut(), value);
290 }
291
292 fn Protocol(&self) -> USVString {
294 UrlHelper::Protocol(&self.url.borrow())
295 }
296
297 fn SetProtocol(&self, value: USVString) {
299 UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value);
300 }
301
302 fn Origin(&self) -> USVString {
304 UrlHelper::Origin(&self.url.borrow())
305 }
306
307 fn Search(&self) -> USVString {
309 UrlHelper::Search(&self.url.borrow())
310 }
311
312 fn SetSearch(&self, value: USVString) {
314 UrlHelper::SetSearch(&mut self.url.borrow_mut(), value);
315 if let Some(search_params) = self.search_params.get() {
316 search_params.set_list(self.query_pairs());
317 }
318 }
319
320 fn SearchParams(&self, can_gc: CanGc) -> DomRoot<URLSearchParams> {
322 self.search_params
323 .or_init(|| URLSearchParams::new(&self.global(), Some(self), can_gc))
324 }
325
326 fn Username(&self) -> USVString {
328 UrlHelper::Username(&self.url.borrow())
329 }
330
331 fn SetUsername(&self, value: USVString) {
333 UrlHelper::SetUsername(&mut self.url.borrow_mut(), value);
334 }
335
336 fn ToJSON(&self) -> USVString {
338 self.Href()
339 }
340}