tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestURLPatternGlue.cpp (32116B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "gtest/gtest.h"
      7 #include "gtest/MozGTestBench.h"  // For MOZ_GTEST_BENCH
      8 #include "mozilla/net/URLPatternGlue.h"
      9 #include "mozilla/net/urlpattern_glue.h"
     10 
     11 using namespace mozilla::net;
     12 
     13 template <typename T>
     14 using Optional = mozilla::Maybe<T>;
     15 
     16 // pattern construction from string
     17 TEST(TestURLPatternGlue, PatternFromString)
     18 {
     19  nsCString str(":café://:foo");
     20  UrlpPattern pattern{};
     21  UrlpOptions options = {.ignore_case = false};
     22  bool res = urlp_parse_pattern_from_string(&str, nullptr, options, &pattern);
     23  ASSERT_TRUE(res);
     24  ASSERT_TRUE(pattern._0);
     25 }
     26 
     27 TEST(TestURLPatternGlue, PatternFromStringOnlyPathname)
     28 {
     29  nsCString str("/foo/thing");
     30  UrlpPattern pattern{};
     31  UrlpOptions options = {.ignore_case = false};
     32  bool res = urlp_parse_pattern_from_string(&str, nullptr, options, &pattern);
     33  ASSERT_FALSE(res);
     34  ASSERT_FALSE(pattern._0);
     35 }
     36 
     37 bool operator==(const UrlpInnerMatcher& a, const UrlpInnerMatcher& b) {
     38  if (a.inner_type != b.inner_type) {
     39    return false;
     40  }
     41 
     42  switch (a.inner_type) {
     43    case UrlpInnerMatcherType::Literal: {
     44      return a.literal.Equals(b.literal);
     45    }
     46    case UrlpInnerMatcherType::SingleCapture: {
     47      if (a.allow_empty == b.allow_empty &&
     48          a.filter_exists == b.filter_exists) {
     49        if (a.filter_exists) {
     50          return a.filter == b.filter;
     51        }
     52        return true;
     53      }
     54      return false;
     55    }
     56    case UrlpInnerMatcherType::RegExp: {
     57      return a.regexp.Equals(b.regexp);
     58    }
     59    default: {
     60      return false;
     61    }
     62  }
     63 }
     64 
     65 bool operator==(const UrlpMatcher& a, const UrlpMatcher& b) {
     66  return a.prefix.Equals(b.prefix) && a.suffix.Equals(b.suffix) &&
     67         a.inner == b.inner;
     68 }
     69 
     70 UrlpInit CreateInit(const nsCString& protocol, const nsCString& username,
     71                    const nsCString& password, const nsCString& hostname,
     72                    const nsCString& port, const nsCString& pathname,
     73                    const nsCString& search, const nsCString& hash,
     74                    const nsCString& baseUrl) {
     75  return UrlpInit{
     76      .protocol = CreateMaybeString(protocol, !protocol.IsEmpty()),
     77      .username = CreateMaybeString(username, !username.IsEmpty()),
     78      .password = CreateMaybeString(password, !password.IsEmpty()),
     79      .hostname = CreateMaybeString(hostname, !hostname.IsEmpty()),
     80      .port = CreateMaybeString(port, !port.IsEmpty()),
     81      .pathname = CreateMaybeString(pathname, !pathname.IsEmpty()),
     82      .search = CreateMaybeString(search, !search.IsEmpty()),
     83      .hash = CreateMaybeString(hash, !hash.IsEmpty()),
     84      .base_url = CreateMaybeString(baseUrl, !baseUrl.IsEmpty()),
     85  };
     86 }
     87 
     88 UrlpInit CreateSimpleInit(const nsCString& protocol, const nsCString& hostname,
     89                          const nsCString& pathname) {
     90  return CreateInit(protocol, ""_ns, ""_ns, hostname, ""_ns, pathname, ""_ns,
     91                    ""_ns, ""_ns);
     92 }
     93 
     94 UrlpInit CreateInit(const char* protocol, const char* username,
     95                    const char* password, const char* hostname,
     96                    const char* port, const char* pathname, const char* search,
     97                    const char* hash, const char* base = "") {
     98  return CreateInit(nsCString(protocol), nsCString(username),
     99                    nsCString(password), nsCString(hostname), nsCString(port),
    100                    nsCString(pathname), nsCString(search), nsCString(hash),
    101                    nsCString(base));
    102 }
    103 
    104 // pattern construction from init
    105 TEST(TestURLPatternGlue, PatternFromInit)
    106 {
    107  UrlpPattern pattern{};
    108  UrlpOptions options = {.ignore_case = false};
    109  UrlpInit init = CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns);
    110  bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    111  ASSERT_TRUE(res);
    112  ASSERT_TRUE(pattern._0);
    113 
    114  auto proto = UrlpGetProtocol(pattern);
    115  ASSERT_EQ(proto, "https"_ns);
    116 }
    117 
    118 TEST(TestURLPatternGlue, PatternFromInitOnlyPathname)
    119 {
    120  UrlpPattern pattern{};
    121  UrlpOptions options = {.ignore_case = false};
    122  UrlpInit init = CreateSimpleInit(""_ns, ""_ns, "/foo/thing"_ns);
    123  bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    124  ASSERT_TRUE(res);
    125  ASSERT_TRUE(pattern._0);
    126 
    127  auto proto = UrlpGetProtocol(pattern);
    128  ASSERT_EQ(proto, nsCString("*"));
    129  auto host = UrlpGetHostname(pattern);
    130  ASSERT_EQ(host, nsCString("*"));
    131  auto path = UrlpGetPathname(pattern);
    132  ASSERT_EQ(path, nsCString("/foo/thing"));
    133 
    134  Optional<nsAutoCString> execBaseUrl;  // None
    135  UrlpInput input = CreateUrlpInput(init);
    136  Optional<UrlpResult> r = UrlpPatternExec(pattern, input, execBaseUrl);
    137  ASSERT_TRUE(r.isSome());
    138  ASSERT_TRUE(r->mProtocol.isSome());
    139  ASSERT_EQ(r->mProtocol.value().mInput, nsCString(""));
    140  ASSERT_TRUE(r->mPathname.isSome());
    141  ASSERT_EQ(r->mPathname.value().mInput, "/foo/thing"_ns);
    142 }
    143 
    144 // pattern getters
    145 TEST(TestURLPatternGlue, UrlPatternGetters)
    146 {
    147  UrlpPattern pattern{};
    148  UrlpOptions options = {.ignore_case = false};
    149 
    150  UrlpInit init =
    151      CreateInit("https"_ns, "user"_ns, "passw"_ns, "example.com"_ns, "66"_ns,
    152                 "/"_ns, "find"_ns, "anchor"_ns, ""_ns);
    153  bool rv = urlp_parse_pattern_from_init(&init, options, &pattern);
    154  ASSERT_TRUE(rv);
    155  ASSERT_TRUE(pattern._0);
    156 
    157  nsAutoCString res;
    158  res = UrlpGetProtocol(pattern);
    159  ASSERT_EQ(res, nsCString("https"));
    160  res = UrlpGetUsername(pattern);
    161  ASSERT_EQ(res, nsCString("user"));
    162  res = UrlpGetPassword(pattern);
    163  ASSERT_EQ(res, nsCString("passw"));
    164  res = UrlpGetHostname(pattern);
    165  ASSERT_EQ(res, nsCString("example.com"));
    166  res = UrlpGetPort(pattern);
    167  ASSERT_EQ(res, nsCString("66"));
    168  res = UrlpGetPathname(pattern);
    169  ASSERT_EQ(res, nsCString("/"));
    170  res = UrlpGetSearch(pattern);
    171  ASSERT_EQ(res, nsCString("find"));
    172  res = UrlpGetHash(pattern);
    173  ASSERT_EQ(res, nsCString("anchor"));
    174  // neither lib or quirks URLPattern has base_url so nothing to check
    175 }
    176 
    177 // UrlPattern.test() from_init
    178 TEST(TestURLPatternGlue, UrlPatternTestInit)
    179 {
    180  // check basic literal matching (minimal fields)
    181  {
    182    UrlpPattern pattern{};
    183    UrlpOptions options = {.ignore_case = false};
    184    UrlpInit init = CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns);
    185    bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    186    ASSERT_TRUE(res);
    187 
    188    Optional<nsCString> base;
    189    {  // path not fixed up (?)
    190      auto test = CreateUrlpInput(
    191          CreateSimpleInit("https"_ns, "example.com"_ns, ""_ns));
    192      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    193    }
    194    {
    195      auto test = CreateUrlpInput(
    196          CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns));
    197      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    198    }
    199    {  // unspecified user and password is fine
    200      auto test = CreateUrlpInput(
    201          CreateInit("https", "user", "pass", "example.com", "", "/", "", ""));
    202      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    203    }
    204    {  // unspecified port is fine
    205      auto test = CreateUrlpInput(
    206          CreateInit("https", "", "", "example.com", "444", "/", "", ""));
    207      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    208    }
    209    {  // unspecified search is fine
    210      auto test = CreateUrlpInput(
    211          CreateInit("https", "", "", "example.com", "", "/", "thisok", ""));
    212      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    213    }
    214    {  // unspecified hash is fine
    215      auto test = CreateUrlpInput(
    216          CreateInit("https", "", "", "example.com", "", "/", "", "thisok"));
    217      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    218    }
    219    {  // pathname different
    220      auto test = CreateUrlpInput(
    221          CreateSimpleInit("https"_ns, "example.com"_ns, "/a"_ns));
    222      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    223    }
    224    {  // scheme different
    225      auto test = CreateUrlpInput(
    226          CreateSimpleInit("http"_ns, "example.com"_ns, "/"_ns));
    227      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    228    }
    229    {  // domain different
    230      auto test = CreateUrlpInput(
    231          CreateSimpleInit("https"_ns, "example.org"_ns, "/"_ns));
    232      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    233    }
    234  }
    235 
    236  // check basic literal matching
    237  {
    238    UrlpPattern pattern{};
    239    UrlpOptions options = {.ignore_case = false};
    240    auto init =
    241        CreateInit("https"_ns, "user"_ns, "anything"_ns, "example.com"_ns,
    242                   "444"_ns, "/"_ns, "query"_ns, "frag"_ns, ""_ns);
    243    bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    244    ASSERT_TRUE(res);
    245 
    246    nsCString anything("anything");
    247    Optional<nsCString> base;
    248    {  // exact match
    249      auto test = CreateUrlpInput(init);
    250      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    251    }
    252    {  // missing protocol
    253      auto test =
    254          CreateUrlpInput(CreateInit("", "user", anything.get(), "example.com",
    255                                     "444", "/", "query", "frag"));
    256      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    257    }
    258    {  // missing user
    259      auto test =
    260          CreateUrlpInput(CreateInit("https", "", anything.get(), "example.com",
    261                                     "444", "/", "query", "frag"));
    262      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    263    }
    264    {  // missing password
    265      auto test = CreateUrlpInput(CreateInit("https", "user", "", "example.com",
    266                                             "444", "/", "query", "frag"));
    267      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    268    }
    269    {  // missing hostname
    270      auto test = CreateUrlpInput(CreateInit("https", "user", anything.get(),
    271                                             "", "444", "/", "query", "frag"));
    272      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    273    }
    274    {  // missing port
    275      auto test =
    276          CreateUrlpInput(CreateInit("https", "user", anything.get(),
    277                                     "example.com", "", "/", "query", "frag"));
    278      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    279    }
    280    {  // missing query
    281      auto test =
    282          CreateUrlpInput(CreateInit("https", "user", anything.get(),
    283                                     "example.com", "444", "/", "", "frag"));
    284      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    285    }
    286    {  // missing frag
    287      auto test =
    288          CreateUrlpInput(CreateInit("https", "user", anything.get(),
    289                                     "example.com", "444", "/", "query", ""));
    290      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    291    }
    292  }
    293 
    294  // check basic url with wildcard
    295  {
    296    UrlpPattern pattern{};
    297    UrlpOptions options = {.ignore_case = false};
    298    auto init = CreateSimpleInit("https"_ns, "example.com"_ns, "/*"_ns);
    299    bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    300    ASSERT_TRUE(res);
    301 
    302    Optional<nsCString> base;
    303    {  // root path matches wildcard
    304      auto test = CreateUrlpInput(
    305          CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns));
    306      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    307    }
    308    {  // filename matches wildcard
    309      auto test = CreateUrlpInput(
    310          CreateSimpleInit("https"_ns, "example.com"_ns, "/thing"_ns));
    311      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    312    }
    313    {  // dir/filename matches wildcard
    314      auto test = CreateUrlpInput(
    315          CreateSimpleInit("https"_ns, "example.com"_ns, "/dir/thing"_ns));
    316      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    317    }
    318  }
    319 
    320  // check matching in pathname (needs to be at least two slashes)
    321  {
    322    UrlpPattern pattern{};
    323    UrlpOptions options = {.ignore_case = false};
    324    auto init =
    325        CreateSimpleInit("https"_ns, "example.com"_ns, "/:category/*"_ns);
    326    bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    327    ASSERT_TRUE(res);
    328 
    329    Optional<nsCString> base;
    330    {  // no directory and not enough slashes
    331      auto test = CreateUrlpInput(
    332          CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns));
    333      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    334    }
    335    {  // no directory
    336      auto test = CreateUrlpInput(
    337          CreateSimpleInit("https"_ns, "example.com"_ns, "//"_ns));
    338      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    339    }
    340    {  // not enough slashes
    341      auto test = CreateUrlpInput(
    342          CreateSimpleInit("https"_ns, "example.com"_ns, "/products"_ns));
    343      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    344    }
    345    {  // dir/ works
    346      auto test = CreateUrlpInput(
    347          CreateSimpleInit("https"_ns, "example.com"_ns, "/products/"_ns));
    348      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    349    }
    350    {  // diretory/filename
    351      auto test = CreateUrlpInput(
    352          CreateSimpleInit("https"_ns, "example.com"_ns, "/blog/thing"_ns));
    353      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    354    }
    355    {  // nested directory
    356      auto test = CreateUrlpInput(
    357          CreateSimpleInit("https"_ns, "example.com"_ns, "/blog/thing/"_ns));
    358      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    359    }
    360  }
    361 
    362  // check optional `s` in protocol
    363  {
    364    UrlpPattern pattern{};
    365    UrlpOptions options = {.ignore_case = false};
    366    auto init = CreateSimpleInit("http{s}?"_ns, "example.com"_ns, "/"_ns);
    367    bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    368    ASSERT_TRUE(res);
    369 
    370    Optional<nsCString> base;
    371    {  // insecure matches
    372      auto test = CreateUrlpInput(
    373          CreateSimpleInit("http"_ns, "example.com"_ns, "/"_ns));
    374      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    375    }
    376    {  // secure matches
    377      auto test = CreateUrlpInput(
    378          CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns));
    379      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    380    }
    381  }
    382 
    383  // basic relative wildcard path with base domain
    384  {
    385    UrlpPattern pattern{};
    386    UrlpOptions options = {.ignore_case = false};
    387    auto init = CreateInit(""_ns, ""_ns, ""_ns, ""_ns, ""_ns, "/admin/*"_ns,
    388                           ""_ns, ""_ns, "https://example.com"_ns);
    389    bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    390    ASSERT_TRUE(res);
    391 
    392    Optional<nsCString> base;
    393    {
    394      auto test = CreateUrlpInput(
    395          CreateSimpleInit("https"_ns, "example.com"_ns, "/admin/"_ns));
    396      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    397    }
    398    {
    399      auto test = CreateUrlpInput(
    400          CreateSimpleInit("https"_ns, "example.com"_ns, "/admin/thing"_ns));
    401      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    402    }
    403    {  // incorrect relative path doesn't match
    404      //
    405      auto test = CreateUrlpInput(
    406          CreateSimpleInit("https"_ns, "example.com"_ns, "/nonadmin/"_ns));
    407      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    408    }
    409    {  // root path not matching relative path doesn't match
    410      auto test = CreateUrlpInput(
    411          CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns));
    412      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    413    }
    414  }
    415 }
    416 
    417 // UrlPattern.test() from_string
    418 TEST(TestURLPatternGlue, UrlPatternTestString)
    419 {
    420  // check basic literal matching (minimal fields)
    421  {
    422    nsCString str("https://example.com/");
    423    UrlpPattern pattern{};
    424    UrlpOptions options = {.ignore_case = false};
    425    bool res = urlp_parse_pattern_from_string(&str, nullptr, options, &pattern);
    426    ASSERT_TRUE(res);
    427 
    428    Optional<nsCString> base;
    429    {  // path fixed up "/"
    430      auto test = CreateUrlpInput("https://example.com"_ns);
    431      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    432    }
    433    {
    434      auto test = CreateUrlpInput("https://example.com/"_ns);
    435      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    436    }
    437    {  // unspecified user and password is fine
    438      auto test = CreateUrlpInput("https://user:passw@example.com"_ns);
    439      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    440    }
    441    {  // unspecified port is empty so 444 doesn't match
    442      auto test = CreateUrlpInput("https://example.com:444/"_ns);
    443      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    444    }
    445    {  // unspecified search is fine
    446      auto test = CreateUrlpInput("https://example.com/?thisok"_ns);
    447      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    448    }
    449    {  // unspecified hash is fine
    450      auto test = CreateUrlpInput("https://example.com/#thisok"_ns);
    451      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    452    }
    453    {  // pathname different
    454      auto test = CreateUrlpInput("https://example.com/a"_ns);
    455      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    456    }
    457    {  // scheme different
    458      auto test = CreateUrlpInput("http://example.com/"_ns);
    459      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    460    }
    461    {  // domain different
    462      auto test = CreateUrlpInput("http://example.org"_ns);
    463      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    464    }
    465  }
    466 
    467  // check basic literal matching (all fields, except password)
    468  // because user:pass is parsed as: `username: user:pass, password: *`
    469  // when pattern is from_string
    470  {
    471    nsCString str("https://user:*@example.com:444/?query#frag");
    472    UrlpPattern pattern{};
    473    UrlpOptions options = {.ignore_case = false};
    474    nsCString baseUrl("");
    475    bool res =
    476        urlp_parse_pattern_from_string(&str, &baseUrl, options, &pattern);
    477    ASSERT_TRUE(res);
    478 
    479    Optional<nsCString> base;
    480    {  // exact match, except password
    481      auto test = CreateUrlpInput(
    482          "https://user:anything@example.com:444/?query#frag"_ns);
    483      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    484    }
    485    {  // missing protocol
    486      auto test =
    487          CreateUrlpInput("user:anything@example.com:444/?query#frag"_ns);
    488      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    489    }
    490    {  // missing user
    491      auto test =
    492          CreateUrlpInput("https://:anything@example.com:444/?query#frag"_ns);
    493      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    494    }
    495    {  // missing password is fine
    496      auto test =
    497          CreateUrlpInput("https://user@example.com:444/?query#frag"_ns);
    498      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    499    }
    500    {  // missing password is fine
    501      auto test =
    502          CreateUrlpInput("https://user@example.com:444/?query#frag"_ns);
    503      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    504    }
    505    {  // missing hostname
    506      auto test = CreateUrlpInput("https://user:anything@:444/?query#frag"_ns);
    507      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    508    }
    509    {  // missing port
    510      auto test =
    511          CreateUrlpInput("https://user:anything@example.com/?query#frag"_ns);
    512      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    513    }
    514    {  // missing query
    515      auto test =
    516          CreateUrlpInput("https://user:anything@example.com:444/#frag"_ns);
    517      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    518    }
    519    {  // missing frag
    520      auto test =
    521          CreateUrlpInput("https://user:anything@example.com:444/?query"_ns);
    522      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    523    }
    524  }
    525 
    526  // check basic url with wildcard
    527  {
    528    nsCString str("https://example.com/*");
    529    UrlpPattern pattern{};
    530    UrlpOptions options = {.ignore_case = false};
    531    nsCString baseUrl("");
    532    bool res =
    533        urlp_parse_pattern_from_string(&str, &baseUrl, options, &pattern);
    534    ASSERT_TRUE(res);
    535 
    536    Optional<nsCString> base;
    537    {
    538      auto test = CreateUrlpInput("https://example.com/"_ns);
    539      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    540    }
    541    {
    542      auto test = CreateUrlpInput("https://example.com/thing"_ns);
    543      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    544    }
    545    {
    546      auto test = CreateUrlpInput("https://example.com/dir/thing"_ns);
    547      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    548    }
    549  }
    550 
    551  // check matching in pathname (needs to be at least two slashes)
    552  {
    553    nsCString str("https://example.com/:category/*");
    554    UrlpPattern pattern{};
    555    UrlpOptions options = {.ignore_case = false};
    556    nsCString baseUrl("");
    557    bool res =
    558        urlp_parse_pattern_from_string(&str, &baseUrl, options, &pattern);
    559    ASSERT_TRUE(res);
    560 
    561    Optional<nsCString> base;
    562    {
    563      auto test = CreateUrlpInput("https://example.com/"_ns);
    564      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    565    }
    566    {  // not enough slashes
    567      auto test = CreateUrlpInput("https://example.com/products"_ns);
    568      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    569    }
    570    {
    571      auto test = CreateUrlpInput("https://example.com/products/"_ns);
    572      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    573    }
    574    {
    575      auto test = CreateUrlpInput("https://example.com/blog/thing"_ns);
    576      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    577    }
    578    {  // 3 slashes
    579      auto test = CreateUrlpInput("https://example.com/blog/thing/"_ns);
    580      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    581    }
    582  }
    583 
    584  // check optional `s` in protocol
    585  {
    586    nsCString str("http{s}?://example.com/");
    587    UrlpPattern pattern{};
    588    UrlpOptions options = {.ignore_case = false};
    589    nsCString baseUrl("");
    590    bool res =
    591        urlp_parse_pattern_from_string(&str, &baseUrl, options, &pattern);
    592    ASSERT_TRUE(res);
    593 
    594    Optional<nsCString> base;
    595    {
    596      auto test = CreateUrlpInput("http://example.com/"_ns);
    597      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    598    }
    599    {
    600      auto test = CreateUrlpInput("https://example.com/"_ns);
    601      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    602    }
    603  }
    604 
    605  // basic relative wildcard path with base domain
    606  {
    607    nsCString str("../admin/*");
    608    UrlpPattern pattern{};
    609    UrlpOptions options = {.ignore_case = false};
    610    nsCString baseUrl("https://example.com/forum");
    611    // MaybeString baseUrl {.string = "https://example.com/forum"_ns, .valid =
    612    // true };
    613    bool res =
    614        urlp_parse_pattern_from_string(&str, &baseUrl, options, &pattern);
    615    ASSERT_TRUE(res);
    616 
    617    Optional<nsCString> base;
    618    {
    619      auto test = CreateUrlpInput("https://example.com/admin/"_ns);
    620      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    621    }
    622    {
    623      auto test = CreateUrlpInput("https://example.com/admin/thing"_ns);
    624      ASSERT_TRUE(UrlpPatternTest(pattern, test, base));
    625    }
    626    {
    627      auto test = CreateUrlpInput("https://example.com/nonadmin/"_ns);
    628      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    629    }
    630    {
    631      auto test = CreateUrlpInput("https://example.com/"_ns);
    632      ASSERT_FALSE(UrlpPatternTest(pattern, test, base));
    633    }
    634  }
    635 }
    636 
    637 TEST(TestURLPatternGlue, MatchInputFromString)
    638 {
    639  {
    640    nsCString url("https://example.com/");
    641    UrlpMatchInputAndInputs matchInputAndInputs;
    642    bool res = urlp_process_match_input_from_string(&url, nullptr,
    643                                                    &matchInputAndInputs);
    644    ASSERT_TRUE(res);
    645    ASSERT_EQ(matchInputAndInputs.input.protocol, "https"_ns);
    646    ASSERT_EQ(matchInputAndInputs.input.hostname, "example.com"_ns);
    647    ASSERT_EQ(matchInputAndInputs.input.pathname, "/"_ns);
    648    ASSERT_EQ(matchInputAndInputs.input.username, "");
    649    ASSERT_EQ(matchInputAndInputs.input.password, "");
    650    ASSERT_EQ(matchInputAndInputs.input.port, "");
    651    ASSERT_EQ(matchInputAndInputs.input.search, "");
    652    ASSERT_EQ(matchInputAndInputs.input.hash, "");
    653    ASSERT_EQ(matchInputAndInputs.inputs.string_or_init_type,
    654              UrlpStringOrInitType::String);
    655    ASSERT_EQ(matchInputAndInputs.inputs.str, url);
    656    ASSERT_EQ(matchInputAndInputs.inputs.base.valid, false);
    657  }
    658  {
    659    nsCString expected("https://example.com/some/dir");
    660    nsCString base_url("https://example.com");
    661    nsCString relative_url("/some/dir");
    662    UrlpMatchInputAndInputs matchInputAndInputs;
    663    bool res = urlp_process_match_input_from_string(&relative_url, &base_url,
    664                                                    &matchInputAndInputs);
    665    ASSERT_TRUE(res);
    666    ASSERT_EQ(matchInputAndInputs.input.protocol, "https"_ns);
    667    ASSERT_EQ(matchInputAndInputs.input.hostname, "example.com"_ns);
    668    ASSERT_EQ(matchInputAndInputs.input.pathname, "/some/dir"_ns);
    669    ASSERT_EQ(matchInputAndInputs.input.username, "");
    670    ASSERT_EQ(matchInputAndInputs.input.password, "");
    671    ASSERT_EQ(matchInputAndInputs.input.port, "");
    672    ASSERT_EQ(matchInputAndInputs.input.search, "");
    673    ASSERT_EQ(matchInputAndInputs.input.hash, "");
    674    ASSERT_EQ(matchInputAndInputs.inputs.string_or_init_type,
    675              UrlpStringOrInitType::String);
    676    ASSERT_EQ(matchInputAndInputs.inputs.str, relative_url);
    677    ASSERT_EQ(matchInputAndInputs.inputs.base.string, base_url);
    678  }
    679 }
    680 
    681 void assert_maybe_string_same(const MaybeString& s1, const MaybeString& s2) {
    682  ASSERT_EQ(s1.valid, s2.valid);
    683  if (s1.valid) {
    684    ASSERT_EQ(s1.string, s2.string);
    685  }
    686 }
    687 
    688 void assert_inits_same(const UrlpInit& i1, const UrlpInit& i2) {
    689  assert_maybe_string_same(i1.protocol, i2.protocol);
    690  assert_maybe_string_same(i1.username, i2.username);
    691  assert_maybe_string_same(i1.password, i2.password);
    692  assert_maybe_string_same(i1.hostname, i2.hostname);
    693  assert_maybe_string_same(i1.port, i2.port);
    694  assert_maybe_string_same(i1.pathname, i2.pathname);
    695  assert_maybe_string_same(i1.search, i2.search);
    696  assert_maybe_string_same(i1.hash, i2.hash);
    697  assert_maybe_string_same(i1.base_url, i2.base_url);
    698 }
    699 
    700 void assert_match_inputs_same(const UrlpMatchInput& input,
    701                              const UrlpMatchInput& expected) {
    702  ASSERT_EQ(input.protocol, expected.protocol);
    703  ASSERT_EQ(input.hostname, expected.hostname);
    704  ASSERT_EQ(input.pathname, expected.pathname);
    705  ASSERT_EQ(input.username, expected.username);
    706  ASSERT_EQ(input.password, expected.password);
    707  ASSERT_EQ(input.port, expected.port);
    708  ASSERT_EQ(input.search, expected.search);
    709  ASSERT_EQ(input.hash, expected.hash);
    710 }
    711 
    712 UrlpMatchInput createMatchInputHelper(const nsCString& proto,
    713                                      const nsCString& host,
    714                                      const nsCString& path) {
    715  return {
    716      .protocol = proto,
    717      .username = ""_ns,
    718      .password = ""_ns,
    719      .hostname = host,
    720      .port = ""_ns,
    721      .pathname = path,
    722      .search = ""_ns,
    723      .hash = ""_ns,
    724  };
    725 }
    726 
    727 TEST(TestURLPatternGlue, MatchInputFromInit)
    728 {
    729  {  // no base init
    730    UrlpMatchInputAndInputs matchInputAndInputs;
    731    auto init = CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns);
    732    auto expected = CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns);
    733    bool res = urlp_process_match_input_from_init(&init, nullptr,
    734                                                  &matchInputAndInputs);
    735    ASSERT_TRUE(res);
    736 
    737    UrlpMatchInput expected_match_input =
    738        createMatchInputHelper("https"_ns, "example.com"_ns, "/"_ns);
    739    assert_match_inputs_same(matchInputAndInputs.input, expected_match_input);
    740    ASSERT_EQ(matchInputAndInputs.inputs.string_or_init_type,
    741              UrlpStringOrInitType::Init);
    742    assert_inits_same(matchInputAndInputs.inputs.init, init);
    743    ASSERT_EQ(matchInputAndInputs.inputs.str, ""_ns);
    744    ASSERT_EQ(matchInputAndInputs.inputs.base.valid, false);
    745  }
    746  {  // base + relative url produces expected match input
    747    nsCString expected_base_url("https://example.com");
    748 
    749    auto init = CreateInit("", "", "", "", "", "/some/dir", "", "",
    750                           "https://example.com");
    751    UrlpMatchInputAndInputs matchInputAndInputs;
    752    bool res = urlp_process_match_input_from_init(&init, nullptr,
    753                                                  &matchInputAndInputs);
    754    ASSERT_TRUE(res);
    755 
    756    UrlpMatchInput expected_match_input =
    757        createMatchInputHelper("https"_ns, "example.com"_ns, "/some/dir"_ns);
    758    assert_match_inputs_same(matchInputAndInputs.input, expected_match_input);
    759    ASSERT_EQ(matchInputAndInputs.inputs.string_or_init_type,
    760              UrlpStringOrInitType::Init);
    761    assert_inits_same(matchInputAndInputs.inputs.init, init);
    762    ASSERT_EQ(matchInputAndInputs.inputs.str, ""_ns);
    763    ASSERT_EQ(matchInputAndInputs.inputs.base.valid, false);
    764  }
    765 }
    766 
    767 void assert_matcher_same(UrlpMatcher& componentMatcher, UrlpMatcher& expected) {
    768  ASSERT_EQ(componentMatcher.prefix, expected.prefix);
    769  ASSERT_EQ(componentMatcher.suffix, expected.suffix);
    770  ASSERT_EQ(componentMatcher.inner.inner_type, expected.inner.inner_type);
    771  ASSERT_EQ(componentMatcher.inner.literal, expected.inner.literal);
    772  ASSERT_EQ(componentMatcher.inner.allow_empty, expected.inner.allow_empty);
    773  ASSERT_EQ(componentMatcher.inner.filter_exists, expected.inner.filter_exists);
    774  ASSERT_EQ(componentMatcher.inner.filter, expected.inner.filter);
    775  ASSERT_EQ(componentMatcher.inner.regexp, expected.inner.regexp);
    776  ASSERT_TRUE(componentMatcher == expected);
    777 }
    778 
    779 TEST(TestURLPatternGlue, UrlPatternGetComponentBasic)
    780 {
    781  nsCString str("http://:foo");
    782  UrlpOptions options = {.ignore_case = false};
    783  UrlpPattern pattern{};
    784  bool res = urlp_parse_pattern_from_string(&str, nullptr, options, &pattern);
    785  ASSERT_TRUE(res);
    786  ASSERT_TRUE(pattern._0);
    787 
    788  UrlpInnerMatcher expectedInnerMatcher = {
    789      .inner_type = UrlpInnerMatcherType::Literal,
    790      .literal = "http"_ns,
    791      .allow_empty = false,
    792      .filter_exists = false,
    793      .filter = 'x',
    794      .regexp = ""_ns,
    795  };
    796  UrlpMatcher expectedMatcher = {
    797      .prefix = ""_ns,
    798      .suffix = ""_ns,
    799      .inner = expectedInnerMatcher,
    800  };
    801  nsCString expectedPatternString("http");
    802  nsCString expectedRegexp("^http$");
    803 
    804  UrlpComponent componentProtocol{};
    805  auto protoStr = UrlpGetProtocol(pattern);
    806  ASSERT_EQ(protoStr, expectedPatternString);
    807 
    808  auto hostnameStr = UrlpGetHostname(pattern);
    809  nsCString expectedHostnamePatternString(":foo");
    810  nsCString expectedHostnameRegexp("^([^\\.]+?)$");
    811  expectedMatcher.inner.inner_type = UrlpInnerMatcherType::SingleCapture;
    812  expectedMatcher.inner.literal = ""_ns;
    813  expectedMatcher.inner.filter_exists = true;
    814  expectedMatcher.inner.filter = '.';
    815  ASSERT_EQ(hostnameStr, expectedHostnamePatternString);
    816 
    817  auto pathnameStr = UrlpGetPathname(pattern);
    818  nsCString expectedPathnamePatternString("*");
    819  nsCString expectedPathnameRegexp("^(.*)$");
    820  expectedMatcher.inner.filter = 'x';
    821  expectedMatcher.inner.allow_empty = true;
    822  ASSERT_EQ(pathnameStr, expectedPathnamePatternString);
    823 }
    824 
    825 void assert_pattern_result(UrlpResult& res) {
    826  ASSERT_TRUE(res.mProtocol.isSome());
    827  ASSERT_TRUE(res.mUsername.isSome());
    828  ASSERT_TRUE(res.mPassword.isSome());
    829  ASSERT_TRUE(res.mHostname.isSome());
    830  ASSERT_TRUE(res.mPort.isSome());
    831  ASSERT_TRUE(res.mPathname.isSome());
    832  ASSERT_TRUE(res.mSearch.isSome());
    833  ASSERT_TRUE(res.mHash.isSome());
    834  ASSERT_TRUE(res.mInputs.Length() == 1);
    835 }
    836 
    837 TEST(TestURLPatternGlue, UrlPatternExecFromString)
    838 {
    839  nsCString str(":café://:foo");
    840  UrlpOptions options = {.ignore_case = false};
    841  UrlpPattern pattern{};
    842  bool res = urlp_parse_pattern_from_string(&str, nullptr, options, &pattern);
    843  ASSERT_TRUE(res);
    844  ASSERT_TRUE(pattern._0);
    845 
    846  nsCString inputString("https://example.com/");
    847  UrlpInput input = CreateUrlpInput(inputString);
    848  Optional<nsAutoCString> execBaseUrl;
    849  Optional<UrlpResult> res2 = UrlpPatternExec(pattern, input, execBaseUrl);
    850 
    851  ASSERT_TRUE(res2.isNothing());
    852 }
    853 
    854 TEST(TestURLPatternGlue, UrlPatternExecFromInit)
    855 {
    856  UrlpPattern pattern{};
    857  auto init = CreateSimpleInit("https"_ns, "example.com"_ns, "/"_ns);
    858  UrlpOptions options = {.ignore_case = false};
    859  bool res = urlp_parse_pattern_from_init(&init, options, &pattern);
    860  ASSERT_TRUE(res);
    861  ASSERT_TRUE(pattern._0);
    862 
    863  UrlpInput input = CreateUrlpInput(init);
    864  Optional<nsAutoCString> execBaseUrl;
    865  Optional<UrlpResult> res2 = UrlpPatternExec(pattern, input, execBaseUrl);
    866  ASSERT_TRUE(res2.isSome());
    867  assert_pattern_result(*res2);
    868  ASSERT_EQ(res2->mProtocol->mInput, "https");
    869  ASSERT_EQ(res2->mUsername->mInput, ""_ns);
    870  ASSERT_EQ(res2->mPassword->mInput, ""_ns);
    871  ASSERT_EQ(res2->mHostname->mInput, "example.com");
    872  ASSERT_EQ(res2->mPort->mInput, ""_ns);
    873  ASSERT_EQ(res2->mPathname->mInput, "/"_ns);
    874  ASSERT_EQ(res2->mSearch->mInput, ""_ns);
    875  ASSERT_EQ(res2->mHash->mInput, ""_ns);
    876 }