Bug 1448553 - Part 3: Decodeds Punycode-encoded international domain names in the Network Monitor so that they are displayed as human-readable Unicode text. r?Honza draft
authorZhang Junzhi <zjz@zjz.name>
Mon, 02 Apr 2018 16:42:13 +0800
changeset 780907 f99f741aee3b722cbe762eec5096a09d9242b0f3
parent 780906 5e680208cb2eb24f8805d37499f112b4ed9dcfec
child 780908 381b0c274d435603d188090f6df4eea5996c327f
push id106159
push userbmo:zjz@zjz.name
push dateThu, 12 Apr 2018 07:30:36 +0000
reviewersHonza
bugs1448553
milestone61.0a1
Bug 1448553 - Part 3: Decodeds Punycode-encoded international domain names in the Network Monitor so that they are displayed as human-readable Unicode text. r?Honza The Punycode-encoded international domain names are human-unreadable, so they should be displayed as human-readable Unicode text. This commit decodes this kind of names in the Network Monitor. MozReview-Commit-ID: HlGOVZi1lIm
devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
devtools/client/netmonitor/src/utils/open-request-in-tab.js
devtools/client/netmonitor/src/utils/request-utils.js
devtools/client/netmonitor/test/head.js
devtools/client/netmonitor/webpack.config.js
--- a/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
+++ b/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
@@ -1,13 +1,22 @@
 /* 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/. */
 /* eslint-disable mozilla/reject-some-requires */
 
+// This file is a chrome-API-dependent version of the module
+// devtools/client/netmonitor/src/utils/open-request-in-tab.js, so that it can
+// take advantage of utilizing chrome APIs. But because of this, it isn't
+// intended to be used in Chrome-API-free applications, such as the Launchpad.
+//
+// Please keep in mind that if the feature in this file has changed, don't
+// forget to also change that accordingly in
+// devtools/client/netmonitor/src/utils/open-request-in-tab.js.
+
 "use strict";
 
 let { Cc, Ci } = require("chrome");
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 /**
  * Opens given request in a new tab.
--- a/devtools/client/netmonitor/src/utils/open-request-in-tab.js
+++ b/devtools/client/netmonitor/src/utils/open-request-in-tab.js
@@ -1,12 +1,22 @@
 /* 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/. */
 
+// This file is a chrome-API-free version of the module
+// devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js, so that
+// it can be used in Chrome-API-free applications, such as the Launchpad. But
+// because of this, it cannot take advantage of utilizing chrome APIs and should
+// implement the similar functionalities on its own.
+//
+// Please keep in mind that if the feature in this file has changed, don't
+// forget to also change that accordingly in
+// devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js.
+
 "use strict";
 
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 /**
  * Opens given request in a new tab.
  */
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -1,16 +1,19 @@
 /* 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/. */
 
 /* eslint-disable mozilla/reject-some-requires */
 
 "use strict";
 
+const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } =
+  require("devtools/client/shared/unicode-url");
+
 const {
   UPDATE_PROPS,
 } = require("devtools/client/netmonitor/src/constants");
 
 const CONTENT_MIME_TYPE_ABBREVIATIONS = {
   "ecmascript": "js",
   "javascript": "js",
   "x-javascript": "js"
@@ -116,32 +119,16 @@ function formDataURI(mimeType, encoding,
  * @param {array} headers - array of headers info { name, value }
  * @return {string} list of headers in text format
  */
 function writeHeaderText(headers) {
   return headers.map(({name, value}) => name + ": " + value).join("\n");
 }
 
 /**
- * Convert a string into unicode if string is valid.
- * If there is a malformed URI sequence, it returns input string.
- *
- * @param {string} url - a string
- * @return {string} unicode string
- */
-function decodeUnicodeUrl(string) {
-  try {
-    return decodeURIComponent(string);
-  } catch (err) {
-    // Ignore error and return input string directly.
-  }
-  return string;
-}
-
-/**
  * Decode base64 string.
  *
  * @param {string} url - a string
  * @return {string} decoded string
  */
 function decodeUnicodeBase64(string) {
   try {
     return decodeURIComponent(atob(string));
@@ -170,17 +157,17 @@ function getAbbreviatedMimeType(mimeType
  * For example helper returns "basename" from http://domain.com/path/basename
  * If basename portion is empty, it returns the url pathname.
  *
  * @param {string} url - url string
  * @return {string} unicode basename of a url
  */
 function getUrlBaseName(url) {
   const pathname = (new URL(url)).pathname;
-  return decodeUnicodeUrl(
+  return getUnicodeUrlPath(
     pathname.replace(/\S*\//, "") || pathname || "/");
 }
 
 /**
  * Helpers for getting the query portion of a url.
  *
  * @param {string} url - url string
  * @return {string} unicode query of a url
@@ -191,37 +178,37 @@ function getUrlQuery(url) {
 
 /**
  * Helpers for getting unicode name and query portions of a url.
  *
  * @param {string} url - url string
  * @return {string} unicode basename and query portions of a url
  */
 function getUrlBaseNameWithQuery(url) {
-  return getUrlBaseName(url) + decodeUnicodeUrl((new URL(url)).search);
+  return getUrlBaseName(url) + getUnicodeUrlPath((new URL(url)).search);
 }
 
 /**
- * Helpers for getting unicode hostname portion of an URL.
+ * Helpers for getting hostname portion of an URL.
  *
  * @param {string} url - url string
  * @return {string} unicode hostname of a url
  */
 function getUrlHostName(url) {
-  return decodeUnicodeUrl((new URL(url)).hostname);
+  return new URL(url).hostname;
 }
 
 /**
- * Helpers for getting unicode host portion of an URL.
+ * Helpers for getting host portion of an URL.
  *
  * @param {string} url - url string
  * @return {string} unicode host of a url
  */
 function getUrlHost(url) {
-  return decodeUnicodeUrl((new URL(url)).host);
+  return new URL(url).host;
 }
 
 /**
  * Helpers for getting the shceme portion of a url.
  * For example helper returns "http" from http://domain.com/path/basename
  *
  * @param {string} url - url string
  * @return {string} string scheme of a url
@@ -232,19 +219,32 @@ function getUrlScheme(url) {
 
 /**
  * Extract several details fields from a URL at once.
  */
 function getUrlDetails(url) {
   let baseNameWithQuery = getUrlBaseNameWithQuery(url);
   let host = getUrlHost(url);
   let hostname = getUrlHostName(url);
-  let unicodeUrl = decodeUnicodeUrl(url);
+  let unicodeUrl = getUnicodeUrl(url);
   let scheme = getUrlScheme(url);
 
+  // If the hostname contains unreadable ASCII characters, we need to do the
+  // following two steps:
+  // 1. Converting the unreadable hostname to a readable Unicode domain name.
+  //    For example, converting xn--g6w.xn--8pv into a Unicode domain name.
+  // 2. Replacing the unreadable hostname portion in the `host` with the
+  //    readable hostname.
+  //    For example, replacing xn--g6w.xn--8pv:8000 with [Unicode domain]:8000
+  // After finishing the two steps, we get a readable `host`.
+  const unicodeHostname = getUnicodeHostname(hostname);
+  if (unicodeHostname !== hostname) {
+    host = host.replace(hostname, unicodeHostname);
+  }
+
   // Mark local hosts specially, where "local" is  as defined in the W3C
   // spec for secure contexts.
   // http://www.w3.org/TR/powerful-features/
   //
   //  * If the name falls under 'localhost'
   //  * If the name is an IPv4 address within 127.0.0.0/8
   //  * If the name is an IPv6 address within ::1/128
   //
@@ -272,18 +272,18 @@ function getUrlDetails(url) {
 function parseQueryString(query) {
   if (!query) {
     return null;
   }
 
   return query.replace(/^[?&]/, "").split("&").map(e => {
     let param = e.split("=");
     return {
-      name: param[0] ? decodeUnicodeUrl(param[0]) : "",
-      value: param[1] ? decodeUnicodeUrl(param[1]) : "",
+      name: param[0] ? getUnicodeUrlPath(param[0]) : "",
+      value: param[1] ? getUnicodeUrlPath(param[1]) : "",
     };
   });
 }
 
 /**
  * Parse a string of formdata sections into its components
  *
  * @param {string} sections - sections of formdata joined by &
@@ -292,18 +292,18 @@ function parseQueryString(query) {
 function parseFormData(sections) {
   if (!sections) {
     return null;
   }
 
   return sections.replace(/^&/, "").split("&").map(e => {
     let param = e.split("=");
     return {
-      name: param[0] ? decodeUnicodeUrl(param[0]) : "",
-      value: param[1] ? decodeUnicodeUrl(param[1]) : "",
+      name: param[0] ? getUnicodeUrlPath(param[0]) : "",
+      value: param[1] ? getUnicodeUrlPath(param[1]) : "",
     };
   });
 }
 
 /**
  * Reduces an IP address into a number for easier sorting
  *
  * @param {string} ip - IP address to reduce
@@ -486,17 +486,16 @@ function processNetworkUpdates(request =
 
 module.exports = {
   decodeUnicodeBase64,
   getFormDataSections,
   fetchHeaders,
   fetchNetworkUpdatePacket,
   formDataURI,
   writeHeaderText,
-  decodeUnicodeUrl,
   getAbbreviatedMimeType,
   getEndTime,
   getFormattedProtocol,
   getResponseHeader,
   getResponseTime,
   getStartTime,
   getUrlBaseName,
   getUrlBaseNameWithQuery,
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -13,18 +13,18 @@
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
   this);
 
 const {
   getFormattedIPAndPort,
   getFormattedTime,
 } = require("devtools/client/netmonitor/src/utils/format-utils");
+const { getUnicodeUrl } = require("devtools/client/shared/unicode-url");
 const {
-  decodeUnicodeUrl,
   getFormattedProtocol,
   getUrlBaseName,
   getUrlHost,
   getUrlQuery,
   getUrlScheme,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
 const { EVENTS } = require("devtools/client/netmonitor/src/constants");
 
@@ -405,17 +405,17 @@ function verifyRequestItemTarget(documen
 
   info("Visible index of item: " + visibleIndex);
 
   let { fuzzyUrl, status, statusText, cause, type, fullMimeType,
         transferred, size, time, displayedStatus } = data;
 
   let target = document.querySelectorAll(".request-list-item")[visibleIndex];
   // Bug 1414981 - Request URL should not show #hash
-  let unicodeUrl = decodeUnicodeUrl(url).split("#")[0];
+  let unicodeUrl = getUnicodeUrl(url.split("#")[0]);
   let name = getUrlBaseName(url);
   let query = getUrlQuery(url);
   let host = getUrlHost(url);
   let scheme = getUrlScheme(url);
   let {
     remoteAddress,
     remotePort,
     totalTime,
--- a/devtools/client/netmonitor/webpack.config.js
+++ b/devtools/client/netmonitor/webpack.config.js
@@ -83,16 +83,17 @@ let webpackConfig = {
       "devtools/client/shared/vendor/jszip": "jszip",
 
       "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
 
       "devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter",
       "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
       "devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"),
       "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab": path.join(__dirname, "src/utils/open-request-in-tab"),
+      "devtools/client/shared/unicode-url": path.join(__dirname, "../../client/shared/webpack/shims/unicode-url-stub"),
 
       // Locales need to be explicitly mapped to the en-US subfolder
       "devtools/client/locales": path.join(__dirname, "../../client/locales/en-US"),
       "devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
       "devtools/startup/locales": path.join(__dirname, "../../shared/locales/en-US"),
       "toolkit/locales": path.join(__dirname, "../../../toolkit/locales/en-US"),
 
       // Unless a path explicitly needs to be rewritten or shimmed, all devtools paths can