1use std::default::Default;
6
7use base::IpcSend;
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use net_traits::CoreResourceMsg;
11use net_traits::blob_url_store::{get_blob_origin, parse_blob_url};
12use net_traits::filemanager_thread::FileManagerThreadMsg;
13use profile_traits::ipc;
14use servo_url::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#[allow(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 set_query_pairs(&self, pairs: &[(String, String)]) {
71 let mut url = self.url.borrow_mut();
72
73 if pairs.is_empty() {
74 url.as_mut_url().set_query(None);
75 } else {
76 url.as_mut_url()
77 .query_pairs_mut()
78 .clear()
79 .extend_pairs(pairs);
80 }
81 }
82
83 fn unicode_serialization_blob_url(origin: &str, id: &Uuid) -> String {
85 let mut result = "blob:".to_string();
87
88 result.push_str(origin);
90
91 result.push('/');
93
94 result.push_str(&id.to_string());
96
97 result
98 }
99}
100
101impl URLMethods<crate::DomTypeHolder> for URL {
102 fn Constructor(
104 global: &GlobalScope,
105 proto: Option<HandleObject>,
106 can_gc: CanGc,
107 url: USVString,
108 base: Option<USVString>,
109 ) -> Fallible<DomRoot<URL>> {
110 let parsed_base = match base {
112 None => None,
113 Some(base) => {
114 match ServoUrl::parse(&base.0) {
115 Ok(base) => Some(base),
116 Err(error) => {
117 return Err(Error::Type(format!("could not parse base: {}", error)));
119 },
120 }
121 },
122 };
123 let parsed_url = match ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0) {
124 Ok(url) => url,
125 Err(error) => {
126 return Err(Error::Type(format!("could not parse URL: {}", error)));
128 },
129 };
130
131 Ok(URL::new(global, proto, parsed_url, can_gc))
142 }
143
144 fn CanParse(_global: &GlobalScope, url: USVString, base: Option<USVString>) -> bool {
146 let parsed_base = match base {
148 None => None,
149 Some(base) => match ServoUrl::parse(&base.0) {
150 Ok(base) => Some(base),
151 Err(_) => {
152 return false;
154 },
155 },
156 };
157 ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0).is_ok()
159 }
160
161 fn Parse(
163 global: &GlobalScope,
164 url: USVString,
165 base: Option<USVString>,
166 can_gc: CanGc,
167 ) -> Option<DomRoot<URL>> {
168 let parsed_base = base.and_then(|base| ServoUrl::parse(base.0.as_str()).ok());
171 let parsed_url = ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0);
172
173 Some(URL::new(global, None, parsed_url.ok()?, can_gc))
182 }
183
184 fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
186 let origin = get_blob_origin(&global.get_url());
189
190 let id = blob.get_blob_url_id();
191
192 DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
193 }
194
195 fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
197 let origin = get_blob_origin(&global.get_url());
201
202 if let Ok(url) = ServoUrl::parse(&url.str()) {
203 if url.fragment().is_none() && origin == get_blob_origin(&url) {
204 if let Ok((id, _)) = parse_blob_url(&url) {
205 let resource_threads = global.resource_threads();
206 let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
207 let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin, tx);
208 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
209
210 let _ = rx.recv().unwrap();
211 }
212 }
213 }
214 }
215
216 fn Hash(&self) -> USVString {
218 UrlHelper::Hash(&self.url.borrow())
219 }
220
221 fn SetHash(&self, value: USVString) {
223 UrlHelper::SetHash(&mut self.url.borrow_mut(), value);
224 }
225
226 fn Host(&self) -> USVString {
228 UrlHelper::Host(&self.url.borrow())
229 }
230
231 fn SetHost(&self, value: USVString) {
233 UrlHelper::SetHost(&mut self.url.borrow_mut(), value);
234 }
235
236 fn Hostname(&self) -> USVString {
238 UrlHelper::Hostname(&self.url.borrow())
239 }
240
241 fn SetHostname(&self, value: USVString) {
243 UrlHelper::SetHostname(&mut self.url.borrow_mut(), value);
244 }
245
246 fn Href(&self) -> USVString {
248 UrlHelper::Href(&self.url.borrow())
249 }
250
251 fn SetHref(&self, value: USVString) -> ErrorResult {
253 match ServoUrl::parse(&value.0) {
254 Ok(url) => {
255 *self.url.borrow_mut() = url;
256 self.search_params.set(None); Ok(())
258 },
259 Err(error) => Err(Error::Type(format!("could not parse URL: {}", error))),
260 }
261 }
262
263 fn Password(&self) -> USVString {
265 UrlHelper::Password(&self.url.borrow())
266 }
267
268 fn SetPassword(&self, value: USVString) {
270 UrlHelper::SetPassword(&mut self.url.borrow_mut(), value);
271 }
272
273 fn Pathname(&self) -> USVString {
275 UrlHelper::Pathname(&self.url.borrow())
276 }
277
278 fn SetPathname(&self, value: USVString) {
280 UrlHelper::SetPathname(&mut self.url.borrow_mut(), value);
281 }
282
283 fn Port(&self) -> USVString {
285 UrlHelper::Port(&self.url.borrow())
286 }
287
288 fn SetPort(&self, value: USVString) {
290 UrlHelper::SetPort(&mut self.url.borrow_mut(), value);
291 }
292
293 fn Protocol(&self) -> USVString {
295 UrlHelper::Protocol(&self.url.borrow())
296 }
297
298 fn SetProtocol(&self, value: USVString) {
300 UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value);
301 }
302
303 fn Origin(&self) -> USVString {
305 UrlHelper::Origin(&self.url.borrow())
306 }
307
308 fn Search(&self) -> USVString {
310 UrlHelper::Search(&self.url.borrow())
311 }
312
313 fn SetSearch(&self, value: USVString) {
315 UrlHelper::SetSearch(&mut self.url.borrow_mut(), value);
316 if let Some(search_params) = self.search_params.get() {
317 search_params.set_list(self.query_pairs());
318 }
319 }
320
321 fn SearchParams(&self, can_gc: CanGc) -> DomRoot<URLSearchParams> {
323 self.search_params
324 .or_init(|| URLSearchParams::new(&self.global(), Some(self), can_gc))
325 }
326
327 fn Username(&self) -> USVString {
329 UrlHelper::Username(&self.url.borrow())
330 }
331
332 fn SetUsername(&self, value: USVString) {
334 UrlHelper::SetUsername(&mut self.url.borrow_mut(), value);
335 }
336
337 fn ToJSON(&self) -> USVString {
339 self.Href()
340 }
341}