script/dom/
url.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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/// <https://url.spec.whatwg.org/#url>
29#[dom_struct]
30#[allow(clippy::upper_case_acronyms)]
31pub(crate) struct URL {
32    reflector_: Reflector,
33
34    /// <https://url.spec.whatwg.org/#concept-url-url>
35    #[no_trace]
36    url: DomRefCell<ServoUrl>,
37
38    /// <https://url.spec.whatwg.org/#dom-url-searchparams>
39    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    /// <https://w3c.github.io/FileAPI/#unicodeSerializationOfBlobURL>
83    fn unicode_serialization_blob_url(origin: &str, id: &Uuid) -> String {
84        // Step 1, 2
85        let mut result = "blob:".to_string();
86
87        // Step 3
88        result.push_str(origin);
89
90        // Step 4
91        result.push('/');
92
93        // Step 5
94        result.push_str(&id.to_string());
95
96        result
97    }
98}
99
100impl URLMethods<crate::DomTypeHolder> for URL {
101    /// <https://url.spec.whatwg.org/#constructors>
102    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        // Step 1. Parse url with base.
110        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                        // Step 2. Throw a TypeError if URL parsing fails.
117                        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                // Step 2. Throw a TypeError if URL parsing fails.
126                return Err(Error::Type(format!("could not parse URL: {}", error)));
127            },
128        };
129
130        // Skip the steps below.
131        // Instead of constructing a new `URLSearchParams` object here, construct it
132        // on-demand inside `URL::SearchParams`.
133        //
134        // Step 3. Let query be parsedURL’s query.
135        // Step 5. Set this’s query object to a new URLSearchParams object.
136        // Step 6. Initialize this’s query object with query.
137        // Step 7. Set this’s query object’s URL object to this.
138
139        // Step 4. Set this’s URL to parsedURL.
140        Ok(URL::new(global, proto, parsed_url, can_gc))
141    }
142
143    /// <https://url.spec.whatwg.org/#dom-url-canparse>
144    fn CanParse(_global: &GlobalScope, url: USVString, base: Option<USVString>) -> bool {
145        // Step 1.
146        let parsed_base = match base {
147            None => None,
148            Some(base) => match ServoUrl::parse(&base.0) {
149                Ok(base) => Some(base),
150                Err(_) => {
151                    // Step 2.1
152                    return false;
153                },
154            },
155        };
156        // Step 2.2, 3
157        ServoUrl::parse_with_base(parsed_base.as_ref(), &url.0).is_ok()
158    }
159
160    /// <https://url.spec.whatwg.org/#dom-url-parse>
161    fn Parse(
162        global: &GlobalScope,
163        url: USVString,
164        base: Option<USVString>,
165        can_gc: CanGc,
166    ) -> Option<DomRoot<URL>> {
167        // Step 1: Let parsedURL be the result of running the API URL parser on url with base,
168        // if given.
169        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        // Step 2: If parsedURL is failure, then return null.
173        // Step 3: Let url be a new URL object.
174        // Step 4: Initialize url with parsedURL.
175        // Step 5: Return url.
176
177        // These steps are all handled while mapping the Result to an Option<ServoUrl>.
178        // Regarding initialization, the same condition should apply here as stated in the comments
179        // in Self::Constructor above - construct it on-demand inside `URL::SearchParams`.
180        Some(URL::new(global, None, parsed_url.ok()?, can_gc))
181    }
182
183    /// <https://w3c.github.io/FileAPI/#dfn-createObjectURL>
184    fn CreateObjectURL(global: &GlobalScope, blob: &Blob) -> DOMString {
185        // XXX: Second field is an unicode-serialized Origin, it is a temporary workaround
186        //      and should not be trusted. See issue https://github.com/servo/servo/issues/11722
187        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    /// <https://w3c.github.io/FileAPI/#dfn-revokeObjectURL>
195    fn RevokeObjectURL(global: &GlobalScope, url: DOMString) {
196        // If the value provided for the url argument is not a Blob URL OR
197        // if the value provided for the url argument does not have an entry in the Blob URL Store,
198        // this method call does nothing. User agents may display a message on the error console.
199        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    /// <https://url.spec.whatwg.org/#dom-url-hash>
216    fn Hash(&self) -> USVString {
217        UrlHelper::Hash(&self.url.borrow())
218    }
219
220    /// <https://url.spec.whatwg.org/#dom-url-hash>
221    fn SetHash(&self, value: USVString) {
222        UrlHelper::SetHash(&mut self.url.borrow_mut(), value);
223    }
224
225    /// <https://url.spec.whatwg.org/#dom-url-host>
226    fn Host(&self) -> USVString {
227        UrlHelper::Host(&self.url.borrow())
228    }
229
230    /// <https://url.spec.whatwg.org/#dom-url-host>
231    fn SetHost(&self, value: USVString) {
232        UrlHelper::SetHost(&mut self.url.borrow_mut(), value);
233    }
234
235    /// <https://url.spec.whatwg.org/#dom-url-hostname>
236    fn Hostname(&self) -> USVString {
237        UrlHelper::Hostname(&self.url.borrow())
238    }
239
240    /// <https://url.spec.whatwg.org/#dom-url-hostname>
241    fn SetHostname(&self, value: USVString) {
242        UrlHelper::SetHostname(&mut self.url.borrow_mut(), value);
243    }
244
245    /// <https://url.spec.whatwg.org/#dom-url-href>
246    fn Href(&self) -> USVString {
247        UrlHelper::Href(&self.url.borrow())
248    }
249
250    /// <https://url.spec.whatwg.org/#dom-url-href>
251    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); // To be re-initialized in the SearchParams getter.
256                Ok(())
257            },
258            Err(error) => Err(Error::Type(format!("could not parse URL: {}", error))),
259        }
260    }
261
262    /// <https://url.spec.whatwg.org/#dom-url-password>
263    fn Password(&self) -> USVString {
264        UrlHelper::Password(&self.url.borrow())
265    }
266
267    /// <https://url.spec.whatwg.org/#dom-url-password>
268    fn SetPassword(&self, value: USVString) {
269        UrlHelper::SetPassword(&mut self.url.borrow_mut(), value);
270    }
271
272    /// <https://url.spec.whatwg.org/#dom-url-pathname>
273    fn Pathname(&self) -> USVString {
274        UrlHelper::Pathname(&self.url.borrow())
275    }
276
277    /// <https://url.spec.whatwg.org/#dom-url-pathname>
278    fn SetPathname(&self, value: USVString) {
279        UrlHelper::SetPathname(&mut self.url.borrow_mut(), value);
280    }
281
282    /// <https://url.spec.whatwg.org/#dom-url-port>
283    fn Port(&self) -> USVString {
284        UrlHelper::Port(&self.url.borrow())
285    }
286
287    /// <https://url.spec.whatwg.org/#dom-url-port>
288    fn SetPort(&self, value: USVString) {
289        UrlHelper::SetPort(&mut self.url.borrow_mut(), value);
290    }
291
292    /// <https://url.spec.whatwg.org/#dom-url-protocol>
293    fn Protocol(&self) -> USVString {
294        UrlHelper::Protocol(&self.url.borrow())
295    }
296
297    /// <https://url.spec.whatwg.org/#dom-url-protocol>
298    fn SetProtocol(&self, value: USVString) {
299        UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value);
300    }
301
302    /// <https://url.spec.whatwg.org/#dom-url-origin>
303    fn Origin(&self) -> USVString {
304        UrlHelper::Origin(&self.url.borrow())
305    }
306
307    /// <https://url.spec.whatwg.org/#dom-url-search>
308    fn Search(&self) -> USVString {
309        UrlHelper::Search(&self.url.borrow())
310    }
311
312    /// <https://url.spec.whatwg.org/#dom-url-search>
313    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    /// <https://url.spec.whatwg.org/#dom-url-searchparams>
321    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    /// <https://url.spec.whatwg.org/#dom-url-username>
327    fn Username(&self) -> USVString {
328        UrlHelper::Username(&self.url.borrow())
329    }
330
331    /// <https://url.spec.whatwg.org/#dom-url-username>
332    fn SetUsername(&self, value: USVString) {
333        UrlHelper::SetUsername(&mut self.url.borrow_mut(), value);
334    }
335
336    /// <https://url.spec.whatwg.org/#dom-url-tojson>
337    fn ToJSON(&self) -> USVString {
338        self.Href()
339    }
340}