--- a/devtools/client/netmonitor/src/components/monitor-panel.js
+++ b/devtools/client/netmonitor/src/components/monitor-panel.js
@@ -9,17 +9,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 { getFormDataSections } = require("../utils/request-utils");
+const { updateFormDataSections } = require("../utils/request-utils");
const { getSelectedRequest } = require("../selectors/index");
// Components
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
const NetworkDetailsPanel = createFactory(require("./network-details-panel"));
const RequestList = createFactory(require("./request-list"));
const Toolbar = createFactory(require("./toolbar"));
const { div } = DOM;
@@ -49,42 +49,17 @@ const MonitorPanel = createClass({
};
},
componentDidMount() {
MediaQueryList.addListener(this.onLayoutChange);
},
componentWillReceiveProps(nextProps) {
- let {
- request = {},
- updateRequest,
- } = nextProps;
- let {
- formDataSections,
- requestHeaders,
- requestHeadersFromUploadStream,
- requestPostData,
- } = request;
-
- if (!formDataSections && requestHeaders &&
- requestHeadersFromUploadStream && requestPostData) {
- getFormDataSections(
- requestHeaders,
- requestHeadersFromUploadStream,
- requestPostData,
- this.props.connector.getLongString,
- ).then((newFormDataSections) => {
- updateRequest(
- request.id,
- { formDataSections: newFormDataSections },
- true,
- );
- });
- }
+ updateFormDataSections(nextProps);
},
componentWillUnmount() {
MediaQueryList.removeListener(this.onLayoutChange);
let { clientWidth, clientHeight } = findDOMNode(this.refs.endPanel) || {};
if (this.state.isVerticalSpliter && clientWidth) {
@@ -103,40 +78,41 @@ const MonitorPanel = createClass({
});
},
render() {
let {
connector,
isEmpty,
networkDetailsOpen,
+ openLink,
sourceMapService,
- openLink
} = this.props;
let initialWidth = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-width");
let initialHeight = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-height");
+
return (
div({ className: "monitor-panel" },
Toolbar(),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${initialWidth}px`,
initialHeight: `${initialHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
startPanel: RequestList({ isEmpty, connector }),
endPanel: networkDetailsOpen && NetworkDetailsPanel({
ref: "endPanel",
connector,
+ openLink,
sourceMapService,
- openLink,
}),
endPanelCollapsed: !networkDetailsOpen,
endPanelControl: true,
vert: this.state.isVerticalSpliter,
}),
)
);
}
--- a/devtools/client/netmonitor/src/components/params-panel.js
+++ b/devtools/client/netmonitor/src/components/params-panel.js
@@ -1,22 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
+ createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
const { L10N } = require("../utils/l10n");
const { getUrlQuery, parseQueryString, parseFormData } = require("../utils/request-utils");
const { sortObjectKeys } = require("../utils/sort-utils");
+const { updateFormDataSections } = require("../utils/request-utils");
+const Actions = require("../actions/index");
// Components
const PropertiesView = createFactory(require("./properties-view"));
const { div } = DOM;
const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
const PARAMS_EMPTY_TEXT = L10N.getStr("paramsEmptyText");
@@ -26,93 +30,106 @@ const PARAMS_POST_PAYLOAD = L10N.getStr(
const PARAMS_QUERY_STRING = L10N.getStr("paramsQueryString");
const SECTION_NAMES = [
JSON_SCOPE_NAME,
PARAMS_FORM_DATA,
PARAMS_POST_PAYLOAD,
PARAMS_QUERY_STRING,
];
-/*
+/**
* Params panel component
* Displays the GET parameters and POST data of a request
*/
-function ParamsPanel({
- openLink,
- request,
-}) {
- let {
- formDataSections,
- mimeType,
- requestPostData,
- url,
- } = request;
- let postData = requestPostData ? requestPostData.postData.text : null;
- let query = getUrlQuery(url);
+const ParamsPanel = createClass({
+ displayName: "ParamsPanel",
+
+ propTypes: {
+ connector: PropTypes.object.isRequired,
+ openLink: PropTypes.func,
+ request: PropTypes.object.isRequired,
+ updateRequest: PropTypes.func.isRequired,
+ },
+
+ componentDidMount() {
+ updateFormDataSections(this.props);
+ },
+
+ componentWillReceiveProps(nextProps) {
+ updateFormDataSections(nextProps);
+ },
+
+ render() {
+ let {
+ openLink,
+ request
+ } = this.props;
+ let {
+ formDataSections,
+ mimeType,
+ requestPostData,
+ url,
+ } = request;
+ let postData = requestPostData ? requestPostData.postData.text : null;
+ let query = getUrlQuery(url);
+
+ if (!formDataSections && !postData && !query) {
+ return div({ className: "empty-notice" },
+ PARAMS_EMPTY_TEXT
+ );
+ }
+
+ let object = {};
+ let json;
- if (!formDataSections && !postData && !query) {
- return div({ className: "empty-notice" },
- PARAMS_EMPTY_TEXT
+ // Query String section
+ if (query) {
+ object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
+ }
+
+ // Form Data section
+ if (formDataSections && formDataSections.length > 0) {
+ let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
+ object[PARAMS_FORM_DATA] = getProperties(parseFormData(sections));
+ }
+
+ // Request payload section
+ if (formDataSections && formDataSections.length === 0 && postData) {
+ try {
+ json = JSON.parse(postData);
+ } catch (error) {
+ // Continue regardless of parsing error
+ }
+
+ if (json) {
+ object[JSON_SCOPE_NAME] = sortObjectKeys(json);
+ } else {
+ object[PARAMS_POST_PAYLOAD] = {
+ EDITOR_CONFIG: {
+ text: postData,
+ mode: mimeType.replace(/;.+/, ""),
+ },
+ };
+ }
+ } else {
+ postData = "";
+ }
+
+ return (
+ div({ className: "panel-container" },
+ PropertiesView({
+ object,
+ filterPlaceHolder: PARAMS_FILTER_TEXT,
+ sectionNames: SECTION_NAMES,
+ openLink,
+ })
+ )
);
}
-
- let object = {};
- let json;
-
- // Query String section
- if (query) {
- object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
- }
-
- // Form Data section
- if (formDataSections && formDataSections.length > 0) {
- let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
- object[PARAMS_FORM_DATA] = getProperties(parseFormData(sections));
- }
-
- // Request payload section
- if (formDataSections && formDataSections.length === 0 && postData) {
- try {
- json = JSON.parse(postData);
- } catch (error) {
- // Continue regardless of parsing error
- }
-
- if (json) {
- object[JSON_SCOPE_NAME] = sortObjectKeys(json);
- } else {
- object[PARAMS_POST_PAYLOAD] = {
- EDITOR_CONFIG: {
- text: postData,
- mode: mimeType.replace(/;.+/, ""),
- },
- };
- }
- } else {
- postData = "";
- }
-
- return (
- div({ className: "panel-container" },
- PropertiesView({
- object,
- filterPlaceHolder: PARAMS_FILTER_TEXT,
- sectionNames: SECTION_NAMES,
- openLink,
- })
- )
- );
-}
-
-ParamsPanel.displayName = "ParamsPanel";
-
-ParamsPanel.propTypes = {
- request: PropTypes.object.isRequired,
- openLink: PropTypes.func,
-};
+});
/**
* Mapping array to dict for TreeView usage.
* Since TreeView only support Object(dict) format.
* This function also deal with duplicate key case
* (for multiple selection and query params with same keys)
*
* @param {Object[]} arr - key-value pair array like query or form params
@@ -128,9 +145,13 @@ function getProperties(arr) {
map[obj.name].push(obj.value);
} else {
map[obj.name] = obj.value;
}
return map;
}, {}));
}
-module.exports = ParamsPanel;
+module.exports = connect(null,
+ (dispatch) => ({
+ updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
+ }),
+)(ParamsPanel);
--- a/devtools/client/netmonitor/src/components/tabbox-panel.js
+++ b/devtools/client/netmonitor/src/components/tabbox-panel.js
@@ -67,17 +67,17 @@ function TabboxPanel({
title: COOKIES_TITLE,
},
CookiesPanel({ request, openLink }),
),
TabPanel({
id: PANELS.PARAMS,
title: PARAMS_TITLE,
},
- ParamsPanel({ request, openLink }),
+ ParamsPanel({ connector, openLink, request }),
),
TabPanel({
id: PANELS.RESPONSE,
title: RESPONSE_TITLE,
},
ResponsePanel({ request, openLink }),
),
TabPanel({
@@ -86,17 +86,17 @@ function TabboxPanel({
},
TimingsPanel({ request }),
),
request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 &&
TabPanel({
id: PANELS.STACK_TRACE,
title: STACK_TRACE_TITLE,
},
- StackTracePanel({ request, sourceMapService, openLink, connector }),
+ StackTracePanel({ connector, openLink, request, sourceMapService }),
),
request.securityState && request.securityState !== "insecure" &&
TabPanel({
id: PANELS.SECURITY,
title: SECURITY_TITLE,
},
SecurityPanel({ request, openLink }),
),
--- a/devtools/client/netmonitor/src/reducers/requests.js
+++ b/devtools/client/netmonitor/src/reducers/requests.js
@@ -1,27 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const I = require("devtools/client/shared/vendor/immutable");
-const { getUrlDetails } = require("../utils/request-utils");
+const {
+ getUrlDetails,
+ processNetworkUpdates,
+} = require("../utils/request-utils");
const {
ADD_REQUEST,
CLEAR_REQUESTS,
CLONE_SELECTED_REQUEST,
OPEN_NETWORK_DETAILS,
REMOVE_SELECTED_CUSTOM_REQUEST,
SELECT_REQUEST,
SEND_CUSTOM_REQUEST,
TOGGLE_RECORDING,
UPDATE_REQUEST,
- UPDATE_PROPS,
} = require("../constants");
const Request = I.Record({
id: null,
// Set to true in case of a request that's being edited as part of "edit and resend"
isCustom: false,
// Request properties - at the beginning, they are unknown and are gradually filled in
startedMillis: undefined,
@@ -185,40 +187,18 @@ function requestsReducer(state = new Req
let { requests, lastEndedMillis } = state;
let updatedRequest = requests.get(action.id);
if (!updatedRequest) {
return state;
}
updatedRequest = updatedRequest.withMutations(request => {
- for (let [key, value] of Object.entries(action.data)) {
- if (!UPDATE_PROPS.includes(key)) {
- continue;
- }
-
- request[key] = value;
-
- switch (key) {
- case "url":
- // Compute the additional URL details
- request.urlDetails = getUrlDetails(value);
- break;
- case "totalTime":
- request.endedMillis = request.startedMillis + value;
- lastEndedMillis = Math.max(lastEndedMillis, request.endedMillis);
- break;
- case "requestPostData":
- request.requestHeadersFromUploadStream = {
- headers: [],
- headersSize: 0,
- };
- break;
- }
- }
+ let values = processNetworkUpdates(action.data);
+ request = Object.assign(request, values);
});
return state.withMutations(st => {
st.requests = requests.set(updatedRequest.id, updatedRequest);
st.lastEndedMillis = lastEndedMillis;
});
}
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -1,16 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-disable mozilla/reject-some-requires */
"use strict";
+const {
+ UPDATE_PROPS,
+} = require("devtools/client/netmonitor/src/constants");
+
const CONTENT_MIME_TYPE_ABBREVIATIONS = {
"ecmascript": "js",
"javascript": "js",
"x-javascript": "js"
};
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
@@ -384,16 +388,79 @@ function getResponseHeader(item, header)
for (let responseHeader of responseHeaders.headers) {
if (responseHeader.name.toLowerCase() == header) {
return responseHeader.value;
}
}
return null;
}
+/**
+ * Extracts any urlencoded form data sections from a POST request.
+ */
+function updateFormDataSections(props) {
+ let {
+ connector,
+ request = {},
+ updateRequest,
+ } = props;
+ let {
+ formDataSections,
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ requestPostData,
+ } = request;
+
+ if (!formDataSections && requestHeaders &&
+ requestHeadersFromUploadStream && requestPostData) {
+ getFormDataSections(
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ requestPostData,
+ connector.getLongString,
+ ).then((newFormDataSections) => {
+ updateRequest(
+ request.id,
+ { formDataSections: newFormDataSections },
+ true,
+ );
+ });
+ }
+}
+
+/**
+ * This helper function is used for additional processing of
+ * incoming network update packets. It's used by Network and
+ * Console panel reducers.
+ */
+function processNetworkUpdates(request) {
+ let result = {};
+ for (let [key, value] of Object.entries(request)) {
+ if (UPDATE_PROPS.includes(key)) {
+ result[key] = value;
+
+ switch (key) {
+ case "securityInfo":
+ result.securityState = value.state;
+ break;
+ case "totalTime":
+ result.totalTime = request.totalTime;
+ break;
+ case "requestPostData":
+ result.requestHeadersFromUploadStream = {
+ headers: [],
+ headersSize: 0,
+ };
+ break;
+ }
+ }
+ }
+ return result;
+}
+
module.exports = {
decodeUnicodeBase64,
getFormDataSections,
fetchHeaders,
formDataURI,
writeHeaderText,
decodeUnicodeUrl,
getAbbreviatedMimeType,
@@ -406,11 +473,13 @@ module.exports = {
getUrlBaseNameWithQuery,
getUrlDetails,
getUrlHost,
getUrlHostName,
getUrlQuery,
getUrlScheme,
parseQueryString,
parseFormData,
+ updateFormDataSections,
+ processNetworkUpdates,
propertiesEqual,
ipToLong,
};
--- a/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
@@ -103,17 +103,19 @@ function NetworkEventMessage({
// API consumed by Net monitor UI components. Most of the method
// are not needed in context of the Console panel (atm) and thus
// let's just provide empty implementation.
// Individual methods might be implemented step by step as needed.
let connector = {
viewSourceInDebugger: (url, line) => {
serviceContainer.onViewSourceInDebugger({url, line});
},
- getLongString: () => {},
+ getLongString: (grip) => {
+ return serviceContainer.getLongString(grip);
+ },
getTabTarget: () => {},
getNetworkRequest: () => {},
sendHTTPRequest: () => {},
setPreferences: () => {},
triggerActivity: () => {},
};
// Only render the attachment if the network-event is
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -69,32 +69,37 @@ NewConsoleOutputWrapper.prototype = {
let selection = this.document.defaultView.getSelection();
if (selection && !selection.isCollapsed) {
return;
}
this.jsterm.focus();
});
+ let { hud } = this.jsterm;
+
const serviceContainer = {
attachRefToHud,
emitNewMessage: (node, messageId, timeStamp) => {
this.jsterm.hud.emit("new-messages", new Set([{
node,
messageId,
timeStamp,
}]));
},
- hudProxy: this.jsterm.hud.proxy,
+ hudProxy: hud.proxy,
openLink: url => {
- this.jsterm.hud.owner.openLink(url);
+ hud.owner.openLink(url);
},
createElement: nodename => {
return this.document.createElement(nodename);
},
+ getLongString: (grip) => {
+ return hud.proxy.webConsoleClient.getString(grip);
+ },
};
// Set `openContextMenu` this way so, `serviceContainer` variable
// is available in the current scope and we can pass it into
// `createContextMenu` method.
serviceContainer.openContextMenu = (e, message) => {
let { screenX, screenY, target } = e;
@@ -228,18 +233,25 @@ NewConsoleOutputWrapper.prototype = {
dispatchTimestampsToggle: function (enabled) {
store.dispatch(actions.timestampsToggle(enabled));
},
dispatchMessageUpdate: function (message, res) {
// network-message-updated will emit when all the update message arrives.
// Since we can't ensure the order of the network update, we check
// that networkInfo.updates has all we need.
+ // Note that 'requestPostData' is sent only for POST requests, so we need
+ // to count with that.
const NUMBER_OF_NETWORK_UPDATE = 8;
- if (res.networkInfo.updates.length === NUMBER_OF_NETWORK_UPDATE) {
+ let expectedLength = NUMBER_OF_NETWORK_UPDATE;
+ if (res.networkInfo.updates.indexOf("requestPostData") != -1) {
+ expectedLength++;
+ }
+
+ if (res.networkInfo.updates.length === expectedLength) {
this.batchedMessageUpdates({ res, message });
}
},
dispatchRequestUpdate: function (id, data) {
this.batchedRequestUpdates({ id, data });
},
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -18,19 +18,23 @@ const {
FILTERS,
MESSAGE_TYPE,
MESSAGE_SOURCE,
} = constants;
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const {
- UPDATE_PROPS
+ UPDATE_REQUEST,
} = require("devtools/client/netmonitor/src/constants");
+const {
+ processNetworkUpdates,
+} = require("devtools/client/netmonitor/src/utils/request-utils");
+
const MessageState = Immutable.Record({
// List of all the messages added to the console.
messagesById: Immutable.OrderedMap(),
// Array of the visible messages.
visibleMessages: [],
// Object for the filtered messages.
filteredMessagesCount: getDefaultFiltersCounter(),
// List of the message ids which are opened.
@@ -269,44 +273,24 @@ function messages(state = new MessageSta
case constants.NETWORK_MESSAGE_UPDATE:
return state.set(
"networkMessagesUpdateById",
Object.assign({}, networkMessagesUpdateById, {
[action.message.id]: action.message
})
);
+ case UPDATE_REQUEST:
case constants.NETWORK_UPDATE_REQUEST: {
let request = networkMessagesUpdateById[action.id];
if (!request) {
return state;
}
- let values = {};
- for (let [key, value] of Object.entries(action.data)) {
- if (UPDATE_PROPS.includes(key)) {
- values[key] = value;
-
- switch (key) {
- case "securityInfo":
- values.securityState = value.state;
- break;
- case "totalTime":
- values.totalTime = request.totalTime;
- break;
- case "requestPostData":
- values.requestHeadersFromUploadStream = {
- headers: [],
- headersSize: 0,
- };
- break;
- }
- }
- }
-
+ let values = processNetworkUpdates(action.data);
newState = state.set(
"networkMessagesUpdateById",
Object.assign({}, networkMessagesUpdateById, {
[action.id]: Object.assign({}, request, values)
})
);
return newState;