add_task(function* () {
  let parsed = sourceUtils.parseURL("https://foo.com:8888/boo/bar.js?q=query");
  equal(parsed.fileName, "bar.js", "parseURL parsed valid fileName");
  equal(parsed.host, "foo.com:8888", "parseURL parsed valid host");
  equal(parsed.hostname, "foo.com", "parseURL parsed valid hostname");
  equal(parsed.port, "8888", "parseURL parsed valid port");
  equal(parsed.href, "https://foo.com:8888/boo/bar.js?q=query", "parseURL parsed valid href");

  parsed = sourceUtils.parseURL("https://foo.com");
  equal(parsed.host, "foo.com", "parseURL parsed valid host when no port given");
  equal(parsed.hostname, "foo.com", "parseURL parsed valid hostname when no port given");

  equal(sourceUtils.parseURL("self-hosted"), null, "parseURL returns `null` for invalid URLs");
});
Example #2
0
/**
 * Parses the raw location of this function call to retrieve the actual
 * function name, source url, host name, line and column.
 */
function parseLocation(location, fallbackLine, fallbackColumn) {
  // Parse the `location` for the function name, source url, line, column etc.

  let line, column, url;

  // These two indices are used to extract the resource substring, which is
  // location[parenIndex + 1 .. lineAndColumnIndex].
  //
  // There are 3 variants of location strings in the profiler (with optional
  // column numbers):
  //   1) "name (resource:line)"
  //   2) "resource:line"
  //   3) "resource"
  //
  // For example for (1), take "foo (bar.js:1)".
  //                                ^      ^
  //                                |      |
  //                                |      |
  //                                |      |
  // parenIndex will point to ------+      |
  //                                       |
  // lineAndColumnIndex will point to -----+
  //
  // For an example without parentheses, take "bar.js:2".
  //                                          ^      ^
  //                                          |      |
  // parenIndex will point to ----------------+      |
  //                                                 |
  // lineAndColumIndex will point to ----------------+
  //
  // To parse, we look for the last occurrence of the string ' ('.
  //
  // For 1), all occurrences of space ' ' characters in the resource string
  // are urlencoded, so the last occurrence of ' (' is the separator between
  // the function name and the resource.
  //
  // For 2) and 3), there can be no occurences of ' (' since ' ' characters
  // are urlencoded in the resource string.
  //
  // XXX: Note that 3) is ambiguous with Gecko Profiler marker locations like
  // "EnterJIT". We can't distinguish the two, so we treat 3) like a function
  // name.
  let parenIndex = -1;
  let lineAndColumnIndex = -1;

  let lastCharCode = location.charCodeAt(location.length - 1);
  let i;
  if (lastCharCode === CHAR_CODE_RPAREN) {
    // Case 1)
    i = location.length - 2;
  } else if (isNumeric(lastCharCode)) {
    // Case 2)
    i = location.length - 1;
  } else {
    // Case 3)
    i = 0;
  }

  if (i !== 0) {
    // Look for a :number.
    let end = i;
    while (isNumeric(location.charCodeAt(i))) {
      i--;
    }
    if (location.charCodeAt(i) === CHAR_CODE_COLON) {
      column = location.substr(i + 1, end - i);
      i--;
    }

    // Look for a preceding :number.
    end = i;
    while (isNumeric(location.charCodeAt(i))) {
      i--;
    }

    // If two were found, the first is the line and the second is the
    // column. If only a single :number was found, then it is the line number.
    if (location.charCodeAt(i) === CHAR_CODE_COLON) {
      line = location.substr(i + 1, end - i);
      lineAndColumnIndex = i;
      i--;
    } else {
      lineAndColumnIndex = i + 1;
      line = column;
      column = undefined;
    }
  }

  // Look for the last occurrence of ' (' in case 1).
  if (lastCharCode === CHAR_CODE_RPAREN) {
    for (; i >= 0; i--) {
      if (location.charCodeAt(i) === CHAR_CODE_LPAREN &&
          i > 0 &&
          location.charCodeAt(i - 1) === CHAR_CODE_SPACE) {
        parenIndex = i;
        break;
      }
    }
  }

  let parsedUrl;
  if (lineAndColumnIndex > 0) {
    let resource = location.substring(parenIndex + 1, lineAndColumnIndex);
    url = resource.split(" -> ").pop();
    if (url) {
      parsedUrl = parseURL(url);
    }
  }

  let functionName, fileName, port, host;
  line = line || fallbackLine;
  column = column || fallbackColumn;

  // If the URL digged out from the `location` is valid, this is a JS frame.
  if (parsedUrl) {
    functionName = location.substring(0, parenIndex - 1);
    fileName = parsedUrl.fileName;
    port = parsedUrl.port;
    host = parsedUrl.host;

    // Check for the case of the filename containing eval
    // e.g. "file.js%20line%2065%20%3E%20eval"
    let evalIndex = fileName.indexOf(EVAL_TOKEN);
    if (evalIndex !== -1 && evalIndex === (fileName.length - EVAL_TOKEN.length)) {
      // Match the filename
      let evalLine = line;
      let [, _fileName, , _line] = fileName.match(/(.+)(%20line%20(\d+)%20%3E%20eval)/)
                                   || [];
      fileName = `${_fileName} (eval:${evalLine})`;
      line = _line;
      assert(_fileName !== undefined,
             "Filename could not be found from an eval location site");
      assert(_line !== undefined,
             "Line could not be found from an eval location site");

      // Match the url as well
      [, url] = url.match(/(.+)( line (\d+) > eval)/) || [];
      assert(url !== undefined,
             "The URL could not be parsed correctly from an eval location site");
    }
  } else {
    functionName = location;
    url = null;
  }

  return { functionName, fileName, host, port, url, line, column };
}
Example #3
0
  render() {
    let frame, isSourceMapped;
    const {
      onClick,
      showFunctionName,
      showAnonymousFunctionName,
      showHost,
      showEmptyPathAsHost,
      showFullSourceUrl
    } = this.props;

    if (this.state && this.state.isSourceMapped && this.state.frame) {
      frame = this.state.frame;
      isSourceMapped = this.state.isSourceMapped;
    } else {
      frame = this.props.frame;
    }

    // If the resource was loaded by browser-loader.js, `frame.source` looks like:
    // resource://devtools/shared/base-loader.js -> resource://devtools/path/to/file.js .
    // What's needed is only the last part after " -> ".
    const source = frame.source
      ? String(frame.source).split(" -> ").pop()
      : "";
    const line = frame.line != void 0 ? Number(frame.line) : null;
    const column = frame.column != void 0 ? Number(frame.column) : null;

    const { short, long, host } = getSourceNames(source);
    const unicodeShort = getUnicodeUrlPath(short);
    const unicodeLong  = getUnicodeUrl(long);
    const unicodeHost  = host ? getUnicodeHostname(host) : "";

    // Reparse the URL to determine if we should link this; `getSourceNames`
    // has already cached this indirectly. We don't want to attempt to
    // link to "self-hosted" and "(unknown)". However, we do want to link
    // to Scratchpad URIs.
    // Source mapped sources might not necessary linkable, but they
    // are still valid in the debugger.
    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
      || isSourceMapped;
    const elements = [];
    const sourceElements = [];
    let sourceEl;
    let tooltip = unicodeLong;

    // Exclude all falsy values, including `0`, as line numbers start with 1.
    if (line) {
      tooltip += `:${line}`;
      // Intentionally exclude 0
      if (column) {
        tooltip += `:${column}`;
      }
    }

    const attributes = {
      "data-url": long,
      className: "frame-link",
    };

    if (showFunctionName) {
      let functionDisplayName = frame.functionDisplayName;
      if (!functionDisplayName && showAnonymousFunctionName) {
        functionDisplayName = webl10n.getStr("stacktrace.anonymousFunction");
      }

      if (functionDisplayName) {
        elements.push(
          dom.span({
            key: "function-display-name",
            className: "frame-link-function-display-name",
          }, functionDisplayName),
          " "
        );
      }
    }

    let displaySource = showFullSourceUrl ? unicodeLong : unicodeShort;
    if (isSourceMapped) {
      displaySource = getSourceMappedFile(displaySource);
    } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
      displaySource = host;
    }

    sourceElements.push(dom.span({
      key: "filename",
      className: "frame-link-filename",
    }, displaySource));

    // If we have a line number > 0.
    if (line) {
      let lineInfo = `:${line}`;
      // Add `data-line` attribute for testing
      attributes["data-line"] = line;

      // Intentionally exclude 0
      if (column) {
        lineInfo += `:${column}`;
        // Add `data-column` attribute for testing
        attributes["data-column"] = column;
      }

      sourceElements.push(dom.span({
        key: "line",
        className: "frame-link-line"
      }, lineInfo));
    }

    // Inner el is useful for achieving ellipsis on the left and correct LTR/RTL
    // ordering. See CSS styles for frame-link-source-[inner] and bug 1290056.
    const sourceInnerEl = dom.span({
      key: "source-inner",
      className: "frame-link-source-inner",
      title: isLinkable ?
        l10n.getFormatStr("frame.viewsourceindebugger", tooltip) : tooltip,
    }, sourceElements);

    // If source is not a URL (self-hosted, eval, etc.), don't make
    // it an anchor link, as we can't link to it.
    if (isLinkable) {
      sourceEl = dom.a({
        onClick: e => {
          e.preventDefault();
          e.stopPropagation();
          onClick(this.getSourceForClick({...frame, source}));
        },
        href: source,
        className: "frame-link-source",
        draggable: false,
      }, sourceInnerEl);
    } else {
      sourceEl = dom.span({
        key: "source",
        className: "frame-link-source",
      }, sourceInnerEl);
    }
    elements.push(sourceEl);

    if (showHost && unicodeHost) {
      elements.push(" ");
      elements.push(dom.span({
        key: "host",
        className: "frame-link-host",
      }, unicodeHost));
    }

    return dom.span(attributes, ...elements);
  }
Example #4
0
    } else {
      frame = this.props.frame;
    }

    let source = frame.source ? String(frame.source) : "";
    let line = frame.line != void 0 ? Number(frame.line) : null;
    let column = frame.column != void 0 ? Number(frame.column) : null;

    const { short, long, host } = getSourceNames(source);
    // Reparse the URL to determine if we should link this; `getSourceNames`
    // has already cached this indirectly. We don't want to attempt to
    // link to "self-hosted" and "(unknown)". However, we do want to link
    // to Scratchpad URIs.
    // Source mapped sources might not necessary linkable, but they
    // are still valid in the debugger.
    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
      || isSourceMapped;
    const elements = [];
    const sourceElements = [];
    let sourceEl;

    let tooltip = long;

    // If the source is linkable and line > 0
    const shouldDisplayLine = isLinkable && line;

    // Exclude all falsy values, including `0`, as even
    // a number 0 for line doesn't make sense, and should not be displayed.
    // If source isn't linkable, don't attempt to append line and column
    // info, as this probably doesn't make sense.
    if (shouldDisplayLine) {
Example #5
0
/**
 * Parses the raw location of this function call to retrieve the actual
 * function name, source url, host name, line and column.
 */
function parseLocation(location, fallbackLine, fallbackColumn) {
  // Parse the `location` for the function name, source url, line, column etc.

  let line, column, url;

  // These two indices are used to extract the resource substring, which is
  // location[parenIndex + 1 .. lineAndColumnIndex].
  //
  // There are 3 variants of location strings in the profiler (with optional
  // column numbers):
  //   1) "name (resource:line)"
  //   2) "resource:line"
  //   3) "resource"
  //
  // For example for (1), take "foo (bar.js:1)".
  //                                ^      ^
  //                                |      |
  //                                |      |
  //                                |      |
  // parenIndex will point to ------+      |
  //                                       |
  // lineAndColumnIndex will point to -----+
  //
  // For an example without parentheses, take "bar.js:2".
  //                                          ^      ^
  //                                          |      |
  // parenIndex will point to ----------------+      |
  //                                                 |
  // lineAndColumIndex will point to ----------------+
  //
  // To parse, we look for the last occurrence of the string ' ('.
  //
  // For 1), all occurrences of space ' ' characters in the resource string
  // are urlencoded, so the last occurrence of ' (' is the separator between
  // the function name and the resource.
  //
  // For 2) and 3), there can be no occurences of ' (' since ' ' characters
  // are urlencoded in the resource string.
  //
  // XXX: Note that 3) is ambiguous with SPS marker locations like
  // "EnterJIT". We can't distinguish the two, so we treat 3) like a function
  // name.
  let parenIndex = -1;
  let lineAndColumnIndex = -1;

  let lastCharCode = location.charCodeAt(location.length - 1);
  let i;
  if (lastCharCode === CHAR_CODE_RPAREN) {
    // Case 1)
    i = location.length - 2;
  } else if (isNumeric(lastCharCode)) {
    // Case 2)
    i = location.length - 1;
  } else {
    // Case 3)
    i = 0;
  }

  if (i !== 0) {
    // Look for a :number.
    let end = i;
    while (isNumeric(location.charCodeAt(i))) {
      i--;
    }
    if (location.charCodeAt(i) === CHAR_CODE_COLON) {
      column = location.substr(i + 1, end - i);
      i--;
    }

    // Look for a preceding :number.
    end = i;
    while (isNumeric(location.charCodeAt(i))) {
      i--;
    }

    // If two were found, the first is the line and the second is the
    // column. If only a single :number was found, then it is the line number.
    if (location.charCodeAt(i) === CHAR_CODE_COLON) {
      line = location.substr(i + 1, end - i);
      lineAndColumnIndex = i;
      i--;
    } else {
      lineAndColumnIndex = i + 1;
      line = column;
      column = undefined;
    }
  }

  // Look for the last occurrence of ' (' in case 1).
  if (lastCharCode === CHAR_CODE_RPAREN) {
    for (; i >= 0; i--) {
      if (location.charCodeAt(i) === CHAR_CODE_LPAREN &&
          i > 0 &&
          location.charCodeAt(i - 1) === CHAR_CODE_SPACE) {
        parenIndex = i;
        break;
      }
    }
  }

  let parsedUrl;
  if (lineAndColumnIndex > 0) {
    let resource = location.substring(parenIndex + 1, lineAndColumnIndex);
    url = resource.split(" -> ").pop();
    if (url) {
      parsedUrl = parseURL(url);
    }
  }

  let functionName, fileName, port, host;
  line = line || fallbackLine;
  column = column || fallbackColumn;

  // If the URL digged out from the `location` is valid, this is a JS frame.
  if (parsedUrl) {
    functionName = location.substring(0, parenIndex - 1);
    fileName = parsedUrl.fileName;
    port = parsedUrl.port;
    host = parsedUrl.host;
  } else {
    functionName = location;
    url = null;
  }

  return { functionName, fileName, host, port, url, line, column };
};