Bug 1448553 - Part 1: Decodeds Punycode-encoded international domain names and URI-encoded filenames in the Web console(developer tool) so that they are displayed as human-readable Unicode text. r?jsantell
The Punycode-encoded international domain names and URI-encoded filenames are
human-unreadable, so they should be displayed as human-readable Unicode text.
This commit decodes this kind of names in the Web console.
MozReview-Commit-ID: Jev7OlF0U9I
--- a/devtools/client/shared/components/Frame.js
+++ b/devtools/client/shared/components/Frame.js
@@ -127,29 +127,30 @@ class Frame extends Component {
// resource://devtools/shared/base-loader.js -> resource://devtools/path/to/file.js .
// What's needed is only the last part after " -> ".
let source = frame.source
? String(frame.source).split(" -> ").pop()
: "";
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);
+ const { short, long, host, readableShort, readableLong, readableHost } =
+ 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;
+ let tooltip = readableLong ? readableLong : long;
// Exclude all falsy values, including `0`, as line numbers start with 1.
if (line) {
tooltip += `:${line}`;
// Intentionally exclude 0
if (column) {
tooltip += `:${column}`;
}
@@ -172,17 +173,22 @@ class Frame extends Component {
key: "function-display-name",
className: "frame-link-function-display-name",
}, functionDisplayName),
" "
);
}
}
- let displaySource = showFullSourceUrl ? long : short;
+ let displaySource;
+ if (showFullSourceUrl) {
+ displaySource = readableLong ? readableLong : long;
+ } else {
+ displaySource = readableShort ? readableShort : short;
+ }
if (isSourceMapped) {
displaySource = getSourceMappedFile(displaySource);
} else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
displaySource = host;
}
sourceElements.push(dom.span({
key: "filename",
@@ -233,21 +239,21 @@ class Frame extends Component {
} else {
sourceEl = dom.span({
key: "source",
className: "frame-link-source",
}, sourceInnerEl);
}
elements.push(sourceEl);
- if (showHost && host) {
+ if (showHost && host || readableHost) {
elements.push(" ");
elements.push(dom.span({
key: "host",
className: "frame-link-host",
- }, host));
+ }, readableHost ? readableHost : host));
}
return dom.span(attributes, ...elements);
}
}
module.exports = Frame;
--- a/devtools/client/shared/source-utils.js
+++ b/devtools/client/shared/source-utils.js
@@ -1,13 +1,15 @@
/* 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 { Cc, Ci } = require("chrome");
+
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_B = "b".charCodeAt(0);
@@ -116,17 +118,34 @@ function parseURL(location) {
*/
function getSourceNames(source) {
let data = gSourceNamesStore.get(source);
if (data) {
return data;
}
+ // The difference between values with or without readable is that the values
+ // without readable may be sometimes human-unreadable, in this case, the
+ // corresponding ones with readable will be in a converted format which is
+ // human-readable.
+ //
+ // For example, when denoting a Javascript filename with non-ASCII
+ // characters, the `short` would be something like %E6%B8%AC.js whereas the
+ // |readableShort| would be the string in a human-readble format converted by
+ // decodeURIComponent; or for example, when denoting a Unicode domain name,
+ // the `host` would be something like xn--g6w.xn--8pv in Punycode whereas the
+ // `readableHost` would be the human-readble string decoded from Punycode.
+ //
+ // If a value without readable is human-readable on its own, then the value of
+ // the corresponding one with readable will be "undefined"(primitive value,
+ // not adjective).
let short, long, host;
+ let readableShort, readableLong, readableHost;
+
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.
@@ -164,26 +183,41 @@ function getSourceNames(source) {
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;
}
+
+ // Get the readable names.
+ const idnService =
+ Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
+ readableHost = idnService.convertToDisplayIDN(host, {});
+ try {
+ readableLong = decodeURIComponent(long.replace(host, readableHost));
+ } catch (_) {
+ // Skip decoding if the URI is malformed.
+ }
+ try {
+ readableShort = decodeURIComponent(short);
+ } catch (_) {
+ // Skip decoding if the URI is malformed.
+ }
}
if (!short) {
if (!long) {
long = UNKNOWN_SOURCE_STRING;
}
short = long.slice(0, 100);
}
- let result = { short, long, host };
+ let result = { short, long, host, readableShort, readableLong, readableHost };
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