--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -251,16 +251,51 @@ function waitForNEvents(target, eventNam
break;
}
}
return deferred.promise;
}
/**
+ * Wait for DOM change on target.
+ *
+ * @param {Object} target
+ * The Node on which to observe DOM mutations.
+ * @param {String} selector
+ * Given a selector to watch whether the expected element is changed
+ * on target.
+ * @param {Number} expectedLength
+ * Optional, default set to 1
+ * There may be more than one element match an array match the selector,
+ * give an expected length to wait for more elements.
+ * @return A promise that resolves when the event has been handled
+ */
+function waitForDOM(target, selector, expectedLength = 1) {
+ return new Promise((resolve) => {
+ let observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ let elements = mutation.target.querySelectorAll(selector);
+
+ if (elements.length === expectedLength) {
+ observer.disconnect();
+ resolve(elements);
+ }
+ });
+ });
+
+ observer.observe(target, {
+ attributes: true,
+ childList: true,
+ subtree: true,
+ });
+ });
+}
+
+/**
* Wait for eventName on target.
*
* @param {Object} target
* An observable object that either supports on/off or
* addEventListener/removeEventListener
* @param {String} eventName
* @param {Boolean} useCapture
* Optional, for addEventListener/removeEventListener
--- a/devtools/client/netmonitor/constants.js
+++ b/devtools/client/netmonitor/constants.js
@@ -1,17 +1,17 @@
/* 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 general = {
CONTENT_SIZE_DECIMALS: 2,
- FREETEXT_FILTER_SEARCH_DELAY: 200,
+ FILTER_SEARCH_DELAY: 200,
REQUEST_TIME_DECIMALS: 2,
};
const actionTypes = {
ADD_REQUEST: "ADD_REQUEST",
ADD_TIMING_MARKER: "ADD_TIMING_MARKER",
BATCH_ACTIONS: "BATCH_ACTIONS",
BATCH_ENABLE: "BATCH_ENABLE",
--- a/devtools/client/netmonitor/details-view.js
+++ b/devtools/client/netmonitor/details-view.js
@@ -16,24 +16,22 @@ const { ToolSidebar } = require("devtool
const { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
const { VariablesViewController } = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
const { EVENTS } = require("./events");
const { L10N } = require("./l10n");
const { Filters } = require("./filter-predicates");
const {
decodeUnicodeUrl,
formDataURI,
- getFormDataSections,
getUrlBaseName,
- getUrlQuery,
- parseQueryString,
} = require("./request-utils");
const { createFactory } = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+const ParamsPanel = createFactory(require("./shared/components/params-panel"));
const PreviewPanel = createFactory(require("./shared/components/preview-panel"));
const SecurityPanel = createFactory(require("./shared/components/security-panel"));
const TimingsPanel = createFactory(require("./shared/components/timings-panel"));
// 100 KB in bytes
const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400;
const HEADERS_SIZE_DECIMALS = 3;
const CONTENT_MIME_TYPE_MAPPINGS = {
@@ -89,16 +87,23 @@ DetailsView.prototype = {
},
/**
* Initialization function, called when the network monitor is started.
*/
initialize: function (store) {
dumpn("Initializing the DetailsView");
+ this._paramsPanelNode = $("#react-params-tabpanel-hook");
+
+ ReactDOM.render(Provider(
+ { store },
+ ParamsPanel()
+ ), this._paramsPanelNode);
+
this._previewPanelNode = $("#react-preview-tabpanel-hook");
ReactDOM.render(Provider(
{ store },
PreviewPanel()
), this._previewPanelNode);
this._securityPanelNode = $("#react-security-tabpanel-hook");
@@ -126,45 +131,38 @@ DetailsView.prototype = {
emptyText: L10N.getStr("headersEmptyText"),
searchPlaceholder: L10N.getStr("headersFilterText")
}));
this._cookies = new VariablesView($("#all-cookies"),
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
emptyText: L10N.getStr("cookiesEmptyText"),
searchPlaceholder: L10N.getStr("cookiesFilterText")
}));
- this._params = new VariablesView($("#request-params"),
- Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
- emptyText: L10N.getStr("paramsEmptyText"),
- searchPlaceholder: L10N.getStr("paramsFilterText")
- }));
this._json = new VariablesView($("#response-content-json"),
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
onlyEnumVisible: true,
searchPlaceholder: L10N.getStr("jsonFilterText")
}));
VariablesViewController.attach(this._json);
- this._paramsQueryString = L10N.getStr("paramsQueryString");
- this._paramsFormData = L10N.getStr("paramsFormData");
- this._paramsPostPayload = L10N.getStr("paramsPostPayload");
this._requestHeaders = L10N.getStr("requestHeaders");
this._requestHeadersFromUpload = L10N.getStr("requestHeadersFromUpload");
this._responseHeaders = L10N.getStr("responseHeaders");
this._requestCookies = L10N.getStr("requestCookies");
this._responseCookies = L10N.getStr("responseCookies");
$("tabpanels", this.widget).addEventListener("select", this._onTabSelect);
},
/**
* Destruction function, called when the network monitor is closed.
*/
destroy: function () {
dumpn("Destroying the DetailsView");
+ ReactDOM.unmountComponentAtNode(this._paramsPanelNode);
ReactDOM.unmountComponentAtNode(this._previewPanelNode);
ReactDOM.unmountComponentAtNode(this._securityPanelNode);
ReactDOM.unmountComponentAtNode(this._timingsPanelNode);
this.sidebar.destroy();
$("tabpanels", this.widget).removeEventListener("select",
this._onTabSelect);
},
@@ -172,19 +170,16 @@ DetailsView.prototype = {
* Populates this view with the specified data.
*
* @param object data
* The data source (this should be the attachment of a request item).
* @return object
* Returns a promise that resolves upon population the view.
*/
populate: function (data) {
- $("#request-params-box").setAttribute("flex", "1");
- $("#request-params-box").hidden = false;
- $("#request-post-data-textarea-box").hidden = true;
$("#response-content-info-header").hidden = true;
$("#response-content-json-box").hidden = true;
$("#response-content-textarea-box").hidden = true;
$("#raw-headers").hidden = true;
$("#response-content-image-box").hidden = true;
let isHtml = Filters.html(data);
@@ -205,17 +200,16 @@ DetailsView.prototype = {
if (!isHtml && this.widget.selectedPanel === $("#preview-tabpanel") ||
!hasSecurityInfo && this.widget.selectedPanel ===
$("#security-tabpanel")) {
this.widget.selectedIndex = 0;
}
this._headers.empty();
this._cookies.empty();
- this._params.empty();
this._json.empty();
this._dataSrc = { src: data, populated: [] };
this._onTabSelect();
window.emit(EVENTS.NETWORKDETAILSVIEW_POPULATED);
return promise.resolve();
},
@@ -255,24 +249,16 @@ DetailsView.prototype = {
src.requestHeaders,
src.requestHeadersFromUploadStream);
break;
// "Cookies"
case 1:
yield view._setResponseCookies(src.responseCookies);
yield view._setRequestCookies(src.requestCookies);
break;
- // "Params"
- case 2:
- yield view._setRequestGetParams(src.url);
- yield view._setRequestPostParams(
- src.requestHeaders,
- src.requestHeadersFromUploadStream,
- src.requestPostData);
- break;
// "Response"
case 3:
yield view._setResponseBody(src.url, src.responseContent);
break;
}
viewState.updating[tab] = false;
}).then(() => {
if (tab == this.widget.selectedIndex) {
@@ -481,118 +467,16 @@ DetailsView.prototype = {
}
cookieVar.populate(rawObject);
cookieVar.twisty = true;
cookieVar.expanded = true;
}
}),
/**
- * Sets the network request get params shown in this view.
- *
- * @param string url
- * The request's url.
- */
- _setRequestGetParams: function (url) {
- let query = getUrlQuery(url);
- if (query) {
- this._addParams(this._paramsQueryString, query);
- }
- },
-
- /**
- * Sets the network request post params shown in this view.
- *
- * @param object headers
- * The "requestHeaders" message received from the server.
- * @param object uploadHeaders
- * The "requestHeadersFromUploadStream" inferred from the POST payload.
- * @param object postData
- * The "requestPostData" message received from the server.
- * @return object
- * A promise that is resolved when the request post params are set.
- */
- _setRequestPostParams: Task.async(function* (headers, uploadHeaders,
- postData) {
- if (!headers || !uploadHeaders || !postData) {
- return;
- }
-
- let formDataSections = yield getFormDataSections(
- headers,
- uploadHeaders,
- postData,
- gNetwork.getString.bind(gNetwork));
-
- this._params.onlyEnumVisible = false;
-
- // Handle urlencoded form data sections (e.g. "?foo=bar&baz=42").
- if (formDataSections.length > 0) {
- formDataSections.forEach(section => {
- this._addParams(this._paramsFormData, section);
- });
- } else {
- // Handle JSON and actual forms ("multipart/form-data" content type).
- let postDataLongString = postData.postData.text;
- let text = yield gNetwork.getString(postDataLongString);
- let jsonVal = null;
- try {
- jsonVal = JSON.parse(text);
- } catch (ex) { // eslint-disable-line
- }
-
- if (jsonVal) {
- this._params.onlyEnumVisible = true;
- let jsonScopeName = L10N.getStr("jsonScopeName");
- let jsonScope = this._params.addScope(jsonScopeName);
- jsonScope.expanded = true;
- let jsonItem = jsonScope.addItem(undefined, { enumerable: true });
- jsonItem.populate(jsonVal, { sorted: true });
- } else {
- // This is really awkward, but hey, it works. Let's show an empty
- // scope in the params view and place the source editor containing
- // the raw post data directly underneath.
- $("#request-params-box").removeAttribute("flex");
- let paramsScope = this._params.addScope(this._paramsPostPayload);
- paramsScope.expanded = true;
- paramsScope.locked = true;
-
- $("#request-post-data-textarea-box").hidden = false;
- let editor = yield NetMonitorView.editor("#request-post-data-textarea");
- editor.setMode(Editor.modes.text);
- editor.setText(text);
- }
- }
-
- window.emit(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
- }),
-
- /**
- * Populates the params container in this view with the specified data.
- *
- * @param string name
- * The type of params to populate (get or post).
- * @param string queryString
- * A query string of params (e.g. "?foo=bar&baz=42").
- */
- _addParams: function (name, queryString) {
- let paramsArray = parseQueryString(queryString);
- if (!paramsArray) {
- return;
- }
- let paramsScope = this._params.addScope(name);
- paramsScope.expanded = true;
-
- for (let param of paramsArray) {
- let paramVar = paramsScope.addItem(param.name, {}, {relaxed: true});
- paramVar.setGrip(param.value);
- }
- },
-
- /**
* Sets the network response body shown in this view.
*
* @param string url
* The request's url.
* @param object response
* The message received from the server.
* @return object
* A promise that is resolved when the response body is set.
@@ -700,20 +584,16 @@ DetailsView.prototype = {
}
window.emit(EVENTS.RESPONSE_BODY_DISPLAYED);
}),
_dataSrc: null,
_headers: null,
_cookies: null,
- _params: null,
_json: null,
- _paramsQueryString: "",
- _paramsFormData: "",
- _paramsPostPayload: "",
_requestHeaders: "",
_responseHeaders: "",
_requestCookies: "",
_responseCookies: ""
};
exports.DetailsView = DetailsView;
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -207,24 +207,18 @@
<tabpanel id="cookies-tabpanel"
class="tabpanel-content">
<vbox flex="1">
<vbox id="all-cookies" flex="1"/>
</vbox>
</tabpanel>
<tabpanel id="params-tabpanel"
class="tabpanel-content">
- <vbox flex="1">
- <vbox id="request-params-box" flex="1" hidden="true">
- <vbox id="request-params" flex="1"/>
- </vbox>
- <vbox id="request-post-data-textarea-box" flex="1" hidden="true">
- <vbox id="request-post-data-textarea" flex="1"/>
- </vbox>
- </vbox>
+ <html:div xmlns="http://www.w3.org/1999/xhtml"
+ id="react-params-tabpanel-hook"/>
</tabpanel>
<tabpanel id="response-tabpanel"
class="tabpanel-content">
<vbox flex="1">
<label id="response-content-info-header"/>
<vbox id="response-content-json-box" flex="1" hidden="true">
<vbox id="response-content-json" flex="1" context="network-response-popup" />
</vbox>
--- a/devtools/client/netmonitor/reducers/requests.js
+++ b/devtools/client/netmonitor/reducers/requests.js
@@ -9,17 +9,17 @@ const { getUrlDetails } = require("../re
const {
ADD_REQUEST,
UPDATE_REQUEST,
CLEAR_REQUESTS,
SELECT_REQUEST,
PRESELECT_REQUEST,
CLONE_SELECTED_REQUEST,
REMOVE_SELECTED_CUSTOM_REQUEST,
- OPEN_SIDEBAR
+ OPEN_SIDEBAR,
} = 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,
@@ -32,30 +32,31 @@ const Request = I.Record({
cause: undefined,
fromCache: undefined,
fromServiceWorker: undefined,
status: undefined,
statusText: undefined,
httpVersion: undefined,
securityState: undefined,
securityInfo: undefined,
- mimeType: undefined,
+ mimeType: "text/plain",
contentSize: undefined,
transferredSize: undefined,
totalTime: undefined,
eventTimings: undefined,
headersSize: undefined,
requestHeaders: undefined,
requestHeadersFromUploadStream: undefined,
requestCookies: undefined,
requestPostData: undefined,
responseHeaders: undefined,
responseCookies: undefined,
responseContent: undefined,
responseContentDataUri: undefined,
+ formDataSections: undefined,
});
const Requests = I.Record({
// The request list
requests: I.List(),
// Selection state
selectedId: null,
preselectedId: null,
@@ -82,17 +83,18 @@ const UPDATE_PROPS = [
"headersSize",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
"requestPostData",
"responseHeaders",
"responseCookies",
"responseContent",
- "responseContentDataUri"
+ "responseContentDataUri",
+ "formDataSections",
];
function requestsReducer(state = new Requests(), action) {
switch (action.type) {
case ADD_REQUEST: {
return state.withMutations(st => {
let newRequest = new Request(Object.assign(
{ id: action.id },
@@ -134,23 +136,16 @@ function requestsReducer(state = new Req
request[key] = value;
switch (key) {
case "url":
// Compute the additional URL details
request.urlDetails = getUrlDetails(value);
break;
- case "responseContent":
- // If there's no mime type available when the response content
- // is received, assume text/plain as a fallback.
- if (!request.mimeType) {
- request.mimeType = "text/plain";
- }
- break;
case "totalTime":
const endedMillis = request.startedMillis + value;
lastEndedMillis = Math.max(lastEndedMillis, endedMillis);
break;
case "requestPostData":
request.requestHeadersFromUploadStream = {
headers: [],
headersSize: 0,
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -18,25 +18,26 @@ const { Provider } = require("devtools/c
const RequestList = createFactory(require("./components/request-list"));
const RequestListContextMenu = require("./request-list-context-menu");
const Actions = require("./actions/index");
const { Prefs } = require("./prefs");
const {
formDataURI,
writeHeaderText,
- loadCauseString
+ loadCauseString,
+ getFormDataSections,
} = require("./request-utils");
const {
getActiveFilters,
getSortedRequests,
getDisplayedRequests,
getRequestById,
- getSelectedRequest
+ getSelectedRequest,
} = require("./selectors/index");
// ms
const RESIZE_REFRESH_RATE = 50;
// A smart store watcher to notify store changes as necessary
function storeWatcher(initialValue, reduceValue, onChange) {
let currentValue = initialValue;
@@ -83,16 +84,67 @@ RequestsMenuView.prototype = {
// Watch the sidebar status and resize the waterfall column on change
this.store.subscribe(storeWatcher(
false,
() => this.store.getState().ui.sidebarOpen,
() => this.onResize()
));
+ // Watch the requestHeaders, requestHeadersFromUploadStream and requestPostData
+ // in order to update formDataSections for composing form data
+ this.store.subscribe(storeWatcher(
+ false,
+ (currentRequest) => {
+ const request = getSelectedRequest(this.store.getState());
+ if (!request) {
+ return {};
+ }
+
+ const isChanged = request.requestHeaders !== currentRequest.requestHeaders ||
+ request.requestHeadersFromUploadStream !==
+ currentRequest.requestHeadersFromUploadStream ||
+ request.requestPostData !== currentRequest.requestPostData;
+
+ if (isChanged) {
+ return {
+ id: request.id,
+ requestHeaders: request.requestHeaders,
+ requestHeadersFromUploadStream: request.requestHeadersFromUploadStream,
+ requestPostData: request.requestPostData,
+ };
+ }
+
+ return currentRequest;
+ },
+ (newRequest) => {
+ const {
+ id,
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ requestPostData,
+ } = newRequest;
+
+ if (requestHeaders && requestHeadersFromUploadStream && requestPostData) {
+ getFormDataSections(
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ requestPostData,
+ gNetwork.getString.bind(gNetwork),
+ ).then((formDataSections) => {
+ this.store.dispatch(Actions.updateRequest(
+ id,
+ { formDataSections },
+ true,
+ ));
+ });
+ }
+ },
+ ));
+
this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
this.cloneSelectedRequestEvent = this.cloneSelectedRequest.bind(this);
this.toggleRawHeadersEvent = this.toggleRawHeaders.bind(this);
$("#toggle-raw-headers")
.addEventListener("click", this.toggleRawHeadersEvent, false);
--- a/devtools/client/netmonitor/shared/components/moz.build
+++ b/devtools/client/netmonitor/shared/components/moz.build
@@ -1,11 +1,12 @@
# 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/.
DevToolsModules(
'editor.js',
+ 'params-panel.js',
'preview-panel.js',
'properties-view.js',
'security-panel.js',
'timings-panel.js',
)
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/shared/components/params-panel.js
@@ -0,0 +1,128 @@
+/* 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 {
+ createFactory,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { L10N } = require("../../l10n");
+const { getSelectedRequest } = require("../../selectors/index");
+const { getUrlQuery, parseQueryString } = require("../../request-utils");
+
+// Components
+const PropertiesView = createFactory(require("./properties-view"));
+
+const { div } = DOM;
+
+const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
+const PARAMS_EMPTY_TEXT = L10N.getStr("paramsEmptyText");
+const PARAMS_FILTER_TEXT = L10N.getStr("paramsFilterText");
+const PARAMS_FORM_DATA = L10N.getStr("paramsFormData");
+const PARAMS_POST_PAYLOAD = L10N.getStr("paramsPostPayload");
+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({
+ formDataSections,
+ mimeType,
+ postData,
+ query,
+}) {
+ if (!formDataSections && !postData && !query) {
+ return div({ className: "empty-notice" },
+ PARAMS_EMPTY_TEXT
+ );
+ }
+
+ let object = {};
+ let json;
+
+ // Query String section
+ if (query) {
+ object[PARAMS_QUERY_STRING] =
+ parseQueryString(query)
+ .reduce((acc, { name, value }) =>
+ name ? Object.assign(acc, { [name]: value }) : acc
+ , {});
+ }
+ // Form Data section
+ if (formDataSections && formDataSections.length > 0) {
+ let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
+ object[PARAMS_FORM_DATA] =
+ parseQueryString(sections)
+ .reduce((acc, { name, value }) =>
+ name ? Object.assign(acc, { [name]: value }) : acc
+ , {});
+ }
+
+ // 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] = json;
+ } else {
+ object[PARAMS_POST_PAYLOAD] = {
+ EDITOR_CONFIG: {
+ text: postData,
+ mode: mimeType.replace(/;.+/, ""),
+ },
+ };
+ }
+ } else {
+ postData = "";
+ }
+
+ return (
+ PropertiesView({
+ object,
+ filterPlaceHolder: PARAMS_FILTER_TEXT,
+ sectionNames: SECTION_NAMES,
+ })
+ );
+}
+
+ParamsPanel.displayName = "ParamsPanel";
+
+ParamsPanel.propTypes = {
+ formDataSections: PropTypes.array,
+ postData: PropTypes.string,
+ query: PropTypes.string,
+};
+
+module.exports = connect(
+ (state) => {
+ const selectedRequest = getSelectedRequest(state);
+
+ if (selectedRequest) {
+ const { formDataSections, mimeType, requestPostData, url } = selectedRequest;
+
+ return {
+ formDataSections,
+ mimeType,
+ postData: requestPostData ? requestPostData.postData.text : null,
+ query: getUrlQuery(url),
+ };
+ }
+
+ return {};
+ }
+)(ParamsPanel);
--- a/devtools/client/netmonitor/shared/components/properties-view.js
+++ b/devtools/client/netmonitor/shared/components/properties-view.js
@@ -17,25 +17,27 @@ const { FILTER_SEARCH_DELAY } = require(
// Components
const Editor = createFactory(require("devtools/client/netmonitor/shared/components/editor"));
const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view"));
const TreeRow = createFactory(require("devtools/client/shared/components/tree/tree-row"));
const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
const { div, tr, td } = DOM;
+const AUTO_EXPAND_MAX_LEVEL = 7;
+const EDITOR_CONFIG_ID = "EDITOR_CONFIG";
/*
* Properties View component
* A scrollable tree view component which provides some useful features for
* representing object properties.
*
* Search filter - Set enableFilter to enable / disable SearchBox feature.
* Tree view - Default enabled.
- * Source editor - Enable by specifying object level 1 property name to "editorText".
+ * Source editor - Enable by specifying object level 1 property name to EDITOR_CONFIG_ID.
* Rep - Default enabled.
*/
const PropertiesView = createClass({
displayName: "PropertiesView",
propTypes: {
object: PropTypes.object,
enableInput: PropTypes.bool,
@@ -71,28 +73,34 @@ const PropertiesView = createClass({
return true;
}
let jsonString = JSON.stringify({ [name]: value }).toLowerCase();
return jsonString.includes(filterText.toLowerCase());
},
renderRowWithEditor(props) {
- const { level, name, value } = props.member;
- // Display source editor when prop name specify to editorText
- if (level === 1 && name === "editorText") {
+ const { level, name, value, path } = props.member;
+
+ // Display source editor when specifying to EDITOR_CONFIG_ID along with config
+ if (level === 1 && name === EDITOR_CONFIG_ID) {
return (
tr({},
td({ colSpan: 2 },
- Editor({ text: value })
+ Editor(value)
)
)
);
}
+ // Skip for editor config
+ if (level >= 1 && path.includes(EDITOR_CONFIG_ID)) {
+ return null;
+ }
+
return TreeRow(props);
},
renderValueWithRep(props) {
// Hide rep summary for sections
if (props.member.level === 0) {
return null;
}
@@ -101,58 +109,85 @@ const PropertiesView = createClass({
// FIXME: A workaround for the issue in StringRep
// Force StringRep to crop the text everytime
member: Object.assign({}, props.member, { open: false }),
mode: MODE.TINY,
cropLimit: 60,
}));
},
+ shouldRenderSearchBox(object) {
+ return this.props.enableFilter && object && Object.keys(object)
+ .filter((section) => !object[section][EDITOR_CONFIG_ID]).length > 0;
+ },
+
updateFilterText(filterText) {
this.setState({
filterText,
});
},
+ getExpandedNodes: function (object, path = "", level = 0) {
+ if (typeof object != "object") {
+ return null;
+ }
+
+ if (level > AUTO_EXPAND_MAX_LEVEL) {
+ return null;
+ }
+
+ let expandedNodes = new Set();
+ for (let prop in object) {
+ let nodePath = path + "/" + prop;
+ expandedNodes.add(nodePath);
+
+ let nodes = this.getExpandedNodes(object[prop], nodePath, level + 1);
+ if (nodes) {
+ expandedNodes = new Set([...expandedNodes, ...nodes]);
+ }
+ }
+ return expandedNodes;
+ },
+
render() {
const {
object,
decorator,
enableInput,
- enableFilter,
expandableStrings,
filterPlaceHolder,
renderRow,
renderValue,
sectionNames,
} = this.props;
return (
div({ className: "properties-view" },
- enableFilter && div({ className: "searchbox-section" },
- SearchBox({
- delay: FILTER_SEARCH_DELAY,
- type: "filter",
- onChange: this.updateFilterText,
- placeholder: filterPlaceHolder,
- }),
- ),
+ this.shouldRenderSearchBox(object) &&
+ div({ className: "searchbox-section" },
+ SearchBox({
+ delay: FILTER_SEARCH_DELAY,
+ type: "filter",
+ onChange: this.updateFilterText,
+ placeholder: filterPlaceHolder,
+ }),
+ ),
div({ className: "tree-container" },
TreeView({
object,
columns: [{
id: "value",
width: "100%",
}],
decorator: decorator || {
getRowClass: (rowObject) => this.getRowClass(rowObject, sectionNames),
},
enableInput,
expandableStrings,
- expandedNodes: new Set(sectionNames.map((sec) => "/" + sec)),
+ expandedNodes: this.getExpandedNodes(object),
onFilter: (props) => this.onFilter(props, sectionNames),
renderRow: renderRow || this.renderRowWithEditor,
renderValue: renderValue || this.renderValueWithRep,
}),
),
)
);
}
--- a/devtools/client/netmonitor/test/browser_net_cached-status.js
+++ b/devtools/client/netmonitor/test/browser_net_cached-status.js
@@ -10,17 +10,16 @@
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL, null, true);
info("Starting test... ");
let { NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
- NetworkDetails._params.lazyEmpty = false;
const REQUEST_DATA = [
{
method: "GET",
uri: STATUS_CODES_SJS + "?sts=ok&cached",
details: {
status: 200,
statusText: "OK",
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -9,187 +9,174 @@
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/l10n");
let { tab, monitor } = yield initNetMonitor(PARAMS_URL);
info("Starting test... ");
- let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin;
- let { RequestsMenu, NetworkDetails } = NetMonitorView;
+ let { document, NetMonitorView } = monitor.panelWin;
+ let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
- NetworkDetails._params.lazyEmpty = false;
let wait = waitForNetworkEvents(monitor, 1, 6);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
- let onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
+ wait = waitForDOM(document, "#params-tabpanel .tree-section", 2);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[2]);
- yield onEvent;
- yield testParamsTab1("a", '""', '{ "foo": "bar" }', '""');
+ yield wait;
+ testParamsTab1("a", '""', '{ "foo": "bar" }', '""');
- onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
+ wait = waitForDOM(document, "#params-tabpanel .tree-section", 2);
RequestsMenu.selectedIndex = 1;
- yield onEvent;
- yield testParamsTab1("a", '"b"', '{ "foo": "bar" }', '""');
+ yield wait;
+ testParamsTab1("a", '"b"', '{ "foo": "bar" }', '""');
- onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
+ wait = waitForDOM(document, "#params-tabpanel .tree-section", 2);
RequestsMenu.selectedIndex = 2;
- yield onEvent;
- yield testParamsTab1("a", '"b"', "foo", '"bar"');
+ yield wait;
+ testParamsTab1("a", '"b"', "foo", '"bar"');
- onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
+ wait = waitForDOM(document, "#params-tabpanel tr:not(.tree-section).treeRow", 2);
RequestsMenu.selectedIndex = 3;
- yield onEvent;
- yield testParamsTab2("a", '""', '{ "foo": "bar" }', "js");
+ yield wait;
+ testParamsTab2("a", '""', '{ "foo": "bar" }', "js");
- onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
+ wait = waitForDOM(document, "#params-tabpanel tr:not(.tree-section).treeRow", 2);
RequestsMenu.selectedIndex = 4;
- yield onEvent;
- yield testParamsTab2("a", '"b"', '{ "foo": "bar" }', "js");
+ yield wait;
+ testParamsTab2("a", '"b"', '{ "foo": "bar" }', "js");
- onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
+ // Wait for all tree sections and editor updated by react
+ let waitSections = waitForDOM(document, "#params-tabpanel .tree-section", 2);
+ let waitEditor = waitForDOM(document, "#params-tabpanel .editor-mount iframe");
RequestsMenu.selectedIndex = 5;
- yield onEvent;
- yield testParamsTab2("a", '"b"', "?foo=bar", "text");
+ let [, editorFrames] = yield Promise.all([waitSections, waitEditor]);
+ yield once(editorFrames[0], "DOMContentLoaded");
+ yield waitForDOM(editorFrames[0].contentDocument, ".CodeMirror-code");
+ testParamsTab2("a", '"b"', "?foo=bar", "text");
- onEvent = monitor.panelWin.once(EVENTS.SIDEBAR_POPULATED);
+ wait = waitForDOM(document, "#params-tabpanel .empty-notice");
RequestsMenu.selectedIndex = 6;
- yield onEvent;
- yield testParamsTab3("a", '"b"');
+ yield wait;
+ testParamsTab3();
yield teardown(monitor);
function testParamsTab1(queryStringParamName, queryStringParamValue,
formDataParamName, formDataParamValue) {
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 2,
- "The number of param scopes displayed in this tabpanel is incorrect.");
- is(tabpanel.querySelectorAll(".variable-or-property").length, 2,
- "The number of param values displayed in this tabpanel is incorrect.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+ is(tabpanel.querySelectorAll(".tree-section").length, 2,
+ "The number of param tree sections displayed in this tabpanel is incorrect.");
+ is(tabpanel.querySelectorAll("tr:not(.tree-section).treeRow").length, 2,
+ "The number of param rows displayed in this tabpanel is incorrect.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 0,
"The empty notice should not be displayed in this tabpanel.");
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), false,
- "The request params box should not be hidden.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), true,
- "The request post data textarea box should be hidden.");
+ ok(tabpanel.querySelector(".treeTable"),
+ "The request params box should be displayed.");
+ ok(tabpanel.querySelector(".editor-mount") === null,
+ "The request post data editor should not be displayed.");
- let paramsScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
- let formDataScope = tabpanel.querySelectorAll(".variables-view-scope")[1];
+ let treeSections = tabpanel.querySelectorAll(".tree-section");
+ let labels = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
- is(paramsScope.querySelector(".name").getAttribute("value"),
+ is(treeSections[0].querySelector(".treeLabel").textContent,
L10N.getStr("paramsQueryString"),
- "The params scope doesn't have the correct title.");
- is(formDataScope.querySelector(".name").getAttribute("value"),
+ "The params section doesn't have the correct title.");
+ is(treeSections[1].querySelector(".treeLabel").textContent,
L10N.getStr("paramsFormData"),
- "The form data scope doesn't have the correct title.");
+ "The form data section doesn't have the correct title.");
- is(paramsScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- queryStringParamName,
+ is(labels[0].textContent, queryStringParamName,
"The first query string param name was incorrect.");
- is(paramsScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- queryStringParamValue,
+ is(values[0].textContent, queryStringParamValue,
"The first query string param value was incorrect.");
- is(formDataScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- formDataParamName,
+ is(labels[1].textContent, formDataParamName,
"The first form data param name was incorrect.");
- is(formDataScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- formDataParamValue,
+ is(values[1].textContent, formDataParamValue,
"The first form data param value was incorrect.");
}
- function* testParamsTab2(queryStringParamName, queryStringParamValue,
+ function testParamsTab2(queryStringParamName, queryStringParamValue,
requestPayload, editorMode) {
- let isJSON = editorMode == "js";
+ let isJSON = editorMode === "js";
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 2,
- "The number of param scopes displayed in this tabpanel is incorrect.");
- is(tabpanel.querySelectorAll(".variable-or-property").length, isJSON ? 4 : 1,
- "The number of param values displayed in this tabpanel is incorrect.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+ is(tabpanel.querySelectorAll(".tree-section").length, 2,
+ "The number of param tree sections displayed in this tabpanel is incorrect.");
+ is(tabpanel.querySelectorAll("tr:not(.tree-section).treeRow").length, isJSON ? 2 : 1,
+ "The number of param rows displayed in this tabpanel is incorrect.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 0,
"The empty notice should not be displayed in this tabpanel.");
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), false,
- "The request params box should not be hidden.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), isJSON,
- "The request post data textarea box should be hidden.");
+ ok(tabpanel.querySelector(".treeTable"),
+ "The request params box should be displayed.");
+ is(tabpanel.querySelector(".editor-mount") === null,
+ isJSON,
+ "The request post data editor should be not displayed.");
- let paramsScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
- let payloadScope = tabpanel.querySelectorAll(".variables-view-scope")[1];
+ let treeSections = tabpanel.querySelectorAll(".tree-section");
- is(paramsScope.querySelector(".name").getAttribute("value"),
+ is(treeSections[0].querySelector(".treeLabel").textContent,
L10N.getStr("paramsQueryString"),
- "The params scope doesn't have the correct title.");
- is(payloadScope.querySelector(".name").getAttribute("value"),
+ "The query section doesn't have the correct title.");
+ is(treeSections[1].querySelector(".treeLabel").textContent,
isJSON ? L10N.getStr("jsonScopeName") : L10N.getStr("paramsPostPayload"),
- "The request payload scope doesn't have the correct title.");
+ "The post section doesn't have the correct title.");
- is(paramsScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- queryStringParamName,
+ let labels = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll("tr:not(.treeS-section) .treeValueCell .objectBox");
+
+ is(labels[0].textContent, queryStringParamName,
"The first query string param name was incorrect.");
- is(paramsScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- queryStringParamValue,
+ is(values[0].textContent, queryStringParamValue,
"The first query string param value was incorrect.");
if (isJSON) {
let requestPayloadObject = JSON.parse(requestPayload);
let requestPairs = Object.keys(requestPayloadObject)
.map(k => [k, requestPayloadObject[k]]);
- let displayedNames = payloadScope.querySelectorAll(
- ".variables-view-property.variable-or-property .name");
- let displayedValues = payloadScope.querySelectorAll(
- ".variables-view-property.variable-or-property .value");
- for (let i = 0; i < requestPairs.length; i++) {
+ for (let i = 1; i < requestPairs.length; i++) {
let [requestPayloadName, requestPayloadValue] = requestPairs[i];
- is(requestPayloadName, displayedNames[i].getAttribute("value"),
+ is(requestPayloadName, labels[i].textContent,
"JSON property name " + i + " should be displayed correctly");
- is('"' + requestPayloadValue + '"', displayedValues[i].getAttribute("value"),
+ is('"' + requestPayloadValue + '"', values[i].textContent,
"JSON property value " + i + " should be displayed correctly");
}
} else {
- let editor = yield NetMonitorView.editor("#request-post-data-textarea");
- is(editor.getText(), requestPayload,
+ let editor = editorFrames[0].contentDocument.querySelector(".CodeMirror-code");
+ ok(editor.textContent.includes(requestPayload),
"The text shown in the source editor is incorrect.");
- is(editor.getMode(), Editor.modes[editorMode],
- "The mode active in the source editor is incorrect.");
}
}
- function testParamsTab3(queryStringParamName, queryStringParamValue) {
+ function testParamsTab3() {
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 0,
- "The number of param scopes displayed in this tabpanel is incorrect.");
- is(tabpanel.querySelectorAll(".variable-or-property").length, 0,
- "The number of param values displayed in this tabpanel is incorrect.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 1,
+ is(tabpanel.querySelectorAll(".tree-section").length, 0,
+ "The number of param tree sections displayed in this tabpanel is incorrect.");
+ is(tabpanel.querySelectorAll("tr:not(.tree-section).treeRow").length, 0,
+ "The number of param rows displayed in this tabpanel is incorrect.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 1,
"The empty notice should be displayed in this tabpanel.");
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), false,
- "The request params box should not be hidden.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), true,
- "The request post data textarea box should be hidden.");
+ ok(!tabpanel.querySelector(".treeTable"),
+ "The request params box should be hidden.");
+ ok(!tabpanel.querySelector(".editor-mount iframe"),
+ "The request post data editor should be hidden.");
}
});
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -8,21 +8,20 @@
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/l10n");
let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
info("Starting test... ");
- let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin;
- let { RequestsMenu, NetworkDetails } = NetMonitorView;
+ let { document, NetMonitorView } = monitor.panelWin;
+ let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
- NetworkDetails._params.lazyEmpty = false;
let wait = waitForNetworkEvents(monitor, 0, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
@@ -39,128 +38,99 @@ add_task(function* () {
status: 200,
statusText: "Och Aye",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
time: true
});
- let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
+ // Wait for all tree sections updated by react
+ wait = waitForDOM(document, "#params-tabpanel .tree-section", 2);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[2]);
- yield onEvent;
+ yield wait;
yield testParamsTab("urlencoded");
- onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
+ // Wait for all tree sections and editor updated by react
+ let waitForSections = waitForDOM(document, "#params-tabpanel .tree-section", 2);
+ let waitForEditor = waitForDOM(document, "#params-tabpanel .editor-mount iframe");
RequestsMenu.selectedIndex = 1;
- yield onEvent;
+ let [, editorFrames] = yield Promise.all([waitForSections, waitForEditor]);
+ yield once(editorFrames[0], "DOMContentLoaded");
+ yield waitForDOM(editorFrames[0].contentDocument, ".CodeMirror-code");
yield testParamsTab("multipart");
return teardown(monitor);
function* testParamsTab(type) {
let tabEl = document.querySelectorAll("#details-pane tab")[2];
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
is(tabEl.getAttribute("selected"), "true",
"The params tab in the network details pane should be selected.");
function checkVisibility(box) {
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), !box.includes("params"),
- "The request params box doesn't have the indended visibility.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), !box.includes("textarea"),
- "The request post data textarea box doesn't have the indended visibility.");
+ is(!tabpanel.querySelector(".treeTable"), !box.includes("params"),
+ "The request params doesn't have the indended visibility.");
+ is(tabpanel.querySelector(".editor-mount") === null,
+ !box.includes("editor"),
+ "The request post data doesn't have the indended visibility.");
}
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 2,
- "There should be 2 param scopes displayed in this tabpanel.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+ is(tabpanel.querySelectorAll(".tree-section").length, 2,
+ "There should be 2 tree sections displayed in this tabpanel.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 0,
"The empty notice should not be displayed in this tabpanel.");
- let queryScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
- let postScope = tabpanel.querySelectorAll(".variables-view-scope")[1];
+ let treeSections = tabpanel.querySelectorAll(".tree-section");
- is(queryScope.querySelector(".name").getAttribute("value"),
+ is(treeSections[0].querySelector(".treeLabel").textContent,
L10N.getStr("paramsQueryString"),
- "The query scope doesn't have the correct title.");
+ "The query section doesn't have the correct title.");
- is(postScope.querySelector(".name").getAttribute("value"),
+ is(treeSections[1].querySelector(".treeLabel").textContent,
L10N.getStr(type == "urlencoded" ? "paramsFormData" : "paramsPostPayload"),
- "The post scope doesn't have the correct title.");
+ "The post section doesn't have the correct title.");
- is(queryScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- "foo", "The first query param name was incorrect.");
- is(queryScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- "\"bar\"", "The first query param value was incorrect.");
- is(queryScope.querySelectorAll(".variables-view-variable .name")[1]
- .getAttribute("value"),
- "baz", "The second query param name was incorrect.");
- is(queryScope.querySelectorAll(".variables-view-variable .value")[1]
- .getAttribute("value"),
- "\"42\"", "The second query param value was incorrect.");
- is(queryScope.querySelectorAll(".variables-view-variable .name")[2]
- .getAttribute("value"),
- "type", "The third query param name was incorrect.");
- is(queryScope.querySelectorAll(".variables-view-variable .value")[2]
- .getAttribute("value"),
- "\"" + type + "\"", "The third query param value was incorrect.");
+ let labels = tabpanel.querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel.querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
+
+ is(labels[0].textContent, "foo", "The first query param name was incorrect.");
+ is(values[0].textContent, "\"bar\"", "The first query param value was incorrect.");
+ is(labels[1].textContent, "baz", "The second query param name was incorrect.");
+ is(values[1].textContent, "\"42\"", "The second query param value was incorrect.");
+ is(labels[2].textContent, "type", "The third query param name was incorrect.");
+ is(values[2].textContent, "\"" + type + "\"", "The third query param value was incorrect.");
if (type == "urlencoded") {
checkVisibility("params");
-
- is(tabpanel.querySelectorAll(".variables-view-variable").length, 5,
- "There should be 5 param values displayed in this tabpanel.");
- is(queryScope.querySelectorAll(".variables-view-variable").length, 3,
- "There should be 3 param values displayed in the query scope.");
- is(postScope.querySelectorAll(".variables-view-variable").length, 2,
- "There should be 2 param values displayed in the post scope.");
+ is(labels.length, 5, "There should be 5 param values displayed in this tabpanel.");
+ is(labels[3].textContent, "foo", "The first post param name was incorrect.");
+ is(values[3].textContent, "\"bar\"", "The first post param value was incorrect.");
+ is(labels[4].textContent, "baz", "The second post param name was incorrect.");
+ is(values[4].textContent, "\"123\"", "The second post param value was incorrect.");
+ } else {
+ checkVisibility("params editor");
- is(postScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- "foo", "The first post param name was incorrect.");
- is(postScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- "\"bar\"", "The first post param value was incorrect.");
- is(postScope.querySelectorAll(".variables-view-variable .name")[1]
- .getAttribute("value"),
- "baz", "The second post param name was incorrect.");
- is(postScope.querySelectorAll(".variables-view-variable .value")[1]
- .getAttribute("value"),
- "\"123\"", "The second post param value was incorrect.");
- } else {
- checkVisibility("params textarea");
+ is(labels.length, 3, "There should be 3 param values displayed in this tabpanel.");
- is(tabpanel.querySelectorAll(".variables-view-variable").length, 3,
- "There should be 3 param values displayed in this tabpanel.");
- is(queryScope.querySelectorAll(".variables-view-variable").length, 3,
- "There should be 3 param values displayed in the query scope.");
- is(postScope.querySelectorAll(".variables-view-variable").length, 0,
- "There should be 0 param values displayed in the post scope.");
-
- let editor = yield NetMonitorView.editor("#request-post-data-textarea");
- let text = editor.getText();
+ let text = editorFrames[0].contentDocument.querySelector(".CodeMirror-code").textContent;
ok(text.includes("Content-Disposition: form-data; name=\"text\""),
"The text shown in the source editor is incorrect (1.1).");
ok(text.includes("Content-Disposition: form-data; name=\"email\""),
"The text shown in the source editor is incorrect (2.1).");
ok(text.includes("Content-Disposition: form-data; name=\"range\""),
"The text shown in the source editor is incorrect (3.1).");
ok(text.includes("Content-Disposition: form-data; name=\"Custom field\""),
"The text shown in the source editor is incorrect (4.1).");
ok(text.includes("Some text..."),
"The text shown in the source editor is incorrect (2.2).");
ok(text.includes("42"),
"The text shown in the source editor is incorrect (3.2).");
ok(text.includes("Extra data"),
"The text shown in the source editor is incorrect (4.2).");
- is(editor.getMode(), Editor.modes.text,
- "The mode active in the source editor is incorrect.");
}
}
});
--- a/devtools/client/netmonitor/test/browser_net_post-data-02.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js
@@ -9,65 +9,55 @@
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/l10n");
let { tab, monitor } = yield initNetMonitor(POST_RAW_URL);
info("Starting test... ");
- let { document, EVENTS, NetMonitorView } = monitor.panelWin;
- let { RequestsMenu, NetworkDetails } = NetMonitorView;
+ let { document, NetMonitorView } = monitor.panelWin;
+ let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
- NetworkDetails._params.lazyEmpty = false;
let wait = waitForNetworkEvents(monitor, 0, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
- let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
- NetMonitorView.toggleDetailsPane({ visible: true }, 2);
- RequestsMenu.selectedIndex = 0;
- yield onEvent;
-
- let tabEl = document.querySelectorAll("#event-details-pane tab")[2];
- let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2];
-
- is(tabEl.getAttribute("selected"), "true",
- "The params tab in the network details pane should be selected.");
+ // Wait for all tree view updated by react
+ wait = waitForDOM(document, "#params-tabpanel .tree-section");
+ EventUtils.sendMouseEvent({ type: "mousedown" },
+ document.getElementById("details-pane-toggle"));
+ EventUtils.sendMouseEvent({ type: "mousedown" },
+ document.querySelectorAll("#details-pane tab")[2]);
+ yield wait;
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), false,
- "The request params box doesn't have the indended visibility.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), true,
- "The request post data textarea box doesn't have the indended visibility.");
+ let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
- "There should be 1 param scopes displayed in this tabpanel.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+ ok(tabpanel.querySelector(".treeTable"),
+ "The request params doesn't have the indended visibility.");
+ ok(tabpanel.querySelector(".editor-mount") === null,
+ "The request post data doesn't have the indended visibility.");
+
+ is(tabpanel.querySelectorAll(".tree-section").length, 1,
+ "There should be 1 tree sections displayed in this tabpanel.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 0,
"The empty notice should not be displayed in this tabpanel.");
- let postScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
- is(postScope.querySelector(".name").getAttribute("value"),
+ is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
L10N.getStr("paramsFormData"),
- "The post scope doesn't have the correct title.");
+ "The post section doesn't have the correct title.");
- is(postScope.querySelectorAll(".variables-view-variable").length, 2,
- "There should be 2 param values displayed in the post scope.");
- is(postScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- "foo", "The first query param name was incorrect.");
- is(postScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- "\"bar\"", "The first query param value was incorrect.");
- is(postScope.querySelectorAll(".variables-view-variable .name")[1]
- .getAttribute("value"),
- "baz", "The second query param name was incorrect.");
- is(postScope.querySelectorAll(".variables-view-variable .value")[1]
- .getAttribute("value"),
- "\"123\"", "The second query param value was incorrect.");
+ let labels = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
+
+ is(labels[0].textContent, "foo", "The first query param name was incorrect.");
+ is(values[0].textContent, "\"bar\"", "The first query param value was incorrect.");
+ is(labels[1].textContent, "baz", "The second query param name was incorrect.");
+ is(values[1].textContent, "\"123\"", "The second query param value was incorrect.");
return teardown(monitor);
});
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -9,31 +9,34 @@
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/l10n");
let { tab, monitor } = yield initNetMonitor(POST_RAW_WITH_HEADERS_URL);
info("Starting test... ");
- let { document, EVENTS, NetMonitorView } = monitor.panelWin;
+ let { document, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
let wait = waitForNetworkEvents(monitor, 0, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
- let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
- NetMonitorView.toggleDetailsPane({ visible: true });
- RequestsMenu.selectedIndex = 0;
- yield onEvent;
+ // Wait for all tree view updated by react
+ wait = waitForDOM(document, "#headers-tabpanel .variables-view-scope", 3);
+ EventUtils.sendMouseEvent({ type: "mousedown" },
+ document.getElementById("details-pane-toggle"));
+ EventUtils.sendMouseEvent({ type: "mousedown" },
+ document.querySelectorAll("#details-pane tab")[0]);
+ yield wait;
let tabEl = document.querySelectorAll("#details-pane tab")[0];
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[0];
let requestFromUploadScope = tabpanel.querySelectorAll(".variables-view-scope")[2];
is(tabEl.getAttribute("selected"), "true",
"The headers tab in the network details pane should be selected.");
is(tabpanel.querySelectorAll(".variables-view-scope").length, 3,
@@ -55,44 +58,37 @@ add_task(function* () {
"The first request header value was incorrect.");
is(requestFromUploadScope.querySelectorAll(".variables-view-variable .name")[1]
.getAttribute("value"),
"custom-header", "The second request header name was incorrect.");
is(requestFromUploadScope.querySelectorAll(".variables-view-variable .value")[1]
.getAttribute("value"),
"\"hello world!\"", "The second request header value was incorrect.");
- onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
+ // Wait for all tree sections updated by react
+ wait = waitForDOM(document, "#params-tabpanel .tree-section");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[2]);
- yield onEvent;
+ yield wait;
- tabEl = document.querySelectorAll("#details-pane tab")[2];
tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
- let formDataScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
- is(tab.getAttribute("selected"), "true",
- "The response tab in the network details pane should be selected.");
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
- "There should be 1 header scope displayed in this tabpanel.");
-
- is(formDataScope.querySelector(".name").getAttribute("value"),
- L10N.getStr("paramsFormData"),
- "The form data scope doesn't have the correct title.");
+ ok(tabpanel.querySelector(".treeTable"),
+ "The params tree view should be displayed.");
+ ok(tabpanel.querySelector(".editor-mount") === null,
+ "The post data shouldn't be displayed.");
- is(formDataScope.querySelectorAll(".variables-view-variable").length, 2,
- "There should be 2 payload values displayed in the form data scope.");
+ is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
+ L10N.getStr("paramsFormData"),
+ "The form data section doesn't have the correct title.");
- is(formDataScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- "foo", "The first payload param name was incorrect.");
- is(formDataScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- "\"bar\"", "The first payload param value was incorrect.");
- is(formDataScope.querySelectorAll(".variables-view-variable .name")[1]
- .getAttribute("value"),
- "baz", "The second payload param name was incorrect.");
- is(formDataScope.querySelectorAll(".variables-view-variable .value")[1]
- .getAttribute("value"),
- "\"123\"", "The second payload param value was incorrect.");
+ let labels = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
+
+ is(labels[0].textContent, "foo", "The first payload param name was incorrect.");
+ is(values[0].textContent, "\"bar\"", "The first payload param value was incorrect.");
+ is(labels[1].textContent, "baz", "The second payload param name was incorrect.");
+ is(values[1].textContent, "\"123\"", "The second payload param value was incorrect.");
return teardown(monitor);
});
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -9,66 +9,53 @@
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/l10n");
let { tab, monitor } = yield initNetMonitor(POST_JSON_URL);
info("Starting test... ");
- let { document, EVENTS, NetMonitorView } = monitor.panelWin;
- let { RequestsMenu, NetworkDetails } = NetMonitorView;
+ let { document, NetMonitorView } = monitor.panelWin;
+ let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
- NetworkDetails._params.lazyEmpty = false;
let wait = waitForNetworkEvents(monitor, 0, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
- let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
- NetMonitorView.toggleDetailsPane({ visible: true }, 2);
- RequestsMenu.selectedIndex = 0;
- yield onEvent;
+ // Wait for all tree view updated by react
+ wait = waitForDOM(document, "#params-tabpanel .tree-section");
+ EventUtils.sendMouseEvent({ type: "mousedown" },
+ document.getElementById("details-pane-toggle"));
+ EventUtils.sendMouseEvent({ type: "mousedown" },
+ document.querySelectorAll("#details-pane tab")[2]);
+ yield wait;
- let tabEl = document.querySelectorAll("#event-details-pane tab")[2];
let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2];
- is(tabEl.getAttribute("selected"), "true",
- "The params tab in the network details pane should be selected.");
+ ok(tabpanel.querySelector(".treeTable"),
+ "The request params doesn't have the indended visibility.");
+ ok(tabpanel.querySelector(".editor-mount") === null,
+ "The request post data doesn't have the indended visibility.");
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), false,
- "The request params box doesn't have the intended visibility.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), true,
- "The request post data textarea box doesn't have the intended visibility.");
-
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
- "There should be 1 param scopes displayed in this tabpanel.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+ is(tabpanel.querySelectorAll(".tree-section").length, 1,
+ "There should be 1 tree sections displayed in this tabpanel.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 0,
"The empty notice should not be displayed in this tabpanel.");
- let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
- is(jsonScope.querySelector(".name").getAttribute("value"),
+ is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
L10N.getStr("jsonScopeName"),
- "The JSON scope doesn't have the correct title.");
-
- let valueScope = tabpanel.querySelector(
- ".variables-view-scope > .variables-view-element-details");
+ "The JSON section doesn't have the correct title.");
- is(valueScope.querySelectorAll(".variables-view-variable").length, 1,
- "There should be 1 value displayed in the JSON scope.");
- is(valueScope.querySelector(".variables-view-property .name")
- .getAttribute("value"),
- "a", "The JSON var name was incorrect.");
- is(valueScope.querySelector(".variables-view-property .value")
- .getAttribute("value"),
- "1", "The JSON var value was incorrect.");
+ let labels = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
- let detailsParent = valueScope.querySelector(".variables-view-property .name")
- .closest(".variables-view-element-details");
- is(detailsParent.hasAttribute("open"), true, "The JSON value must be visible");
+ is(labels[0].textContent, "a", "The JSON var name was incorrect.");
+ is(values[0].textContent, "1", "The JSON var value was incorrect.");
return teardown(monitor);
});
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -14,17 +14,16 @@ add_task(function* () {
info("Starting test... ");
let { document, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
let requestItems = [];
RequestsMenu.lazyUpdate = false;
- NetworkDetails._params.lazyEmpty = false;
const REQUEST_DATA = [
{
// request #0
method: "GET",
uri: STATUS_CODES_SJS + "?sts=100",
details: {
status: 101,
@@ -168,43 +167,42 @@ add_task(function* () {
/**
* A function that tests "Params" tab contains correct information.
*/
function* testParams(data) {
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
let statusParamValue = data.uri.split("=").pop();
let statusParamShownValue = "\"" + statusParamValue + "\"";
+ let treeSections = tabpanel.querySelectorAll(".tree-section");
- is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
- "There should be 1 param scope displayed in this tabpanel.");
- is(tabpanel.querySelectorAll(".variable-or-property").length, 1,
- "There should be 1 param value displayed in this tabpanel.");
- is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+ is(treeSections.length, 1,
+ "There should be 1 param section displayed in this tabpanel.");
+ is(tabpanel.querySelectorAll("tr:not(.tree-section).treeRow").length, 1,
+ "There should be 1 param row displayed in this tabpanel.");
+ is(tabpanel.querySelectorAll(".empty-notice").length, 0,
"The empty notice should not be displayed in this tabpanel.");
- let paramsScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
+ let labels = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
- is(paramsScope.querySelector(".name").getAttribute("value"),
+ is(treeSections[0].querySelector(".treeLabel").textContent,
L10N.getStr("paramsQueryString"),
"The params scope doesn't have the correct title.");
- is(paramsScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- "sts", "The param name was incorrect.");
- is(paramsScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"),
- statusParamShownValue, "The param value was incorrect.");
+ is(labels[0].textContent, "sts", "The param name was incorrect.");
+ is(values[0].textContent, statusParamShownValue, "The param value was incorrect.");
- is(tabpanel.querySelector("#request-params-box")
- .hasAttribute("hidden"), false,
- "The request params box should not be hidden.");
- is(tabpanel.querySelector("#request-post-data-textarea-box")
- .hasAttribute("hidden"), true,
- "The request post data textarea box should be hidden.");
+ ok(tabpanel.querySelector(".treeTable"),
+ "The request params tree view should be displayed.");
+ is(tabpanel.querySelector(".editor-mount") === null,
+ true,
+ "The request post data editor should be hidden.");
}
/**
* A helper that clicks on a specified request and returns a promise resolved
* when NetworkDetails has been populated with the data of the given request.
*/
function chooseRequest(index) {
let onTabUpdated = monitor.panelWin.once(EVENTS.TAB_UPDATED);
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1103,28 +1103,23 @@
}
.treeTable .textbox-input {
text-overflow: ellipsis;
border: none;
background: none;
color: inherit;
width: 100%;
- margin-inline-end: 2px;
}
.treeTable .textbox-input:focus {
outline: 0;
box-shadow: var(--theme-focus-box-shadow-textbox);
}
-.treeTable .treeLabel {
- font-weight: 600;
-}
-
.properties-view {
/* FIXME: Minus 24px * 2 for toolbox height + panel height
* Give a fixed panel container height in order to force tree view scrollable */
height: calc(100vh - 48px);
display: flex;
flex-direction: column;
}
@@ -1157,16 +1152,21 @@
}
.properties-view .devtools-searchbox,
.tree-container .treeTable .tree-section {
width: 100%;
background-color: var(--theme-toolbar-background);
}
+.properties-view .devtools-searchbox,
+.tree-container .treeTable tr:not(:last-child) td:not([class=""]) {
+ border-bottom: 1px solid var(--theme-splitter-color);
+}
+
.tree-container .treeTable .tree-section > * {
vertical-align: middle;
}
.tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel,
.tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel:hover {
color: var(--theme-body-color-alt);
}
@@ -1176,39 +1176,46 @@
max-width: 0;
padding-inline-end: 5px;
}
.tree-container .objectBox {
white-space: nowrap;
}
+.empty-notice {
+ color: var(--theme-body-color-alt);
+ padding: 3px 8px;
+}
+
.editor-container,
.editor-mount,
.editor-mount iframe {
border: none;
width: 100%;
height: 100%;
}
/*
* FIXME: normal html block element cannot fill outer XUL element
* This workaround should be removed after netmonitor is migrated to react
*/
+#react-params-tabpanel-hook,
#react-preview-tabpanel-hook,
#react-security-tabpanel-hook,
#react-timings-tabpanel-hook,
#network-statistics-charts,
#primed-cache-chart,
#empty-cache-chart {
display: -moz-box;
-moz-box-flex: 1;
}
/* For vbox */
+#react-params-tabpanel-hook,
#react-preview-tabpanel-hook,
#react-security-tabpanel-hook,
#react-timings-tabpanel-hook,
#primed-cache-chart,
#empty-cache-chart {
-moz-box-orient: vertical;
}