michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "unstable" michael@0: }; michael@0: michael@0: const { URL } = require('../url'); michael@0: const cache = {}; michael@0: michael@0: function MatchPattern(pattern) { michael@0: if (cache[pattern]) return cache[pattern]; michael@0: michael@0: if (typeof pattern.test == "function") { michael@0: michael@0: // For compatibility with -moz-document rules, we require the RegExp's michael@0: // global, ignoreCase, and multiline flags to be set to false. michael@0: if (pattern.global) { michael@0: throw new Error("A RegExp match pattern cannot be set to `global` " + michael@0: "(i.e. //g)."); michael@0: } michael@0: if (pattern.ignoreCase) { michael@0: throw new Error("A RegExp match pattern cannot be set to `ignoreCase` " + michael@0: "(i.e. //i)."); michael@0: } michael@0: if (pattern.multiline) { michael@0: throw new Error("A RegExp match pattern cannot be set to `multiline` " + michael@0: "(i.e. //m)."); michael@0: } michael@0: michael@0: this.regexp = pattern; michael@0: } michael@0: else { michael@0: let firstWildcardPosition = pattern.indexOf("*"); michael@0: let lastWildcardPosition = pattern.lastIndexOf("*"); michael@0: if (firstWildcardPosition != lastWildcardPosition) michael@0: throw new Error("There can be at most one '*' character in a wildcard."); michael@0: michael@0: if (firstWildcardPosition == 0) { michael@0: if (pattern.length == 1) michael@0: this.anyWebPage = true; michael@0: else if (pattern[1] != ".") michael@0: throw new Error("Expected a *. string, got: " + pattern); michael@0: else michael@0: this.domain = pattern.substr(2); michael@0: } michael@0: else { michael@0: if (pattern.indexOf(":") == -1) { michael@0: throw new Error("When not using *.example.org wildcard, the string " + michael@0: "supplied is expected to be either an exact URL to " + michael@0: "match or a URL prefix. The provided string ('" + michael@0: pattern + "') is unlikely to match any pages."); michael@0: } michael@0: michael@0: if (firstWildcardPosition == -1) michael@0: this.exactURL = pattern; michael@0: else if (firstWildcardPosition == pattern.length - 1) michael@0: this.urlPrefix = pattern.substr(0, pattern.length - 1); michael@0: else { michael@0: throw new Error("The provided wildcard ('" + pattern + "') has a '*' " + michael@0: "in an unexpected position. It is expected to be the " + michael@0: "first or the last character in the wildcard."); michael@0: } michael@0: } michael@0: } michael@0: michael@0: cache[pattern] = this; michael@0: } michael@0: michael@0: MatchPattern.prototype = { michael@0: michael@0: test: function MatchPattern_test(urlStr) { michael@0: try { michael@0: var url = URL(urlStr); michael@0: } michael@0: catch (err) { michael@0: return false; michael@0: } michael@0: michael@0: // Test the URL against a RegExp pattern. For compatibility with michael@0: // -moz-document rules, we require the RegExp to match the entire URL, michael@0: // so we not only test for a match, we also make sure the matched string michael@0: // is the entire URL string. michael@0: // michael@0: // Assuming most URLs don't match most match patterns, we call `test` for michael@0: // speed when determining whether or not the URL matches, then call `exec` michael@0: // for the small subset that match to make sure the entire URL matches. michael@0: // michael@0: if (this.regexp && this.regexp.test(urlStr) && michael@0: this.regexp.exec(urlStr)[0] == urlStr) michael@0: return true; michael@0: michael@0: if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme)) michael@0: return true; michael@0: if (this.exactURL && this.exactURL == urlStr) michael@0: return true; michael@0: michael@0: // Tests the urlStr against domain and check if michael@0: // wildcard submitted (*.domain.com), it only allows michael@0: // subdomains (sub.domain.com) or from the root (http://domain.com) michael@0: // and reject non-matching domains (otherdomain.com) michael@0: // bug 856913 michael@0: if (this.domain && url.host && michael@0: (url.host === this.domain || michael@0: url.host.slice(-this.domain.length - 1) === "." + this.domain)) michael@0: return true; michael@0: if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix)) michael@0: return true; michael@0: michael@0: return false; michael@0: }, michael@0: michael@0: toString: function () '[object MatchPattern]' michael@0: michael@0: }; michael@0: michael@0: exports.MatchPattern = MatchPattern;