diff --git a/components/config/prefs.rs b/components/config/prefs.rs index f8dfb63a7ac..eaf352f7daa 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -114,6 +114,10 @@ pub struct Preferences { pub dom_testing_element_activation_enabled: bool, pub dom_testing_html_input_element_select_files_enabled: bool, pub dom_testperf_enabled: bool, + /// Enable the [URLPattern] API. + /// + /// [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern + pub dom_urlpattern_enabled: bool, pub dom_xpath_enabled: bool, /// Enable WebGL2 APIs. pub dom_webgl2_enabled: bool, @@ -280,6 +284,7 @@ impl Preferences { dom_testing_element_activation_enabled: false, dom_testing_html_input_element_select_files_enabled: false, dom_testperf_enabled: false, + dom_urlpattern_enabled: false, dom_webgl2_enabled: false, dom_webgpu_enabled: false, dom_webgpu_wgpu_backend: String::new(), diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index e647f19a47d..d21cf081a95 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -2991,7 +2991,10 @@ fn compile_pattern(cx: SafeJSContext, pattern_str: &str, out_regex: MutableHandl if check_js_regex_syntax(cx, pattern_str) { // ...and if it does make pattern that matches only the entirety of string let pattern_str = format!("^(?:{})$", pattern_str); - new_js_regex(cx, &pattern_str, out_regex) + let flags = RegExpFlags { + flags_: RegExpFlag_Unicode, + }; + new_js_regex(cx, &pattern_str, flags, out_regex) } else { false } @@ -3026,17 +3029,22 @@ fn check_js_regex_syntax(cx: SafeJSContext, pattern: &str) -> bool { } } +// TODO: This is also used in the URLPattern implementation. Consider moving it into mozjs or some other +// shared module #[allow(unsafe_code)] -fn new_js_regex(cx: SafeJSContext, pattern: &str, mut out_regex: MutableHandleObject) -> bool { +pub(crate) fn new_js_regex( + cx: SafeJSContext, + pattern: &str, + flags: RegExpFlags, + mut out_regex: MutableHandleObject, +) -> bool { let pattern: Vec = pattern.encode_utf16().collect(); unsafe { out_regex.set(NewUCRegExpObject( *cx, pattern.as_ptr(), pattern.len(), - RegExpFlags { - flags_: RegExpFlag_Unicode, - }, + flags, )); if out_regex.is_null() { JS_ClearPendingException(*cx); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 4581ea56135..717e5a1f5b9 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -576,6 +576,7 @@ pub(crate) mod uievent; pub(crate) mod underlyingsourcecontainer; pub(crate) mod url; pub(crate) mod urlhelper; +pub(crate) mod urlpattern; pub(crate) mod urlsearchparams; pub(crate) mod userscripts; pub(crate) mod validation; diff --git a/components/script/dom/urlpattern.rs b/components/script/dom/urlpattern.rs new file mode 100644 index 00000000000..bc61856680a --- /dev/null +++ b/components/script/dom/urlpattern.rs @@ -0,0 +1,814 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::ptr; + +use dom_struct::dom_struct; +use js::jsapi::{Heap, JSObject, RegExpFlag_IgnoreCase, RegExpFlag_UnicodeSets, RegExpFlags}; +use js::rust::HandleObject; +use script_bindings::error::{Error, Fallible}; +use script_bindings::reflector::Reflector; +use script_bindings::root::DomRoot; +use script_bindings::script_runtime::CanGc; +use script_bindings::str::USVString; + +use crate::dom::bindings::cell::RefCell; +use crate::dom::bindings::codegen::Bindings::URLPatternBinding::{ + URLPatternInit, URLPatternMethods, URLPatternOptions, +}; +use crate::dom::bindings::reflector::reflect_dom_object_with_proto; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlinputelement::new_js_regex; + +/// +const FULL_WILDCARD_REGEXP_VALUE: &str = ".*"; + +/// +#[dom_struct] +pub(crate) struct URLPattern { + reflector: Reflector, + + /// + associated_url_pattern: RefCell, +} + +#[derive(JSTraceable, MallocSizeOf)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +struct URLPatternInternal { + /// + protocol: Component, + + /// + username: Component, + + /// + password: Component, + + /// + hostname: Component, + + /// + port: Component, + + /// + pathname: Component, + + /// + search: Component, + + /// + hash: Component, +} + +/// +#[derive(JSTraceable, MallocSizeOf)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +struct Component { + /// + pattern_string: USVString, + + /// + #[ignore_malloc_size_of = "mozjs"] + regular_expression: Box>, + + /// + group_name_list: Vec, + + /// + has_regexp_groups: bool, +} + +/// +struct Part { + /// + part_type: PartType, + + /// + value: String, + + /// + modifier: PartModifier, + + /// + name: String, + + /// + prefix: String, + + /// + suffix: String, +} + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum PartType { + /// + FixedText, + + /// + Regexp, + + /// + SegmentWildcard, + + /// + FullWildcard, +} + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[allow(dead_code)] // Parser is not implemented yet +enum PartModifier { + /// + None, + + /// + Optional, + + /// + ZeroOrMore, + + /// + OneOrMore, +} + +/// +#[derive(Clone, Copy, Default)] +#[allow(dead_code)] // Parser is not fully implemented yet +struct Options { + /// + delimiter_code_point: Option, + + /// + prefix_code_point: Option, + + /// + ignore_case: bool, +} + +impl Component { + fn new_unrooted() -> Self { + Self { + pattern_string: Default::default(), + regular_expression: Heap::boxed(ptr::null_mut()), + group_name_list: Default::default(), + has_regexp_groups: false, + } + } +} + +impl URLPattern { + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + fn new_inherited() -> URLPattern { + let associated_url_pattern = URLPatternInternal { + protocol: Component::new_unrooted(), + username: Component::new_unrooted(), + password: Component::new_unrooted(), + hostname: Component::new_unrooted(), + port: Component::new_unrooted(), + pathname: Component::new_unrooted(), + search: Component::new_unrooted(), + hash: Component::new_unrooted(), + }; + + URLPattern { + reflector: Reflector::new(), + associated_url_pattern: RefCell::new(associated_url_pattern), + } + } + + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + pub(crate) fn new_with_proto( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + reflect_dom_object_with_proto(Box::new(URLPattern::new_inherited()), global, proto, can_gc) + } + + /// + fn initialize( + global: &GlobalScope, + proto: Option, + input: &URLPatternInit, + options: &URLPatternOptions, + can_gc: CanGc, + ) -> Fallible> { + // Step 1. Set this’s associated URL pattern to the result of create given input, baseURL, and options. + let pattern = URLPattern::new_with_proto(global, proto, can_gc); + URLPatternInternal::create( + input, + options, + &mut pattern.associated_url_pattern.borrow_mut(), + )?; + + Ok(pattern) + } +} + +impl URLPatternMethods for URLPattern { + /// + fn Constructor( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + input: &URLPatternInit, + options: &URLPatternOptions, + ) -> Fallible> { + // Step 1. Run initialize given this, input, null, and options. + URLPattern::initialize(global, proto, input, options, can_gc) + } + + /// + fn Protocol(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s protocol component’s pattern string. + self.associated_url_pattern + .borrow() + .protocol + .pattern_string + .clone() + } + + /// + fn Username(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s username component’s pattern string. + self.associated_url_pattern + .borrow() + .username + .pattern_string + .clone() + } + + /// + fn Password(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s password component’s pattern string. + self.associated_url_pattern + .borrow() + .password + .pattern_string + .clone() + } + + /// + fn Hostname(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s hostname component’s pattern string. + self.associated_url_pattern + .borrow() + .hostname + .pattern_string + .clone() + } + + /// + fn Port(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s port component’s pattern string. + self.associated_url_pattern + .borrow() + .port + .pattern_string + .clone() + } + + /// + fn Pathname(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s pathname component’s pattern string. + self.associated_url_pattern + .borrow() + .pathname + .pattern_string + .clone() + } + + /// + fn Search(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s search component’s pattern string. + self.associated_url_pattern + .borrow() + .search + .pattern_string + .clone() + } + + /// + fn Hash(&self) -> USVString { + // Step 1. Return this’s associated URL pattern’s hash component’s pattern string. + self.associated_url_pattern + .borrow() + .hash + .pattern_string + .clone() + } + + /// + fn HasRegExpGroups(&self) -> bool { + // Step 1. If this’s associated URL pattern’s has regexp groups, then return true. + // Step 2. Return false. + self.associated_url_pattern.borrow().has_regexp_groups() + } +} + +impl URLPatternInternal { + /// + fn create(input: &URLPatternInit, options: &URLPatternOptions, out: &mut Self) -> Fallible<()> { + // Step 1. Let init be null. + // Step 2. If input is a scalar value string then: + // NOTE: We don't support strings as input yet + // Step 3. Otherwise: + // Step 3.1 Assert: input is a URLPatternInit. + // Step 3.2 If baseURL is not null, then throw a TypeError. + if input.baseURL.is_some() { + return Err(Error::Type("baseURL must be none".into())); + } + + // Step 3.3 Set init to input. + let init = input; + + // TODO Step 4. Let processedInit be the result of process a URLPatternInit given init, "pattern", null, null, + // null, null, null, null, null, and null. + let mut processed_init = process_a_url_pattern_init(init); + + // Step 5. For each componentName of « "protocol", "username", "password", "hostname", "port", + // "pathname", "search", "hash" »: + // Step 5.1 If processedInit[componentName] does not exist, then set processedInit[componentName] to "*". + // NOTE: We do this later on + + // Step 6. If processedInit["protocol"] is a special scheme and processedInit["port"] is a string + // which represents its corresponding default port in radix-10 using ASCII digits then set + // processedInit["port"] to the empty string. + let default_port = processed_init + .protocol + .as_deref() + .and_then(default_port_for_special_scheme); + let given_port = processed_init + .port + .as_deref() + .map(str::parse) + .transpose() + .ok() + .flatten(); + if default_port == given_port { + processed_init.port = Some(Default::default()); + } + + // Step 7. Let urlPattern be a new URL pattern. + // NOTE: We construct the pattern once we have all the components + + // Step 8. Set urlPattern’s protocol component to the result of compiling a component given + // processedInit["protocol"], canonicalize a protocol, and default options. + Component::compile( + processed_init.protocol.as_deref().unwrap_or("*"), + Options::default(), + &mut out.protocol, + )?; + + // Step 9. Set urlPattern’s username component to the result of compiling a component given + // processedInit["username"], canonicalize a username, and default options. + Component::compile( + processed_init.username.as_deref().unwrap_or("*"), + Options::default(), + &mut out.username, + )?; + + // Step 10. Set urlPattern’s password component to the result of compiling a component given + // processedInit["password"], canonicalize a password, and default options. + Component::compile( + processed_init.password.as_deref().unwrap_or("*"), + Options::default(), + &mut out.password, + )?; + + // FIXME: Steps 11 and 12: Compile host pattern correctly + Component::compile( + processed_init.hostname.as_deref().unwrap_or("*"), + Options::HOSTNAME, + &mut out.hostname, + )?; + + // Step 13. Set urlPattern’s port component to the result of compiling a component given + // processedInit["port"], canonicalize a port, and default options. + Component::compile( + processed_init.port.as_deref().unwrap_or("*"), + Options::default(), + &mut out.port, + )?; + + // FIXME: Step 14: respect ignore case option from here on out + let _ = options; + + // FIXME: Steps 15-16: Compile path pattern correctly + Component::compile( + processed_init.pathname.as_deref().unwrap_or("*"), + Options::PATHNAME, + &mut out.pathname, + )?; + + // Step 17. Set urlPattern’s search component to the result of compiling a component given + // processedInit["search"], canonicalize a search, and compileOptions. + Component::compile( + processed_init.search.as_deref().unwrap_or("*"), + Options::default(), + &mut out.search, + )?; + + // Step 18. Set urlPattern’s hash component to the result of compiling a component given + // processedInit["hash"], canonicalize a hash, and compileOptions. + Component::compile( + processed_init.hash.as_deref().unwrap_or("*"), + Options::default(), + &mut out.hash, + )?; + + // Step 19. Return urlPattern. + // NOTE: not necessary since we use an out parameter + Ok(()) + } + + /// + fn has_regexp_groups(&self) -> bool { + self.protocol.has_regexp_groups || + self.username.has_regexp_groups || + self.password.has_regexp_groups || + self.hostname.has_regexp_groups || + self.port.has_regexp_groups || + self.pathname.has_regexp_groups || + self.search.has_regexp_groups || + self.hash.has_regexp_groups + } +} + +impl Component { + /// + fn compile(input: &str, options: Options, out: &mut Self) -> Fallible<()> { + // Step 1. Let part list be the result of running parse a pattern string given input, options, + // and encoding callback. + let part_list = parse_a_pattern_string(input, options); + + // Step 2. Let (regular expression string, name list) be the result of running generate a regular expression and + // name list given part list and options. + let (regular_expression_string, name_list) = + generate_a_regular_expression_and_name_list(&part_list, options); + + // Step 3. Let flags be an empty string. + // Step 4. If options’s ignore case is true then set flags to "vi". + let flags = if options.ignore_case { + RegExpFlags { + flags_: RegExpFlag_UnicodeSets | RegExpFlag_IgnoreCase, + } + } + // Step 5. Otherwise set flags to "v" + else { + RegExpFlags { + flags_: RegExpFlag_UnicodeSets, + } + }; + + // Step 6. Let regular expression be RegExpCreate(regular expression string, flags). + // If this throws an exception, catch it, and throw a TypeError. + let cx = GlobalScope::get_cx(); + rooted!(in(*cx) let mut regular_expression: *mut JSObject = ptr::null_mut()); + let succeeded = new_js_regex( + cx, + ®ular_expression_string, + flags, + regular_expression.handle_mut(), + ); + if !succeeded { + return Err(Error::Type(format!( + "Failed to compile {regular_expression_string:?} as a regular expression" + ))); + } + + // TODO Step 7. Let pattern string be the result of running generate a pattern string given + // part list and options. + let pattern_string = Default::default(); + + // Step 8. Let has regexp groups be false. + // Step 9. For each part of part list: + // Step 9.1 If part’s type is "regexp", then set has regexp groups to true. + let has_regexp_groups = part_list + .iter() + .any(|part| part.part_type == PartType::Regexp); + + // Step 10. Return a new component whose pattern string is pattern string, regular expression + // is regular expression, group name list is name list, and has regexp groups is has regexp groups. + out.pattern_string = pattern_string; + out.regular_expression.set(*regular_expression.handle()); + out.group_name_list = name_list; + out.has_regexp_groups = has_regexp_groups; + + Ok(()) + } +} + +/// +fn parse_a_pattern_string(pattern_string: &str, options: Options) -> Vec { + // FIXME: Implement this algorithm + let _ = pattern_string; + let _ = options; + + vec![] +} + +/// +fn generate_a_regular_expression_and_name_list( + part_list: &[Part], + options: Options, +) -> (String, Vec) { + // Step 1. Let result be "^". + let mut result = String::from("^"); + + // Step 2. Let name list be a new list. + let mut name_list = vec![]; + + // Step 3. For each part of part list: + for part in part_list { + // Step 3.1 If part’s type is "fixed-text": + if part.part_type == PartType::FixedText { + // Step 3.1.1 If part’s modifier is "none", then append the result of running escape a regexp string given + // part’s value to the end of result. + if part.modifier == PartModifier::None { + result.push_str(&escape_a_regexp_string(&part.value)); + } + // Step 3.1.2 Otherwise: + else { + // Step 3.1.2.1 Append "(?:" to the end of result. + result.push_str("(?:"); + + // Step 3.1.2.2 Append the result of running escape a regexp string given part’s value + // to the end of result. + result.push_str(&escape_a_regexp_string(&part.value)); + + // Step 3.1.2.3 Append ")" to the end of result. + result.push(')'); + + // Step 3.1.2.4 Append the result of running convert a modifier to a string given part’s + // modifier to the end of result. + result.push_str(part.modifier.convert_to_string()); + } + + // Step 3.1.3 Continue. + continue; + } + + // Step 3.2 Assert: part’s name is not the empty string. + debug_assert!(!part.name.is_empty()); + + // Step 3.3 Append part’s name to name list. + name_list.push(USVString(part.name.to_string())); + + // Step 3.4 Let regexp value be part’s value. + let mut regexp_value = part.value.clone(); + + // Step 3.5 If part’s type is "segment-wildcard", then set regexp value to the result of running + // generate a segment wildcard regexp given options. + if part.part_type == PartType::SegmentWildcard { + regexp_value = generate_a_segment_wildcard_regexp(options); + } + // Step 3.6 Otherwise if part’s type is "full-wildcard", then set regexp value to full wildcard regexp value. + else if part.part_type == PartType::FullWildcard { + regexp_value = FULL_WILDCARD_REGEXP_VALUE.into(); + } + + // Step 3.7 If part’s prefix is the empty string and part’s suffix is the empty string: + if part.prefix.is_empty() && part.suffix.is_empty() { + // Step 3.7.1 If part’s modifier is "none" or "optional", then: + if matches!(part.modifier, PartModifier::None | PartModifier::Optional) { + // Step 3.7.1.1 Append "(" to the end of result. + result.push('('); + + // Step 3.7.1.2 Append regexp value to the end of result. + result.push_str(®exp_value); + + // Step 3.7.1.3 Append ")" to the end of result. + result.push(')'); + + // Step 3.7.1.4 Append the result of running convert a modifier to a string given part’s modifier + // to the end of result. + result.push_str(part.modifier.convert_to_string()); + } + // Step 3.7.2 Otherwise: + else { + // Step 3.7.2.1 Append "((?:" to the end of result. + result.push_str("((?:"); + + // Step 3.7.2.2 Append regexp value to the end of result. + result.push_str(®exp_value); + + // Step 3.7.2.3 Append ")" to the end of result. + result.push(')'); + + // Step 3.7.2.4 Append the result of running convert a modifier to a string given part’s modifier + // to the end of result. + result.push_str(part.modifier.convert_to_string()); + + // Step 3.7.2.5 Append ")" to the end of result. + result.push(')'); + } + + // Step 3.7.3 Continue. + continue; + } + + // Step 3.8 If part’s modifier is "none" or "optional": + if matches!(part.modifier, PartModifier::None | PartModifier::Optional) { + // Step 3.8.1 Append "(?:" to the end of result. + result.push_str("(?:"); + + // Step 3.8.2 Append the result of running escape a regexp string given part’s prefix + // to the end of result. + result.push_str(&escape_a_regexp_string(&part.prefix)); + + // Step 3.8.3 Append "(" to the end of result. + result.push('('); + + // Step 3.8.4 Append regexp value to the end of result. + result.push_str(®exp_value); + + // Step 3.8.5 Append ")" to the end of result. + result.push(')'); + + // Step 3.8.6 Append the result of running escape a regexp string given part’s suffix + // to the end of result. + result.push_str(&escape_a_regexp_string(&part.suffix)); + + // Step 3.8.7 Append ")" to the end of result. + result.push(')'); + + // Step 3.8.8 Append the result of running convert a modifier to a string given part’s modifier to + // the end of result. + result.push_str(part.modifier.convert_to_string()); + + // Step 3.8.9 Continue. + continue; + } + + // Step 3.9 Assert: part’s modifier is "zero-or-more" or "one-or-more". + debug_assert!(matches!( + part.modifier, + PartModifier::ZeroOrMore | PartModifier::OneOrMore + )); + + // Step 3.10 Assert: part’s prefix is not the empty string or part’s suffix is not the empty string. + debug_assert!(!part.prefix.is_empty() || !part.suffix.is_empty()); + + // Step 3.11 Append "(?:" to the end of result. + result.push_str("(?:"); + + // Step 3.12 Append the result of running escape a regexp string given part’s prefix to the end of result. + result.push_str(&escape_a_regexp_string(&part.prefix)); + + // Step 3.13 Append "((?:" to the end of result. + result.push_str("((?:"); + + // Step 3.14 Append regexp value to the end of result. + result.push_str(®exp_value); + + // Step 3.15 Append ")(?:" to the end of result. + result.push_str(")(?:"); + + // Step 3.16 Append the result of running escape a regexp string given part’s suffix to the end of result. + result.push_str(&escape_a_regexp_string(&part.suffix)); + + // Step 3.17 Append the result of running escape a regexp string given part’s prefix to the end of result. + result.push_str(&escape_a_regexp_string(&part.prefix)); + + // Step 3.18 Append "(?:" to the end of result. + result.push_str("(?:"); + + // Step 3.19 Append regexp value to the end of result. + result.push_str(®exp_value); + + // Step 3.20 Append "))*)" to the end of result. + result.push_str("))*)"); + + // Step 3.21 Append the result of running escape a regexp string given part’s suffix to the end of result. + result.push_str(&escape_a_regexp_string(&part.suffix)); + + // Step 3.22 Append ")" to the end of result. + result.push(')'); + + // Step 3.23 If part’s modifier is "zero-or-more" then append "?" to the end of result. + if part.modifier == PartModifier::ZeroOrMore { + result.push('?'); + } + } + + // Step 4. Append "$" to the end of result. + result.push('$'); + + // Step 5. Return (result, name list). + (result, name_list) +} + +/// +fn process_a_url_pattern_init(pattern_init: &URLPatternInit) -> URLPatternInit { + // FIXME: Implement this algorithm + pattern_init.clone() +} + +// FIXME: Deduplicate this with the url crate +/// +fn default_port_for_special_scheme(scheme: &str) -> Option { + match scheme { + "ftp" => Some(21), + "http" | "ws" => Some(80), + "https" | "wss" => Some(443), + _ => None, + } +} + +/// +fn escape_a_regexp_string(input: &str) -> String { + // Step 1. Assert: input is an ASCII string. + debug_assert!(input.is_ascii()); + + // Step 2. Let result be the empty string. + let mut result = String::with_capacity(input.len()); + + // Step 3. Let index be 0. + // Step 4. While index is less than input’s length: + // Step 4.1 Let c be input[index]. + // Step 4.2 Increment index by 1. + for c in input.chars() { + // Step 4.3 If c is one of: [..] then append "\" to the end of result. + if matches!( + c, + '.' | '+' | + '*' | + '?' | + '^' | + '$' | + '{' | + '}' | + '(' | + ')' | + '[' | + ']' | + '|' | + '/' | + '\\' + ) { + result.push('\\'); + } + + // Step 4.4 Append c to the end of result. + result.push(c); + } + + // Step 5. Return result. + result +} + +/// +fn generate_a_segment_wildcard_regexp(options: Options) -> String { + // Step 1. Let result be "[^". + let mut result = String::from("[^"); + + // Step 2. Append the result of running escape a regexp string given options’s + // delimiter code point to the end of result. + result.push_str(&escape_a_regexp_string( + &options + .delimiter_code_point + .map(|c| c.to_string()) + .unwrap_or_default(), + )); + + // Step 3. Append "]+?" to the end of result. + result.push_str("]+?"); + + // Step 4. Return result. + result +} + +impl PartModifier { + /// + fn convert_to_string(&self) -> &'static str { + match self { + // Step 1. If modifier is "zero-or-more", then return "*". + Self::ZeroOrMore => "*", + // Step 2. If modifier is "optional", then return "?". + Self::Optional => "?", + // Step 3. If modifier is "one-or-more", then return "+". + Self::OneOrMore => "+", + // Step 4. Return the empty string. + _ => "", + } + } +} + +impl Options { + /// + const HOSTNAME: Self = Self { + delimiter_code_point: Some('.'), + prefix_code_point: None, + ignore_case: false, + }; + + /// + const PATHNAME: Self = Self { + delimiter_code_point: Some('/'), + prefix_code_point: Some('/'), + ignore_case: false, + }; +} diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index d5b1dc5c281..949b6360537 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -759,6 +759,10 @@ Dictionaries = { 'derives': ['Clone', 'Copy'], }, +'URLPatternInit': { + 'derives': ['Clone'], +}, + 'XRWebGLLayerInit': { 'derives': ['Clone', 'Copy'], }, diff --git a/components/script_bindings/webidls/URLPattern.webidl b/components/script_bindings/webidls/URLPattern.webidl new file mode 100644 index 00000000000..85c0c468fb5 --- /dev/null +++ b/components/script_bindings/webidls/URLPattern.webidl @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://urlpattern.spec.whatwg.org/#urlpattern + +typedef /* USVString or */ URLPatternInit URLPatternInput; + +[Exposed=(Window,Worker), Pref="dom_urlpattern_enabled"] +interface URLPattern { +// constructor(URLPatternInput input, USVString baseURL, optional URLPatternOptions options = {}); + [Throws] constructor(optional URLPatternInput input = {}, optional URLPatternOptions options = {}); + +// boolean test(optional URLPatternInput input = {}, optional USVString baseURL); + +// URLPatternResult? exec(optional URLPatternInput input = {}, optional USVString baseURL); + + readonly attribute USVString protocol; + readonly attribute USVString username; + readonly attribute USVString password; + readonly attribute USVString hostname; + readonly attribute USVString port; + readonly attribute USVString pathname; + readonly attribute USVString search; + readonly attribute USVString hash; + + readonly attribute boolean hasRegExpGroups; +}; + +dictionary URLPatternInit { + USVString protocol; + USVString username; + USVString password; + USVString hostname; + USVString port; + USVString pathname; + USVString search; + USVString hash; + USVString baseURL; +}; + +dictionary URLPatternOptions { + boolean ignoreCase = false; +}; + +// dictionary URLPatternResult { +// sequence inputs; + +// URLPatternComponentResult protocol; +// URLPatternComponentResult username; +// URLPatternComponentResult password; +// URLPatternComponentResult hostname; +// URLPatternComponentResult port; +// URLPatternComponentResult pathname; +// URLPatternComponentResult search; +// URLPatternComponentResult hash; +// }; + +// dictionary URLPatternComponentResult { +// USVString input; +// record groups; +// }; diff --git a/python/tidy/tidy.py b/python/tidy/tidy.py index f312811456b..2b342af96e5 100644 --- a/python/tidy/tidy.py +++ b/python/tidy/tidy.py @@ -90,6 +90,7 @@ WEBIDL_STANDARDS = [ b"//html.spec.whatwg.org", b"//streams.spec.whatwg.org", b"//url.spec.whatwg.org", + b"//urlpattern.spec.whatwg.org", b"//xhr.spec.whatwg.org", b"//w3c.github.io", b"//heycam.github.io/webidl", diff --git a/tests/wpt/meta/__dir__.ini b/tests/wpt/meta/__dir__.ini index 9a3f520ac80..d59b68e4e61 100644 --- a/tests/wpt/meta/__dir__.ini +++ b/tests/wpt/meta/__dir__.ini @@ -1 +1 @@ -prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true", "dom_fontface_enabled:true"] +prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true", "dom_fontface_enabled:true", "dom_urlpattern_enabled:true"] diff --git a/tests/wpt/meta/urlpattern/urlpattern.any.js.ini b/tests/wpt/meta/urlpattern/urlpattern.any.js.ini index d310eb3d5e9..3b0e472f943 100644 --- a/tests/wpt/meta/urlpattern/urlpattern.any.js.ini +++ b/tests/wpt/meta/urlpattern/urlpattern.any.js.ini @@ -464,9 +464,6 @@ [Pattern: ["http://🚲.com/"\] Inputs: ["http://🚲.com/"\]] expected: FAIL - [Pattern: ["http://\\ud83d \\udeb2"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"hostname":"\\ud83d \\udeb2"}\] Inputs: undefined] expected: FAIL @@ -629,12 +626,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo"\] Inputs: undefined] - expected: FAIL - - [Pattern: ["example.com/foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["http{s}?://{*.}?example.com/:product/:endpoint"\] Inputs: ["https://sub.example.com/foo/bar"\]] expected: FAIL @@ -683,33 +674,18 @@ [Pattern: ["https://example.com/"\] Inputs: ["https://example.com:8080/"\]] expected: FAIL - [Pattern: ["data:foobar"\] Inputs: ["data:foobar"\]] - expected: FAIL - [Pattern: ["data\\\\:foobar"\] Inputs: ["data:foobar"\]] expected: FAIL [Pattern: ["https://{sub.}?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["{https://}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub.)?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL [Pattern: ["https://(sub.)?example(.com/)foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["(https://)example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["https://{sub{.}}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub(?:.))?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL @@ -722,9 +698,6 @@ [Pattern: ["foo://bar"\] Inputs: ["foo://bad_url_browser_interop"\]] expected: FAIL - [Pattern: ["(café)://foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["https://example.com/foo?bar#baz"\] Inputs: [{"protocol":"https:","search":"?bar","hash":"#baz","baseURL":"http://example.com/foo"}\]] expected: FAIL @@ -812,27 +785,12 @@ [Pattern: [{"hostname":"*\\\\:1\]"}\] Inputs: undefined] expected: FAIL - [Pattern: ["https://foo{{@}}example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - - [Pattern: ["https://foo{@example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - [Pattern: ["data\\\\:text/javascript,let x = 100/:tens?5;"\] Inputs: ["data:text/javascript,let x = 100/5;"\]] expected: FAIL [Pattern: [{"pathname":"/:id/:id"}\] Inputs: undefined] expected: FAIL - [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined] - expected: FAIL - - [Pattern: ["/foo",""\] Inputs: undefined] - expected: FAIL - - [Pattern: [{"pathname":"/foo"},"https://example.com"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"pathname":":name*"}\] Inputs: [{"pathname":"foobar"}\]] expected: FAIL @@ -1022,9 +980,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080",{"ignoreCase":true}\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo?bar#baz",{"ignoreCase":true},"https://example.com:8080"\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] - expected: FAIL - [Pattern: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\] Inputs: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\]] expected: FAIL @@ -1516,9 +1471,6 @@ [Pattern: ["http://🚲.com/"\] Inputs: ["http://🚲.com/"\]] expected: FAIL - [Pattern: ["http://\\ud83d \\udeb2"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"hostname":"\\ud83d \\udeb2"}\] Inputs: undefined] expected: FAIL @@ -1681,12 +1633,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo"\] Inputs: undefined] - expected: FAIL - - [Pattern: ["example.com/foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["http{s}?://{*.}?example.com/:product/:endpoint"\] Inputs: ["https://sub.example.com/foo/bar"\]] expected: FAIL @@ -1735,33 +1681,18 @@ [Pattern: ["https://example.com/"\] Inputs: ["https://example.com:8080/"\]] expected: FAIL - [Pattern: ["data:foobar"\] Inputs: ["data:foobar"\]] - expected: FAIL - [Pattern: ["data\\\\:foobar"\] Inputs: ["data:foobar"\]] expected: FAIL [Pattern: ["https://{sub.}?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["{https://}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub.)?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL [Pattern: ["https://(sub.)?example(.com/)foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["(https://)example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["https://{sub{.}}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub(?:.))?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL @@ -1774,9 +1705,6 @@ [Pattern: ["foo://bar"\] Inputs: ["foo://bad_url_browser_interop"\]] expected: FAIL - [Pattern: ["(café)://foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["https://example.com/foo?bar#baz"\] Inputs: [{"protocol":"https:","search":"?bar","hash":"#baz","baseURL":"http://example.com/foo"}\]] expected: FAIL @@ -1864,27 +1792,12 @@ [Pattern: [{"hostname":"*\\\\:1\]"}\] Inputs: undefined] expected: FAIL - [Pattern: ["https://foo{{@}}example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - - [Pattern: ["https://foo{@example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - [Pattern: ["data\\\\:text/javascript,let x = 100/:tens?5;"\] Inputs: ["data:text/javascript,let x = 100/5;"\]] expected: FAIL [Pattern: [{"pathname":"/:id/:id"}\] Inputs: undefined] expected: FAIL - [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined] - expected: FAIL - - [Pattern: ["/foo",""\] Inputs: undefined] - expected: FAIL - - [Pattern: [{"pathname":"/foo"},"https://example.com"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"pathname":":name*"}\] Inputs: [{"pathname":"foobar"}\]] expected: FAIL @@ -2074,9 +1987,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080",{"ignoreCase":true}\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo?bar#baz",{"ignoreCase":true},"https://example.com:8080"\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] - expected: FAIL - [Pattern: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\] Inputs: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\]] expected: FAIL diff --git a/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini b/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini index 8198da731ec..50c6b25ba0b 100644 --- a/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini +++ b/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini @@ -467,9 +467,6 @@ [Pattern: ["http://🚲.com/"\] Inputs: ["http://🚲.com/"\]] expected: FAIL - [Pattern: ["http://\\ud83d \\udeb2"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"hostname":"\\ud83d \\udeb2"}\] Inputs: undefined] expected: FAIL @@ -632,12 +629,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo"\] Inputs: undefined] - expected: FAIL - - [Pattern: ["example.com/foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["http{s}?://{*.}?example.com/:product/:endpoint"\] Inputs: ["https://sub.example.com/foo/bar"\]] expected: FAIL @@ -686,33 +677,18 @@ [Pattern: ["https://example.com/"\] Inputs: ["https://example.com:8080/"\]] expected: FAIL - [Pattern: ["data:foobar"\] Inputs: ["data:foobar"\]] - expected: FAIL - [Pattern: ["data\\\\:foobar"\] Inputs: ["data:foobar"\]] expected: FAIL [Pattern: ["https://{sub.}?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["{https://}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub.)?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL [Pattern: ["https://(sub.)?example(.com/)foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["(https://)example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["https://{sub{.}}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub(?:.))?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL @@ -725,9 +701,6 @@ [Pattern: ["foo://bar"\] Inputs: ["foo://bad_url_browser_interop"\]] expected: FAIL - [Pattern: ["(café)://foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["https://example.com/foo?bar#baz"\] Inputs: [{"protocol":"https:","search":"?bar","hash":"#baz","baseURL":"http://example.com/foo"}\]] expected: FAIL @@ -815,27 +788,12 @@ [Pattern: [{"hostname":"*\\\\:1\]"}\] Inputs: undefined] expected: FAIL - [Pattern: ["https://foo{{@}}example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - - [Pattern: ["https://foo{@example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - [Pattern: ["data\\\\:text/javascript,let x = 100/:tens?5;"\] Inputs: ["data:text/javascript,let x = 100/5;"\]] expected: FAIL [Pattern: [{"pathname":"/:id/:id"}\] Inputs: undefined] expected: FAIL - [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined] - expected: FAIL - - [Pattern: ["/foo",""\] Inputs: undefined] - expected: FAIL - - [Pattern: [{"pathname":"/foo"},"https://example.com"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"pathname":":name*"}\] Inputs: [{"pathname":"foobar"}\]] expected: FAIL @@ -1025,9 +983,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080",{"ignoreCase":true}\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo?bar#baz",{"ignoreCase":true},"https://example.com:8080"\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] - expected: FAIL - [Pattern: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\] Inputs: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\]] expected: FAIL @@ -1516,9 +1471,6 @@ [Pattern: ["http://🚲.com/"\] Inputs: ["http://🚲.com/"\]] expected: FAIL - [Pattern: ["http://\\ud83d \\udeb2"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"hostname":"\\ud83d \\udeb2"}\] Inputs: undefined] expected: FAIL @@ -1681,12 +1633,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo"\] Inputs: undefined] - expected: FAIL - - [Pattern: ["example.com/foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["http{s}?://{*.}?example.com/:product/:endpoint"\] Inputs: ["https://sub.example.com/foo/bar"\]] expected: FAIL @@ -1735,33 +1681,18 @@ [Pattern: ["https://example.com/"\] Inputs: ["https://example.com:8080/"\]] expected: FAIL - [Pattern: ["data:foobar"\] Inputs: ["data:foobar"\]] - expected: FAIL - [Pattern: ["data\\\\:foobar"\] Inputs: ["data:foobar"\]] expected: FAIL [Pattern: ["https://{sub.}?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["{https://}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub.)?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL [Pattern: ["https://(sub.)?example(.com/)foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL - [Pattern: ["(https://)example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - - [Pattern: ["https://{sub{.}}example.com/foo"\] Inputs: ["https://example.com/foo"\]] - expected: FAIL - [Pattern: ["https://(sub(?:.))?example.com/foo"\] Inputs: ["https://example.com/foo"\]] expected: FAIL @@ -1774,9 +1705,6 @@ [Pattern: ["foo://bar"\] Inputs: ["foo://bad_url_browser_interop"\]] expected: FAIL - [Pattern: ["(café)://foo"\] Inputs: undefined] - expected: FAIL - [Pattern: ["https://example.com/foo?bar#baz"\] Inputs: [{"protocol":"https:","search":"?bar","hash":"#baz","baseURL":"http://example.com/foo"}\]] expected: FAIL @@ -1864,27 +1792,12 @@ [Pattern: [{"hostname":"*\\\\:1\]"}\] Inputs: undefined] expected: FAIL - [Pattern: ["https://foo{{@}}example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - - [Pattern: ["https://foo{@example.com"\] Inputs: ["https://foo@example.com"\]] - expected: FAIL - [Pattern: ["data\\\\:text/javascript,let x = 100/:tens?5;"\] Inputs: ["data:text/javascript,let x = 100/5;"\]] expected: FAIL [Pattern: [{"pathname":"/:id/:id"}\] Inputs: undefined] expected: FAIL - [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined] - expected: FAIL - - [Pattern: ["/foo",""\] Inputs: undefined] - expected: FAIL - - [Pattern: [{"pathname":"/foo"},"https://example.com"\] Inputs: undefined] - expected: FAIL - [Pattern: [{"pathname":":name*"}\] Inputs: [{"pathname":"foobar"}\]] expected: FAIL @@ -2074,9 +1987,6 @@ [Pattern: ["/foo?bar#baz","https://example.com:8080",{"ignoreCase":true}\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] expected: FAIL - [Pattern: ["/foo?bar#baz",{"ignoreCase":true},"https://example.com:8080"\] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}\]] - expected: FAIL - [Pattern: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\] Inputs: [{"search":"foo","baseURL":"https://example.com/a/+/b"}\]] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index d158393e162..da85f35896e 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13503,14 +13503,14 @@ ] ], "interfaces.https.html": [ - "ad0b03ac70483c152220978cee8b49e74b330fc6", + "3c6184a527d0f5b993e753ce22012210c56a40e5", [ null, {} ] ], "interfaces.worker.js": [ - "f93f9c9f6c877e1915217b64591fe7e34fa7244c", + "dde3a2cb97675c27d292fb3f15e98bc383d58a4a", [ "mozilla/interfaces.worker.html", {} diff --git a/tests/wpt/mozilla/meta/__dir__.ini b/tests/wpt/mozilla/meta/__dir__.ini index 9a3f520ac80..d59b68e4e61 100644 --- a/tests/wpt/mozilla/meta/__dir__.ini +++ b/tests/wpt/mozilla/meta/__dir__.ini @@ -1 +1 @@ -prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true", "dom_fontface_enabled:true"] +prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true", "dom_fontface_enabled:true", "dom_urlpattern_enabled:true"] diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index ad0b03ac704..3c6184a527d 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -282,6 +282,7 @@ test_interfaces([ "TreeWalker", "UIEvent", "URL", + "URLPattern", "URLSearchParams", "ValidityState", "VideoTrack", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index f93f9c9f6c8..dde3a2cb976 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -68,6 +68,7 @@ test_interfaces([ "TextDecoder", "TextEncoder", "URL", + "URLPattern", "URLSearchParams", "WebGLActiveInfo", "WebGLBuffer",