Bug 1280370 - Properly parse MatchPattern for schemes with no authority r=mixedpuppy,zombie
authorRob Wu <rob@robwu.nl>
Tue, 24 Jul 2018 16:35:45 +0200
changeset 430584 0134a212426692593c68d6f40c718377f13e2087
parent 430583 b6cb27b7a5791193f6fbae68cc338e20a767c34e
child 430585 d01b3ac48bdc327764b63c21400e6abb7d53cb85
push id34409
push usertoros@mozilla.com
push dateThu, 09 Aug 2018 10:00:05 +0000
treeherdermozilla-central@eb9ff7de69ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy, zombie
bugs1280370
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1280370 - Properly parse MatchPattern for schemes with no authority r=mixedpuppy,zombie I found these schemes by enumerating all registered nsIProtocolHandler and checking whether they require a "//". The list of schemes in HOST_LOCATOR_SCHEMES only includes schemes that are known to Firefox. Any other scheme is parsed as if the separator is ":". For example, NetUtil.newURI("unknown-scheme://host/path") has its pathQueryRef member set to "//host/path". For the purpose of matching in MatchPattern, this unknown scheme has therefore no host and only a path. MozReview-Commit-ID: KGNRXGcZTZx
toolkit/components/extensions/MatchPattern.cpp
toolkit/components/extensions/test/xpcshell/test_MatchPattern.js
--- a/toolkit/components/extensions/MatchPattern.cpp
+++ b/toolkit/components/extensions/MatchPattern.cpp
@@ -260,16 +260,19 @@ CookieInfo::RawHost() const
 
 
 /*****************************************************************************
  * MatchPattern
  *****************************************************************************/
 
 const char* PERMITTED_SCHEMES[] = {"http", "https", "ws", "wss", "file", "ftp", "data", nullptr};
 
+// Known schemes that are followed by "://" instead of ":".
+const char* HOST_LOCATOR_SCHEMES[] = {"http", "https", "ws", "wss", "file", "ftp", "moz-extension", "chrome", "resource", "moz", "moz-icon", "moz-gio", nullptr};
+
 const char* WILDCARD_SCHEMES[] = {"http", "https", "ws", "wss", nullptr};
 
 /* static */ already_AddRefed<MatchPattern>
 MatchPattern::Constructor(dom::GlobalObject& aGlobal,
                           const nsAString& aPattern,
                           const MatchPatternOptions& aOptions,
                           ErrorResult& aRv)
 {
@@ -305,35 +308,39 @@ MatchPattern::Init(JSContext* aCx, const
    ***************************************************************************/
   int32_t index = aPattern.FindChar(':');
   if (index <= 0) {
     aRv.Throw(NS_ERROR_INVALID_ARG);
     return;
   }
 
   RefPtr<nsAtom> scheme = NS_AtomizeMainThread(StringHead(aPattern, index));
+  bool requireHostLocatorScheme = true;
   if (scheme == nsGkAtoms::_asterisk) {
     mSchemes = AtomSet::Get<WILDCARD_SCHEMES>();
   } else if (!aRestrictSchemes ||
              permittedSchemes->Contains(scheme) ||
              scheme == nsGkAtoms::moz_extension) {
     mSchemes = new AtomSet({scheme});
+    RefPtr<AtomSet> hostLocatorSchemes = AtomSet::Get<HOST_LOCATOR_SCHEMES>();
+    requireHostLocatorScheme = hostLocatorSchemes->Contains(scheme);
   } else {
     aRv.Throw(NS_ERROR_INVALID_ARG);
     return;
   }
 
   /***************************************************************************
    * Host
    ***************************************************************************/
   offset = index + 1;
   tail.Rebind(aPattern, offset);
 
-  if (scheme == nsGkAtoms::about || scheme == nsGkAtoms::data) {
-    // about: and data: URIs don't have hosts, so just match on the path.
+  if (!requireHostLocatorScheme) {
+    // Unrecognized schemes and some schemes such as about: and data: URIs
+    // don't have hosts, so just match on the path.
     // And so, ignorePath doesn't make sense for these matchers.
     aIgnorePath = false;
   } else {
     if (!StringHead(tail, 2).EqualsLiteral("//")) {
       aRv.Throw(NS_ERROR_INVALID_ARG);
       return;
     }
 
--- a/toolkit/components/extensions/test/xpcshell/test_MatchPattern.js
+++ b/toolkit/components/extensions/test/xpcshell/test_MatchPattern.js
@@ -135,16 +135,34 @@ add_task(async function test_MatchPatter
   pass({url: "about:reader?http://e.com/", pattern: ["about:reader*"], options: {ignorePath: true, restrictSchemes: false}});
   pass({url: "data:,", pattern: ["data:,*"], options: {ignorePath: true}});
 
   // Matchers for schems without host should still match even if the explicit (host) flag is set.
   pass({url: "about:reader?explicit", pattern: ["about:reader*"], options: {restrictSchemes: false}, explicit: true});
   pass({url: "about:reader?explicit", pattern: ["about:reader?explicit"], options: {restrictSchemes: false}, explicit: true});
   pass({url: "data:,explicit", pattern: ["data:,explicit"], explicit: true});
   pass({url: "data:,explicit", pattern: ["data:,*"], explicit: true});
+
+  // Matchers without "//" separator in the pattern.
+  pass({url: "data:text/plain;charset=utf-8,foo", pattern: ["data:*"]});
+  pass({url: "about:blank", pattern: ["about:*"], options: {restrictSchemes: false}});
+  pass({url: "view-source:https://example.com", pattern: ["view-source:*"], options: {restrictSchemes: false}});
+  invalid({pattern: ["chrome:*"], options: {restrictSchemes: false}});
+  invalid({pattern: "http:*"});
+
+  // Matchers for unrecognized schemes.
+  invalid({pattern: "unknown-scheme:*"});
+  pass({url: "unknown-scheme:foo", pattern: ["unknown-scheme:foo"], options: {restrictSchemes: false}});
+  pass({url: "unknown-scheme:foo", pattern: ["unknown-scheme:*"], options: {restrictSchemes: false}});
+  pass({url: "unknown-scheme://foo", pattern: ["unknown-scheme://foo"], options: {restrictSchemes: false}});
+  pass({url: "unknown-scheme://foo", pattern: ["unknown-scheme://*"], options: {restrictSchemes: false}});
+  pass({url: "unknown-scheme://foo", pattern: ["unknown-scheme:*"], options: {restrictSchemes: false}});
+  fail({url: "unknown-scheme://foo", pattern: ["unknown-scheme:foo"], options: {restrictSchemes: false}});
+  fail({url: "unknown-scheme:foo", pattern: ["unknown-scheme://foo"], options: {restrictSchemes: false}});
+  fail({url: "unknown-scheme:foo", pattern: ["unknown-scheme://*"], options: {restrictSchemes: false}});
 });
 
 add_task(async function test_MatchPattern_overlaps() {
   function test(filter, hosts, optional) {
     filter = Array.concat(filter);
     hosts = Array.concat(hosts);
     optional = Array.concat(optional);