--- a/devtools/client/netmonitor/components/monitor-panel.js
+++ b/devtools/client/netmonitor/components/monitor-panel.js
@@ -8,16 +8,17 @@ const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Actions = require("../actions/index");
+const { getLongString } = require("../utils/client");
const { Prefs } = require("../utils/prefs");
const { getFormDataSections } = require("../utils/request-utils");
const { getSelectedRequest } = require("../selectors/index");
// Components
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/split-box"));
const NetworkDetailsPanel = createFactory(require("../shared/components/network-details-panel"));
const RequestList = createFactory(require("./request-list"));
@@ -64,17 +65,17 @@ const MonitorPanel = createClass({
} = request;
if (!formDataSections && requestHeaders &&
requestHeadersFromUploadStream && requestPostData) {
getFormDataSections(
requestHeaders,
requestHeadersFromUploadStream,
requestPostData,
- window.gNetwork.getString.bind(window.gNetwork),
+ getLongString,
).then((newFormDataSections) => {
updateRequest(
request.id,
{ formDataSections: newFormDataSections },
true,
);
});
}
--- a/devtools/client/netmonitor/har/har-builder.js
+++ b/devtools/client/netmonitor/har/har-builder.js
@@ -264,17 +264,16 @@ HarBuilder.prototype = {
if (CurlUtils.isUrlEncodedRequest({ headers, postDataText })) {
postData.mimeType = "application/x-www-form-urlencoded";
// Extract form parameters and produce nice HAR array.
getFormDataSections(
file.requestHeaders,
file.requestHeadersFromUploadStream,
file.requestPostData,
- this._options.getString
).then(formDataSections => {
formDataSections.forEach(section => {
let paramsArray = parseQueryString(section);
if (paramsArray) {
postData.params = [...postData.params, ...paramsArray];
}
});
});
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -8,16 +8,17 @@ const { TimelineFront } = require("devto
const { CurlUtils } = require("devtools/client/shared/curl");
const { ACTIVITY_TYPE, EVENTS } = require("./constants");
const Actions = require("./actions/index");
const {
fetchHeaders,
formDataURI,
} = require("./utils/request-utils");
const {
+ getLongString,
getWebConsoleClient,
onFirefoxConnect,
onFirefoxDisconnect,
} = require("./utils/client");
const {
getRequestById,
getDisplayedRequestById,
} = require("./selectors/index");
@@ -103,17 +104,16 @@ var NetMonitorController = {
onFirefoxConnect(this._target);
this._target.on("close", this._onTabDetached);
this.webConsoleClient = getWebConsoleClient();
this.NetworkEventsHandler = new NetworkEventsHandler();
this.NetworkEventsHandler.connect();
- window.gNetwork = this.NetworkEventsHandler;
window.emit(EVENTS.CONNECTED);
resolve();
this._connected = true;
});
return this._connection;
},
@@ -368,17 +368,16 @@ var NetMonitorController = {
};
/**
* Functions handling target network events.
*/
function NetworkEventsHandler() {
this.addRequest = this.addRequest.bind(this);
this.updateRequest = this.updateRequest.bind(this);
- this.getString = this.getString.bind(this);
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);
@@ -511,41 +510,41 @@ NetworkEventsHandler.prototype = {
responseHeaders,
requestCookies,
requestHeaders,
requestPostData,
} = action.data;
let request = getRequestById(gStore.getState(), action.id);
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
- let headers = await fetchHeaders(requestHeaders, this.getString);
+ let headers = await fetchHeaders(requestHeaders, getLongString);
if (headers) {
await gStore.dispatch(Actions.updateRequest(
action.id,
{ requestHeaders: headers },
true,
));
}
}
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
- let headers = await fetchHeaders(responseHeaders, this.getString);
+ let headers = await fetchHeaders(responseHeaders, getLongString);
if (headers) {
await gStore.dispatch(Actions.updateRequest(
action.id,
{ responseHeaders: headers },
true,
));
}
}
if (request && responseContent && responseContent.content) {
let { mimeType } = request;
let { text, encoding } = responseContent.content;
- let response = await this.getString(text);
+ let response = await getLongString(text);
let payload = {};
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
@@ -556,17 +555,17 @@ NetworkEventsHandler.prototype = {
window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
}
}
// Search the POST data upload stream for request headers and add
// them as a separate property, different from the classic headers.
if (requestPostData && requestPostData.postData) {
let { text } = requestPostData.postData;
- let postData = await this.getString(text);
+ 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 };
@@ -581,17 +580,17 @@ NetworkEventsHandler.prototype = {
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 this.getString(cookie.value),
+ value: await getLongString(cookie.value),
}));
}
if (reqCookies.length) {
await gStore.dispatch(Actions.updateRequest(
action.id,
{ requestCookies: reqCookies },
true));
}
@@ -602,17 +601,17 @@ NetworkEventsHandler.prototype = {
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 this.getString(cookie.value),
+ value: await getLongString(cookie.value),
}));
}
if (resCookies.length) {
await gStore.dispatch(Actions.updateRequest(
action.id,
{ responseCookies: resCookies },
true));
}
@@ -799,33 +798,12 @@ NetworkEventsHandler.prototype = {
* The message received from the server.
*/
_onEventTimings: function (response) {
this.updateRequest(response.from, {
eventTimings: response
}).then(() => {
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
});
- },
-
- /**
- * Fetches the full text of a LongString.
- *
- * @param object | string stringGrip
- * The long string grip containing the corresponding actor.
- * If you pass in a plain string (by accident or because you're lazy),
- * then a promise of the same string is simply returned.
- * @return object Promise
- * A promise that is resolved when the full string contents
- * are available, or rejected if something goes wrong.
- */
- getString: function (stringGrip) {
- // FIXME: this.webConsoleClient will be undefined in mochitest,
- // so we return string instantly to skip undefined error
- if (typeof stringGrip === "string") {
- return Promise.resolve(stringGrip);
- }
-
- return this.webConsoleClient.getString(stringGrip);
}
};
exports.NetMonitorController = NetMonitorController;
--- a/devtools/client/netmonitor/request-list-context-menu.js
+++ b/devtools/client/netmonitor/request-list-context-menu.js
@@ -6,16 +6,17 @@
const Services = require("Services");
const { Curl } = require("devtools/client/shared/curl");
const { gDevTools } = require("devtools/client/framework/devtools");
const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const { HarExporter } = require("./har/har-exporter");
+const { getLongString } = require("./utils/client");
const { L10N } = require("./utils/l10n");
const {
formDataURI,
getFormDataSections,
getUrlQuery,
parseQueryString,
} = require("./utils/request-utils");
const {
@@ -141,17 +142,18 @@ RequestListContextMenu.prototype = {
label: L10N.getStr("netmonitor.context.saveAllAsHar"),
accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
visible: this.sortedRequests.size > 0,
click: () => this.saveAllAsHar(),
}));
menu.append(new MenuItem({
type: "separator",
- visible: !!selectedRequest,
+ visible: !!(window.NetMonitorController.supportsCustomRequest &&
+ selectedRequest && !selectedRequest.isCustom),
}));
menu.append(new MenuItem({
id: "request-list-context-resend",
label: L10N.getStr("netmonitor.context.editAndResend"),
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
visible: !!(window.NetMonitorController.supportsCustomRequest &&
selectedRequest && !selectedRequest.isCustom),
@@ -216,34 +218,34 @@ RequestListContextMenu.prototype = {
async copyPostData() {
let selected = this.selectedRequest;
// Try to extract any form data parameters.
let formDataSections = await getFormDataSections(
selected.requestHeaders,
selected.requestHeadersFromUploadStream,
selected.requestPostData,
- window.gNetwork.getString.bind(window.gNetwork));
+ getLongString);
let params = [];
formDataSections.forEach(section => {
let paramsArray = parseQueryString(section);
if (paramsArray) {
params = [...params, ...paramsArray];
}
});
let string = params
.map(param => param.name + (param.value ? "=" + param.value : ""))
.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
// Fall back to raw payload.
if (!string) {
let postData = selected.requestPostData.postData.text;
- string = await window.gNetwork.getString(postData);
+ string = await getLongString(postData);
if (Services.appinfo.OS !== "WINNT") {
string = string.replace(/\r/g, "");
}
}
clipboardHelper.copyString(string);
},
@@ -259,24 +261,24 @@ RequestListContextMenu.prototype = {
method: selected.method,
headers: [],
httpVersion: selected.httpVersion,
postDataText: null
};
// Fetch header values.
for (let { name, value } of selected.requestHeaders.headers) {
- let text = await window.gNetwork.getString(value);
+ let text = await getLongString(value);
data.headers.push({ name: name, value: text });
}
// Fetch the request payload.
if (selected.requestPostData) {
let postData = selected.requestPostData.postData.text;
- data.postDataText = await window.gNetwork.getString(postData);
+ data.postDataText = await getLongString(postData);
}
clipboardHelper.copyString(Curl.generateCommand(data));
},
/**
* Copy the raw request headers from the currently selected item.
*/
@@ -300,29 +302,29 @@ RequestListContextMenu.prototype = {
},
/**
* Copy image as data uri.
*/
copyImageAsDataUri() {
const { mimeType, text, encoding } = this.selectedRequest.responseContent.content;
- window.gNetwork.getString(text).then(string => {
+ getLongString(text).then(string => {
let data = formDataURI(mimeType, encoding, string);
clipboardHelper.copyString(data);
});
},
/**
* Copy response data as a string.
*/
copyResponse() {
const { text } = this.selectedRequest.responseContent.content;
- window.gNetwork.getString(text).then(string => {
+ getLongString(text).then(string => {
clipboardHelper.copyString(string);
});
},
/**
* Copy HAR from the network panel content to the clipboard.
*/
copyAllAsHar() {
@@ -336,16 +338,16 @@ RequestListContextMenu.prototype = {
return HarExporter.save(this.getDefaultHarOptions());
},
getDefaultHarOptions() {
let form = window.NetMonitorController._target.form;
let title = form.title || form.url;
return {
- getString: window.gNetwork.getString.bind(window.gNetwork),
+ getString: getLongString,
items: this.sortedRequests,
title: title
};
}
};
module.exports = RequestListContextMenu;
--- a/devtools/client/netmonitor/request-list-tooltip.js
+++ b/devtools/client/netmonitor/request-list-tooltip.js
@@ -3,32 +3,33 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
setImageTooltip,
getImageDimensions,
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
+const { getLongString } = require("./utils/client");
const { WEBCONSOLE_L10N } = require("./utils/l10n");
const { formDataURI } = require("./utils/request-utils");
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600; // px
const HTML_NS = "http://www.w3.org/1999/xhtml";
async function setTooltipImageContent(tooltip, itemEl, requestItem) {
let { mimeType, text, encoding } = requestItem.responseContent.content;
if (!mimeType || !mimeType.includes("image/")) {
return false;
}
- let string = await window.gNetwork.getString(text);
+ let string = await getLongString(text);
let src = formDataURI(mimeType, encoding, string);
let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
let options = { maxDim, naturalWidth, naturalHeight };
setImageTooltip(tooltip, tooltip.doc, src, options);
return itemEl.querySelector(".requests-list-icon");
}
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -8,49 +8,50 @@
*/
const { CurlUtils } = require("devtools/client/shared/curl");
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL);
info("Starting test... ");
- let { gStore, windowRequire, gNetwork } = monitor.panelWin;
+ let { gStore, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let { getSortedRequests } = windowRequire("devtools/client/netmonitor/selectors/index");
+ let { getLongString } = windowRequire("devtools/client/netmonitor/utils/client");
gStore.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, 1, 3);
yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) {
content.wrappedJSObject.performRequests(url);
});
yield wait;
let requests = {
get: getSortedRequests(gStore.getState()).get(0),
post: getSortedRequests(gStore.getState()).get(1),
multipart: getSortedRequests(gStore.getState()).get(2),
multipartForm: getSortedRequests(gStore.getState()).get(3),
};
- let data = yield createCurlData(requests.get, gNetwork);
+ let data = yield createCurlData(requests.get, getLongString);
testFindHeader(data);
- data = yield createCurlData(requests.post, gNetwork);
+ data = yield createCurlData(requests.post, getLongString);
testIsUrlEncodedRequest(data);
testWritePostDataTextParams(data);
- data = yield createCurlData(requests.multipart, gNetwork);
+ data = yield createCurlData(requests.multipart, getLongString);
testIsMultipartRequest(data);
testGetMultipartBoundary(data);
testMultiPartHeaders(data);
testRemoveBinaryDataFromMultipartText(data);
- data = yield createCurlData(requests.multipartForm, gNetwork);
+ data = yield createCurlData(requests.multipartForm, getLongString);
testMultiPartHeaders(data);
testGetHeadersFromMultipartText({
postDataText: "Content-Type: text/plain\r\n\r\n",
});
if (Services.appinfo.OS != "WINNT") {
testEscapeStringPosix();
@@ -209,34 +210,34 @@ function testEscapeStringWin() {
"Backslashes should be escaped.");
let newLines = "line1\r\nline2\r\nline3";
is(CurlUtils.escapeStringWin(newLines),
'"line1"^\u000d\u000A"line2"^\u000d\u000A"line3"',
"Newlines should be escaped.");
}
-function* createCurlData(selected, network, controller) {
+function* createCurlData(selected, getLongString) {
let { url, method, httpVersion } = selected;
// Create a sanitized object for the Curl command generator.
let data = {
url,
method,
headers: [],
httpVersion,
postDataText: null
};
// Fetch header values.
for (let { name, value } of selected.requestHeaders.headers) {
- let text = yield network.getString(value);
+ let text = yield getLongString(value);
data.headers.push({ name: name, value: text });
}
// Fetch the request payload.
if (selected.requestPostData) {
let postData = selected.requestPostData.postData.text;
- data.postDataText = yield network.getString(postData);
+ data.postDataText = yield getLongString(postData);
}
return data;
}
--- a/devtools/client/netmonitor/utils/client.js
+++ b/devtools/client/netmonitor/utils/client.js
@@ -67,13 +67,35 @@ function onFirefoxDisconnect(tabTarget)
* Retrieve webconsole object
*
* @returns {Object} webConsole
*/
function getWebConsoleClient() {
return activeConsole;
}
+/**
+ * Fetches the full text of a LongString.
+ *
+ * @param object | string stringGrip
+ * The long string grip containing the corresponding actor.
+ * If you pass in a plain string (by accident or because you're lazy),
+ * then a promise of the same string is simply returned.
+ * @return object Promise
+ * A promise that is resolved when the full string contents
+ * are available, or rejected if something goes wrong.
+ */
+function getLongString(stringGrip) {
+ // FIXME: this.webConsoleClient will be undefined in mochitest,
+ // so we return string instantly to skip undefined error
+ if (typeof stringGrip === "string") {
+ return Promise.resolve(stringGrip);
+ }
+
+ return activeConsole.getString(stringGrip);
+}
+
module.exports = {
+ getLongString,
getWebConsoleClient,
onFirefoxConnect,
onFirefoxDisconnect,
};
--- a/devtools/client/netmonitor/utils/request-utils.js
+++ b/devtools/client/netmonitor/utils/request-utils.js
@@ -8,37 +8,36 @@
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
*
* @param {object} headers - the "requestHeaders".
* @param {object} uploadHeaders - the "requestHeadersFromUploadStream".
* @param {object} postData - the "requestPostData".
- * @param {function} getString - callback to retrieve a string from a LongStringGrip.
* @return {array} a promise list that is resolved with the extracted form data.
*/
-async function getFormDataSections(headers, uploadHeaders, postData, getString) {
+async function getFormDataSections(headers, uploadHeaders, postData, getLongString) {
let formDataSections = [];
let requestHeaders = headers.headers;
let payloadHeaders = uploadHeaders ? uploadHeaders.headers : [];
let allHeaders = [...payloadHeaders, ...requestHeaders];
let contentTypeHeader = allHeaders.find(e => {
return e.name.toLowerCase() == "content-type";
});
let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
- let contentType = await getString(contentTypeLongString);
+ let contentType = await getLongString(contentTypeLongString);
if (contentType.includes("x-www-form-urlencoded")) {
let postDataLongString = postData.postData.text;
- let text = await getString(postDataLongString);
+ let text = await getLongString(postDataLongString);
for (let section of text.split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
// isn't a line containing upload stream headers.
if (payloadHeaders.every(header => !section.startsWith(header.name))) {
formDataSections.push(section);
}
}
@@ -46,22 +45,21 @@ async function getFormDataSections(heade
return formDataSections;
}
/**
* Fetch headers full content from actor server
*
* @param {object} headers - a object presents headers data
- * @param {function} getString - callback to retrieve a string from a LongStringGrip
* @return {object} a headers object with updated content payload
*/
-async function fetchHeaders(headers, getString) {
+async function fetchHeaders(headers, getLongString) {
for (let { value } of headers.headers) {
- headers.headers.value = await getString(value);
+ headers.headers.value = await getLongString(value);
}
return headers;
}
/**
* Form a data: URI given a mime type, encoding, and some text.
*
* @param {string} mimeType - mime type