Bug 1250255 - Properly decode response body; r=gasolin draft
authorJan Odvarko <odvarko@gmail.com>
Sat, 07 Oct 2017 07:45:11 +0200
changeset 676285 6d024326c7a0ad51c5fef2be192f348461ad5635
parent 676211 2d7b8b5dd174bd9db5894c6cb770e05f83e95ee3
child 676286 43ec0219d425b89bb71555e1394762ba54494d78
child 676287 01aa038e338a1156cb22d70bcb973258baeb78aa
push id83460
push userjodvarko@mozilla.com
push dateSat, 07 Oct 2017 05:57:24 +0000
reviewersgasolin
bugs1250255
milestone58.0a1
Bug 1250255 - Properly decode response body; r=gasolin MozReview-Commit-ID: Dmm0rdK4qGs
devtools/client/netmonitor/src/components/response-panel.js
devtools/client/netmonitor/src/connector/firefox-data-provider.js
devtools/client/netmonitor/src/utils/request-utils.js
--- a/devtools/client/netmonitor/src/components/response-panel.js
+++ b/devtools/client/netmonitor/src/components/response-panel.js
@@ -6,29 +6,35 @@
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../utils/l10n");
-const { formDataURI, getUrlBaseName } = require("../utils/request-utils");
+const {
+  decodeUnicodeBase64,
+  formDataURI,
+  getUrlBaseName,
+} = require("../utils/request-utils");
 
 // Components
 const PropertiesView = createFactory(require("./properties-view"));
 
 const { div, img } = DOM;
 const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
 const JSON_FILTER_TEXT = L10N.getStr("jsonFilterText");
 const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
 const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
 const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
 const RESPONSE_PAYLOAD = L10N.getStr("responsePayload");
 
+const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
+
 /*
  * Response panel component
  * Displays the GET parameters and POST data of a request
  */
 const ResponsePanel = createClass({
   displayName: "ResponsePanel",
 
   propTypes: {
@@ -141,16 +147,21 @@ const ResponsePanel = createClass({
           div({ className: "response-summary" },
             div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_MIMETYPE),
             div({ className: "tabpanel-summary-value" }, mimeType),
           ),
         )
       );
     }
 
+    // Decode response if it's coming from JSONView.
+    if (mimeType.includes(JSON_VIEW_MIME_TYPE) && encoding === "base64") {
+      text = decodeUnicodeBase64(text);
+    }
+
     // Display Properties View
     let { json, jsonpCallback, error } = this.isJSON(mimeType, text) || {};
     let object = {};
     let sectionName;
 
     if (json) {
       if (jsonpCallback) {
         sectionName = L10N.getFormatStr("jsonpScopeName", jsonpCallback);
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -2,17 +2,20 @@
  * 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 block-scoped-var */
 
 "use strict";
 
 const { EVENTS } = require("../constants");
 const { CurlUtils } = require("devtools/client/shared/curl");
-const { fetchHeaders, formDataURI } = require("../utils/request-utils");
+const {
+  fetchHeaders,
+  formDataURI,
+} = require("../utils/request-utils");
 
 /**
  * This object is responsible for fetching additional HTTP
  * data from the backend.
  */
 class FirefoxDataProvider {
   constructor({webConsoleClient, actions}) {
     // Options
@@ -22,17 +25,17 @@ class FirefoxDataProvider {
     // Internal properties
     this.payloadQueue = [];
 
     // Public methods
     this.addRequest = this.addRequest.bind(this);
     this.updateRequest = this.updateRequest.bind(this);
 
     // Internals
-    this.fetchImage = this.fetchImage.bind(this);
+    this.fetchResponseBody = this.fetchResponseBody.bind(this);
     this.fetchRequestHeaders = this.fetchRequestHeaders.bind(this);
     this.fetchResponseHeaders = this.fetchResponseHeaders.bind(this);
     this.fetchPostData = this.fetchPostData.bind(this);
     this.fetchResponseCookies = this.fetchResponseCookies.bind(this);
     this.fetchRequestCookies = this.fetchRequestCookies.bind(this);
     this.getPayloadFromQueue = this.getPayloadFromQueue.bind(this);
     this.isQueuePayloadReady = this.isQueuePayloadReady.bind(this);
     this.pushPayloadToQueue = this.pushPayloadToQueue.bind(this);
@@ -107,17 +110,17 @@ class FirefoxDataProvider {
     let [
       imageObj,
       requestHeadersObj,
       responseHeadersObj,
       postDataObj,
       requestCookiesObj,
       responseCookiesObj,
     ] = await Promise.all([
-      this.fetchImage(mimeType, responseContent),
+      this.fetchResponseBody(mimeType, responseContent),
       this.fetchRequestHeaders(requestHeaders),
       this.fetchResponseHeaders(responseHeaders),
       this.fetchPostData(requestPostData),
       this.fetchRequestCookies(requestCookies),
       this.fetchResponseCookies(responseCookies),
     ]);
 
     let payload = Object.assign({},
@@ -132,17 +135,17 @@ class FirefoxDataProvider {
 
     this.pushPayloadToQueue(id, payload);
 
     if (this.actions.updateRequest && this.isQueuePayloadReady(id)) {
       await this.actions.updateRequest(id, this.getPayloadFromQueue(id).payload, true);
     }
   }
 
-  async fetchImage(mimeType, responseContent) {
+  async fetchResponseBody(mimeType, responseContent) {
     let payload = {};
     if (mimeType && responseContent && responseContent.content) {
       let { encoding, text } = responseContent.content;
       let response = await this.getLongString(text);
 
       if (mimeType.includes("image/")) {
         payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
       }
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -104,16 +104,31 @@ function decodeUnicodeUrl(string) {
     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));
+  } catch (err) {
+    // Ignore error and return input string directly.
+  }
+  return string;
+}
+
+/**
  * Helper for getting an abbreviated string for a mime type.
  *
  * @param {string} mimeType - mime type
  * @return {string} abbreviated mime type
  */
 function getAbbreviatedMimeType(mimeType) {
   if (!mimeType) {
     return "";
@@ -370,16 +385,17 @@ function getResponseHeader(item, header)
     if (responseHeader.name.toLowerCase() == header) {
       return responseHeader.value;
     }
   }
   return null;
 }
 
 module.exports = {
+  decodeUnicodeBase64,
   getFormDataSections,
   fetchHeaders,
   formDataURI,
   writeHeaderText,
   decodeUnicodeUrl,
   getAbbreviatedMimeType,
   getEndTime,
   getFormattedProtocol,