script/dom/
urlsearchparams.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 dom_struct::dom_struct;
6use js::rust::HandleObject;
7use url::form_urlencoded;
8
9use crate::dom::bindings::cell::DomRefCell;
10use crate::dom::bindings::codegen::Bindings::URLSearchParamsBinding::URLSearchParamsMethods;
11use crate::dom::bindings::codegen::UnionTypes::USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString;
12use crate::dom::bindings::error::{Error, Fallible};
13use crate::dom::bindings::iterable::Iterable;
14use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::bindings::str::{DOMString, USVString};
17use crate::dom::bindings::weakref::MutableWeakRef;
18use crate::dom::globalscope::GlobalScope;
19use crate::dom::url::URL;
20use crate::script_runtime::CanGc;
21
22/// <https://url.spec.whatwg.org/#interface-urlsearchparams>
23#[dom_struct]
24pub(crate) struct URLSearchParams {
25    reflector_: Reflector,
26    /// <https://url.spec.whatwg.org/#concept-urlsearchparams-list>
27    list: DomRefCell<Vec<(String, String)>>,
28    /// <https://url.spec.whatwg.org/#concept-urlsearchparams-url-object>
29    url: MutableWeakRef<URL>,
30}
31
32impl URLSearchParams {
33    fn new_inherited(url: Option<&URL>) -> URLSearchParams {
34        URLSearchParams {
35            reflector_: Reflector::new(),
36            list: DomRefCell::new(url.map_or(Vec::new(), |url| url.query_pairs())),
37            url: MutableWeakRef::new(url),
38        }
39    }
40
41    pub(crate) fn new(
42        global: &GlobalScope,
43        url: Option<&URL>,
44        can_gc: CanGc,
45    ) -> DomRoot<URLSearchParams> {
46        Self::new_with_proto(global, None, url, can_gc)
47    }
48
49    pub(crate) fn new_with_proto(
50        global: &GlobalScope,
51        proto: Option<HandleObject>,
52        url: Option<&URL>,
53        can_gc: CanGc,
54    ) -> DomRoot<URLSearchParams> {
55        reflect_dom_object_with_proto(
56            Box::new(URLSearchParams::new_inherited(url)),
57            global,
58            proto,
59            can_gc,
60        )
61    }
62
63    pub(crate) fn set_list(&self, list: Vec<(String, String)>) {
64        *self.list.borrow_mut() = list;
65    }
66}
67
68impl URLSearchParamsMethods<crate::DomTypeHolder> for URLSearchParams {
69    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams>
70    fn Constructor(
71        global: &GlobalScope,
72        proto: Option<HandleObject>,
73        can_gc: CanGc,
74        init: USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString,
75    ) -> Fallible<DomRoot<URLSearchParams>> {
76        // Step 1.
77        let query = URLSearchParams::new_with_proto(global, proto, None, can_gc);
78        match init {
79            USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString::USVStringSequenceSequence(init) => {
80                // Step 2.
81
82                // Step 2-1.
83                if init.iter().any(|pair| pair.len() != 2) {
84                    return Err(Error::Type("Sequence initializer must only contain pair elements.".to_string()));
85                }
86
87                // Step 2-2.
88                *query.list.borrow_mut() =
89                    init.iter().map(|pair| (pair[0].to_string(), pair[1].to_string())).collect::<Vec<_>>();
90            },
91            USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString::USVStringUSVStringRecord(init) => {
92                // Step 3.
93                *query.list.borrow_mut() =
94                    (*init).iter().map(|(name, value)| (name.to_string(), value.to_string())).collect::<Vec<_>>();
95            },
96            USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString::USVString(init) => {
97                // Step 4.
98                let init_bytes = match init.0.chars().next() {
99                    Some('?') => {
100                        let (_, other_bytes) = init.0.as_bytes().split_at(1);
101
102                        other_bytes
103                    },
104                    _ => init.0.as_bytes(),
105                };
106
107                *query.list.borrow_mut() =
108                    form_urlencoded::parse(init_bytes).into_owned().collect();
109            }
110        }
111
112        // Step 5.
113        Ok(query)
114    }
115
116    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-size>
117    fn Size(&self) -> u32 {
118        self.list.borrow().len() as u32
119    }
120
121    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-append>
122    fn Append(&self, name: USVString, value: USVString) {
123        // Step 1.
124        self.list.borrow_mut().push((name.0, value.0));
125        // Step 2.
126        self.update_steps();
127    }
128
129    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-delete>
130    fn Delete(&self, name: USVString, value: Option<USVString>) {
131        // Step 1.
132        self.list.borrow_mut().retain(|(k, v)| match &value {
133            Some(value) => !(k == &name.0 && v == &value.0),
134            None => k != &name.0,
135        });
136        // Step 2.
137        self.update_steps();
138    }
139
140    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-get>
141    fn Get(&self, name: USVString) -> Option<USVString> {
142        let list = self.list.borrow();
143        list.iter()
144            .find(|&kv| kv.0 == name.0)
145            .map(|kv| USVString(kv.1.clone()))
146    }
147
148    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-getall>
149    fn GetAll(&self, name: USVString) -> Vec<USVString> {
150        let list = self.list.borrow();
151        list.iter()
152            .filter_map(|(k, v)| {
153                if k == &name.0 {
154                    Some(USVString(v.clone()))
155                } else {
156                    None
157                }
158            })
159            .collect()
160    }
161
162    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-has>
163    fn Has(&self, name: USVString, value: Option<USVString>) -> bool {
164        let list = self.list.borrow();
165        list.iter().any(|(k, v)| match &value {
166            Some(value) => k == &name.0 && v == &value.0,
167            None => k == &name.0,
168        })
169    }
170
171    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-set>
172    fn Set(&self, name: USVString, value: USVString) {
173        {
174            // Step 1.
175            let mut list = self.list.borrow_mut();
176            let mut index = None;
177            let mut i = 0;
178            list.retain(|(k, _)| {
179                if index.is_none() {
180                    if k == &name.0 {
181                        index = Some(i);
182                    } else {
183                        i += 1;
184                    }
185                    true
186                } else {
187                    k != &name.0
188                }
189            });
190            match index {
191                Some(index) => list[index].1 = value.0,
192                None => list.push((name.0, value.0)), // Step 2.
193            };
194        } // Un-borrow self.list
195        // Step 3.
196        self.update_steps();
197    }
198
199    /// <https://url.spec.whatwg.org/#dom-urlsearchparams-sort>
200    fn Sort(&self) {
201        // Step 1.
202        self.list
203            .borrow_mut()
204            .sort_by(|(a, _), (b, _)| a.encode_utf16().cmp(b.encode_utf16()));
205
206        // Step 2.
207        self.update_steps();
208    }
209
210    /// <https://url.spec.whatwg.org/#stringification-behavior>
211    fn Stringifier(&self) -> DOMString {
212        DOMString::from(self.serialize_utf8())
213    }
214}
215
216impl URLSearchParams {
217    /// <https://url.spec.whatwg.org/#concept-urlencoded-serializer>
218    pub(crate) fn serialize_utf8(&self) -> String {
219        let list = self.list.borrow();
220        form_urlencoded::Serializer::new(String::new())
221            .extend_pairs(&*list)
222            .finish()
223    }
224
225    /// <https://url.spec.whatwg.org/#concept-urlsearchparams-update>
226    fn update_steps(&self) {
227        if let Some(url) = self.url.root() {
228            url.set_query_pairs(&self.list.borrow())
229        }
230    }
231}
232
233impl Iterable for URLSearchParams {
234    type Key = USVString;
235    type Value = USVString;
236
237    fn get_iterable_length(&self) -> u32 {
238        self.list.borrow().len() as u32
239    }
240
241    fn get_value_at_index(&self, n: u32) -> USVString {
242        let value = self.list.borrow()[n as usize].1.clone();
243        USVString(value)
244    }
245
246    fn get_key_at_index(&self, n: u32) -> USVString {
247        let key = self.list.borrow()[n as usize].0.clone();
248        USVString(key)
249    }
250}