--- a/devtools/client/netmonitor/src/components/CustomRequestPanel.js
+++ b/devtools/client/netmonitor/src/components/CustomRequestPanel.js
@@ -44,34 +44,26 @@ class CustomRequestPanel extends Compone
request: PropTypes.object,
sendCustomRequest: PropTypes.func.isRequired,
updateRequest: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
- this.maybeFetchPostData = this.maybeFetchPostData.bind(this);
- this.parseRequestText = this.parseRequestText.bind(this);
- this.updateCustomRequestFields = this.updateCustomRequestFields.bind(this);
}
componentDidMount() {
this.maybeFetchPostData(this.props);
}
componentWillReceiveProps(nextProps) {
this.maybeFetchPostData(nextProps);
}
- /**
- * When switching to another request, lazily fetch post data
- * from the backend. The CustomRequestPanel will first be empty and then
- * display the content.
- */
maybeFetchPostData(props) {
if (!props.request.requestPostData ||
!props.request.requestPostData.postData.text) {
let actorId = props.request.id.replace("-clone", "");
// This method will set `props.request.requestPostData`
// asynchronously and force another render.
props.connector.requestData(actorId, "requestPostData");
}
--- a/devtools/client/netmonitor/src/components/HeadersPanel.js
+++ b/devtools/client/netmonitor/src/components/HeadersPanel.js
@@ -44,16 +44,17 @@ const SUMMARY_VERSION = L10N.getStr("net
/*
* Headers panel component
* Lists basic information about the request
*/
class HeadersPanel extends Component {
static get propTypes() {
return {
+ connector: PropTypes.object.isRequired,
cloneSelectedRequest: PropTypes.func.isRequired,
request: PropTypes.object.isRequired,
renderValue: PropTypes.func,
openLink: PropTypes.func,
};
}
constructor(props) {
@@ -62,16 +63,34 @@ class HeadersPanel extends Component {
this.state = {
rawHeadersOpened: false,
};
this.getProperties = this.getProperties.bind(this);
this.toggleRawHeaders = this.toggleRawHeaders.bind(this);
this.renderSummary = this.renderSummary.bind(this);
this.renderValue = this.renderValue.bind(this);
+ this.maybeFetchPostData = this.maybeFetchPostData.bind(this);
+ }
+
+ componentDidMount() {
+ this.maybeFetchPostData(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.maybeFetchPostData(nextProps);
+ }
+
+ maybeFetchPostData(props) {
+ if (!props.request.requestPostData ||
+ !props.request.requestPostData.postData.text) {
+ // This method will set `props.request.requestPostData`
+ // asynchronously and force another render.
+ props.connector.requestData(props.request.id, "requestPostData");
+ }
}
getProperties(headers, title) {
if (headers && headers.headers.length) {
let headerKey = `${title} (${getFormattedSize(headers.headersSize, 3)})`;
let propertiesResult = {
[headerKey]:
headers.headers.reduce((acc, { name, value }) =>
--- a/devtools/client/netmonitor/src/components/ParamsPanel.js
+++ b/devtools/client/netmonitor/src/components/ParamsPanel.js
@@ -43,35 +43,28 @@ class ParamsPanel extends Component {
openLink: PropTypes.func,
request: PropTypes.object.isRequired,
updateRequest: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
- this.maybeFetchPostData = this.maybeFetchPostData.bind(this);
- this.getProperties = this.getProperties.bind(this);
}
componentDidMount() {
this.maybeFetchPostData(this.props);
updateFormDataSections(this.props);
}
componentWillReceiveProps(nextProps) {
this.maybeFetchPostData(nextProps);
updateFormDataSections(nextProps);
}
- /**
- * When switching to another request, lazily fetch post data
- * from the backend. The CustomRequestPanel will first be empty and then
- * display the content.
- */
maybeFetchPostData(props) {
if (!props.request.requestPostData ||
!props.request.requestPostData.postData) {
// This method will set `props.request.requestPostData`
// asynchronously and force another render.
props.connector.requestData(props.request.id, "requestPostData");
}
}
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -8,16 +8,17 @@ const { Component, createFactory } = req
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const Actions = require("../actions/index");
const { setTooltipImageContent } = require("../request-list-tooltip");
const {
getDisplayedRequests,
+ getSelectedRequest,
getWaterfallScale,
} = require("../selectors/index");
// Components
const RequestListHeader = createFactory(require("./RequestListHeader"));
const RequestListItem = createFactory(require("./RequestListItem"));
const RequestListContextMenu = require("../request-list-context-menu");
@@ -31,59 +32,57 @@ const MAX_SCROLL_HEIGHT = 2147483647;
/**
* Renders the actual contents of the request list.
*/
class RequestListContent extends Component {
static get propTypes() {
return {
connector: PropTypes.object.isRequired,
columns: PropTypes.object.isRequired,
- dispatch: PropTypes.func.isRequired,
+ cloneSelectedRequest: PropTypes.func.isRequired,
displayedRequests: PropTypes.object.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
fromCache: PropTypes.bool,
onCauseBadgeMouseDown: PropTypes.func.isRequired,
onItemMouseDown: PropTypes.func.isRequired,
onSecurityIconMouseDown: PropTypes.func.isRequired,
onSelectDelta: PropTypes.func.isRequired,
onWaterfallMouseDown: PropTypes.func.isRequired,
+ openStatistics: PropTypes.func.isRequired,
scale: PropTypes.number,
- selectedRequestId: PropTypes.string,
+ selectedRequest: PropTypes.object,
};
}
constructor(props) {
super(props);
this.isScrolledToBottom = this.isScrolledToBottom.bind(this);
this.onHover = this.onHover.bind(this);
this.onScroll = this.onScroll.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onContextMenu = this.onContextMenu.bind(this);
this.onFocusedNodeChange = this.onFocusedNodeChange.bind(this);
}
componentWillMount() {
- const { dispatch, connector } = this.props;
+ const { connector, cloneSelectedRequest, openStatistics } = this.props;
this.contextMenu = new RequestListContextMenu({
- cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
- getTabTarget: connector.getTabTarget,
- getLongString: connector.getLongString,
- openStatistics: (open) => dispatch(Actions.openStatistics(connector, open)),
- requestData: connector.requestData,
+ connector,
+ cloneSelectedRequest,
+ openStatistics,
});
this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" });
}
componentDidMount() {
// Install event handler for displaying a tooltip
this.tooltip.startTogglingOnHover(this.refs.contentEl, this.onHover, {
toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
interactive: true
});
-
// Install event handler to hide the tooltip on scroll
this.refs.contentEl.addEventListener("scroll", this.onScroll, true);
}
componentWillUpdate(nextProps) {
// Check if the list is scrolled to bottom before the UI update.
// The scroll is ever needed only if new rows are added to the list.
const delta = nextProps.displayedRequests.size - this.props.displayedRequests.size;
@@ -215,37 +214,37 @@ class RequestListContent extends Compone
columns,
displayedRequests,
firstRequestStartedMillis,
onCauseBadgeMouseDown,
onItemMouseDown,
onSecurityIconMouseDown,
onWaterfallMouseDown,
scale,
- selectedRequestId,
+ selectedRequest,
} = this.props;
return (
- div({ className: "requests-list-wrapper"},
- div({ className: "requests-list-table"},
+ div({ className: "requests-list-wrapper" },
+ div({ className: "requests-list-table" },
div({
ref: "contentEl",
className: "requests-list-contents",
tabIndex: 0,
onKeyDown: this.onKeyDown,
- style: {"--timings-scale": scale, "--timings-rev-scale": 1 / scale}
+ style: { "--timings-scale": scale, "--timings-rev-scale": 1 / scale }
},
RequestListHeader(),
displayedRequests.map((item, index) => RequestListItem({
firstRequestStartedMillis,
fromCache: item.status === "304" || item.fromCache,
columns,
item,
index,
- isSelected: item.id === selectedRequestId,
+ isSelected: item.id === (selectedRequest && selectedRequest.id),
key: item.id,
onContextMenu: this.onContextMenu,
onFocusedNodeChange: this.onFocusedNodeChange,
onMouseDown: () => onItemMouseDown(item.id),
onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
onWaterfallMouseDown: () => onWaterfallMouseDown(),
}))
@@ -256,21 +255,22 @@ class RequestListContent extends Compone
}
}
module.exports = connect(
(state) => ({
columns: state.ui.columns,
displayedRequests: getDisplayedRequests(state),
firstRequestStartedMillis: state.requests.firstStartedMillis,
- selectedRequestId: state.requests.selectedId,
+ selectedRequest: getSelectedRequest(state),
scale: getWaterfallScale(state),
}),
- (dispatch) => ({
- dispatch,
+ (dispatch, props) => ({
+ cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
+ openStatistics: (open) => dispatch(Actions.openStatistics(props.connector, open)),
/**
* A handler that opens the stack trace tab when a stack trace is available
*/
onCauseBadgeMouseDown: (cause) => {
if (cause.stacktrace && cause.stacktrace.length > 0) {
dispatch(Actions.selectDetailsPanelTab("stack-trace"));
}
},
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -53,17 +53,22 @@ function TabboxPanel({
onSelect: selectTab,
renderOnlySelected: true,
showAllTabsMenu: true,
},
TabPanel({
id: PANELS.HEADERS,
title: HEADERS_TITLE,
},
- HeadersPanel({ request, cloneSelectedRequest, openLink }),
+ HeadersPanel({
+ cloneSelectedRequest,
+ connector,
+ openLink,
+ request,
+ }),
),
TabPanel({
id: PANELS.COOKIES,
title: COOKIES_TITLE,
},
CookiesPanel({ request, openLink }),
),
TabPanel({
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -30,16 +30,17 @@ class FirefoxDataProvider {
this.rdpRequestMap = new Map();
// Map[key string => Promise] used by `requestData` to prevent requesting the same
// request data twice.
this.lazyRequestData = new Map();
// Fetching data from the backend
this.getLongString = this.getLongString.bind(this);
+ this.getRequestFromQueue = this.getRequestFromQueue.bind(this);
// Event handlers
this.onNetworkEvent = this.onNetworkEvent.bind(this);
this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this);
}
/**
* Add a new network request to application state.
@@ -252,19 +253,19 @@ class FirefoxDataProvider {
// Also note that service worker don't have security info set.
// Bug 1404917 should simplify this heuristic by making all these field be lazily
// fetched, only on-demand.
return record.requestHeaders &&
record.requestCookies &&
record.eventTimings &&
(record.securityInfo || record.fromServiceWorker) &&
(
- (record.responseHeaders && record.responseCookies) ||
- payload.securityState == "broken" ||
- (payload.responseContentAvailable && !payload.status)
+ (record.responseHeaders && record.responseCookies) ||
+ payload.securityState == "broken" ||
+ (payload.responseContentAvailable && !payload.status)
);
}
/**
* Merge upcoming networkEventUpdate payload into existing one.
*
* @param {string} id request id
* @param {object} payload request data payload
@@ -376,16 +377,23 @@ class FirefoxDataProvider {
switch (updateType) {
case "requestHeaders":
case "requestCookies":
case "responseHeaders":
case "responseCookies":
this.requestPayloadData(actor, updateType);
break;
+ case "requestPostData":
+ this.updateRequest(actor, {
+ // This field helps knowing when/if requestPostData property is available
+ // and can be requested via `requestData`
+ requestPostDataAvailable: true,
+ });
+ break;
case "securityInfo":
this.updateRequest(actor, {
securityState: networkInfo.securityInfo,
}).then(() => {
this.requestPayloadData(actor, updateType);
});
break;
case "responseStart":
@@ -503,20 +511,22 @@ class FirefoxDataProvider {
// Fetch the data
promise = this._requestData(actor, method);
this.lazyRequestData.set(key, promise);
promise.then(async () => {
// Remove the request from the cache, any new call to requestData will fetch the
// data again.
this.lazyRequestData.delete(key, promise);
- let payloadFromQueue = this.getRequestFromQueue(actor).payload;
- let { updateRequest } = this.actions;
- if (updateRequest) {
- await updateRequest(actor, payloadFromQueue, true);
+ let request = this.getRequestFromQueue(actor);
+ if (request) {
+ let { updateRequest } = this.actions;
+ if (updateRequest) {
+ await updateRequest(actor, request.payload, true);
+ }
}
});
return promise;
}
/**
* Internal helper used to request HTTP details from the backend.
*
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -116,16 +116,17 @@ const UPDATE_PROPS = [
"totalTime",
"eventTimings",
"headersSize",
"customQueryValue",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
"requestPostData",
+ "requestPostDataAvailable",
"responseHeaders",
"responseCookies",
"responseContent",
"responseContentAvailable",
"formDataSections",
];
const PANELS = {
--- a/devtools/client/netmonitor/src/har/har-builder.js
+++ b/devtools/client/netmonitor/src/har/har-builder.js
@@ -115,17 +115,17 @@ HarBuilder.prototype = {
buildEntry: async function (log, file) {
let page = this.getPage(log, file);
let entry = {};
entry.pageref = page.id;
entry.startedDateTime = dateToJSON(new Date(file.startedMillis));
entry.time = file.endedMillis - file.startedMillis;
- entry.request = this.buildRequest(file);
+ entry.request = await this.buildRequest(file);
entry.response = await this.buildResponse(file);
entry.cache = this.buildCache(file);
entry.timings = file.eventTimings ? file.eventTimings.timings : {};
if (file.remoteAddress) {
entry.serverIPAddress = file.remoteAddress;
}
@@ -147,40 +147,34 @@ HarBuilder.prototype = {
let timings = {
onContentLoad: -1,
onLoad: -1
};
return timings;
},
- buildRequest: function (file) {
+ buildRequest: async function (file) {
let request = {
bodySize: 0
};
request.method = file.method;
request.url = file.url;
request.httpVersion = file.httpVersion || "";
-
request.headers = this.buildHeaders(file.requestHeaders);
request.headers = this.appendHeadersPostData(request.headers, file);
request.cookies = this.buildCookies(file.requestCookies);
-
request.queryString = parseQueryString(getUrlQuery(file.url)) || [];
+ request.headersSize = file.requestHeaders.headersSize;
+ request.postData = await this.buildPostData(file);
- if (file.requestPostData) {
- request.postData = this.buildPostData(file);
- }
-
- request.headersSize = file.requestHeaders.headersSize;
-
- // Set request body size, but make sure the body is fetched
- // from the backend.
- if (file.requestPostData) {
+ if (file.requestPostData && file.requestPostData.postData.text) {
+ // Set request body size, but make sure the body is fetched
+ // from the backend.
this.fetchData(file.requestPostData.postData.text).then(value => {
request.bodySize = value.length;
});
}
return request;
},
@@ -238,56 +232,66 @@ HarBuilder.prototype = {
value: value
});
});
});
return result;
},
- buildPostData: function (file) {
+ buildPostData: async function (file) {
+ // When using HarAutomation, HarCollector will automatically fetch requestPostData,
+ // but when we use it from netmonitor, FirefoxDataProvider should fetch it itself
+ // lazily, via requestData.
+ let requestPostData = file.requestPostData;
+
+ if (!requestPostData && this._options.requestData) {
+ requestPostData = await this._options.requestData(file.id, "requestPostData");
+ }
+
+ if (!requestPostData.postData.text) {
+ return undefined;
+ }
+
let postData = {
mimeType: findValue(file.requestHeaders.headers, "content-type"),
params: [],
text: ""
};
- if (!file.requestPostData) {
- return postData;
- }
-
- if (file.requestPostData.postDataDiscarded) {
+ if (requestPostData.postDataDiscarded) {
postData.comment = L10N.getStr("har.requestBodyNotIncluded");
return postData;
}
-
// Load request body from the backend.
- this.fetchData(file.requestPostData.postData.text).then(postDataText => {
+ postData = await this.fetchData(requestPostData.postData.text).then(postDataText => {
postData.text = postDataText;
// If we are dealing with URL encoded body, parse parameters.
let { headers } = file.requestHeaders;
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,
+ requestPostData,
this._options.getString,
).then(formDataSections => {
formDataSections.forEach(section => {
let paramsArray = parseQueryString(section);
if (paramsArray) {
postData.params = [...postData.params, ...paramsArray];
}
});
});
}
+
+ return postData;
});
return postData;
},
buildResponse: async function (file) {
let response = {
status: 0
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
@@ -14,28 +14,28 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { connector, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
- let { getLongString, getTabTarget, requestData } = connector;
+ let { getSortedRequests } = windowRequire(
+ "devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, 1);
tab.linkedBrowser.reload();
yield wait;
- let contextMenu = new RequestListContextMenu({
- getTabTarget, getLongString, requestData });
+ let contextMenu = new RequestListContextMenu({ connector });
- yield contextMenu.copyAllAsHar();
+ yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
let jsonString = SpecialPowers.getClipboardData("text/unicode");
let har = JSON.parse(jsonString);
// Check out HAR log
isnot(har.log, null, "The HAR log must exist");
is(har.log.creator.name, "Firefox", "The creator field must be set");
is(har.log.browser.name, "Firefox", "The browser field must be set");
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
@@ -11,31 +11,31 @@ add_task(function* () {
HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
info("Starting test... ");
let { connector, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
- let { getLongString, getTabTarget, requestData } = connector;
+ let { getSortedRequests } = windowRequire(
+ "devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
// Execute one POST request on the page and wait till its done.
let wait = waitForNetworkEvents(monitor, 0, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.executeTest();
});
yield wait;
// Copy HAR into the clipboard (asynchronous).
- let contextMenu = new RequestListContextMenu({
- getTabTarget, getLongString, requestData });
- let jsonString = yield contextMenu.copyAllAsHar();
+ let contextMenu = new RequestListContextMenu({ connector });
+ let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
let har = JSON.parse(jsonString);
// Check out the HAR log.
isnot(har.log, null, "The HAR log must exist");
is(har.log.pages.length, 1, "There must be one page");
is(har.log.entries.length, 1, "There must be one request");
let entry = har.log.entries[0];
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data_on_get.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data_on_get.js
@@ -11,37 +11,37 @@ add_task(function* () {
HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
info("Starting test... ");
let { connector, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
- let { getLongString, getTabTarget, requestData } = connector;
+ let { getSortedRequests } = windowRequire(
+ "devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
// Execute one GET request on the page and wait till its done.
let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.executeTest3();
});
yield wait;
// Copy HAR into the clipboard (asynchronous).
- let contextMenu = new RequestListContextMenu({
- getTabTarget, getLongString, requestData });
- let jsonString = yield contextMenu.copyAllAsHar();
+ let contextMenu = new RequestListContextMenu({ connector });
+ let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
let har = JSON.parse(jsonString);
// Check out the HAR log.
isnot(har.log, null, "The HAR log must exist");
is(har.log.pages.length, 1, "There must be one page");
is(har.log.entries.length, 1, "There must be one request");
let entry = har.log.entries[0];
- is(entry.request.postData, undefined,
- "Check post data is not present");
+
+ is(entry.request.postData, undefined, "Check post data is not present");
// Clean up
return teardown(monitor);
});
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
@@ -15,17 +15,18 @@ function* throttleUploadTest(actuallyThr
HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
let { connector, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
- let { getLongString, getTabTarget, setPreferences, requestData } = connector;
+ let { getSortedRequests } = windowRequire(
+ "devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
const size = 4096;
const uploadSize = actuallyThrottle ? size / 3 : 0;
const request = {
"NetworkMonitor.throttleData": {
@@ -35,32 +36,31 @@ function* throttleUploadTest(actuallyThr
downloadBPSMax: 200000,
uploadBPSMean: uploadSize,
uploadBPSMax: uploadSize,
},
};
info("sending throttle request");
yield new Promise((resolve) => {
- setPreferences(request, (response) => {
+ connector.setPreferences(request, (response) => {
resolve(response);
});
});
// Execute one POST request on the page and wait till its done.
let wait = waitForNetworkEvents(monitor, 0, 1);
yield ContentTask.spawn(tab.linkedBrowser, { size }, function* (args) {
content.wrappedJSObject.executeTest2(args.size);
});
yield wait;
// Copy HAR into the clipboard (asynchronous).
- let contextMenu = new RequestListContextMenu({
- getTabTarget, getLongString, requestData });
- let jsonString = yield contextMenu.copyAllAsHar();
+ let contextMenu = new RequestListContextMenu({ connector });
+ let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
let har = JSON.parse(jsonString);
// Check out the HAR log.
isnot(har.log, null, "The HAR log must exist");
is(har.log.pages.length, 1, "There must be one page");
is(har.log.entries.length, 1, "There must be one request");
let entry = har.log.entries[0];
--- a/devtools/client/netmonitor/src/reducers/requests.js
+++ b/devtools/client/netmonitor/src/reducers/requests.js
@@ -50,16 +50,17 @@ const Request = I.Record({
headersSize: undefined,
// Text value is used for storing custom request query
// which only appears when user edit the custom requst form
customQueryValue: undefined,
requestHeaders: undefined,
requestHeadersFromUploadStream: undefined,
requestCookies: undefined,
requestPostData: undefined,
+ requestPostDataAvailable: false,
responseHeaders: undefined,
responseCookies: undefined,
responseContent: undefined,
responseContentAvailable: false,
formDataSections: undefined,
});
const Requests = I.Record({
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-context-menu.js
@@ -12,398 +12,381 @@ const { copyString } = require("devtools
const { showMenu } = require("devtools/client/netmonitor/src/utils/menu");
const { HarExporter } = require("./har/har-exporter");
const {
getSelectedRequest,
getSortedRequests,
} = require("./selectors/index");
const { L10N } = require("./utils/l10n");
const {
+ formDataURI,
getUrlQuery,
+ getUrlBaseName,
parseQueryString,
- getUrlBaseName,
- formDataURI,
} = require("./utils/request-utils");
-function RequestListContextMenu({
- cloneSelectedRequest,
- getLongString,
- getTabTarget,
- openStatistics,
- requestData,
-}) {
- this.cloneSelectedRequest = cloneSelectedRequest;
- this.getLongString = getLongString;
- this.getTabTarget = getTabTarget;
- this.openStatistics = openStatistics;
- this.requestData = requestData;
-}
+class RequestListContextMenu {
+ constructor(props) {
+ this.props = props;
+ }
-RequestListContextMenu.prototype = {
- get selectedRequest() {
+ open(event) {
// FIXME: Bug 1336382 - Implement RequestListContextMenu React component
- // Remove window.store
- return getSelectedRequest(window.store.getState());
- },
-
- get sortedRequests() {
- // FIXME: Bug 1336382 - Implement RequestListContextMenu React component
- // Remove window.store
- return getSortedRequests(window.store.getState());
- },
-
- /**
- * Handle the context menu opening. Hide items if no request is selected.
- * Since visible attribute only accept boolean value but the method call may
- * return undefined, we use !! to force convert any object to boolean
- */
- async open(event = {}) {
- let selectedRequest = this.selectedRequest;
+ // Remove window.store.getState()
+ let selectedRequest = getSelectedRequest(window.store.getState());
+ let sortedRequests = getSortedRequests(window.store.getState());
let menu = [];
let copySubmenu = [];
- let menuItemVisible = !!selectedRequest;
- let [
- requestPostData,
- responseContent,
- ] = await Promise.all([
- this.requestData(selectedRequest.id, "requestPostData"),
- this.requestData(selectedRequest.id, "responseContent"),
- ]);
+ let {
+ id,
+ isCustom,
+ method,
+ mimeType,
+ httpVersion,
+ requestHeaders,
+ requestPostDataAvailable,
+ responseHeaders,
+ responseContentAvailable,
+ url,
+ } = selectedRequest || {};
+ let {
+ cloneSelectedRequest,
+ openStatistics,
+ } = this.props;
copySubmenu.push({
id: "request-list-context-copy-url",
label: L10N.getStr("netmonitor.context.copyUrl"),
accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"),
- visible: menuItemVisible,
- click: () => this.copyUrl(),
+ visible: !!selectedRequest,
+ click: () => this.copyUrl(url),
});
copySubmenu.push({
id: "request-list-context-copy-url-params",
label: L10N.getStr("netmonitor.context.copyUrlParams"),
accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
- visible: !!getUrlQuery(selectedRequest.url),
- click: () => this.copyUrlParams(),
+ visible: !!(selectedRequest && getUrlQuery(url)),
+ click: () => this.copyUrlParams(url),
});
copySubmenu.push({
id: "request-list-context-copy-post-data",
label: L10N.getStr("netmonitor.context.copyPostData"),
accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
- visible: !!requestPostData,
- click: () => this.copyPostData(requestPostData),
+ visible: !!(selectedRequest && requestPostDataAvailable),
+ click: () => this.copyPostData(id),
});
copySubmenu.push({
id: "request-list-context-copy-as-curl",
label: L10N.getStr("netmonitor.context.copyAsCurl"),
accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
- visible: menuItemVisible,
- click: () => this.copyAsCurl(),
+ visible: !!selectedRequest,
+ click: () => this.copyAsCurl(id, url, method, requestHeaders, httpVersion),
});
copySubmenu.push({
type: "separator",
- visible: menuItemVisible,
+ visible: copySubmenu.slice(0, 4).some((subMenu) => subMenu.visible),
});
copySubmenu.push({
id: "request-list-context-copy-request-headers",
label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
- visible: !!selectedRequest.requestHeaders,
- click: () => this.copyRequestHeaders(),
+ visible: !!(selectedRequest && requestHeaders && requestHeaders.rawHeaders),
+ click: () => this.copyRequestHeaders(requestHeaders.rawHeaders.trim()),
});
copySubmenu.push({
id: "response-list-context-copy-response-headers",
label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
- visible: !!selectedRequest.responseHeaders,
- click: () => this.copyResponseHeaders(),
+ visible: !!(selectedRequest && responseHeaders && responseHeaders.rawHeaders),
+ click: () => this.copyResponseHeaders(responseHeaders.rawHeaders.trim()),
});
copySubmenu.push({
id: "request-list-context-copy-response",
label: L10N.getStr("netmonitor.context.copyResponse"),
accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
- visible: !!selectedRequest.responseContentAvailable,
- click: () => this.copyResponse(responseContent),
+ visible: !!(selectedRequest && responseContentAvailable),
+ click: () => this.copyResponse(id),
});
copySubmenu.push({
id: "request-list-context-copy-image-as-data-uri",
label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
- visible: !!(selectedRequest.mimeType &&
- selectedRequest.mimeType.includes("image/")),
- click: () => this.copyImageAsDataUri(responseContent),
+ visible: !!(selectedRequest && mimeType && mimeType.includes("image/")),
+ click: () => this.copyImageAsDataUri(id, mimeType),
});
copySubmenu.push({
type: "separator",
- visible: menuItemVisible,
+ visible: copySubmenu.slice(5, 9).some((subMenu) => subMenu.visible),
});
copySubmenu.push({
id: "request-list-context-copy-all-as-har",
label: L10N.getStr("netmonitor.context.copyAllAsHar"),
accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
- visible: this.sortedRequests.size > 0,
- click: () => this.copyAllAsHar(),
+ visible: sortedRequests.size > 0,
+ click: () => this.copyAllAsHar(sortedRequests),
});
menu.push({
label: L10N.getStr("netmonitor.context.copy"),
accesskey: L10N.getStr("netmonitor.context.copy.accesskey"),
- visible: menuItemVisible,
+ visible: !!selectedRequest,
submenu: copySubmenu,
});
menu.push({
id: "request-list-context-save-all-as-har",
label: L10N.getStr("netmonitor.context.saveAllAsHar"),
accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
- visible: this.sortedRequests.size > 0,
- click: () => this.saveAllAsHar(),
+ visible: sortedRequests.size > 0,
+ click: () => this.saveAllAsHar(sortedRequests),
});
menu.push({
id: "request-list-context-save-image-as",
label: L10N.getStr("netmonitor.context.saveImageAs"),
accesskey: L10N.getStr("netmonitor.context.saveImageAs.accesskey"),
- visible: !!(selectedRequest.mimeType &&
- selectedRequest.mimeType.includes("image/")),
- click: () => this.saveImageAs(responseContent),
+ visible: !!(selectedRequest && mimeType && mimeType.includes("image/")),
+ click: () => this.saveImageAs(id, url),
});
menu.push({
type: "separator",
- visible: !selectedRequest.isCustom,
+ visible: copySubmenu.slice(10, 14).some((subMenu) => subMenu.visible),
});
menu.push({
id: "request-list-context-resend",
label: L10N.getStr("netmonitor.context.editAndResend"),
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
- visible: !selectedRequest.isCustom,
- click: this.cloneSelectedRequest,
+ visible: !!(selectedRequest && !isCustom),
+ click: cloneSelectedRequest,
});
menu.push({
type: "separator",
- visible: menuItemVisible,
+ visible: copySubmenu.slice(15, 16).some((subMenu) => subMenu.visible),
});
menu.push({
id: "request-list-context-newtab",
label: L10N.getStr("netmonitor.context.newTab"),
accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"),
- visible: menuItemVisible,
- click: () => this.openRequestInTab(),
+ visible: !!selectedRequest,
+ click: () => this.openRequestInTab(url),
});
menu.push({
id: "request-list-context-open-in-debugger",
label: L10N.getStr("netmonitor.context.openInDebugger"),
accesskey: L10N.getStr("netmonitor.context.openInDebugger.accesskey"),
- visible: !!(selectedRequest.mimeType &&
- selectedRequest.mimeType.includes("javascript")),
- click: () => this.openInDebugger(),
+ visible: !!(selectedRequest && mimeType && mimeType.includes("javascript")),
+ click: () => this.openInDebugger(url),
});
menu.push({
id: "request-list-context-open-in-style-editor",
label: L10N.getStr("netmonitor.context.openInStyleEditor"),
accesskey: L10N.getStr("netmonitor.context.openInStyleEditor.accesskey"),
- visible: !!(Services.prefs.getBoolPref("devtools.styleeditor.enabled") &&
- selectedRequest.mimeType &&
- selectedRequest.mimeType.includes("css")),
- click: () => this.openInStyleEditor(),
+ visible: !!(selectedRequest &&
+ Services.prefs.getBoolPref("devtools.styleeditor.enabled") &&
+ mimeType && mimeType.includes("css")),
+ click: () => this.openInStyleEditor(url),
});
menu.push({
id: "request-list-context-perf",
label: L10N.getStr("netmonitor.context.perfTools"),
accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
- visible: this.sortedRequests.size > 0,
- click: () => this.openStatistics(true),
+ visible: sortedRequests.size > 0,
+ click: () => openStatistics(true),
});
showMenu(event, menu);
- },
+ }
/**
* Opens selected item in a new tab.
*/
- openRequestInTab() {
+ openRequestInTab(url) {
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
- win.openUILinkIn(this.selectedRequest.url, "tab", { relatedToCurrent: true });
- },
+ win.openUILinkIn(url, "tab", { relatedToCurrent: true });
+ }
/**
* Opens selected item in the debugger
*/
- openInDebugger() {
- let toolbox = gDevTools.getToolbox(this.getTabTarget());
- toolbox.viewSourceInDebugger(this.selectedRequest.url, 0);
- },
+ openInDebugger(url) {
+ let toolbox = gDevTools.getToolbox(this.props.connector.getTabTarget());
+ toolbox.viewSourceInDebugger(url, 0);
+ }
/**
* Opens selected item in the style editor
*/
- openInStyleEditor() {
- let toolbox = gDevTools.getToolbox(this.getTabTarget());
- toolbox.viewSourceInStyleEditor(this.selectedRequest.url, 0);
- },
+ openInStyleEditor(url) {
+ let toolbox = gDevTools.getToolbox(this.props.connector.getTabTarget());
+ toolbox.viewSourceInStyleEditor(url, 0);
+ }
/**
* Copy the request url from the currently selected item.
*/
- copyUrl() {
- copyString(this.selectedRequest.url);
- },
+ copyUrl(url) {
+ copyString(url);
+ }
/**
* Copy the request url query string parameters from the currently
* selected item.
*/
- copyUrlParams() {
- let params = getUrlQuery(this.selectedRequest.url).split("&");
+ copyUrlParams(url) {
+ let params = getUrlQuery(url).split("&");
copyString(params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n"));
- },
+ }
/**
* Copy the request form data parameters (or raw payload) from
* the currently selected item.
*/
- copyPostData(requestPostData) {
- let { formDataSections } = this.selectedRequest;
+ async copyPostData(id) {
+ // FIXME: Bug 1336382 - Implement RequestListContextMenu React component
+ // Remove window.store.getState()
+ let { formDataSections } = getSelectedRequest(window.store.getState());
let params = [];
// Try to extract any form data parameters.
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 requestPostData = await this.props.connector.requestData(id, "requestPostData");
string = requestPostData.postData.text;
if (Services.appinfo.OS !== "WINNT") {
string = string.replace(/\r/g, "");
}
}
copyString(string);
- },
+ }
/**
* Copy a cURL command from the currently selected item.
*/
- copyAsCurl() {
- let selected = this.selectedRequest;
+ async copyAsCurl(id, url, method, requestHeaders, httpVersion) {
+ let requestPostData = await this.props.connector.requestData(id, "requestPostData");
// Create a sanitized object for the Curl command generator.
let data = {
- url: selected.url,
- method: selected.method,
- headers: selected.requestHeaders.headers,
- httpVersion: selected.httpVersion,
- postDataText: selected.requestPostData && selected.requestPostData.postData.text,
+ url,
+ method,
+ headers: requestHeaders.headers,
+ httpVersion: httpVersion,
+ postDataText: requestPostData ? requestPostData.postData.text : "",
};
copyString(Curl.generateCommand(data));
- },
+ }
/**
* Copy the raw request headers from the currently selected item.
*/
- copyRequestHeaders() {
- let rawHeaders = this.selectedRequest.requestHeaders.rawHeaders.trim();
+ copyRequestHeaders(rawHeaders) {
if (Services.appinfo.OS !== "WINNT") {
rawHeaders = rawHeaders.replace(/\r/g, "");
}
copyString(rawHeaders);
- },
+ }
/**
* Copy the raw response headers from the currently selected item.
*/
- copyResponseHeaders() {
- let rawHeaders = this.selectedRequest.responseHeaders.rawHeaders.trim();
+ copyResponseHeaders(rawHeaders) {
if (Services.appinfo.OS !== "WINNT") {
rawHeaders = rawHeaders.replace(/\r/g, "");
}
copyString(rawHeaders);
- },
+ }
/**
* Copy image as data uri.
*/
- copyImageAsDataUri(responseContent) {
- let { mimeType } = this.selectedRequest;
+ async copyImageAsDataUri(id, mimeType) {
+ let responseContent = await this.props.connector.requestData(id, "responseContent");
let { encoding, text } = responseContent.content;
- let src = formDataURI(mimeType, encoding, text);
- copyString(src);
- },
+ copyString(formDataURI(mimeType, encoding, text));
+ }
/**
* Save image as.
*/
- saveImageAs(responseContent) {
+ async saveImageAs(id, url) {
+ let responseContent = await this.props.connector.requestData(id, "responseContent");
let { encoding, text } = responseContent.content;
- let fileName = getUrlBaseName(this.selectedRequest.url);
+ let fileName = getUrlBaseName(url);
let data;
if (encoding === "base64") {
let decoded = atob(text);
data = new Uint8Array(decoded.length);
for (let i = 0; i < decoded.length; ++i) {
data[i] = decoded.charCodeAt(i);
}
} else {
data = text;
}
saveAs(new Blob([data]), fileName, document);
- },
+ }
/**
* Copy response data as a string.
*/
- copyResponse(responseContent) {
+ async copyResponse(id) {
+ let responseContent = await this.props.connector.requestData(id, "responseContent");
copyString(responseContent.content.text);
- },
+ }
/**
* Copy HAR from the network panel content to the clipboard.
*/
- copyAllAsHar() {
- return HarExporter.copy(this.getDefaultHarOptions());
- },
+ copyAllAsHar(sortedRequests) {
+ return HarExporter.copy(this.getDefaultHarOptions(sortedRequests));
+ }
/**
* Save HAR from the network panel content to a file.
*/
- saveAllAsHar() {
- // FIXME: This will not work in launchpad
+ saveAllAsHar(sortedRequests) {
+ // This will not work in launchpad
// document.execCommand(‘cut’/‘copy’) was denied because it was not called from
// inside a short running user-generated event handler.
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
- return HarExporter.save(this.getDefaultHarOptions());
- },
+ return HarExporter.save(this.getDefaultHarOptions(sortedRequests));
+ }
- getDefaultHarOptions() {
- let form = this.getTabTarget().form;
- let title = form.title || form.url;
+ getDefaultHarOptions(sortedRequests) {
+ let { getLongString, getTabTarget, requestData } = this.props.connector;
+ let { form: { title, url } } = getTabTarget();
return {
- requestData: this.requestData,
- getString: this.getLongString,
- items: this.sortedRequests,
- title: title
+ getString: getLongString,
+ items: sortedRequests,
+ requestData,
+ title: title || url,
};
}
-};
+}
module.exports = RequestListContextMenu;
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -13,49 +13,52 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL);
info("Starting test... ");
let { store, windowRequire, connector } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
- let { getLongString } = connector;
+ let {
+ getLongString,
+ requestData,
+ } = connector;
store.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(store.getState()).get(0),
post: getSortedRequests(store.getState()).get(1),
multipart: getSortedRequests(store.getState()).get(2),
multipartForm: getSortedRequests(store.getState()).get(3),
};
- let data = yield createCurlData(requests.get, getLongString);
+ let data = yield createCurlData(requests.get, getLongString, requestData);
testFindHeader(data);
- data = yield createCurlData(requests.post, getLongString);
+ data = yield createCurlData(requests.post, getLongString, requestData);
testIsUrlEncodedRequest(data);
testWritePostDataTextParams(data);
testWriteEmptyPostDataTextParams(data);
testDataArgumentOnGeneratedCommand(data);
- data = yield createCurlData(requests.multipart, getLongString);
+ data = yield createCurlData(requests.multipart, getLongString, requestData);
testIsMultipartRequest(data);
testGetMultipartBoundary(data);
testMultiPartHeaders(data);
testRemoveBinaryDataFromMultipartText(data);
- data = yield createCurlData(requests.multipartForm, getLongString);
+ data = yield createCurlData(requests.multipartForm, getLongString, requestData);
testMultiPartHeaders(data);
testGetHeadersFromMultipartText({
postDataText: "Content-Type: text/plain\r\n\r\n",
});
if (Services.appinfo.OS != "WINNT") {
testEscapeStringPosix();
@@ -226,17 +229,17 @@ 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, getLongString) {
+function* createCurlData(selected, getLongString, requestData) {
let { url, method, httpVersion } = selected;
// Create a sanitized object for the Curl command generator.
let data = {
url,
method,
headers: [],
httpVersion,
@@ -244,16 +247,17 @@ function* createCurlData(selected, getLo
};
// Fetch header values.
for (let { name, value } of selected.requestHeaders.headers) {
let text = yield getLongString(value);
data.headers.push({ name: name, value: text });
}
+ let requestPostData = yield requestData(selected.id, "requestPostData");
// Fetch the request payload.
- if (selected.requestPostData) {
- let postData = selected.requestPostData.postData.text;
+ if (requestPostData) {
+ let postData = requestPostData.postData.text;
data.postDataText = yield getLongString(postData);
}
return data;
}
--- a/devtools/client/netmonitor/test/browser_net_open_in_debugger.js
+++ b/devtools/client/netmonitor/test/browser_net_open_in_debugger.js
@@ -7,33 +7,34 @@
* Test the 'Open in debugger' feature
*/
add_task(function* () {
let { tab, monitor, toolbox} = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
-
+ let contextMenuDoc = monitor.panelWin.parent.document;
// Avoid async processing
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
+ wait = waitForDOM(contextMenuDoc, "#request-list-context-open-in-debugger");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[2]);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[2]);
+ yield wait;
let onDebuggerReady = toolbox.once("jsdebugger-ready");
- monitor.panelWin.parent.document
- .querySelector("#request-list-context-open-in-debugger").click();
+ contextMenuDoc.querySelector("#request-list-context-open-in-debugger").click();
yield onDebuggerReady;
ok(true, "Debugger has been open");
yield teardown(monitor);
});
--- a/devtools/client/netmonitor/test/browser_net_open_in_style_editor.js
+++ b/devtools/client/netmonitor/test/browser_net_open_in_style_editor.js
@@ -7,31 +7,33 @@
* Test the 'Open in debugger' feature
*/
add_task(function* () {
let { tab, monitor, toolbox} = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
-
+ let contextMenuDoc = monitor.panelWin.parent.document;
// Avoid async processing
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
+ wait = waitForDOM(contextMenuDoc, "#request-list-context-open-in-style-editor");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[1]);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[1]);
+ yield wait;
let onStyleEditorReady = toolbox.once("styleeditor-ready");
monitor.panelWin.parent.document
.querySelector("#request-list-context-open-in-style-editor").click();
yield onStyleEditorReady;
ok(true, "Style Editor has been open");
--- a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
+++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
@@ -7,30 +7,33 @@
* Tests if Open in new tab works.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
info("Starting test...");
let { document, store, windowRequire } = monitor.panelWin;
+ let contextMenuDoc = monitor.panelWin.parent.document;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests(1);
});
yield wait;
+ wait = waitForDOM(contextMenuDoc, "#request-list-context-newtab");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[0]);
+ yield wait;
let onTabOpen = once(gBrowser.tabContainer, "TabOpen", false);
monitor.panelWin.parent.document
.querySelector("#request-list-context-newtab").click();
yield onTabOpen;
ok(true, "A new tab has been opened");
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -21,25 +21,24 @@ add_task(function* () {
let wait = waitForNetworkEvents(monitor, 0, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
// Wait for all tree view updated by react
- wait = waitForDOM(document, "#headers-panel");
+ wait = waitForDOM(document, "#headers-panel .tree-section .treeLabel", 3);
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector(".network-details-panel-toggle"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#headers-tab"));
yield wait;
let tabpanel = document.querySelector("#headers-panel");
-
is(tabpanel.querySelectorAll(".tree-section .treeLabel").length, 3,
"There should be 3 header sections displayed in this tabpanel.");
is(tabpanel.querySelectorAll(".tree-section .treeLabel")[2].textContent,
L10N.getStr("requestHeadersFromUpload") + " (" +
L10N.getFormatStr("networkMenu.sizeB", 74) + ")",
"The request headers from upload section doesn't have the correct title.");
--- a/devtools/client/netmonitor/test/browser_net_resend.js
+++ b/devtools/client/netmonitor/test/browser_net_resend.js
@@ -12,16 +12,17 @@ const ADD_HEADER = "Test-header: true";
const ADD_UA_HEADER = "User-Agent: Custom-Agent";
const ADD_POSTDATA = "&t3=t4";
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
info("Starting test... ");
let { document, store, windowRequire, connector } = monitor.panelWin;
+ let { requestData } = connector;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSelectedRequest,
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
@@ -50,17 +51,17 @@ add_task(function* () {
testCustomItemChanged(customItem, origItem);
// send the new request
wait = waitForNetworkEvents(monitor, 0, 1);
store.dispatch(Actions.sendCustomRequest(connector));
yield wait;
let sentItem = getSelectedRequest(store.getState());
- testSentRequest(sentItem, origItem);
+ testSentRequest(sentItem, origItem, requestData);
return teardown(monitor);
function testCustomItem(item, orig) {
is(item.method, orig.method, "item is showing the same method as original request");
is(item.url, orig.url, "item is showing the same URL as original request");
}
@@ -137,28 +138,30 @@ add_task(function* () {
// add to POST data
type(ADD_POSTDATA);
}
/*
* Make sure newly created event matches expected request
*/
- function testSentRequest(data, origData) {
+ function* testSentRequest(data, origData) {
is(data.method, origData.method, "correct method in sent request");
is(data.url, origData.url + "&" + ADD_QUERY, "correct url in sent request");
let { headers } = data.requestHeaders;
let hasHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_HEADER);
ok(hasHeader, "new header added to sent request");
let hasUAHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_UA_HEADER);
ok(hasUAHeader, "User-Agent header added to sent request");
- is(data.requestPostData.postData.text,
+ let requestPostData = yield requestData(data.id, "requestPostData");
+
+ is(requestPostData.postData.text,
origData.requestPostData.postData.text + ADD_POSTDATA,
"post data added to sent request");
}
function type(string) {
for (let ch of string) {
EventUtils.synthesizeKey(ch, {}, monitor.panelWin);
}
--- a/devtools/client/netmonitor/test/browser_net_resend_cors.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js
@@ -58,20 +58,21 @@ add_task(function* () {
// Check the resent requests
for (let i = 0; i < ITEMS.length; i++) {
let item = ITEMS[i];
is(item.method, METHODS[i], `The ${item.method} request has the right method`);
is(item.url, requestUrl, `The ${item.method} request has the right URL`);
is(item.status, 200, `The ${item.method} response has the right status`);
if (item.method === "POST") {
- // Force fetching response content
+ // Force fetching lazy load data
let responseContent = yield connector.requestData(item.id, "responseContent");
+ let requestPostData = yield connector.requestData(item.id, "requestPostData");
- is(item.requestPostData.postData.text, "post-data",
+ is(requestPostData.postData.text, "post-data",
"The POST request has the right POST data");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(responseContent.content.text, "Access-Control-Allow-Origin: *",
"The POST response has the right content");
}
}
info("Finishing the test");
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -305,18 +305,16 @@ function waitForNetworkEvents(monitor, g
let genericEvents = 0;
let postEvents = 0;
let payloadReady = 0;
let awaitedEventsToListeners = [
["UPDATING_REQUEST_HEADERS", onGenericEvent],
["RECEIVED_REQUEST_HEADERS", onGenericEvent],
["UPDATING_REQUEST_COOKIES", onGenericEvent],
["RECEIVED_REQUEST_COOKIES", onGenericEvent],
- ["UPDATING_REQUEST_POST_DATA", onPostEvent],
- ["RECEIVED_REQUEST_POST_DATA", onPostEvent],
["UPDATING_RESPONSE_HEADERS", onGenericEvent],
["RECEIVED_RESPONSE_HEADERS", onGenericEvent],
["UPDATING_RESPONSE_COOKIES", onGenericEvent],
["RECEIVED_RESPONSE_COOKIES", onGenericEvent],
["UPDATING_EVENT_TIMINGS", onGenericEvent],
["RECEIVED_EVENT_TIMINGS", onGenericEvent],
["PAYLOAD_READY", onPayloadReady]
];
--- a/devtools/client/webconsole/webconsole-connection-proxy.js
+++ b/devtools/client/webconsole/webconsole-connection-proxy.js
@@ -239,17 +239,19 @@ WebConsoleConnectionProxy.prototype = {
/**
* Dispatch a message event on the new frontend and emit an event for tests.
*/
dispatchMessageUpdate: function (networkInfo, response) {
this.webConsoleFrame.newConsoleOutput.dispatchMessageUpdate(networkInfo, response);
},
dispatchRequestUpdate: function (id, data) {
- this.webConsoleFrame.newConsoleOutput.dispatchRequestUpdate(id, data);
+ if (this.webConsoleFrame) {
+ this.webConsoleFrame.newConsoleOutput.dispatchRequestUpdate(id, data);
+ }
},
/**
* The "cachedMessages" response handler.
*
* @private
* @param object response
* The JSON response object received from the server.