script/dom/
urlpattern.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 script_bindings::codegen::GenericBindings::URLPatternBinding::URLPatternResult;
8use script_bindings::codegen::GenericUnionTypes::USVStringOrURLPatternInit;
9use script_bindings::error::{Error, Fallible};
10use script_bindings::reflector::Reflector;
11use script_bindings::root::DomRoot;
12use script_bindings::script_runtime::CanGc;
13use script_bindings::str::USVString;
14
15use crate::dom::bindings::codegen::Bindings::URLPatternBinding;
16use crate::dom::bindings::codegen::Bindings::URLPatternBinding::URLPatternMethods;
17use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
18use crate::dom::globalscope::GlobalScope;
19
20/// <https://urlpattern.spec.whatwg.org/#urlpattern>
21#[dom_struct]
22pub(crate) struct URLPattern {
23    reflector: Reflector,
24
25    /// <https://urlpattern.spec.whatwg.org/#urlpattern-associated-url-pattern>
26    #[no_trace]
27    associated_url_pattern: urlpattern::UrlPattern,
28}
29
30impl URLPattern {
31    fn new_inherited(associated_url_pattern: urlpattern::UrlPattern) -> URLPattern {
32        URLPattern {
33            reflector: Reflector::new(),
34            associated_url_pattern,
35        }
36    }
37
38    /// <https://urlpattern.spec.whatwg.org/#urlpattern-initialize>
39    pub(crate) fn initialize(
40        global: &GlobalScope,
41        proto: Option<HandleObject>,
42        input: USVStringOrURLPatternInit,
43        base_url: Option<USVString>,
44        options: &URLPatternBinding::URLPatternOptions,
45        can_gc: CanGc,
46    ) -> Fallible<DomRoot<URLPattern>> {
47        // The section below converts from servos types to the types used in the urlpattern crate
48        let base_url = base_url.map(|usv_string| usv_string.0);
49        let input = bindings_to_third_party::map_urlpattern_input(input);
50        let options = urlpattern::UrlPatternOptions {
51            ignore_case: options.ignoreCase,
52        };
53
54        // Parse and initialize the URL pattern.
55        let pattern_init =
56            urlpattern::quirks::process_construct_pattern_input(input, base_url.as_deref())
57                .map_err(|error| Error::Type(format!("{error}")))?;
58
59        let pattern = urlpattern::UrlPattern::parse(pattern_init, options)
60            .map_err(|error| Error::Type(format!("{error}")))?;
61
62        let url_pattern = reflect_dom_object_with_proto(
63            Box::new(URLPattern::new_inherited(pattern)),
64            global,
65            proto,
66            can_gc,
67        );
68        Ok(url_pattern)
69    }
70}
71
72impl URLPatternMethods<crate::DomTypeHolder> for URLPattern {
73    // <https://urlpattern.spec.whatwg.org/#dom-urlpattern-urlpattern>
74    fn Constructor(
75        global: &GlobalScope,
76        proto: Option<HandleObject>,
77        can_gc: CanGc,
78        input: USVStringOrURLPatternInit,
79        base_url: USVString,
80        options: &URLPatternBinding::URLPatternOptions,
81    ) -> Fallible<DomRoot<URLPattern>> {
82        URLPattern::initialize(global, proto, input, Some(base_url), options, can_gc)
83    }
84
85    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-urlpattern-input-options>
86    fn Constructor_(
87        global: &GlobalScope,
88        proto: Option<HandleObject>,
89        can_gc: CanGc,
90        input: USVStringOrURLPatternInit,
91        options: &URLPatternBinding::URLPatternOptions,
92    ) -> Fallible<DomRoot<URLPattern>> {
93        // Step 1. Run initialize given this, input, null, and options.
94        URLPattern::initialize(global, proto, input, None, options, can_gc)
95    }
96
97    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-test>
98    fn Test(
99        &self,
100        input: USVStringOrURLPatternInit,
101        base_url: Option<USVString>,
102    ) -> Fallible<bool> {
103        let input = bindings_to_third_party::map_urlpattern_input(input);
104        let inputs = urlpattern::quirks::process_match_input(input, base_url.as_deref())
105            .map_err(|error| Error::Type(format!("{error}")))?;
106        let Some((match_input, _)) = inputs else {
107            return Ok(false);
108        };
109
110        self.associated_url_pattern
111            .test(match_input)
112            .map_err(|error| Error::Type(format!("{error}")))
113    }
114
115    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec>
116    fn Exec(
117        &self,
118        input: USVStringOrURLPatternInit,
119        base_url: Option<USVString>,
120    ) -> Fallible<Option<URLPatternResult>> {
121        let input = bindings_to_third_party::map_urlpattern_input(input);
122        let inputs = urlpattern::quirks::process_match_input(input, base_url.as_deref())
123            .map_err(|error| Error::Type(format!("{error}")))?;
124        let Some((match_input, inputs)) = inputs else {
125            return Ok(None);
126        };
127
128        let result = self
129            .associated_url_pattern
130            .exec(match_input)
131            .map_err(|error| Error::Type(format!("{error}")))?;
132        let Some(result) = result else {
133            return Ok(None);
134        };
135
136        Ok(Some(third_party_to_bindings::map_urlpattern_result(
137            result, inputs,
138        )))
139    }
140
141    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol>
142    fn Protocol(&self) -> USVString {
143        // Step 1. Return this’s associated URL pattern’s protocol component’s pattern string.
144        USVString(self.associated_url_pattern.protocol().to_owned())
145    }
146
147    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-username>
148    fn Username(&self) -> USVString {
149        // Step 1. Return this’s associated URL pattern’s username component’s pattern string.
150        USVString(self.associated_url_pattern.username().to_owned())
151    }
152
153    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-password>
154    fn Password(&self) -> USVString {
155        // Step 1. Return this’s associated URL pattern’s password component’s pattern string.
156        USVString(self.associated_url_pattern.password().to_owned())
157    }
158
159    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname>
160    fn Hostname(&self) -> USVString {
161        // Step 1. Return this’s associated URL pattern’s hostname component’s pattern string.
162        USVString(self.associated_url_pattern.hostname().to_owned())
163    }
164
165    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-port>
166    fn Port(&self) -> USVString {
167        // Step 1. Return this’s associated URL pattern’s port component’s pattern string.
168        USVString(self.associated_url_pattern.port().to_owned())
169    }
170
171    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname>
172    fn Pathname(&self) -> USVString {
173        // Step 1. Return this’s associated URL pattern’s pathname component’s pattern string.
174        USVString(self.associated_url_pattern.pathname().to_owned())
175    }
176
177    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-search>
178    fn Search(&self) -> USVString {
179        // Step 1. Return this’s associated URL pattern’s search component’s pattern string.
180        USVString(self.associated_url_pattern.search().to_owned())
181    }
182
183    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash>
184    fn Hash(&self) -> USVString {
185        // Step 1. Return this’s associated URL pattern’s hash component’s pattern string.
186        USVString(self.associated_url_pattern.hash().to_owned())
187    }
188
189    /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hasregexpgroups>
190    fn HasRegExpGroups(&self) -> bool {
191        // Step 1. If this’s associated URL pattern’s has regexp groups, then return true.
192        // Step 2. Return false.
193        self.associated_url_pattern.has_regexp_groups()
194    }
195}
196
197mod bindings_to_third_party {
198    use script_bindings::codegen::GenericBindings::URLPatternBinding::URLPatternInit;
199
200    use crate::dom::urlpattern::USVStringOrURLPatternInit;
201
202    fn map_urlpatterninit(pattern_init: URLPatternInit) -> urlpattern::quirks::UrlPatternInit {
203        urlpattern::quirks::UrlPatternInit {
204            protocol: pattern_init.protocol.map(|protocol| protocol.0),
205            username: pattern_init.username.map(|username| username.0),
206            password: pattern_init.password.map(|password| password.0),
207            hostname: pattern_init.hostname.map(|hostname| hostname.0),
208            port: pattern_init.port.map(|hash| hash.0),
209            pathname: pattern_init
210                .pathname
211                .as_ref()
212                .map(|usv_string| usv_string.to_string()),
213            search: pattern_init.search.map(|search| search.0),
214            hash: pattern_init.hash.map(|hash| hash.0),
215            base_url: pattern_init.baseURL.map(|base_url| base_url.0),
216        }
217    }
218
219    pub(super) fn map_urlpattern_input(
220        input: USVStringOrURLPatternInit,
221    ) -> urlpattern::quirks::StringOrInit {
222        match input {
223            USVStringOrURLPatternInit::USVString(usv_string) => {
224                urlpattern::quirks::StringOrInit::String(usv_string.0)
225            },
226            USVStringOrURLPatternInit::URLPatternInit(pattern_init) => {
227                urlpattern::quirks::StringOrInit::Init(map_urlpatterninit(pattern_init))
228            },
229        }
230    }
231}
232
233mod third_party_to_bindings {
234    use script_bindings::codegen::GenericBindings::URLPatternBinding::{
235        URLPatternComponentResult, URLPatternInit, URLPatternResult,
236    };
237    use script_bindings::codegen::GenericUnionTypes::USVStringOrUndefined;
238    use script_bindings::record::Record;
239    use script_bindings::str::USVString;
240
241    use crate::dom::bindings::codegen::UnionTypes::USVStringOrURLPatternInit;
242
243    // FIXME: For some reason codegen puts a lot of options into these types that don't make sense
244
245    fn map_component_result(
246        component_result: urlpattern::UrlPatternComponentResult,
247    ) -> URLPatternComponentResult {
248        let mut groups = Record::new();
249        for (key, value) in component_result.groups.iter() {
250            let value = match value {
251                Some(value) => USVStringOrUndefined::USVString(USVString(value.to_owned())),
252                None => USVStringOrUndefined::Undefined(()),
253            };
254
255            groups.insert(USVString(key.to_owned()), value);
256        }
257
258        URLPatternComponentResult {
259            input: Some(component_result.input.into()),
260            groups: Some(groups),
261        }
262    }
263
264    fn map_urlpatterninit(pattern_init: urlpattern::quirks::UrlPatternInit) -> URLPatternInit {
265        URLPatternInit {
266            baseURL: pattern_init.base_url.map(USVString),
267            protocol: pattern_init.protocol.map(USVString),
268            username: pattern_init.username.map(USVString),
269            password: pattern_init.password.map(USVString),
270            hostname: pattern_init.hostname.map(USVString),
271            port: pattern_init.port.map(USVString),
272            pathname: pattern_init.pathname.map(USVString),
273            search: pattern_init.search.map(USVString),
274            hash: pattern_init.hash.map(USVString),
275        }
276    }
277
278    pub(super) fn map_urlpattern_result(
279        result: urlpattern::UrlPatternResult,
280        (string_or_init, base_url): urlpattern::quirks::Inputs,
281    ) -> URLPatternResult {
282        let string_or_init = match string_or_init {
283            urlpattern::quirks::StringOrInit::String(string) => {
284                USVStringOrURLPatternInit::USVString(USVString(string))
285            },
286            urlpattern::quirks::StringOrInit::Init(pattern_init) => {
287                USVStringOrURLPatternInit::URLPatternInit(map_urlpatterninit(pattern_init))
288            },
289        };
290
291        let mut inputs = vec![string_or_init];
292
293        if let Some(base_url) = base_url {
294            inputs.push(USVStringOrURLPatternInit::USVString(USVString(base_url)));
295        }
296
297        URLPatternResult {
298            inputs: Some(inputs),
299            protocol: Some(map_component_result(result.protocol)),
300            username: Some(map_component_result(result.username)),
301            password: Some(map_component_result(result.password)),
302            hostname: Some(map_component_result(result.hostname)),
303            port: Some(map_component_result(result.port)),
304            pathname: Some(map_component_result(result.pathname)),
305            search: Some(map_component_result(result.search)),
306            hash: Some(map_component_result(result.hash)),
307        }
308    }
309}