From b10fa88b21805d51d8f53e4aae6d780aa0aee21a Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Fri, 27 Oct 2023 15:15:56 -0400 Subject: [PATCH 01/10] changes to base URL init application --- spec.bs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec.bs b/spec.bs index 387540c..9ac0649 100644 --- a/spec.bs +++ b/spec.bs @@ -1688,14 +1688,14 @@ To convert a modifier to a string given a [=part/modifier=] |modifier 1. If |init|["{{URLPatternInit/baseURL}}"] is not null: 1. Set |baseURL| to the result of [=URL parser|parsing=] |init|["{{URLPatternInit/baseURL}}"]. 1. If |baseURL| is failure, then throw a {{TypeError}}. - 1. Set |result|["{{URLPatternInit/protocol}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/scheme=] and |type|. - 1. Set |result|["{{URLPatternInit/username}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/username=] and |type|. - 1. Set |result|["{{URLPatternInit/password}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/password=] and |type|. - 1. Set |result|["{{URLPatternInit/hostname}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/host=] and |type|. - 1. Set |result|["{{URLPatternInit/port}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/port=] and |type|. - 1. Set |result|["{{URLPatternInit/pathname}}"] to the result of [=processing a base URL string=] given the result of [=URL path serializing=] |baseURL| and |type|. - 1. Set |result|["{{URLPatternInit/search}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/query=] and |type|. - 1. Set |result|["{{URLPatternInit/hash}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/fragment=] and |type|. + 1. If |init|["{{URLPatternInit/protocol}}"] does not [=map/exist=], then set |result|["{{URLPatternInit/protocol}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/scheme=] and |type|. + 1. If |type| is not "`pattern`" and none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], and |init|["{{URLPatternInit/username}}"] [=map/exist=], then set |result|["{{URLPatternInit/username}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/username=] and |type|. + 1. If |type| is not "`pattern`" and none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], |init|["{{URLPatternInit/username}}"] and |init|["{{URLPatternInit/password}}"] [=map/exist=], then set |result|["{{URLPatternInit/password}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/password=] and |type|. + 1. If neither |init|["{{URLPatternInit/protocol}}"] nor |init|["{{URLPatternInit/hostname}}"] [=map/exists=], then set |result|["{{URLPatternInit/hostname}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/host=] and |type|. + 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], and |init|["{{URLPatternInit/port}}"] [=map/exist=], then set |result|["{{URLPatternInit/port}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/port=] and |type|. + 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], and |init|["{{URLPatternInit/pathname}}"] [=map/exist=], then set |result|["{{URLPatternInit/pathname}}"] to the result of [=processing a base URL string=] given the result of [=URL path serializing=] |baseURL| and |type|. + 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], |init|["{{URLPatternInit/pathname}}"], and |init|["{{URLPatternInit/search}}"] [=map/exist=], then set |result|["{{URLPatternInit/search}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/query=] and |type|. + 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], |init|["{{URLPatternInit/pathname}}"], |init|["{{URLPatternInit/search}}"], and |init|["{{URLPatternInit/hash}}"] [=map/exist=], then set |result|["{{URLPatternInit/hash}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/fragment=] and |type|. 1. If |init|["{{URLPatternInit/protocol}}"] is not null then set |result|["{{URLPatternInit/protocol}}"] to the result of [=process protocol for init=] given |init|["{{URLPatternInit/protocol}}"] and |type|. 1. If |init|["{{URLPatternInit/username}}"] is not null then set |result|["{{URLPatternInit/username}}"] to the result of [=process username for init=] given |init|["{{URLPatternInit/username}}"] and |type|. 1. If |init|["{{URLPatternInit/password}}"] is not null then set |result|["{{URLPatternInit/password}}"] to the result of [=process password for init=] given |init|["{{URLPatternInit/password}}"] and |type|. From 2c9346c6c7032b7c70d1edca49a3a54d3d2b4d7f Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Fri, 27 Oct 2023 15:43:37 -0400 Subject: [PATCH 02/10] factor out the use of map/contains to scan better --- spec.bs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec.bs b/spec.bs index 9ac0649..9649b35 100644 --- a/spec.bs +++ b/spec.bs @@ -1689,13 +1689,13 @@ To convert a modifier to a string given a [=part/modifier=] |modifier 1. Set |baseURL| to the result of [=URL parser|parsing=] |init|["{{URLPatternInit/baseURL}}"]. 1. If |baseURL| is failure, then throw a {{TypeError}}. 1. If |init|["{{URLPatternInit/protocol}}"] does not [=map/exist=], then set |result|["{{URLPatternInit/protocol}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/scheme=] and |type|. - 1. If |type| is not "`pattern`" and none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], and |init|["{{URLPatternInit/username}}"] [=map/exist=], then set |result|["{{URLPatternInit/username}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/username=] and |type|. - 1. If |type| is not "`pattern`" and none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], |init|["{{URLPatternInit/username}}"] and |init|["{{URLPatternInit/password}}"] [=map/exist=], then set |result|["{{URLPatternInit/password}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/password=] and |type|. - 1. If neither |init|["{{URLPatternInit/protocol}}"] nor |init|["{{URLPatternInit/hostname}}"] [=map/exists=], then set |result|["{{URLPatternInit/hostname}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/host=] and |type|. - 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], and |init|["{{URLPatternInit/port}}"] [=map/exist=], then set |result|["{{URLPatternInit/port}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/port=] and |type|. - 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], and |init|["{{URLPatternInit/pathname}}"] [=map/exist=], then set |result|["{{URLPatternInit/pathname}}"] to the result of [=processing a base URL string=] given the result of [=URL path serializing=] |baseURL| and |type|. - 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], |init|["{{URLPatternInit/pathname}}"], and |init|["{{URLPatternInit/search}}"] [=map/exist=], then set |result|["{{URLPatternInit/search}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/query=] and |type|. - 1. If none of |init|["{{URLPatternInit/protocol}}"], |init|["{{URLPatternInit/hostname}}"], |init|["{{URLPatternInit/port}}"], |init|["{{URLPatternInit/pathname}}"], |init|["{{URLPatternInit/search}}"], and |init|["{{URLPatternInit/hash}}"] [=map/exist=], then set |result|["{{URLPatternInit/hash}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/fragment=] and |type|. + 1. If |type| is not "`pattern`" and |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}" and "{{URLPatternInit/username}}", then set |result|["{{URLPatternInit/username}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/username=] and |type|. + 1. If |type| is not "`pattern`" and |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/username}}" and "{{URLPatternInit/password}}", then set |result|["{{URLPatternInit/password}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/password=] and |type|. + 1. If |init| [=map/contains=] neither "{{URLPatternInit/protocol}}" nor "{{URLPatternInit/hostname}}", then set |result|["{{URLPatternInit/hostname}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/host=] and |type|. + 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", and "{{URLPatternInit/port}}", then set |result|["{{URLPatternInit/port}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/port=] and |type|. + 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", and "{{URLPatternInit/pathname}}", then set |result|["{{URLPatternInit/pathname}}"] to the result of [=processing a base URL string=] given the result of [=URL path serializing=] |baseURL| and |type|. + 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/pathname}}", and "{{URLPatternInit/search}}", then set |result|["{{URLPatternInit/search}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/query=] and |type|. + 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/pathname}}", "{{URLPatternInit/search}}", and "{{URLPatternInit/hash}}", then set |result|["{{URLPatternInit/hash}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/fragment=] and |type|. 1. If |init|["{{URLPatternInit/protocol}}"] is not null then set |result|["{{URLPatternInit/protocol}}"] to the result of [=process protocol for init=] given |init|["{{URLPatternInit/protocol}}"] and |type|. 1. If |init|["{{URLPatternInit/username}}"] is not null then set |result|["{{URLPatternInit/username}}"] to the result of [=process username for init=] given |init|["{{URLPatternInit/username}}"] and |type|. 1. If |init|["{{URLPatternInit/password}}"] is not null then set |result|["{{URLPatternInit/password}}"] to the result of [=process password for init=] given |init|["{{URLPatternInit/password}}"] and |type|. From 466a7928d5eefb987eb56a1792f882bcb303a63a Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Fri, 27 Oct 2023 16:36:53 -0400 Subject: [PATCH 03/10] constructor string parser state machine changes --- spec.bs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/spec.bs b/spec.bs index 9649b35..3287120 100644 --- a/spec.bs +++ b/spec.bs @@ -532,11 +532,8 @@ To parse a constructor string given a string |input|: 1. If the result of running [=is a hash prefix=] given |parser| is true, then run [=change state=] given |parser|, "`hash`" and 1. 1. Otherwise if the result of running [=is a search prefix=] given |parser| is true: 1. Run [=change state=] given |parser|, "`search`" and 1. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPattern/hash}}"] to the empty string. 1. Otherwise: 1. Run [=change state=] given |parser|, "`pathname`" and 0. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPattern/search}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPattern/hash}}"] to the empty string. 1. Increment |parser|'s [=constructor string parser/token index=] by |parser|'s [=constructor string parser/token increment=]. 1. [=Continue=]. 1. If |parser|'s [=constructor string parser/state=] is "`authority`": @@ -564,14 +561,6 @@ To parse a constructor string given a string |input|:
"`init`"
1. If the result of running [=is a protocol suffix=] given |parser| is true: -

We found a protocol suffix, so this is an absolute URLPattern constructor string. Therefore initialize all component to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/username}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/password}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/port}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] to the empty string. - 1. Set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hash}}"] to the empty string. 1. Run [=rewind and set state=] given |parser| and "`protocol`".

"`protocol`"
@@ -579,7 +568,6 @@ To parse a constructor string given a string |input|: 1. If the result of running [=is a protocol suffix=] given |parser| is true: 1. Run [=compute protocol matches a special scheme flag=] given |parser|.

We need to eagerly compile the protocol component to determine if it matches any [=special schemes=]. If it does then certain special rules apply. It determines if the pathname defaults to a "`/`" and also whether we will look for the username, password, hostname, and port components. Authority slashes can also cause us to look for these components as well. Otherwise we treat this as an "opaque path URL" and go straight to the pathname component. - 1. If |parser|'s [=constructor string parser/protocol matches a special scheme flag=] is true, then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to "`/`". 1. Let |next state| be "`pathname`". 1. Let |skip| be 1. 1. If the result of running [=next is authority slashes=] given |parser| is true: @@ -642,6 +630,9 @@ To parse a constructor string given a string |input|: 1. Increment |parser|'s [=constructor string parser/token index=] by |parser|'s [=constructor string parser/token increment=]. + 1. If |parser|'s [=constructor string parser/result=] [=map/contains=] "{{URLPatternInit/hostname}}" and not "{{URLPatternInit/port}}", then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/port}}"] to the empty string. + +

This is special-cased because when an author does not specify a port, they usually intend the default port. If any port is acceptable, the author can specify it as a wildcard explicitly. For example, "`https://example.com/*`" should not match URLs beginning with "`https://example.com:8443/`", which is a different origin.
1. Return |parser|'s [=constructor string parser/result=]. @@ -649,6 +640,12 @@ To parse a constructor string given a string |input|: To change state given a [=constructor string parser=] |parser|, a [=constructor string parser/state=] |new state|, and a number |skip|: 1. If |parser|'s [=constructor string parser/state=] is not "`init`", not "`authority`", and not "`done`", then set |parser|'s [=constructor string parser/result=][|parser|'s [=constructor string parser/state=]] to the result of running [=make a component string=] given |parser|. + 1. If |parser|'s [=constructor string parser/state=] is not "`init`" and |new state| is not "`done`", then: + 1. It |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", or "`password`"; |new state| is "`port`", "`pathname`", "`search`", or "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] to the empty string. + 1. It |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", "`password`", "`hostname`", or "`port`"; |new state| is "`search`", or "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] does not [=map/exist=], then: + 1. If |parser|'s [=constructor string parser/protocol matches a special scheme flag=] is true, then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to "`/`". + 1. Otherwise, set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to the empty string. + 1. It |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", "`password`", "`hostname`", "`port`", or "`pathname`"; |new state| is "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] to the empty string. 1. Set |parser|'s [=constructor string parser/state=] to |new state|. 1. Increment |parser|'s [=constructor string parser/token index=] by |skip|. 1. Set |parser|'s [=constructor string parser/component start=] to |parser|'s [=constructor string parser/token index=]. From 05c12a40fa3a86461a190cfb1a423aefbe0cc49f Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Fri, 27 Oct 2023 17:06:40 -0400 Subject: [PATCH 04/10] spelling: It -> If --- spec.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec.bs b/spec.bs index 3287120..4dd6b88 100644 --- a/spec.bs +++ b/spec.bs @@ -641,11 +641,11 @@ To change state given a [=constructor string parser=] |parser|, a [=c 1. If |parser|'s [=constructor string parser/state=] is not "`init`", not "`authority`", and not "`done`", then set |parser|'s [=constructor string parser/result=][|parser|'s [=constructor string parser/state=]] to the result of running [=make a component string=] given |parser|. 1. If |parser|'s [=constructor string parser/state=] is not "`init`" and |new state| is not "`done`", then: - 1. It |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", or "`password`"; |new state| is "`port`", "`pathname`", "`search`", or "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] to the empty string. - 1. It |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", "`password`", "`hostname`", or "`port`"; |new state| is "`search`", or "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] does not [=map/exist=], then: + 1. If |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", or "`password`"; |new state| is "`port`", "`pathname`", "`search`", or "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] to the empty string. + 1. If |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", "`password`", "`hostname`", or "`port`"; |new state| is "`search`", or "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] does not [=map/exist=], then: 1. If |parser|'s [=constructor string parser/protocol matches a special scheme flag=] is true, then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to "`/`". 1. Otherwise, set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to the empty string. - 1. It |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", "`password`", "`hostname`", "`port`", or "`pathname`"; |new state| is "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] to the empty string. + 1. If |parser|'s [=constructor string parser/state=] is "`protocol`", "`authority`", "`username`", "`password`", "`hostname`", "`port`", or "`pathname`"; |new state| is "`hash`"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] to the empty string. 1. Set |parser|'s [=constructor string parser/state=] to |new state|. 1. Increment |parser|'s [=constructor string parser/token index=] by |skip|. 1. Set |parser|'s [=constructor string parser/component start=] to |parser|'s [=constructor string parser/token index=]. From 2d122ee3fb44304d472839a33062f2f58cf7d49d Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Mon, 30 Oct 2023 22:54:03 -0400 Subject: [PATCH 05/10] update some notes and the main example --- spec.bs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/spec.bs b/spec.bs index 4dd6b88..eb39884 100644 --- a/spec.bs +++ b/spec.bs @@ -34,10 +34,10 @@ It can be constructed using a string for each component, or from a shorthand str
"`https`"
[=URLPattern/username component|username=] -
"" +
"`*`"
[=URLPattern/password component|password=] -
"" +
"`*`"
[=URLPattern/hostname component|hostname=]
"`example.com`" @@ -49,10 +49,10 @@ It can be constructed using a string for each component, or from a shorthand str
"`/:category/*`"
[=URLPattern/search component|search=] -
"" +
"`*`"
[=URLPattern/hash component|hash=] -
"" +
"`*`" It matches the following URLs: @@ -510,6 +510,7 @@ A [=constructor string parser=] has an associated Second, the URLPattern constructor string parser needs to avoid applying URL canonicalization to all code points like [=basic URL parser=] does. Instead we perform canonicalization on only parts of the pattern string we know are safe later when compiling each component pattern string.

Finally, the URLPattern constructor string parser does not handle some parts of the [=basic URL parser=] state machine. For example, it does not treat backslashes specially as they would all be treated as pattern characters and would require excessive escaping. In addition, this parser might not handle some more esoteric parts of the URL parsing algorithm like file URLs with a hostname. The goal with this parser was to handle the most common URLs while allowing any niche case to be handled instead via the {{URLPatternInit}} constructor. +

In the constructor string algorithm, the pathname, search and hash are wildcarded if earlier components are specified but later ones are not. For example, "`https://example.com/foo`" matches any search and any hash. Similarly, "`https://example.com`" matches any URL on that origin. The username and password components are always wildcard unless they are explicitly specified.

@@ -517,9 +518,8 @@ To parse a constructor string given a string |input|: 1. Let |parser| be a new [=constructor string parser=] whose [=constructor string parser/input=] is |input| and [=constructor string parser/token list=] is the result of running [=tokenize=] given |input| and "`lenient`".
-

When constructing a pattern using a {{URLPatternInit}} like `new URLPattern({ pathname: 'foo' })` any missing components will be defaulted to wildcards. In the constructor string case, however, all components are precisely defined as either empty string or a longer value. This is due to there being no way to simply "leave out" a component when writing a URL. -

To implement this we initialize components in |parser|'s [=constructor string parser/result=] with empty string in advance. -

We can't, however, do this immediately. We want to allow the `baseURL` to provide information for relative URLs, so we only want to set the default empty string values for components following the first component in the relative URL. We therefore wait to set the default component values until after we exit the "`init`" [=constructor string parser/state=]. +

When constructing a pattern using a {{URLPatternInit}} like `new URLPattern({ pathname: 'foo' })` any missing components will be defaulted to wildcards. +

Components which appear "later" than those specified are instead treated as wildcards (see above). As a result, these values are not initialized to be empty in |parser|'s [=constructor string parser/result=] until a "later" component is seen.

1. [=While=] |parser|'s [=constructor string parser/token index=] is less than |parser|'s [=constructor string parser/token list=] [=list/size=]: 1. Set |parser|'s [=constructor string parser/token increment=] to 1. @@ -1683,6 +1683,16 @@ To convert a modifier to a string given a [=part/modifier=] |modifier 1. Set |result|["{{URLPatternInit/hash}}"] to |hash|. 1. Let |baseURL| be null. 1. If |init|["{{URLPatternInit/baseURL}}"] is not null: +
+ The base URL can be used to supply additional context, but for each component, if |init| includes a component which is at least as specific as one in the base URL, none is inherited. + + A component is more specific if it appears later in one of the following two lists (which are very similar to the order they appear in the URL syntax): + + * protocol, hostname, port, pathname, search, hash + * protocol, hostname, port, username, password + + Username and password are also never inherited when constructing patterns, only for a URL being matched. +
1. Set |baseURL| to the result of [=URL parser|parsing=] |init|["{{URLPatternInit/baseURL}}"]. 1. If |baseURL| is failure, then throw a {{TypeError}}. 1. If |init|["{{URLPatternInit/protocol}}"] does not [=map/exist=], then set |result|["{{URLPatternInit/protocol}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/scheme=] and |type|. From dc6a52d95d65694e3b60ade627eb5a33c15ad609 Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Mon, 6 Nov 2023 17:18:36 -0500 Subject: [PATCH 06/10] Add more examples. --- spec.bs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/spec.bs b/spec.bs index eb39884..83b7167 100644 --- a/spec.bs +++ b/spec.bs @@ -71,6 +71,97 @@ It can be constructed using a string for each component, or from a shorthand str
+
+

The shorthand "`http{s}?://{:subdomain.}?shop.example/products/:id([0-9]+)#reviews`" corresponds to the following components: + +

+
[=URLPattern/protocol component|protocol=] +
"`http{s}?`" + +
[=URLPattern/username component|username=] +
"`*`" + +
[=URLPattern/password component|password=] +
"`*`" + +
[=URLPattern/hostname component|hostname=] +
"`{:subdomain.}?shop.example`" + +
[=URLPattern/port component|port=] +
"" + +
[=URLPattern/pathname component|pathname=] +
"`/products/:id([0-9]+)`" + +
[=URLPattern/search component|search=] +
"" + +
[=URLPattern/hash component|hash=] +
"`reviews`" +
+ + It matches the following URLs: + +
    + * `https://shop.example/products/74205#reviews` + * `https://kathryn@voyager.shop.example/products/74656#reviews` + * `http://insecure.shop.example/products/1701#reviews` +
+ + It does not match the following URLs: + +
    + * `https://shop.example/products/2000` + * `http://shop.example:8080/products/0#reviews` + * `https://nx.shop.example/products/01?speed=5#reviews` + * `https://shop.example/products/chair#reviews` +
+
+ +
+

The shorthand "`../admin/*`" with the base URL "`https://discussion.example/forum/?page=2`" corresponds to the following components: + +

+
[=URLPattern/protocol component|protocol=] +
"`https`" + +
[=URLPattern/username component|username=] +
"`*`" + +
[=URLPattern/password component|password=] +
"`*`" + +
[=URLPattern/hostname component|hostname=] +
"`discussion.example`" + +
[=URLPattern/port component|port=] +
"" + +
[=URLPattern/pathname component|pathname=] +
"`/admin/*`" + +
[=URLPattern/search component|search=] +
"*" + +
[=URLPattern/hash component|hash=] +
"`*`" +
+ + It matches the following URLs: + +
    + * `https://discussion.example/admin/` + * `https://edd:librarian@discussion.example/admin/update?id=1` +
+ + It does not match the following URLs: + +
    + * `https://discussion.example/forum/admin/` + * `http://discussion.example:8080/admin/update?id=1` +
+
+ typedef (USVString or URLPatternInit) URLPatternInput; From f9e1c99d05d20402aa93007c620b9b0e832e2388 Mon Sep 17 00:00:00 2001 From: Jeremy Roman <jbroman@chromium.org> Date: Mon, 6 Nov 2023 17:19:00 -0500 Subject: [PATCH 07/10] domenic review --- spec.bs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/spec.bs b/spec.bs index 83b7167..43347e7 100644 --- a/spec.bs +++ b/spec.bs @@ -601,17 +601,14 @@ A [=constructor string parser=] has an associated <dfn export for="constructor s <p>First, the URLPattern constructor string parser operates on [=tokens=] generated using the "`lenient`" [=tokenize policy=]. In constrast, [=basic URL parser=] operates on code points. Operating on [=tokens=] allows the URLPattern constructor string parser to more easily distinguish between code points that are significant pattern syntax and code points that might be a URL component separator. For example, it makes it trivial to handle named groups like "`:hmm`" in "`https://a.c:hmm.example.com:8080`" without getting confused with the port number. <p>Second, the URLPattern constructor string parser needs to avoid applying URL canonicalization to all code points like [=basic URL parser=] does. Instead we perform canonicalization on only parts of the pattern string we know are safe later when compiling each component pattern string. <p>Finally, the URLPattern constructor string parser does not handle some parts of the [=basic URL parser=] state machine. For example, it does not treat backslashes specially as they would all be treated as pattern characters and would require excessive escaping. In addition, this parser might not handle some more esoteric parts of the URL parsing algorithm like file URLs with a hostname. The goal with this parser was to handle the most common URLs while allowing any niche case to be handled instead via the {{URLPatternInit}} constructor. - <p>In the constructor string algorithm, the pathname, search and hash are wildcarded if earlier components are specified but later ones are not. For example, "`https://example.com/foo`" matches any search and any hash. Similarly, "`https://example.com`" matches any URL on that origin. The username and password components are always wildcard unless they are explicitly specified. + <p>In the constructor string algorithm, the pathname, search, and hash are wildcarded if earlier components are specified but later ones are not. For example, "`https://example.com/foo`" matches any search and any hash. Similarly, "`https://example.com`" matches any URL on that origin. This is analogous to the notion of a more specific component in the notes about [=process a URLPatternInit=] (e.g., a search is more specific than a pathname), but the constructor syntax only has a few cases where it is possible to specify a more specific component without also specifying the less specific components. + <p>The username and password components are always wildcard unless they are explicitly specified. </div> <div algorithm> To <dfn>parse a constructor string</dfn> given a string |input|: 1. Let |parser| be a new [=constructor string parser=] whose [=constructor string parser/input=] is |input| and [=constructor string parser/token list=] is the result of running [=tokenize=] given |input| and "<a for="tokenize policy">`lenient`</a>". - <div class=note> - <p>When constructing a pattern using a {{URLPatternInit}} like `new URLPattern({ pathname: 'foo' })` any missing components will be defaulted to wildcards. - <p>Components which appear "later" than those specified are instead treated as wildcards (see above). As a result, these values are not initialized to be empty in |parser|'s [=constructor string parser/result=] until a "later" component is seen. - </div> 1. [=While=] |parser|'s [=constructor string parser/token index=] is less than |parser|'s [=constructor string parser/token list=] [=list/size=]: 1. Set |parser|'s [=constructor string parser/token increment=] to 1. <p class="note">On every iteration of the parse loop the |parser|'s [=constructor string parser/token index=] will be incremented by its [=constructor string parser/token increment=] value. Typically this means incrementing by 1, but at certain times it is set to zero. The [=constructor string parser/token increment=] is then always reset back to 1 at the top of the loop. @@ -733,7 +730,7 @@ To <dfn>change state</dfn> given a [=constructor string parser=] |parser|, a [=c 1. If |parser|'s [=constructor string parser/state=] is not "<a for="constructor string parser/state">`init`</a>", not "<a for="constructor string parser/state">`authority`</a>", and not "<a for="constructor string parser/state">`done`</a>", then set |parser|'s [=constructor string parser/result=][|parser|'s [=constructor string parser/state=]] to the result of running [=make a component string=] given |parser|. 1. If |parser|'s [=constructor string parser/state=] is not "<a for="constructor string parser/state">`init`</a>" and |new state| is not "<a for="constructor string parser/state">`done`</a>", then: 1. If |parser|'s [=constructor string parser/state=] is "<a for="constructor string parser/state">`protocol`</a>", "<a for="constructor string parser/state">`authority`</a>", "<a for="constructor string parser/state">`username`</a>", or "<a for="constructor string parser/state">`password`</a>"; |new state| is "<a for="constructor string parser/state">`port`</a>", "<a for="constructor string parser/state">`pathname`</a>", "<a for="constructor string parser/state">`search`</a>", or "<a for="constructor string parser/state">`hash`</a>"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/hostname}}"] to the empty string. - 1. If |parser|'s [=constructor string parser/state=] is "<a for="constructor string parser/state">`protocol`</a>", "<a for="constructor string parser/state">`authority`</a>", "<a for="constructor string parser/state">`username`</a>", "<a for="constructor string parser/state">`password`</a>", "<a for="constructor string parser/state">`hostname`</a>", or "<a for="constructor string parser/state">`port`</a>"; |new state| is "<a for="constructor string parser/state">`search`</a>", or "<a for="constructor string parser/state">`hash`</a>"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] does not [=map/exist=], then: + 1. If |parser|'s [=constructor string parser/state=] is "<a for="constructor string parser/state">`protocol`</a>", "<a for="constructor string parser/state">`authority`</a>", "<a for="constructor string parser/state">`username`</a>", "<a for="constructor string parser/state">`password`</a>", "<a for="constructor string parser/state">`hostname`</a>", or "<a for="constructor string parser/state">`port`</a>"; |new state| is "<a for="constructor string parser/state">`search`</a>" or "<a for="constructor string parser/state">`hash`</a>"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] does not [=map/exist=], then: 1. If |parser|'s [=constructor string parser/protocol matches a special scheme flag=] is true, then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to "`/`". 1. Otherwise, set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/pathname}}"] to the empty string. 1. If |parser|'s [=constructor string parser/state=] is "<a for="constructor string parser/state">`protocol`</a>", "<a for="constructor string parser/state">`authority`</a>", "<a for="constructor string parser/state">`username`</a>", "<a for="constructor string parser/state">`password`</a>", "<a for="constructor string parser/state">`hostname`</a>", "<a for="constructor string parser/state">`port`</a>", or "<a for="constructor string parser/state">`pathname`</a>"; |new state| is "<a for="constructor string parser/state">`hash`</a>"; and |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] does not [=map/exist=], then set |parser|'s [=constructor string parser/result=]["{{URLPatternInit/search}}"] to the empty string. @@ -1782,7 +1779,7 @@ To <dfn>convert a modifier to a string</dfn> given a [=part/modifier=] |modifier * protocol, hostname, port, pathname, search, hash * protocol, hostname, port, username, password - Username and password are also never inherited when constructing patterns, only for a URL being matched. + Username and password are also never inherited from a base URL when constructing a {{URLPattern}}. (They are, however, inherited from the base URL supplied as an argument to {{URLPattern/test()}} or {{URLPattern/exec()}}.) </div> 1. Set |baseURL| to the result of [=URL parser|parsing=] |init|["{{URLPatternInit/baseURL}}"]. 1. If |baseURL| is failure, then throw a {{TypeError}}. From cad7463045d8858587caaca052df5b13684851c1 Mon Sep 17 00:00:00 2001 From: Jeremy Roman <jbroman@chromium.org> Date: Mon, 6 Nov 2023 17:26:41 -0500 Subject: [PATCH 08/10] Use map/exists instead of 'is not null' in existing normative text. --- spec.bs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spec.bs b/spec.bs index 43347e7..620a226 100644 --- a/spec.bs +++ b/spec.bs @@ -343,7 +343,7 @@ Each {{URLPattern}} object has an associated <dfn for=URLPattern>hash component< 1. Let |init| be null. 1. If |input| is a [=scalar value string=] then: 1. Set |init| to the result of running [=parse a constructor string=] given |input|. - 1. If |baseURL| is null and |init|["{{URLPatternInit/protocol}}"] is null, then throw a {{TypeError}}. + 1. If |baseURL| is null and |init|["{{URLPatternInit/protocol}}"] does not [=map/exist=], then throw a {{TypeError}}. 1. Set |init|["{{URLPatternInit/baseURL}}"] to |baseURL|. 1. Otherwise: 1. [=Assert=]: |input| is a {{URLPatternInit}}. @@ -1770,7 +1770,7 @@ To <dfn>convert a modifier to a string</dfn> given a [=part/modifier=] |modifier 1. Set |result|["{{URLPatternInit/search}}"] to |search|. 1. Set |result|["{{URLPatternInit/hash}}"] to |hash|. 1. Let |baseURL| be null. - 1. If |init|["{{URLPatternInit/baseURL}}"] is not null: + 1. If |init|["{{URLPatternInit/baseURL}}"] [=map/exists=]: <div class="note"> The base URL can be used to supply additional context, but for each component, if |init| includes a component which is at least as specific as one in the base URL, none is inherited. @@ -1791,12 +1791,12 @@ To <dfn>convert a modifier to a string</dfn> given a [=part/modifier=] |modifier 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", and "{{URLPatternInit/pathname}}", then set |result|["{{URLPatternInit/pathname}}"] to the result of [=processing a base URL string=] given the result of [=URL path serializing=] |baseURL| and |type|. 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/pathname}}", and "{{URLPatternInit/search}}", then set |result|["{{URLPatternInit/search}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/query=] and |type|. 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/pathname}}", "{{URLPatternInit/search}}", and "{{URLPatternInit/hash}}", then set |result|["{{URLPatternInit/hash}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/fragment=] and |type|. - 1. If |init|["{{URLPatternInit/protocol}}"] is not null then set |result|["{{URLPatternInit/protocol}}"] to the result of [=process protocol for init=] given |init|["{{URLPatternInit/protocol}}"] and |type|. - 1. If |init|["{{URLPatternInit/username}}"] is not null then set |result|["{{URLPatternInit/username}}"] to the result of [=process username for init=] given |init|["{{URLPatternInit/username}}"] and |type|. - 1. If |init|["{{URLPatternInit/password}}"] is not null then set |result|["{{URLPatternInit/password}}"] to the result of [=process password for init=] given |init|["{{URLPatternInit/password}}"] and |type|. - 1. If |init|["{{URLPatternInit/hostname}}"] is not null then set |result|["{{URLPatternInit/hostname}}"] to the result of [=process hostname for init=] given |init|["{{URLPatternInit/hostname}}"] and |type|. - 1. If |init|["{{URLPatternInit/port}}"] is not null then set |result|["{{URLPatternInit/port}}"] to the result of [=process port for init=] given |init|["{{URLPatternInit/port}}"], |result|["{{URLPatternInit/protocol}}"], and |type|. - 1. If |init|["{{URLPatternInit/pathname}}"] is not null: + 1. If |init|["{{URLPatternInit/protocol}}"] [=map/exists=] then set |result|["{{URLPatternInit/protocol}}"] to the result of [=process protocol for init=] given |init|["{{URLPatternInit/protocol}}"] and |type|. + 1. If |init|["{{URLPatternInit/username}}"] [=map/exists=] then set |result|["{{URLPatternInit/username}}"] to the result of [=process username for init=] given |init|["{{URLPatternInit/username}}"] and |type|. + 1. If |init|["{{URLPatternInit/password}}"] [=map/exists=] then set |result|["{{URLPatternInit/password}}"] to the result of [=process password for init=] given |init|["{{URLPatternInit/password}}"] and |type|. + 1. If |init|["{{URLPatternInit/hostname}}"] [=map/exists=] then set |result|["{{URLPatternInit/hostname}}"] to the result of [=process hostname for init=] given |init|["{{URLPatternInit/hostname}}"] and |type|. + 1. If |init|["{{URLPatternInit/port}}"] [=map/exists=] then set |result|["{{URLPatternInit/port}}"] to the result of [=process port for init=] given |init|["{{URLPatternInit/port}}"], |result|["{{URLPatternInit/protocol}}"], and |type|. + 1. If |init|["{{URLPatternInit/pathname}}"] [=map/exists=]: 1. Set |result|["{{URLPatternInit/pathname}}"] to |init|["{{URLPatternInit/pathname}}"]. 1. If the following are all true: <ul> @@ -1812,8 +1812,8 @@ To <dfn>convert a modifier to a string</dfn> given a [=part/modifier=] |modifier 1. Append |result|["{{URLPatternInit/pathname}}"] to the end of |new pathname|. 1. Set |result|["{{URLPatternInit/pathname}}"] to |new pathname|. 1. Set |result|["{{URLPatternInit/pathname}}"] to the result of [=process pathname for init=] given |result|["{{URLPatternInit/pathname}}"], |result|["{{URLPatternInit/protocol}}"], and |type|. - 1. If |init|["{{URLPatternInit/search}}"] is not null then set |result|["{{URLPatternInit/search}}"] to the result of [=process search for init=] given |init|["{{URLPatternInit/search}}"] and |type|. - 1. If |init|["{{URLPatternInit/hash}}"] is not null then set |result|["{{URLPatternInit/hash}}"] to the result of [=process hash for init=] given |init|["{{URLPatternInit/hash}}"] and |type|. + 1. If |init|["{{URLPatternInit/search}}"] [=map/exists=] then set |result|["{{URLPatternInit/search}}"] to the result of [=process search for init=] given |init|["{{URLPatternInit/search}}"] and |type|. + 1. If |init|["{{URLPatternInit/hash}}"] [=map/exists=] then set |result|["{{URLPatternInit/hash}}"] to the result of [=process hash for init=] given |init|["{{URLPatternInit/hash}}"] and |type|. 1. Return |result|. </div> From 551c8825f4d64812a9467ce7107fef02a7b73a4f Mon Sep 17 00:00:00 2001 From: Jeremy Roman <jbroman@chromium.org> Date: Mon, 6 Nov 2023 17:59:52 -0500 Subject: [PATCH 09/10] unique IDs for the examples --- spec.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec.bs b/spec.bs index 620a226..c9cb2bb 100644 --- a/spec.bs +++ b/spec.bs @@ -71,7 +71,7 @@ It can be constructed using a string for each component, or from a shorthand str </ul> </div> -<div class="example" id="example-intro"> +<div class="example" id="example-intro-2"> <p>The shorthand "`http{s}?://{:subdomain.}?shop.example/products/:id([0-9]+)#reviews`" corresponds to the following components: <dl class="props"> @@ -118,7 +118,7 @@ It can be constructed using a string for each component, or from a shorthand str </ul> </div> -<div class="example" id="example-intro"> +<div class="example" id="example-intro-3"> <p>The shorthand "`../admin/*`" with the base URL "`https://discussion.example/forum/?page=2`" corresponds to the following components: <dl class="props"> From 7cad49ee2b0ff854563a7d373099701996ffa8cc Mon Sep 17 00:00:00 2001 From: Jeremy Roman <jbroman@chromium.org> Date: Tue, 7 Nov 2023 12:19:17 -0500 Subject: [PATCH 10/10] domenic & sisidovski review --- spec.bs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/spec.bs b/spec.bs index c9cb2bb..6412b28 100644 --- a/spec.bs +++ b/spec.bs @@ -141,7 +141,7 @@ It can be constructed using a string for each component, or from a shorthand str <dd>"`/admin/*`" <dt>[=URLPattern/search component|search=] - <dd>"*" + <dd>"`*`" <dt>[=URLPattern/hash component|hash=] <dd>"`*`" @@ -601,8 +601,12 @@ A [=constructor string parser=] has an associated <dfn export for="constructor s <p>First, the URLPattern constructor string parser operates on [=tokens=] generated using the "`lenient`" [=tokenize policy=]. In constrast, [=basic URL parser=] operates on code points. Operating on [=tokens=] allows the URLPattern constructor string parser to more easily distinguish between code points that are significant pattern syntax and code points that might be a URL component separator. For example, it makes it trivial to handle named groups like "`:hmm`" in "`https://a.c:hmm.example.com:8080`" without getting confused with the port number. <p>Second, the URLPattern constructor string parser needs to avoid applying URL canonicalization to all code points like [=basic URL parser=] does. Instead we perform canonicalization on only parts of the pattern string we know are safe later when compiling each component pattern string. <p>Finally, the URLPattern constructor string parser does not handle some parts of the [=basic URL parser=] state machine. For example, it does not treat backslashes specially as they would all be treated as pattern characters and would require excessive escaping. In addition, this parser might not handle some more esoteric parts of the URL parsing algorithm like file URLs with a hostname. The goal with this parser was to handle the most common URLs while allowing any niche case to be handled instead via the {{URLPatternInit}} constructor. +</div> + +<div class=note> <p>In the constructor string algorithm, the pathname, search, and hash are wildcarded if earlier components are specified but later ones are not. For example, "`https://example.com/foo`" matches any search and any hash. Similarly, "`https://example.com`" matches any URL on that origin. This is analogous to the notion of a more specific component in the notes about [=process a URLPatternInit=] (e.g., a search is more specific than a pathname), but the constructor syntax only has a few cases where it is possible to specify a more specific component without also specifying the less specific components. <p>The username and password components are always wildcard unless they are explicitly specified. + <p>If a hostname is specified and the port is not, the port is assumed to be the default port. If any port should match, authors can write `:*` explicitly. For example, "`https://*`" is any HTTPS origin on port 443, and "`https://*:*`" is any HTTPS origin on any port. </div> <div algorithm> @@ -1779,7 +1783,7 @@ To <dfn>convert a modifier to a string</dfn> given a [=part/modifier=] |modifier * protocol, hostname, port, pathname, search, hash * protocol, hostname, port, username, password - Username and password are also never inherited from a base URL when constructing a {{URLPattern}}. (They are, however, inherited from the base URL supplied as an argument to {{URLPattern/test()}} or {{URLPattern/exec()}}.) + Username and password are also never inherited from a base URL when constructing a {{URLPattern}}. (They are, however, inherited from the base URL when parsing a URL supplied as an argument to {{URLPattern/test()}} or {{URLPattern/exec()}}.) </div> 1. Set |baseURL| to the result of [=URL parser|parsing=] |init|["{{URLPatternInit/baseURL}}"]. 1. If |baseURL| is failure, then throw a {{TypeError}}. @@ -1791,11 +1795,11 @@ To <dfn>convert a modifier to a string</dfn> given a [=part/modifier=] |modifier 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", and "{{URLPatternInit/pathname}}", then set |result|["{{URLPatternInit/pathname}}"] to the result of [=processing a base URL string=] given the result of [=URL path serializing=] |baseURL| and |type|. 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/pathname}}", and "{{URLPatternInit/search}}", then set |result|["{{URLPatternInit/search}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/query=] and |type|. 1. If |init| [=map/contains=] none of "{{URLPatternInit/protocol}}", "{{URLPatternInit/hostname}}", "{{URLPatternInit/port}}", "{{URLPatternInit/pathname}}", "{{URLPatternInit/search}}", and "{{URLPatternInit/hash}}", then set |result|["{{URLPatternInit/hash}}"] to the result of [=processing a base URL string=] given |baseURL|'s [=url/fragment=] and |type|. - 1. If |init|["{{URLPatternInit/protocol}}"] [=map/exists=] then set |result|["{{URLPatternInit/protocol}}"] to the result of [=process protocol for init=] given |init|["{{URLPatternInit/protocol}}"] and |type|. - 1. If |init|["{{URLPatternInit/username}}"] [=map/exists=] then set |result|["{{URLPatternInit/username}}"] to the result of [=process username for init=] given |init|["{{URLPatternInit/username}}"] and |type|. - 1. If |init|["{{URLPatternInit/password}}"] [=map/exists=] then set |result|["{{URLPatternInit/password}}"] to the result of [=process password for init=] given |init|["{{URLPatternInit/password}}"] and |type|. - 1. If |init|["{{URLPatternInit/hostname}}"] [=map/exists=] then set |result|["{{URLPatternInit/hostname}}"] to the result of [=process hostname for init=] given |init|["{{URLPatternInit/hostname}}"] and |type|. - 1. If |init|["{{URLPatternInit/port}}"] [=map/exists=] then set |result|["{{URLPatternInit/port}}"] to the result of [=process port for init=] given |init|["{{URLPatternInit/port}}"], |result|["{{URLPatternInit/protocol}}"], and |type|. + 1. If |init|["{{URLPatternInit/protocol}}"] [=map/exists=], then set |result|["{{URLPatternInit/protocol}}"] to the result of [=process protocol for init=] given |init|["{{URLPatternInit/protocol}}"] and |type|. + 1. If |init|["{{URLPatternInit/username}}"] [=map/exists=], then set |result|["{{URLPatternInit/username}}"] to the result of [=process username for init=] given |init|["{{URLPatternInit/username}}"] and |type|. + 1. If |init|["{{URLPatternInit/password}}"] [=map/exists=], then set |result|["{{URLPatternInit/password}}"] to the result of [=process password for init=] given |init|["{{URLPatternInit/password}}"] and |type|. + 1. If |init|["{{URLPatternInit/hostname}}"] [=map/exists=], then set |result|["{{URLPatternInit/hostname}}"] to the result of [=process hostname for init=] given |init|["{{URLPatternInit/hostname}}"] and |type|. + 1. If |init|["{{URLPatternInit/port}}"] [=map/exists=], then set |result|["{{URLPatternInit/port}}"] to the result of [=process port for init=] given |init|["{{URLPatternInit/port}}"], |result|["{{URLPatternInit/protocol}}"], and |type|. 1. If |init|["{{URLPatternInit/pathname}}"] [=map/exists=]: 1. Set |result|["{{URLPatternInit/pathname}}"] to |init|["{{URLPatternInit/pathname}}"]. 1. If the following are all true: