--- a/devtools/client/netmonitor/src/components/request-list-column-file.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-file.js
@@ -35,17 +35,16 @@ const RequestListColumnFile = createClas
return (
div({
className: "requests-list-column requests-list-file",
title: urlDetails.unicodeUrl,
},
img({
className: "requests-list-icon",
src: responseContentDataUri,
- "data-type": responseContentDataUri ? "thumbnail" : undefined,
}),
urlDetails.baseNameWithQuery
)
);
}
});
module.exports = RequestListColumnFile;
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -88,28 +88,16 @@ const EVENTS = {
UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
// When response content begins, updates and finishes receiving.
STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
- // When the request post params are displayed in the UI.
- REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable",
-
- // When the image response thumbnail is displayed in the UI.
- RESPONSE_IMAGE_THUMBNAIL_DISPLAYED:
- "NetMonitor:ResponseImageThumbnailAvailable",
-
- // Fired when charts have been displayed in the PerformanceStatisticsView.
- PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
- PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
- EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
-
// Fired once the NetMonitorController establishes a connection to the debug
// target.
CONNECTED: "connected",
};
const HEADERS = [
{
name: "status",
--- a/devtools/client/netmonitor/src/netmonitor-controller.js
+++ b/devtools/client/netmonitor/src/netmonitor-controller.js
@@ -2,20 +2,17 @@
* 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 { TimelineFront } = require("devtools/shared/fronts/timeline");
const { CurlUtils } = require("devtools/client/shared/curl");
const { ACTIVITY_TYPE, EVENTS } = require("./constants");
-const {
- getRequestById,
- getDisplayedRequestById,
-} = require("./selectors/index");
+const { getDisplayedRequestById } = require("./selectors/index");
const {
fetchHeaders,
formDataURI,
} = require("./utils/request-utils");
const {
getLongString,
getWebConsoleClient,
onFirefoxConnect,
@@ -302,17 +299,16 @@ function NetworkEventsHandler() {
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
this._onRequestHeaders = this._onRequestHeaders.bind(this);
this._onRequestCookies = this._onRequestCookies.bind(this);
this._onRequestPostData = this._onRequestPostData.bind(this);
this._onResponseHeaders = this._onResponseHeaders.bind(this);
this._onResponseCookies = this._onResponseCookies.bind(this);
- this._onResponseContent = this._onResponseContent.bind(this);
this._onSecurityInfo = this._onSecurityInfo.bind(this);
this._onEventTimings = this._onEventTimings.bind(this);
}
NetworkEventsHandler.prototype = {
get client() {
return NetMonitorController._target.client;
},
@@ -423,125 +419,151 @@ NetworkEventsHandler.prototype = {
fromCache,
fromServiceWorker,
},
true
)
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
},
- async updateRequest(id, data) {
- await this.actions.updateRequest(id, data, true);
- let {
- responseContent,
- responseCookies,
- responseHeaders,
- requestCookies,
- requestHeaders,
- requestPostData,
- } = data;
- let request = getRequestById(window.gStore.getState(), id);
-
- if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
- let headers = await fetchHeaders(requestHeaders, getLongString);
- if (headers) {
- await this.actions.updateRequest(
- id,
- { requestHeaders: headers },
- true,
- );
- }
- }
-
- if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
- let headers = await fetchHeaders(responseHeaders, getLongString);
- if (headers) {
- await this.actions.updateRequest(
- id,
- { responseHeaders: headers },
- true,
- );
- }
- }
-
- if (request && responseContent && responseContent.content) {
- let { mimeType } = request;
- let { text, encoding } = responseContent.content;
+ async fetchImage(mimeType, responseContent) {
+ let payload = {};
+ if (mimeType && responseContent && responseContent.content) {
+ let { encoding, text } = responseContent.content;
let response = await getLongString(text);
- let payload = {};
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
-
- await this.actions.updateRequest(id, payload, true);
+ }
+ return payload;
+ },
- if (mimeType.includes("image/")) {
- window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
+ async fetchRequestHeaders(requestHeaders) {
+ let payload = {};
+ if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
+ let headers = await fetchHeaders(requestHeaders, getLongString);
+ if (headers) {
+ payload.requestHeaders = headers;
}
}
+ return payload;
+ },
- // Search the POST data upload stream for request headers and add
- // them as a separate property, different from the classic headers.
+ async fetchResponseHeaders(responseHeaders) {
+ let payload = {};
+ if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
+ let headers = await fetchHeaders(responseHeaders, getLongString);
+ if (headers) {
+ payload.responseHeaders = headers;
+ }
+ }
+ return payload;
+ },
+
+ // Search the POST data upload stream for request headers and add
+ // them as a separate property, different from the classic headers.
+ async fetchPostData(requestPostData) {
+ let payload = {};
if (requestPostData && requestPostData.postData) {
let { text } = requestPostData.postData;
let postData = await getLongString(text);
const headers = CurlUtils.getHeadersFromMultipartText(postData);
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
}, 0);
- let payload = {};
requestPostData.postData.text = postData;
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
-
- await this.actions.updateRequest(id, payload, true);
}
+ return payload;
+ },
- // Fetch request and response cookies long value.
- // Actor does not provide full sized cookie value when the value is too long
- // To display values correctly, we need fetch them in each request.
+ async fetchResponseCookies(responseCookies) {
+ let payload = {};
+ if (responseCookies) {
+ let resCookies = [];
+ // response store cookies in responseCookies or responseCookies.cookies
+ let cookies = responseCookies.cookies ?
+ responseCookies.cookies : responseCookies;
+ // make sure cookies is iterable
+ if (typeof cookies[Symbol.iterator] === "function") {
+ for (let cookie of cookies) {
+ resCookies.push(Object.assign({}, cookie, {
+ value: await getLongString(cookie.value),
+ }));
+ }
+ if (resCookies.length) {
+ payload.responseCookies = resCookies;
+ }
+ }
+ }
+ return payload;
+ },
+
+ // Fetch request and response cookies long value.
+ // Actor does not provide full sized cookie value when the value is too long
+ // To display values correctly, we need fetch them in each request.
+ async fetchRequestCookies(requestCookies) {
+ let payload = {};
if (requestCookies) {
let reqCookies = [];
// request store cookies in requestCookies or requestCookies.cookies
let cookies = requestCookies.cookies ?
requestCookies.cookies : requestCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
reqCookies.push(Object.assign({}, cookie, {
value: await getLongString(cookie.value),
}));
}
if (reqCookies.length) {
- await this.actions.updateRequest(id, { requestCookies: reqCookies }, true);
+ payload.requestCookies = reqCookies;
}
}
}
+ return payload;
+ },
- if (responseCookies) {
- let resCookies = [];
- // response store cookies in responseCookies or responseCookies.cookies
- let cookies = responseCookies.cookies ?
- responseCookies.cookies : responseCookies;
- // make sure cookies is iterable
- if (typeof cookies[Symbol.iterator] === "function") {
- for (let cookie of cookies) {
- resCookies.push(Object.assign({}, cookie, {
- value: await getLongString(cookie.value),
- }));
- }
- if (resCookies.length) {
- await this.actions.updateRequest(id, { responseCookies: resCookies }, true);
- }
- }
- }
+ async updateRequest(id, data) {
+ let {
+ mimeType,
+ responseContent,
+ responseCookies,
+ responseHeaders,
+ requestCookies,
+ requestHeaders,
+ requestPostData,
+ } = data;
+
+ // fetch request detail contents in parallel
+ let [
+ imageObj,
+ requestHeadersObj,
+ responseHeadersObj,
+ postDataObj,
+ requestCookiesObj,
+ responseCookiesObj,
+ ] = await Promise.all([
+ this.fetchImage(mimeType, responseContent),
+ this.fetchRequestHeaders(requestHeaders),
+ this.fetchResponseHeaders(responseHeaders),
+ this.fetchPostData(requestPostData),
+ this.fetchRequestCookies(requestCookies),
+ this.fetchResponseCookies(responseCookies),
+ ]);
+
+ let payload = Object.assign({}, data,
+ imageObj, requestHeadersObj, responseHeadersObj,
+ postDataObj, requestCookiesObj, responseCookiesObj);
+ await this.actions.updateRequest(id, payload, true);
},
/**
* The "networkEventUpdate" message type handler.
*
* @param string type
* Message type.
* @param object packet
@@ -563,19 +585,20 @@ NetworkEventsHandler.prototype = {
case "requestPostData":
this.webConsoleClient.getRequestPostData(actor,
this._onRequestPostData);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break;
case "securityInfo":
this.updateRequest(actor, {
securityState: networkInfo.securityInfo,
+ }).then(() => {
+ this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
+ window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
});
- this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
- window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
break;
case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor,
this._onResponseHeaders);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
break;
case "responseCookies":
this.webConsoleClient.getResponseCookies(actor,
@@ -585,35 +608,36 @@ NetworkEventsHandler.prototype = {
case "responseStart":
this.updateRequest(actor, {
httpVersion: networkInfo.response.httpVersion,
remoteAddress: networkInfo.response.remoteAddress,
remotePort: networkInfo.response.remotePort,
status: networkInfo.response.status,
statusText: networkInfo.response.statusText,
headersSize: networkInfo.response.headersSize
+ }).then(() => {
+ window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
});
- window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
break;
case "responseContent":
- this.updateRequest(actor, {
- contentSize: networkInfo.response.bodySize,
- transferredSize: networkInfo.response.transferredSize,
- mimeType: networkInfo.response.content.mimeType
- });
this.webConsoleClient.getResponseContent(actor,
- this._onResponseContent);
+ this._onResponseContent.bind(this, {
+ contentSize: networkInfo.response.bodySize,
+ transferredSize: networkInfo.response.transferredSize,
+ mimeType: networkInfo.response.content.mimeType
+ }));
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
break;
case "eventTimings":
this.updateRequest(actor, {
totalTime: networkInfo.totalTime
+ }).then(() => {
+ this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
+ window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
});
- this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
- window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
break;
}
},
/**
* Handles additional information received for a "requestHeaders" packet.
*
* @param object response
@@ -695,23 +719,24 @@ NetworkEventsHandler.prototype = {
}).then(() => {
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
});
},
/**
* Handles additional information received for a "responseContent" packet.
*
+ * @param object data
+ * The message received from the server event.
* @param object response
* The message received from the server.
*/
- _onResponseContent: function (response) {
- this.updateRequest(response.from, {
- responseContent: response
- }).then(() => {
+ _onResponseContent: function (data, response) {
+ let payload = Object.assign({ responseContent: response }, data);
+ this.updateRequest(response.from, payload).then(() => {
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
});
},
/**
* Handles additional information received for a "eventTimings" packet.
*
* @param object response
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -4,74 +4,66 @@
"use strict";
/**
* Tests if image responses show a thumbnail in the requests menu.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
+ const SELECTOR = ".requests-list-icon[src]";
info("Starting test... ");
let { document, gStore, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { NetMonitorController } =
windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
- let {
- ACTIVITY_TYPE,
- EVENTS,
- } = windowRequire("devtools/client/netmonitor/src/constants");
+ let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
gStore.dispatch(Actions.batchEnable(false));
- let wait = waitForEvents();
+ let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield performRequests();
yield wait;
+ yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail when all items are shown.");
checkImageThumbnail();
gStore.dispatch(Actions.sortBy("contentSize"));
info("Checking the image thumbnail when all items are sorted.");
checkImageThumbnail();
gStore.dispatch(Actions.toggleRequestFilterType("images"));
info("Checking the image thumbnail when only images are shown.");
checkImageThumbnail();
info("Reloading the debuggee and performing all requests again...");
- wait = waitForEvents();
+ wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield reloadAndPerformRequests();
yield wait;
+ yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail after a reload.");
checkImageThumbnail();
yield teardown(monitor);
- function waitForEvents() {
- return promise.all([
- waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS),
- monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED)
- ]);
- }
-
function performRequests() {
return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
}
function* reloadAndPerformRequests() {
yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
yield performRequests();
}
function checkImageThumbnail() {
- is(document.querySelectorAll(".requests-list-icon[data-type=thumbnail]").length, 1,
+ is(document.querySelectorAll(SELECTOR).length, 1,
"There should be only one image request with a thumbnail displayed.");
- is(document.querySelector(".requests-list-icon[data-type=thumbnail]").src,
- TEST_IMAGE_DATA_URI,
+ is(document.querySelector(SELECTOR).src, TEST_IMAGE_DATA_URI,
"The image requests-list-icon thumbnail is displayed correctly.");
- is(document.querySelector(".requests-list-icon[data-type=thumbnail]").hidden, false,
+ is(document.querySelector(SELECTOR).hidden, false,
"The image requests-list-icon thumbnail should not be hidden.");
}
});
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js
+++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js
@@ -6,53 +6,49 @@
const IMAGE_TOOLTIP_URL = EXAMPLE_URL + "html_image-tooltip-test-page.html";
const IMAGE_TOOLTIP_REQUESTS = 1;
/**
* Tests if image responses show a popup in the requests menu when hovered.
*/
add_task(function* test() {
let { tab, monitor } = yield initNetMonitor(IMAGE_TOOLTIP_URL);
+ const SELECTOR = ".requests-list-icon[src]";
info("Starting test... ");
let { document, gStore, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { NetMonitorController } =
windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
- let {
- ACTIVITY_TYPE,
- EVENTS,
- } = windowRequire("devtools/client/netmonitor/src/constants");
+ let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
let toolboxDoc = monitor.panelWin.parent.document;
gStore.dispatch(Actions.batchEnable(false));
let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS);
- let onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
yield performRequests();
yield onEvents;
- yield onThumbnail;
+ yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail after a few requests were made...");
yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
// Hide tooltip before next test, to avoid the situation that tooltip covers
// the icon for the request of the next test.
info("Checking the image thumbnail gets hidden...");
yield hideTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
// +1 extra document reload
onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
- onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
info("Reloading the debuggee and performing all requests again...");
yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
yield performRequests();
yield onEvents;
- yield onThumbnail;
+ yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail after a reload.");
yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[1]);
info("Checking if the image thumbnail is hidden when mouse leaves the menu widget");
let requestsListContents = document.querySelector(".requests-list-contents");
EventUtils.synthesizeMouse(requestsListContents, 0, 0, { type: "mouseout" },
monitor.panelWin);
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -188,44 +188,26 @@ function test() {
SIMPLE_SJS,
{
status: "200",
statusText: "Och Aye"
}
);
});
- monitor.panelWin.once(EVENTS.UPDATING_RESPONSE_CONTENT, () => {
+ monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
let requestItem = getSortedRequests(gStore.getState()).get(0);
is(requestItem.transferredSize, "12",
"The transferredSize data has an incorrect value.");
is(requestItem.contentSize, "12",
"The contentSize data has an incorrect value.");
is(requestItem.mimeType, "text/plain; charset=utf-8",
"The mimeType data has an incorrect value.");
- verifyRequestItemTarget(
- document,
- getDisplayedRequests(gStore.getState()),
- requestItem,
- "GET",
- SIMPLE_SJS,
- {
- type: "plain",
- fullMimeType: "text/plain; charset=utf-8",
- transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
- size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
- }
- );
- });
-
- monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
- let requestItem = getSortedRequests(gStore.getState()).get(0);
-
ok(requestItem.responseContent,
"There should be a responseContent data available.");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(requestItem.responseContent.content.mimeType,
"text/plain; charset=utf-8",
"The responseContent data has an incorrect |content.mimeType| property.");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(requestItem.responseContent.content.text,