Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  URLPattern.sys.mjs   Sprache: unbekannt

 
Spracherkennung für: .mjs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
  error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
  pprint: "chrome://remote/content/shared/Format.sys.mjs",
});

/**
 * Parsed pattern to use for URL matching.
 *
 * @typedef {object} ParsedURLPattern
 * @property {string|null} protocol
 *     The protocol, for instance "https".
 * @property {string|null} hostname
 *     The hostname, for instance "example.com".
 * @property {string|null} port
 *     The serialized port. Empty string for default ports of special schemes.
 * @property {string|null} path
 *     The path, starting with "/".
 * @property {string|null} search
 *     The search query string, without the leading "?"
 */

/**
 * Subset of properties extracted from a parsed URL.
 *
 * @typedef {object} ParsedURL
 * @property {string=} host
 * @property {string|Array<string>} path
 *     Either a string if the path is an opaque path, or an array of strings
 *     (path segments).
 * @property {number=} port
 * @property {string=} query
 * @property {string=} scheme
 */

/**
 * Enum of URLPattern types.
 *
 * @readonly
 * @enum {URLPatternType}
 */
const URLPatternType = {
  Pattern: "pattern",
  String: "string",
};

const supportedURLPatternTypes = Object.values(URLPatternType);

const SPECIAL_SCHEMES = ["file", "http", "https", "ws", "wss"];
const DEFAULT_PORTS = {
  file: null,
  http: 80,
  https: 443,
  ws: 80,
  wss: 443,
};

/**
 * Check if a given URL pattern is compatible with the provided URL.
 *
 * Implements https://w3c.github.io/webdriver-bidi/#match-url-pattern
 *
 * @param {ParsedURLPattern} urlPattern
 *     The URL pattern to match.
 * @param {string} url
 *     The string representation of a URL to test against the pattern.
 *
 * @returns {boolean}
 *     True if the pattern is compatible with the provided URL, false otherwise.
 */
export function matchURLPattern(urlPattern, url) {
  const parsedURL = parseURL(url);

  if (urlPattern.protocol !== null && urlPattern.protocol != parsedURL.scheme) {
    return false;
  }

  if (urlPattern.hostname !== null && urlPattern.hostname != parsedURL.host) {
    return false;
  }

  if (urlPattern.port !== null && urlPattern.port != serializePort(parsedURL)) {
    return false;
  }

  if (
    urlPattern.pathname !== null &&
    urlPattern.pathname != serializePath(parsedURL)
  ) {
    return false;
  }

  if (urlPattern.search !== null) {
    const urlQuery = parsedURL.query === null ? "" : parsedURL.query;
    if (urlPattern.search != urlQuery) {
      return false;
    }
  }

  return true;
}

/**
 * Parse a URLPattern into a parsed pattern object which can be used to match
 * URLs using `matchURLPattern`.
 *
 * Implements https://w3c.github.io/webdriver-bidi/#parse-url-pattern
 *
 * @param {URLPattern} pattern
 *     The pattern to parse.
 *
 * @returns {ParsedURLPattern}
 *     The parsed URL pattern.
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 * @throws {UnsupportedOperationError}
 *     Raised if the pattern uses a protocol not supported by Firefox.
 */
export function parseURLPattern(pattern) {
  lazy.assert.object(
    pattern,
    lazy.pprint`Expected URL pattern to be an object, got ${pattern}`
  );

  let hasProtocol = true;
  let hasHostname = true;
  let hasPort = true;
  let hasPathname = true;
  let hasSearch = true;

  let patternUrl;
  switch (pattern.type) {
    case URLPatternType.Pattern:
      patternUrl = "";
      if ("protocol" in pattern) {
        patternUrl += parseProtocol(pattern.protocol);
      } else {
        hasProtocol = false;
        patternUrl += "http";
      }

      const scheme = patternUrl.toLowerCase();
      patternUrl += ":";
      if (SPECIAL_SCHEMES.includes(scheme)) {
        patternUrl += "//";
      }

      if ("hostname" in pattern) {
        patternUrl += parseHostname(pattern.hostname, scheme);
      } else {
        if (scheme != "file") {
          patternUrl += "placeholder";
        }
        hasHostname = false;
      }

      if ("port" in pattern) {
        patternUrl += parsePort(pattern.port);
      } else {
        hasPort = false;
      }

      if ("pathname" in pattern) {
        patternUrl += parsePathname(pattern.pathname);
      } else {
        hasPathname = false;
      }

      if ("search" in pattern) {
        patternUrl += parseSearch(pattern.search);
      } else {
        hasSearch = false;
      }
      break;
    case URLPatternType.String:
      lazy.assert.string(
        pattern.pattern,
        lazy.pprint`Expected URL pattern "pattern" to be a string, got ${pattern.pattern}`
      );
      patternUrl = unescapeUrlPattern(pattern.pattern);
      break;
    default:
      throw new lazy.error.InvalidArgumentError(
        `Expected "urlPattern" type to be one of ${supportedURLPatternTypes}, got ${pattern.type}`
      );
  }

  if (!URL.canParse(patternUrl)) {
    throw new lazy.error.InvalidArgumentError(
      `Unable to parse URL "${patternUrl}"`
    );
  }

  let parsedURL;
  try {
    parsedURL = parseURL(patternUrl);
  } catch (e) {
    throw new lazy.error.InvalidArgumentError(
      `Failed to parse URL "${patternUrl}"`
    );
  }

  if (hasProtocol && !SPECIAL_SCHEMES.includes(parsedURL.scheme)) {
    throw new lazy.error.UnsupportedOperationError(
      `URL pattern did not specify a supported protocol (one of ${SPECIAL_SCHEMES}), got ${parsedURL.scheme}`
    );
  }

  return {
    protocol: hasProtocol ? parsedURL.scheme : null,
    hostname: hasHostname ? parsedURL.host : null,
    port: hasPort ? serializePort(parsedURL) : null,
    pathname:
      hasPathname && parsedURL.path.length ? serializePath(parsedURL) : null,
    search: hasSearch ? parsedURL.query || "" : null,
  };
}

/**
 * Parse the hostname property of a URLPatternPattern.
 *
 * @param {string} hostname
 *     A hostname property.
 * @param {string} scheme
 *     The scheme for the URLPatternPattern.
 *
 * @returns {string}
 *     The parsed property.
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 */
function parseHostname(hostname, scheme) {
  if (typeof hostname != "string" || hostname == "") {
    throw new lazy.error.InvalidArgumentError(
      `Expected URLPattern "hostname" to be a non-empty string, got ${hostname}`
    );
  }

  if (scheme == "file") {
    throw new lazy.error.InvalidArgumentError(
      `URLPattern with "file" scheme cannot specify a hostname, got ${hostname}`
    );
  }

  hostname = unescapeUrlPattern(hostname);

  const forbiddenHostnameCharacters = ["/", "?", "#"];
  let insideBrackets = false;
  for (const codepoint of hostname) {
    if (
      forbiddenHostnameCharacters.includes(codepoint) ||
      (!insideBrackets && codepoint == ":")
    ) {
      throw new lazy.error.InvalidArgumentError(
        `URL pattern "hostname" contained a forbidden character, got "${hostname}"`
      );
    }

    if (codepoint == "[") {
      insideBrackets = true;
    } else if (codepoint == "]") {
      insideBrackets = false;
    }
  }

  return hostname;
}

/**
 * Parse the pathname property of a URLPatternPattern.
 *
 * @param {string} pathname
 *     A pathname property.
 *
 * @returns {string}
 *     The parsed property.
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 */
function parsePathname(pathname) {
  lazy.assert.string(
    pathname,
    lazy.pprint`Expected URL pattern "pathname" to be a string, got ${pathname}`
  );

  pathname = unescapeUrlPattern(pathname);
  if (!pathname.startsWith("/")) {
    pathname = `/${pathname}`;
  }

  if (pathname.includes("?") || pathname.includes("#")) {
    throw new lazy.error.InvalidArgumentError(
      `URL pattern "pathname" contained a forbidden character, got "${pathname}"`
    );
  }

  return pathname;
}

/**
 * Parse the port property of a URLPatternPattern.
 *
 * @param {string} port
 *     A port property.
 *
 * @returns {string}
 *     The parsed property.
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 */
function parsePort(port) {
  if (typeof port != "string" || port == "") {
    throw new lazy.error.InvalidArgumentError(
      `Expected URLPattern "port" to be a non-empty string, got ${port}`
    );
  }

  port = unescapeUrlPattern(port);

  const isNumber = /^\d*$/.test(port);
  if (!isNumber) {
    throw new lazy.error.InvalidArgumentError(
      `URL pattern "port" is not a valid number, got "${port}"`
    );
  }

  return `:${port}`;
}

/**
 * Parse the protocol property of a URLPatternPattern.
 *
 * @param {string} protocol
 *     A protocol property.
 *
 * @returns {string}
 *     The parsed property.
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 */
function parseProtocol(protocol) {
  if (typeof protocol != "string" || protocol == "") {
    throw new lazy.error.InvalidArgumentError(
      `Expected URLPattern "protocol" to be a non-empty string, got ${protocol}`
    );
  }

  protocol = unescapeUrlPattern(protocol);
  if (!/^[a-zA-Z0-9+-.]*$/.test(protocol)) {
    throw new lazy.error.InvalidArgumentError(
      `URL pattern "protocol" contained a forbidden character, got "${protocol}"`
    );
  }

  return protocol;
}

/**
 * Parse the search property of a URLPatternPattern.
 *
 * @param {string} search
 *     A search property.
 *
 * @returns {string}
 *     The parsed property.
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 */
function parseSearch(search) {
  lazy.assert.string(
    search,
    lazy.pprint`Expected URL pattern "search" to be a string, got ${search}`
  );

  search = unescapeUrlPattern(search);
  if (!search.startsWith("?")) {
    search = `?${search}`;
  }

  if (search.includes("#")) {
    throw new lazy.error.InvalidArgumentError(
      `Expected URLPattern "search" to never contain "#", got ${search}`
    );
  }

  return search;
}

/**
 * Parse a string URL. This tries to be close to Basic URL Parser, however since
 * this is not currently implemented in Firefox and URL parsing has many edge
 * cases, it does not try to be a faithful implementation.
 *
 * Edge cases which are not supported are mostly about non-special URLs, which
 * in practice should not be observable in automation.
 *
 * @param {string} url
 *     The string based URL to parse.
 * @returns {ParsedURL}
 *     The parsed URL.
 */
function parseURL(url) {
  const urlObj = new URL(url);
  const uri = urlObj.URI;

  return {
    scheme: uri.scheme,
    // Note: Use urlObj instead of uri for hostname:
    // nsIURI removes brackets from ipv6 hostnames (eg [::1] becomes ::1).
    host: urlObj.hostname,
    path: uri.filePath,
    // Note: Use urlObj instead of uri for port:
    // nsIURI throws on the port getter for non-special schemes.
    port: urlObj.port != "" ? Number(uri.port) : null,
    query: uri.hasQuery ? uri.query : null,
  };
}

/**
 * Serialize the path of a parsed URL.
 *
 * @see https://pr-preview.s3.amazonaws.com/w3c/webdriver-bidi/pull/429.html#parse-url-pattern
 *
 * @param {ParsedURL} url
 *     A parsed url.
 *
 * @returns {string}
 *     The serialized path
 */
function serializePath(url) {
  // Check for opaque path
  if (typeof url.path == "string") {
    return url.path;
  }

  let serialized = "";
  for (const segment of url.path) {
    serialized += `/${segment}`;
  }

  return serialized;
}

/**
 * Serialize the port of a parsed URL.
 *
 * @see https://pr-preview.s3.amazonaws.com/w3c/webdriver-bidi/pull/429.html#parse-url-pattern
 *
 * @param {ParsedURL} url
 *     A parsed url.
 *
 * @returns {string}
 *     The serialized port
 */
function serializePort(url) {
  let port = null;
  if (
    SPECIAL_SCHEMES.includes(url.scheme) &&
    DEFAULT_PORTS[url.scheme] !== null &&
    (url.port === null || url.port == DEFAULT_PORTS[url.scheme])
  ) {
    port = "";
  } else if (url.port !== null) {
    port = `${url.port}`;
  }

  return port;
}

/**
 * Unescape and check a pattern string against common forbidden characters.
 *
 * @see https://pr-preview.s3.amazonaws.com/w3c/webdriver-bidi/pull/429.html#unescape-url-pattern
 *
 * @param {string} pattern
 *     Either a full URLPatternString pattern or a property of a URLPatternPattern.
 *
 * @returns {string}
 *     The unescaped pattern
 *
 * @throws {InvalidArgumentError}
 *     Raised if an argument is of an invalid type or value.
 */
function unescapeUrlPattern(pattern) {
  const forbiddenCharacters = ["(", ")", "*", "{", "}"];
  const escapeCharacter = "\\";

  let isEscaped = false;
  let result = "";

  for (const codepoint of Array.from(pattern)) {
    if (!isEscaped) {
      if (forbiddenCharacters.includes(codepoint)) {
        throw new lazy.error.InvalidArgumentError(
          `URL pattern contained an unescaped forbidden character ${codepoint}`
        );
      }

      if (codepoint == escapeCharacter) {
        isEscaped = true;
        continue;
      }
    }

    result += codepoint;
    isEscaped = false;
  }

  return result;
}

[ Dauer der Verarbeitung: 0.39 Sekunden  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge