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