Bug 1312690: Speed up MatchPattern matching. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Wed, 02 Nov 2016 13:45:01 -0700
changeset 432817 0f1d3bad5005996f8e6edf3a27d1d214120b5976
parent 432816 4e946e7685bcc7863a9aef8edb0d9bdd1cc296f5
child 432818 a8b91bd31af109b3ac109b8a9125e0dc42b05153
push id34435
push usermaglione.k@gmail.com
push dateWed, 02 Nov 2016 20:58:46 +0000
reviewersaswan
bugs1312690
milestone52.0a1
Bug 1312690: Speed up MatchPattern matching. r?aswan MozReview-Commit-ID: 2QmKnG0aBQw
toolkit/modules/addons/MatchPattern.jsm
--- a/toolkit/modules/addons/MatchPattern.jsm
+++ b/toolkit/modules/addons/MatchPattern.jsm
@@ -36,70 +36,69 @@ function globToRegexp(pat, allowQuestion
   return new RegExp("^" + pat + "$");
 }
 
 // These patterns follow the syntax in
 // https://developer.chrome.com/extensions/match_patterns
 function SingleMatchPattern(pat) {
   if (pat == "<all_urls>") {
     this.schemes = PERMITTED_SCHEMES;
-    this.host = "*";
-    this.path = new RegExp(".*");
+    this.hostMatch = () => true;
+    this.pathMatch = () => true;
   } else if (!pat) {
     this.schemes = [];
   } else {
     let re = new RegExp(`^(${PERMITTED_SCHEMES_REGEXP}|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$`);
     let match = re.exec(pat);
     if (!match) {
       Cu.reportError(`Invalid match pattern: '${pat}'`);
       this.schemes = [];
       return;
     }
 
     if (match[1] == "*") {
       this.schemes = ["http", "https"];
     } else {
       this.schemes = [match[1]];
     }
-    this.host = match[2];
-    this.path = globToRegexp(match[3], false);
 
     // We allow the host to be empty for file URLs.
-    if (this.host == "" && this.schemes[0] != "file") {
+    if (match[2] == "" && this.schemes[0] != "file") {
       Cu.reportError(`Invalid match pattern: '${pat}'`);
       this.schemes = [];
       return;
     }
+
+    this.host = match[2];
+    this.hostMatch = this.getHostMatcher(match[2]);
+
+    let pathMatch = globToRegexp(match[3], false);
+    this.pathMatch = pathMatch.test.bind(pathMatch);
   }
 }
 
 SingleMatchPattern.prototype = {
-  matches(uri, ignorePath = false) {
-    if (!this.schemes.includes(uri.scheme)) {
-      return false;
-    }
-
+  getHostMatcher(host) {
     // This code ignores the port, as Chrome does.
-    if (this.host == "*") {
-      // Don't check anything.
-    } else if (this.host[0] == "*") {
-      // It must be *.foo. We also need to allow foo by itself.
-      let suffix = this.host.substr(2);
-      if (uri.host != suffix && !uri.host.endsWith("." + suffix)) {
-        return false;
-      }
-    } else if (this.host != uri.host) {
-      return false;
+    if (host == "*") {
+      return () => true;
     }
+    if (host.startsWith("*.")) {
+      let suffix = host.substr(2);
+      let dotSuffix = "." + suffix;
 
-    if (!ignorePath && !this.path.test(uri.path)) {
-      return false;
+      return ({host}) => host === suffix || host.endsWith(dotSuffix);
     }
+    return uri => uri.host === host;
+  },
 
-    return true;
+  matches(uri, ignorePath = false) {
+    return (this.schemes.includes(uri.scheme) &&
+            this.hostMatch(uri) &&
+            (ignorePath || this.pathMatch(uri.path)));
   },
 };
 
 this.MatchPattern = function(pat) {
   this.pat = pat;
   if (!pat) {
     this.matchers = [];
   } else if (pat instanceof String || typeof(pat) == "string") {
@@ -107,31 +106,21 @@ this.MatchPattern = function(pat) {
   } else {
     this.matchers = pat.map(p => new SingleMatchPattern(p));
   }
 };
 
 MatchPattern.prototype = {
   // |uri| should be an nsIURI.
   matches(uri) {
-    for (let matcher of this.matchers) {
-      if (matcher.matches(uri)) {
-        return true;
-      }
-    }
-    return false;
+    return this.matchers.some(matcher => matcher.matches(uri));
   },
 
   matchesIgnoringPath(uri) {
-    for (let matcher of this.matchers) {
-      if (matcher.matches(uri, true)) {
-        return true;
-      }
-    }
-    return false;
+    return this.matchers.some(matcher => matcher.matches(uri, true));
   },
 
   // Checks that this match pattern grants access to read the given
   // cookie. |cookie| should be an |nsICookie2| instance.
   matchesCookie(cookie) {
     // First check for simple matches.
     let secureURI = NetUtil.newURI(`https://${cookie.rawHost}/`);
     if (this.matchesIgnoringPath(secureURI)) {