123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /* 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/. */
- "use strict";
- const { LocalizationHelper } = require("devtools/shared/l10n");
- const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
- const UNKNOWN_SOURCE_STRING = l10n.getStr("frame.unknownSource");
- // Character codes used in various parsing helper functions.
- const CHAR_CODE_A = "a".charCodeAt(0);
- const CHAR_CODE_C = "c".charCodeAt(0);
- const CHAR_CODE_D = "d".charCodeAt(0);
- const CHAR_CODE_E = "e".charCodeAt(0);
- const CHAR_CODE_F = "f".charCodeAt(0);
- const CHAR_CODE_H = "h".charCodeAt(0);
- const CHAR_CODE_I = "i".charCodeAt(0);
- const CHAR_CODE_J = "j".charCodeAt(0);
- const CHAR_CODE_L = "l".charCodeAt(0);
- const CHAR_CODE_M = "m".charCodeAt(0);
- const CHAR_CODE_O = "o".charCodeAt(0);
- const CHAR_CODE_P = "p".charCodeAt(0);
- const CHAR_CODE_R = "r".charCodeAt(0);
- const CHAR_CODE_S = "s".charCodeAt(0);
- const CHAR_CODE_T = "t".charCodeAt(0);
- const CHAR_CODE_U = "u".charCodeAt(0);
- const CHAR_CODE_COLON = ":".charCodeAt(0);
- const CHAR_CODE_SLASH = "/".charCodeAt(0);
- const CHAR_CODE_CAP_S = "S".charCodeAt(0);
- // The cache used in the `parseURL` function.
- const gURLStore = new Map();
- // The cache used in the `getSourceNames` function.
- const gSourceNamesStore = new Map();
- /**
- * Takes a string and returns an object containing all the properties
- * available on an URL instance, with additional properties (fileName),
- * Leverages caching.
- *
- * @param {String} location
- * @return {Object?} An object containing most properties available
- * in https://developer.mozilla.org/en-US/docs/Web/API/URL
- */
- function parseURL(location) {
- let url = gURLStore.get(location);
- if (url !== void 0) {
- return url;
- }
- try {
- url = new URL(location);
- // The callers were generally written to expect a URL from
- // sdk/url, which is subtly different. So, work around some
- // important differences here.
- url = {
- href: url.href,
- protocol: url.protocol,
- host: url.host,
- hostname: url.hostname,
- port: url.port || null,
- pathname: url.pathname,
- search: url.search,
- hash: url.hash,
- username: url.username,
- password: url.password,
- origin: url.origin,
- };
- // Definitions:
- // Example: https://foo.com:8888/file.js
- // `hostname`: "foo.com"
- // `host`: "foo.com:8888"
- let isChrome = isChromeScheme(location);
- url.fileName = url.pathname ?
- (url.pathname.slice(url.pathname.lastIndexOf("/") + 1) || "/") :
- "/";
- if (isChrome) {
- url.hostname = null;
- url.host = null;
- }
- gURLStore.set(location, url);
- return url;
- } catch (e) {
- gURLStore.set(location, null);
- return null;
- }
- }
- /**
- * Parse a source into a short and long name as well as a host name.
- *
- * @param {String} source
- * The source to parse. Can be a URI or names like "(eval)" or
- * "self-hosted".
- * @return {Object}
- * An object with the following properties:
- * - {String} short: A short name for the source.
- * - "http://page.com/test.js#go?q=query" -> "test.js"
- * - {String} long: The full, long name for the source, with
- hash/query stripped.
- * - "http://page.com/test.js#go?q=query" -> "http://page.com/test.js"
- * - {String?} host: If available, the host name for the source.
- * - "http://page.com/test.js#go?q=query" -> "page.com"
- */
- function getSourceNames(source) {
- let data = gSourceNamesStore.get(source);
- if (data) {
- return data;
- }
- let short, long, host;
- const sourceStr = source ? String(source) : "";
- // If `data:...` uri
- if (isDataScheme(sourceStr)) {
- let commaIndex = sourceStr.indexOf(",");
- if (commaIndex > -1) {
- // The `short` name for a data URI becomes `data:` followed by the actual
- // encoded content, omitting the MIME type, and charset.
- short = `data:${sourceStr.substring(commaIndex + 1)}`.slice(0, 100);
- let result = { short, long: sourceStr };
- gSourceNamesStore.set(source, result);
- return result;
- }
- }
- // If Scratchpad URI, like "Scratchpad/1"; no modifications,
- // and short/long are the same.
- if (isScratchpadScheme(sourceStr)) {
- let result = { short: sourceStr, long: sourceStr };
- gSourceNamesStore.set(source, result);
- return result;
- }
- const parsedUrl = parseURL(sourceStr);
- if (!parsedUrl) {
- // Malformed URI.
- long = sourceStr;
- short = sourceStr.slice(0, 100);
- } else {
- host = parsedUrl.host;
- long = parsedUrl.href;
- if (parsedUrl.hash) {
- long = long.replace(parsedUrl.hash, "");
- }
- if (parsedUrl.search) {
- long = long.replace(parsedUrl.search, "");
- }
- short = parsedUrl.fileName;
- // If `short` is just a slash, and we actually have a path,
- // strip the slash and parse again to get a more useful short name.
- // e.g. "http://foo.com/bar/" -> "bar", rather than "/"
- if (short === "/" && parsedUrl.pathname !== "/") {
- short = parseURL(long.replace(/\/$/, "")).fileName;
- }
- }
- if (!short) {
- if (!long) {
- long = UNKNOWN_SOURCE_STRING;
- }
- short = long.slice(0, 100);
- }
- let result = { short, long, host };
- gSourceNamesStore.set(source, result);
- return result;
- }
- // For the functions below, we assume that we will never access the location
- // argument out of bounds, which is indeed the vast majority of cases.
- //
- // They are written this way because they are hot. Each frame is checked for
- // being content or chrome when processing the profile.
- function isColonSlashSlash(location, i = 0) {
- return location.charCodeAt(++i) === CHAR_CODE_COLON &&
- location.charCodeAt(++i) === CHAR_CODE_SLASH &&
- location.charCodeAt(++i) === CHAR_CODE_SLASH;
- }
- /**
- * Checks for a Scratchpad URI, like "Scratchpad/1"
- */
- function isScratchpadScheme(location, i = 0) {
- return location.charCodeAt(i) === CHAR_CODE_CAP_S &&
- location.charCodeAt(++i) === CHAR_CODE_C &&
- location.charCodeAt(++i) === CHAR_CODE_R &&
- location.charCodeAt(++i) === CHAR_CODE_A &&
- location.charCodeAt(++i) === CHAR_CODE_T &&
- location.charCodeAt(++i) === CHAR_CODE_C &&
- location.charCodeAt(++i) === CHAR_CODE_H &&
- location.charCodeAt(++i) === CHAR_CODE_P &&
- location.charCodeAt(++i) === CHAR_CODE_A &&
- location.charCodeAt(++i) === CHAR_CODE_D &&
- location.charCodeAt(++i) === CHAR_CODE_SLASH;
- }
- function isDataScheme(location, i = 0) {
- return location.charCodeAt(i) === CHAR_CODE_D &&
- location.charCodeAt(++i) === CHAR_CODE_A &&
- location.charCodeAt(++i) === CHAR_CODE_T &&
- location.charCodeAt(++i) === CHAR_CODE_A &&
- location.charCodeAt(++i) === CHAR_CODE_COLON;
- }
- function isContentScheme(location, i = 0) {
- let firstChar = location.charCodeAt(i);
- switch (firstChar) {
- // "http://" or "https://"
- case CHAR_CODE_H:
- if (location.charCodeAt(++i) === CHAR_CODE_T &&
- location.charCodeAt(++i) === CHAR_CODE_T &&
- location.charCodeAt(++i) === CHAR_CODE_P) {
- if (location.charCodeAt(i + 1) === CHAR_CODE_S) {
- ++i;
- }
- return isColonSlashSlash(location, i);
- }
- return false;
- // "file://"
- case CHAR_CODE_F:
- if (location.charCodeAt(++i) === CHAR_CODE_I &&
- location.charCodeAt(++i) === CHAR_CODE_L &&
- location.charCodeAt(++i) === CHAR_CODE_E) {
- return isColonSlashSlash(location, i);
- }
- return false;
- // "app://"
- case CHAR_CODE_A:
- if (location.charCodeAt(++i) == CHAR_CODE_P &&
- location.charCodeAt(++i) == CHAR_CODE_P) {
- return isColonSlashSlash(location, i);
- }
- return false;
- default:
- return false;
- }
- }
- function isChromeScheme(location, i = 0) {
- let firstChar = location.charCodeAt(i);
- switch (firstChar) {
- // "chrome://"
- case CHAR_CODE_C:
- if (location.charCodeAt(++i) === CHAR_CODE_H &&
- location.charCodeAt(++i) === CHAR_CODE_R &&
- location.charCodeAt(++i) === CHAR_CODE_O &&
- location.charCodeAt(++i) === CHAR_CODE_M &&
- location.charCodeAt(++i) === CHAR_CODE_E) {
- return isColonSlashSlash(location, i);
- }
- return false;
- // "resource://"
- case CHAR_CODE_R:
- if (location.charCodeAt(++i) === CHAR_CODE_E &&
- location.charCodeAt(++i) === CHAR_CODE_S &&
- location.charCodeAt(++i) === CHAR_CODE_O &&
- location.charCodeAt(++i) === CHAR_CODE_U &&
- location.charCodeAt(++i) === CHAR_CODE_R &&
- location.charCodeAt(++i) === CHAR_CODE_C &&
- location.charCodeAt(++i) === CHAR_CODE_E) {
- return isColonSlashSlash(location, i);
- }
- return false;
- // "jar:file://"
- case CHAR_CODE_J:
- if (location.charCodeAt(++i) === CHAR_CODE_A &&
- location.charCodeAt(++i) === CHAR_CODE_R &&
- location.charCodeAt(++i) === CHAR_CODE_COLON &&
- location.charCodeAt(++i) === CHAR_CODE_F &&
- location.charCodeAt(++i) === CHAR_CODE_I &&
- location.charCodeAt(++i) === CHAR_CODE_L &&
- location.charCodeAt(++i) === CHAR_CODE_E) {
- return isColonSlashSlash(location, i);
- }
- return false;
- default:
- return false;
- }
- }
- /**
- * A utility method to get the file name from a sourcemapped location
- * The sourcemap location can be in any form. This method returns a
- * formatted file name for different cases like Windows or OSX.
- * @param source
- * @returns String
- */
- function getSourceMappedFile(source) {
- // If sourcemapped source is a OSX path, return
- // the characters after last "/".
- // If sourcemapped source is a Windowss path, return
- // the characters after last "\\".
- if (source.lastIndexOf("/") >= 0) {
- source = source.slice(source.lastIndexOf("/") + 1);
- } else if (source.lastIndexOf("\\") >= 0) {
- source = source.slice(source.lastIndexOf("\\") + 1);
- }
- return source;
- }
- exports.parseURL = parseURL;
- exports.getSourceNames = getSourceNames;
- exports.isScratchpadScheme = isScratchpadScheme;
- exports.isChromeScheme = isChromeScheme;
- exports.isContentScheme = isContentScheme;
- exports.isDataScheme = isDataScheme;
- exports.getSourceMappedFile = getSourceMappedFile;
|