--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -246,18 +246,52 @@ function waitForNEvents(target, eventNam
if (++count == numTimes) {
target[remove](eventName, onEvent, useCapture);
deferred.resolve.apply(deferred, aArgs);
}
}, useCapture);
break;
}
}
+return deferred.promise;
+}
- 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
--- a/devtools/client/netmonitor/actions/filters.js
+++ b/devtools/client/netmonitor/actions/filters.js
@@ -2,16 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
TOGGLE_REQUEST_FILTER_TYPE,
ENABLE_REQUEST_FILTER_TYPE_ONLY,
+ SET_HEADERS_FILTER_TEXT,
SET_REQUEST_FILTER_TEXT,
} = require("../constants");
/**
* Toggle an existing filter type state.
* If type 'all' is specified, all the other filter types are set to false.
* Available filter types are defined in filters reducer.
*
@@ -35,24 +36,37 @@ function toggleRequestFilterType(filter)
function enableRequestFilterTypeOnly(filter) {
return {
type: ENABLE_REQUEST_FILTER_TYPE_ONLY,
filter,
};
}
/**
+ * Set filter text in headers panel.
+ *
+ * @param {string} text - A filter text is going to be set
+ */
+function setHeadersFilterText(text) {
+ return {
+ type: SET_HEADERS_FILTER_TEXT,
+ text,
+ };
+}
+
+/**
* Set filter text.
*
* @param {string} text - A filter text is going to be set
*/
function setRequestFilterText(text) {
return {
type: SET_REQUEST_FILTER_TEXT,
text,
};
}
module.exports = {
toggleRequestFilterType,
enableRequestFilterTypeOnly,
+ setHeadersFilterText,
setRequestFilterText,
};
--- a/devtools/client/netmonitor/constants.js
+++ b/devtools/client/netmonitor/constants.js
@@ -1,34 +1,36 @@
/* 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,
+ HEADERS_SIZE_DECIMALS: 3,
REQUEST_TIME_DECIMALS: 2,
+ FILTER_SEARCH_DELAY: 200,
};
const actionTypes = {
ADD_REQUEST: "ADD_REQUEST",
ADD_TIMING_MARKER: "ADD_TIMING_MARKER",
BATCH_ACTIONS: "BATCH_ACTIONS",
BATCH_ENABLE: "BATCH_ENABLE",
CLEAR_REQUESTS: "CLEAR_REQUESTS",
CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
OPEN_SIDEBAR: "OPEN_SIDEBAR",
OPEN_STATISTICS: "OPEN_STATISTICS",
PRESELECT_REQUEST: "PRESELECT_REQUEST",
REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
SELECT_REQUEST: "SELECT_REQUEST",
+ SET_HEADERS_FILTER_TEXT: "SET_HEADERS_FILTER_TEXT",
SET_REQUEST_FILTER_TEXT: "SET_REQUEST_FILTER_TEXT",
SORT_BY: "SORT_BY",
TOGGLE_REQUEST_FILTER_TYPE: "TOGGLE_REQUEST_FILTER_TYPE",
UPDATE_REQUEST: "UPDATE_REQUEST",
WATERFALL_RESIZE: "WATERFALL_RESIZE",
};
// Descriptions for what this frontend is currently doing.
--- a/devtools/client/netmonitor/details-view.js
+++ b/devtools/client/netmonitor/details-view.js
@@ -14,33 +14,32 @@ const { Heritage } = require("devtools/c
const { Task } = require("devtools/shared/task");
const { ToolSidebar } = require("devtools/client/framework/sidebar");
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 HeadersPanel = createFactory(require("./shared/components/headers-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 = {
"/ecmascript": Editor.modes.js,
"/javascript": Editor.modes.js,
"/x-javascript": Editor.modes.js,
"/html": Editor.modes.html,
"/xhtml": Editor.modes.html,
"/xml": Editor.modes.html,
"/atom": Editor.modes.html,
@@ -89,16 +88,23 @@ DetailsView.prototype = {
},
/**
* Initialization function, called when the network monitor is started.
*/
initialize: function (store) {
dumpn("Initializing the DetailsView");
+ this._headersPanelNode = $("#react-headers-tabpanel-hook");
+
+ ReactDOM.render(Provider(
+ { store },
+ HeadersPanel()
+ ), this._headersPanelNode);
+
this._previewPanelNode = $("#react-preview-tabpanel-hook");
ReactDOM.render(Provider(
{ store },
PreviewPanel()
), this._previewPanelNode);
this._securityPanelNode = $("#react-security-tabpanel-hook");
@@ -116,21 +122,16 @@ DetailsView.prototype = {
), this._timingsPanelNode);
this.widget = $("#event-details-pane");
this.sidebar = new ToolSidebar(this.widget, this, "netmonitor", {
disableTelemetry: true,
showAllTabsMenu: true
});
- this._headers = new VariablesView($("#all-headers"),
- Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
- 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"),
@@ -141,30 +142,28 @@ DetailsView.prototype = {
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._headersPanelNode);
ReactDOM.unmountComponentAtNode(this._previewPanelNode);
ReactDOM.unmountComponentAtNode(this._securityPanelNode);
ReactDOM.unmountComponentAtNode(this._timingsPanelNode);
this.sidebar.destroy();
$("tabpanels", this.widget).removeEventListener("select",
this._onTabSelect);
},
@@ -178,17 +177,16 @@ DetailsView.prototype = {
*/
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);
// Show the "Preview" tabpanel only for plain HTML responses.
this.sidebar.toggleTab(isHtml, "preview-tab");
// Show the "Security" tab only for requests that
@@ -203,17 +201,16 @@ DetailsView.prototype = {
// request has no security information.
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);
@@ -242,24 +239,16 @@ DetailsView.prototype = {
viewState.dirty[tab] = true;
viewState.latestData = src;
return;
}
Task.spawn(function* () {
viewState.updating[tab] = true;
switch (tab) {
- // "Headers"
- case 0:
- yield view._setSummary(src);
- yield view._setResponseHeaders(src.responseHeaders);
- yield view._setRequestHeaders(
- 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);
@@ -289,140 +278,16 @@ DetailsView.prototype = {
// Tab is dirty but no longer selected. Don't refresh it now, it'll be
// done if the tab is shown again.
viewState.dirty[tab] = false;
}
}, e => console.error(e));
},
/**
- * Sets the network request summary shown in this view.
- *
- * @param object data
- * The data source (this should be the attachment of a request item).
- */
- _setSummary: function (data) {
- if (data.url) {
- let unicodeUrl = decodeUnicodeUrl(data.url);
- $("#headers-summary-url-value").setAttribute("value", unicodeUrl);
- $("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl);
- $("#headers-summary-url").removeAttribute("hidden");
- } else {
- $("#headers-summary-url").setAttribute("hidden", "true");
- }
-
- if (data.method) {
- $("#headers-summary-method-value").setAttribute("value", data.method);
- $("#headers-summary-method").removeAttribute("hidden");
- } else {
- $("#headers-summary-method").setAttribute("hidden", "true");
- }
-
- if (data.remoteAddress) {
- let address = data.remoteAddress;
- if (address.indexOf(":") != -1) {
- address = `[${address}]`;
- }
- if (data.remotePort) {
- address += `:${data.remotePort}`;
- }
- $("#headers-summary-address-value").setAttribute("value", address);
- $("#headers-summary-address-value").setAttribute("tooltiptext", address);
- $("#headers-summary-address").removeAttribute("hidden");
- } else {
- $("#headers-summary-address").setAttribute("hidden", "true");
- }
-
- if (data.status) {
- // "code" attribute is only used by css to determine the icon color
- let code;
- if (data.fromCache) {
- code = "cached";
- } else if (data.fromServiceWorker) {
- code = "service worker";
- } else {
- code = data.status;
- }
- $("#headers-summary-status-circle").setAttribute("data-code", code);
- $("#headers-summary-status-value").setAttribute("value",
- data.status + " " + data.statusText);
- $("#headers-summary-status").removeAttribute("hidden");
- } else {
- $("#headers-summary-status").setAttribute("hidden", "true");
- }
-
- if (data.httpVersion) {
- $("#headers-summary-version-value").setAttribute("value",
- data.httpVersion);
- $("#headers-summary-version").removeAttribute("hidden");
- } else {
- $("#headers-summary-version").setAttribute("hidden", "true");
- }
- },
-
- /**
- * Sets the network request headers shown in this view.
- *
- * @param object headers
- * The "requestHeaders" message received from the server.
- * @param object uploadHeaders
- * The "requestHeadersFromUploadStream" inferred from the POST payload.
- * @return object
- * A promise that resolves when request headers are set.
- */
- _setRequestHeaders: Task.async(function* (headers, uploadHeaders) {
- if (headers && headers.headers.length) {
- yield this._addHeaders(this._requestHeaders, headers);
- }
- if (uploadHeaders && uploadHeaders.headers.length) {
- yield this._addHeaders(this._requestHeadersFromUpload, uploadHeaders);
- }
- }),
-
- /**
- * Sets the network response headers shown in this view.
- *
- * @param object response
- * The message received from the server.
- * @return object
- * A promise that resolves when response headers are set.
- */
- _setResponseHeaders: Task.async(function* (response) {
- if (response && response.headers.length) {
- response.headers.sort((a, b) => a.name > b.name);
- yield this._addHeaders(this._responseHeaders, response);
- }
- }),
-
- /**
- * Populates the headers container in this view with the specified data.
- *
- * @param string name
- * The type of headers to populate (request or response).
- * @param object response
- * The message received from the server.
- * @return object
- * A promise that resolves when headers are added.
- */
- _addHeaders: Task.async(function* (name, response) {
- let kb = response.headersSize / 1024;
- let size = L10N.numberWithDecimals(kb, HEADERS_SIZE_DECIMALS);
- let text = L10N.getFormatStr("networkMenu.sizeKB", size);
-
- let headersScope = this._headers.addScope(name + " (" + text + ")");
- headersScope.expanded = true;
-
- for (let header of response.headers) {
- let headerVar = headersScope.addItem(header.name, {}, {relaxed: true});
- let headerValue = yield gNetwork.getString(header.value);
- headerVar.setGrip(headerValue);
- }
- }),
-
- /**
* Sets the network request cookies shown in this view.
*
* @param object response
* The message received from the server.
* @return object
* A promise that is resolved when the request cookies are set.
*/
_setRequestCookies: Task.async(function* (response) {
@@ -698,22 +563,19 @@ 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
@@ -118,96 +118,18 @@
data-localization="label=netmonitor.tab.security"/>
<tab id="preview-tab"
crop="end"
data-localization="label=netmonitor.tab.preview"/>
</tabs>
<tabpanels flex="1">
<tabpanel id="headers-tabpanel"
class="tabpanel-content">
- <vbox flex="1">
- <hbox id="headers-summary-url"
- class="tabpanel-summary-container"
- align="center">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.url"/>
- <textbox id="headers-summary-url-value"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox id="headers-summary-method"
- class="tabpanel-summary-container"
- align="center">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.method"/>
- <label id="headers-summary-method-value"
- class="plain tabpanel-summary-value devtools-monospace"
- crop="end"
- flex="1"/>
- </hbox>
- <hbox id="headers-summary-address"
- class="tabpanel-summary-container"
- align="center">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.address"/>
- <textbox id="headers-summary-address-value"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox id="headers-summary-status"
- class="tabpanel-summary-container"
- align="center">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.status"/>
- <box id="headers-summary-status-circle"
- class="requests-menu-status-icon"/>
- <label id="headers-summary-status-value"
- class="plain tabpanel-summary-value devtools-monospace"
- crop="end"
- flex="1"/>
- <button id="headers-summary-resend"
- class="devtools-toolbarbutton"
- data-localization="label=netmonitor.summary.editAndResend"/>
- <button id="toggle-raw-headers"
- class="devtools-toolbarbutton"
- data-localization="label=netmonitor.summary.rawHeaders"/>
- </hbox>
- <hbox id="headers-summary-version"
- class="tabpanel-summary-container"
- align="center">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.version"/>
- <label id="headers-summary-version-value"
- class="plain tabpanel-summary-value devtools-monospace"
- crop="end"
- flex="1"/>
- </hbox>
- <hbox id="raw-headers"
- class="tabpanel-summary-container"
- align="center"
- hidden="true">
- <vbox id="raw-request-headers-textarea-box" flex="1" hidden="false">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.rawHeaders.requestHeaders"/>
- <textbox id="raw-request-headers-textarea"
- class="raw-response-textarea"
- flex="1" multiline="true" readonly="true"/>
- </vbox>
- <vbox id="raw-response-headers-textarea-box" flex="1" hidden="false">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.summary.rawHeaders.responseHeaders"/>
- <textbox id="raw-response-headers-textarea"
- class="raw-response-textarea"
- flex="1" multiline="true" readonly="true"/>
- </vbox>
- </hbox>
- <vbox id="all-headers" flex="1"/>
- </vbox>
+ <html:div xmlns="http://www.w3.org/1999/xhtml"
+ id="react-headers-tabpanel-hook"/>
</tabpanel>
<tabpanel id="cookies-tabpanel"
class="tabpanel-content">
<vbox flex="1">
<vbox id="all-cookies" flex="1"/>
</vbox>
</tabpanel>
<tabpanel id="params-tabpanel"
--- a/devtools/client/netmonitor/reducers/filters.js
+++ b/devtools/client/netmonitor/reducers/filters.js
@@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const I = require("devtools/client/shared/vendor/immutable");
const {
TOGGLE_REQUEST_FILTER_TYPE,
ENABLE_REQUEST_FILTER_TYPE_ONLY,
+ SET_HEADERS_FILTER_TEXT,
SET_REQUEST_FILTER_TEXT,
} = require("../constants");
const FilterTypes = I.Record({
all: false,
html: false,
css: false,
js: false,
@@ -22,16 +23,17 @@ const FilterTypes = I.Record({
media: false,
flash: false,
ws: false,
other: false,
});
const Filters = I.Record({
requestFilterTypes: new FilterTypes({ all: true }),
+ headersFilterText: "",
requestFilterText: "",
});
function toggleRequestFilterType(state, action) {
let { filter } = action;
let newState;
// Ignore unknown filter type
@@ -68,16 +70,18 @@ function enableRequestFilterTypeOnly(sta
function filters(state = new Filters(), action) {
switch (action.type) {
case TOGGLE_REQUEST_FILTER_TYPE:
return state.set("requestFilterTypes",
toggleRequestFilterType(state.requestFilterTypes, action));
case ENABLE_REQUEST_FILTER_TYPE_ONLY:
return state.set("requestFilterTypes",
enableRequestFilterTypeOnly(state.requestFilterTypes, action));
+ case SET_HEADERS_FILTER_TEXT:
+ return state.set("headersFilterText", action.text);
case SET_REQUEST_FILTER_TEXT:
return state.set("requestFilterText", action.text);
default:
return state;
}
}
module.exports = filters;
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -17,18 +17,17 @@ const ReactDOM = require("devtools/clien
const { Provider } = require("devtools/client/shared/vendor/react-redux");
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,
} = require("./request-utils");
const {
getActiveFilters,
getSortedRequests,
getDisplayedRequests,
getRequestById,
getSelectedRequest
@@ -85,21 +84,16 @@ RequestsMenuView.prototype = {
this.store.subscribe(storeWatcher(
false,
() => this.store.getState().ui.sidebarOpen,
() => this.onResize()
));
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);
this._summary = $("#requests-menu-network-summary-button");
this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
this.onResize = this.onResize.bind(this);
this._splitter = $("#network-inspector-view-splitter");
this._splitter.addEventListener("mouseup", this.onResize, false);
window.addEventListener("resize", this.onResize, false);
@@ -116,20 +110,16 @@ RequestsMenuView.prototype = {
},
_onConnect() {
if (NetMonitorController.supportsCustomRequest) {
$("#custom-request-send-button")
.addEventListener("click", this.sendCustomRequestEvent, false);
$("#custom-request-close-button")
.addEventListener("click", this.closeCustomRequestEvent, false);
- $("#headers-summary-resend")
- .addEventListener("click", this.cloneSelectedRequestEvent, false);
- } else {
- $("#headers-summary-resend").hidden = true;
}
},
/**
* Destruction function, called when the network monitor is closed.
*/
destroy() {
dumpn("Destroying the RequestsMenuView");
@@ -137,20 +127,16 @@ RequestsMenuView.prototype = {
Prefs.filters = getActiveFilters(this.store.getState());
// this.flushRequestsTask.disarm();
$("#custom-request-send-button")
.removeEventListener("click", this.sendCustomRequestEvent, false);
$("#custom-request-close-button")
.removeEventListener("click", this.closeCustomRequestEvent, false);
- $("#headers-summary-resend")
- .removeEventListener("click", this.cloneSelectedRequestEvent, false);
- $("#toggle-raw-headers")
- .removeEventListener("click", this.toggleRawHeadersEvent, false);
this._splitter.removeEventListener("mouseup", this.onResize, false);
window.removeEventListener("resize", this.onResize, false);
this.tooltip.destroy();
ReactDOM.unmountComponentAtNode(this.mountPoint);
},
@@ -199,17 +185,46 @@ RequestsMenuView.prototype = {
this.store.dispatch(action).then(() => window.emit(EVENTS.REQUEST_ADDED, action.id));
},
updateRequest: Task.async(function* (id, data) {
const action = Actions.updateRequest(id, data, true);
yield this.store.dispatch(action);
- let { responseContent, requestPostData } = action.data;
+ let {
+ requestHeaders,
+ requestPostData,
+ responseContent,
+ responseHeaders,
+ } = action.data;
+
+ if (requestHeaders && requestHeaders.headers &&
+ requestHeaders.headers.length > 0) {
+ for (let { value } of requestHeaders.headers) {
+ requestHeaders.headers.value = yield gNetwork.getString(value);
+ }
+ yield this.store.dispatch(Actions.updateRequest(
+ action.id,
+ { requestHeaders },
+ true,
+ ));
+ }
+
+ if (responseHeaders && responseHeaders.headers &&
+ responseHeaders.headers.length > 0) {
+ for (let { value } of responseHeaders.headers) {
+ responseHeaders.headers.value = yield gNetwork.getString(value);
+ }
+ yield this.store.dispatch(Actions.updateRequest(
+ action.id,
+ { responseHeaders },
+ true,
+ ));
+ }
if (responseContent && responseContent.content) {
let request = getRequestById(this.store.getState(), action.id);
if (request) {
let { mimeType } = request;
let { text, encoding } = responseContent.content;
let response = yield gNetwork.getString(text);
let payload = {};
@@ -332,38 +347,16 @@ RequestsMenuView.prototype = {
* Create a new custom request form populated with the data from
* the currently selected request.
*/
cloneSelectedRequest() {
this.store.dispatch(Actions.cloneSelectedRequest());
},
/**
- * Shows raw request/response headers in textboxes.
- */
- toggleRawHeaders: function () {
- let requestTextarea = $("#raw-request-headers-textarea");
- let responseTextarea = $("#raw-response-headers-textarea");
- let rawHeadersHidden = $("#raw-headers").getAttribute("hidden");
-
- if (rawHeadersHidden) {
- let selected = getSelectedRequest(this.store.getState());
- let selectedRequestHeaders = selected.requestHeaders.headers;
- let selectedResponseHeaders = selected.responseHeaders.headers;
- requestTextarea.value = writeHeaderText(selectedRequestHeaders);
- responseTextarea.value = writeHeaderText(selectedResponseHeaders);
- $("#raw-headers").hidden = false;
- } else {
- requestTextarea.value = null;
- responseTextarea.value = null;
- $("#raw-headers").hidden = true;
- }
- },
-
- /**
* Send a new HTTP request using the data in the custom request form.
*/
sendCustomRequest: function () {
let selected = getSelectedRequest(this.store.getState());
let data = {
url: selected.url,
method: selected.method,
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/shared/components/headers-panel.js
@@ -0,0 +1,311 @@
+/* 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/. */
+
+/* globals NetMonitorController */
+
+"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("../../l10n");
+const { HEADERS_SIZE_DECIMALS } = require("../../constants");
+const Actions = require("../../actions/index");
+const { getSelectedRequest } = require("../../selectors/index");
+const { decodeUnicodeUrl, writeHeaderText } = require("../../request-utils");
+
+// Components
+const PropertiesView = createFactory(require("./properties-view"));
+
+const { div, input, textarea } = DOM;
+const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend");
+const RAW_HEADERS = L10N.getStr("netmonitor.summary.rawHeaders");
+const RAW_HEADERS_REQUEST = L10N.getStr("netmonitor.summary.rawHeaders.requestHeaders");
+const RAW_HEADERS_RESPONSE = L10N.getStr("netmonitor.summary.rawHeaders.responseHeaders");
+const HEADERS_EMPTY_TEXT = L10N.getStr("headersEmptyText");
+const HEADERS_FILTER_TEXT = L10N.getStr("headersFilterText");
+const REQUEST_HEADERS = L10N.getStr("requestHeaders");
+const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload");
+const RESPONSE_HEADERS = L10N.getStr("responseHeaders");
+const SUMMARY_ADDRESS = L10N.getStr("netmonitor.summary.address");
+const SUMMARY_METHOD = L10N.getStr("netmonitor.summary.method");
+const SUMMARY_URL = L10N.getStr("netmonitor.summary.url");
+const SUMMARY_STATUS = L10N.getStr("netmonitor.summary.status");
+const SUMMARY_VERSION = L10N.getStr("netmonitor.summary.version");
+
+/*
+ * Headers panel component
+ * Lists basic information about the request
+ */
+const HeadersPanel = createClass({
+ displayName: "HeadersPanel",
+
+ propTypes: {
+ cloneSelectedRequest: PropTypes.func.isRequired,
+ headersFilterText: PropTypes.string,
+ requestHeaders: PropTypes.object,
+ requestHeadersFromUploadStream: PropTypes.object,
+ responseHeaders: PropTypes.object,
+ selectedRequest: PropTypes.object,
+ setHeadersFilterText: PropTypes.func.isRequired,
+ },
+
+ getInitialState() {
+ return {
+ rawHeadersOpened: false,
+ };
+ },
+
+ getSection(header) {
+ let kb = header.headersSize / 1024;
+ let size = L10N.numberWithDecimals(kb, HEADERS_SIZE_DECIMALS);
+ return L10N.getFormatStr("networkMenu.sizeKB", size);
+ },
+
+ toggleRawHeaders() {
+ this.setState({
+ rawHeadersOpened: !this.state.rawHeadersOpened,
+ });
+ },
+
+ render() {
+ const {
+ cloneSelectedRequest,
+ requestHeaders,
+ requestHeadersFromUploadStream: uploadHeaders,
+ responseHeaders,
+ selectedRequest,
+ } = this.props;
+
+ if ((!requestHeaders || !requestHeaders.headers.length) &&
+ (!uploadHeaders || !uploadHeaders.headers.length) &&
+ (!responseHeaders || !responseHeaders.headers.length)) {
+ return div({ className: "emptyNotice" },
+ HEADERS_EMPTY_TEXT
+ );
+ }
+
+ let object = {};
+ let sectionNames = [];
+
+ if (responseHeaders && responseHeaders.headers.length > 0) {
+ let section = `${RESPONSE_HEADERS} (${this.getSection(responseHeaders)})`;
+ object[section] =
+ responseHeaders.headers
+ .reduce((acc, { name, value }) =>
+ name ? Object.assign(acc, { [name]: value }) : acc
+ , {});
+ sectionNames.push(section);
+ }
+
+ if (requestHeaders && requestHeaders.headers.length > 0) {
+ let section = `${REQUEST_HEADERS} (${this.getSection(requestHeaders)})`;
+ object[section] =
+ requestHeaders.headers
+ .reduce((acc, { name, value }) =>
+ name ? Object.assign(acc, { [name]: value }) : acc
+ , {});
+ sectionNames.push(section);
+ }
+
+ if (uploadHeaders && uploadHeaders.headers.length > 0) {
+ let section =
+ `${REQUEST_HEADERS_FROM_UPLOAD} (${this.getSection(uploadHeaders)})`;
+ object[section] =
+ uploadHeaders.headers
+ .reduce((acc, { name, value }) =>
+ name ? Object.assign(acc, { [name]: value }) : acc
+ , {});
+ sectionNames.push(section);
+ }
+
+ const {
+ url,
+ method,
+ remoteAddress,
+ remotePort,
+ status,
+ statusText,
+ fromCache,
+ fromServiceWorker,
+ httpVersion,
+ } = selectedRequest;
+
+ let summaryUrl;
+ if (url) {
+ summaryUrl = (
+ div({ className: "tabpanel-summary-container headers-summary" },
+ div({ className: "tabpanel-summary-label headers-summary-label" }, SUMMARY_URL),
+ input({
+ className: "tabpanel-summary-value textbox-input devtools-monospace",
+ readOnly: true,
+ value: decodeUnicodeUrl(url),
+ }),
+ )
+ );
+ }
+
+ let summaryMethod;
+ if (method) {
+ summaryMethod = (
+ div({ className: "tabpanel-summary-container headers-summary" },
+ div({
+ className: "tabpanel-summary-label headers-summary-label",
+ }, SUMMARY_METHOD),
+ input({
+ className: "tabpanel-summary-value textbox-input devtools-monospace",
+ readOnly: true,
+ value: method,
+ }),
+ )
+ );
+ }
+
+ let summaryAddress;
+ if (remoteAddress) {
+ summaryAddress = (
+ div({ className: "tabpanel-summary-container headers-summary" },
+ div({
+ className: "tabpanel-summary-label headers-summary-label",
+ }, SUMMARY_ADDRESS),
+ input({
+ className: "tabpanel-summary-value textbox-input devtools-monospace",
+ readOnly: true,
+ value: remotePort ? `${remoteAddress}:${remotePort}` : remoteAddress,
+ }),
+ )
+ );
+ }
+
+ let summaryStatus;
+ if (status) {
+ let code;
+ if (fromCache) {
+ code = "cached";
+ } else if (fromServiceWorker) {
+ code = "service worker";
+ } else {
+ code = status;
+ }
+
+ summaryStatus = (
+ div({ className: "tabpanel-summary-container headers-summary" },
+ div({
+ className: "tabpanel-summary-label headers-summary-label",
+ }, SUMMARY_STATUS),
+ div({
+ className: "requests-menu-status-icon",
+ "data-code": code,
+ }),
+ input({
+ className: "tabpanel-summary-value textbox-input devtools-monospace",
+ readOnly: true,
+ value: `${status} ${statusText}`,
+ }),
+ NetMonitorController.supportsCustomRequest && input({
+ className: "tool-button",
+ onClick: cloneSelectedRequest,
+ type: "button",
+ value: EDIT_AND_RESEND,
+ }),
+ input({
+ className: "tool-button",
+ onClick: this.toggleRawHeaders,
+ type: "button",
+ value: RAW_HEADERS,
+ }),
+ )
+ );
+ }
+
+ let summaryVersion;
+ if (httpVersion) {
+ summaryVersion = (
+ div({ className: "tabpanel-summary-container headers-summary" },
+ div({
+ className: "tabpanel-summary-label headers-summary-label",
+ }, SUMMARY_VERSION),
+ input({
+ className: "tabpanel-summary-value textbox-input devtools-monospace",
+ readOnly: true,
+ value: httpVersion,
+ }),
+ )
+ );
+ }
+
+ let summaryRawHeaders;
+ if (this.state.rawHeadersOpened) {
+ summaryRawHeaders = (
+ div({ className: "tabpanel-summary-container headers-summary" },
+ div({ className: "raw-headers-container" },
+ div({ className: "tabpanel-summary-label" }, RAW_HEADERS_REQUEST),
+ textarea({
+ value: writeHeaderText(requestHeaders.headers),
+ readOnly: true,
+ }),
+ ),
+ div({ className: "raw-headers-container" },
+ div({ className: "tabpanel-summary-label" }, RAW_HEADERS_RESPONSE),
+ textarea({
+ value: writeHeaderText(responseHeaders.headers),
+ readOnly: true,
+ }),
+ ),
+ )
+ );
+ }
+
+ return (
+ div({},
+ div({ className: "section summary" },
+ summaryUrl,
+ summaryMethod,
+ summaryAddress,
+ summaryStatus,
+ summaryVersion,
+ summaryRawHeaders,
+ ),
+ PropertiesView({
+ object,
+ filterPlaceHolder: HEADERS_FILTER_TEXT,
+ sectionNames,
+ }),
+ )
+ );
+ }
+});
+
+module.exports = connect(
+ (state) => {
+ const selectedRequest = getSelectedRequest(state);
+
+ if (selectedRequest) {
+ const {
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ responseHeaders,
+ } = selectedRequest;
+
+ return {
+ headersFilterText: state.filters.headersFilterText,
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ responseHeaders,
+ selectedRequest,
+ };
+ }
+
+ return {};
+ },
+ (dispatch) => ({
+ setHeadersFilterText: (text) =>
+ dispatch(Actions.setHeadersFilterText(text)),
+ cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
+ })
+)(HeadersPanel);
--- 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',
+ 'headers-panel.js',
'preview-panel.js',
'properties-view.js',
'security-panel.js',
'timings-panel.js',
)
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -20,72 +20,67 @@ add_task(function* () {
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, ".properties-view .treeTable");
+ 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,
- "There should be 3 header scopes displayed in this tabpanel.");
+ is(tabpanel.querySelectorAll(".tree-section .treeLabel").length, 3,
+ "There should be 3 header sections displayed in this tabpanel.");
- is(requestFromUploadScope.querySelector(".name").getAttribute("value"),
+ is(tabpanel.querySelectorAll(".tree-section .treeLabel")[2].textContent,
L10N.getStr("requestHeadersFromUpload") + " (" +
L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(74 / 1024, 3)) + ")",
- "The request headers from upload scope doesn't have the correct title.");
+ "The request headers from upload section doesn't have the correct title.");
- is(requestFromUploadScope.querySelectorAll(".variables-view-variable").length, 2,
- "There should be 2 headers displayed in the request headers from upload scope.");
+ let labels = tabpanel
+ .querySelectorAll(".properties-view tr:not(.tree-section) .treeLabelCell .treeLabel");
+ let values = tabpanel
+ .querySelectorAll(".properties-view tr:not(.tree-section) .treeValueCell .objectBox");
- is(requestFromUploadScope.querySelectorAll(".variables-view-variable .name")[0]
- .getAttribute("value"),
- "content-type", "The first request header name was incorrect.");
- is(requestFromUploadScope.querySelectorAll(".variables-view-variable .value")[0]
- .getAttribute("value"), "\"application/x-www-form-urlencoded\"",
+ is(labels[labels.length - 2].textContent, "content-type",
+ "The first request header name was incorrect.");
+ is(values[values.length - 2].textContent, "\"application/x-www-form-urlencoded\"",
"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.");
+ is(labels[labels.length - 1].textContent, "custom-header",
+ "The second request header name was incorrect.");
+ is(values[values.length - 1].textContent, "\"hello world!\"",
+ "The second request header value was incorrect.");
- onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
+ let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[2]);
yield onEvent;
- 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.");
is(formDataScope.querySelectorAll(".variables-view-variable").length, 2,
"There should be 2 payload values displayed in the form data scope.");
-
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"),
--- a/devtools/client/netmonitor/test/browser_net_raw_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js
@@ -6,65 +6,65 @@
/**
* Tests if showing raw headers works.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(POST_DATA_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, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
let origItem = RequestsMenu.getItemAtIndex(0);
- let onTabEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
+ wait = waitForDOM(document, ".summary");
RequestsMenu.selectedItem = origItem;
- yield onTabEvent;
+ yield wait;
+ wait = waitForDOM(document, ".raw-headers-container textarea", 2);
EventUtils.sendMouseEvent({ type: "click" },
- document.getElementById("toggle-raw-headers"));
+ document.querySelectorAll(".tool-button")[1]);
+ yield wait;
testShowRawHeaders(origItem);
EventUtils.sendMouseEvent({ type: "click" },
- document.getElementById("toggle-raw-headers"));
+ document.querySelectorAll(".tool-button")[1]);
testHideRawHeaders(document);
return teardown(monitor);
/*
* Tests that raw headers were displayed correctly
*/
function testShowRawHeaders(data) {
- let requestHeaders = document.getElementById("raw-request-headers-textarea").value;
+ let requestHeaders = document
+ .querySelectorAll(".raw-headers-container textarea")[0].value;
for (let header of data.requestHeaders.headers) {
ok(requestHeaders.includes(header.name + ": " + header.value),
"textarea contains request headers");
}
- let responseHeaders = document.getElementById("raw-response-headers-textarea").value;
+ let responseHeaders = document
+ .querySelectorAll(".raw-headers-container textarea")[1].value;
for (let header of data.responseHeaders.headers) {
ok(responseHeaders.includes(header.name + ": " + header.value),
"textarea contains response headers");
}
}
/*
- * Tests that raw headers textareas are hidden and empty
+ * Tests that raw headers textareas are hidden
*/
function testHideRawHeaders() {
- let rawHeadersHidden = document.getElementById("raw-headers").getAttribute("hidden");
- let requestTextarea = document.getElementById("raw-request-headers-textarea");
- let responseTextarea = document.getElementById("raw-response-headers-textarea");
- ok(rawHeadersHidden, "raw headers textareas are hidden");
- ok(requestTextarea.value == "", "raw request headers textarea is empty");
- ok(responseTextarea.value == "", "raw response headers textarea is empty");
+ ok(!document.querySelector(".raw-headers-container"),
+ "raw request headers textarea is empty");
}
});
--- a/devtools/client/netmonitor/test/browser_net_security-details.js
+++ b/devtools/client/netmonitor/test/browser_net_security-details.js
@@ -34,32 +34,32 @@ add_task(function* () {
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
let errorbox = $("#security-error");
let infobox = $("#security-information");
is(errorbox, null, "Error box is hidden.");
ok(infobox, "Information box visible.");
- let textboxes = $all(".textbox-input");
+ let textboxes = $all("#security-information .textbox-input");
// Connection
// The protocol will be TLS but the exact version depends on which protocol
// the test server example.com supports.
let protocol = textboxes[0].value;
ok(protocol.startsWith("TLS"), "The protocol " + protocol + " seems valid.");
// The cipher suite used by the test server example.com might change at any
// moment but all of them should start with "TLS_".
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
let suite = textboxes[1].value;
ok(suite.startsWith("TLS_"), "The suite " + suite + " seems valid.");
// Host
- let hostLabel = $all(".treeLabel.objectLabel")[1];
+ let hostLabel = $all("#security-information .treeLabel.objectLabel")[1];
is(hostLabel.textContent, "Host example.com:", "Label has the expected value.");
is(textboxes[2].value, "Disabled", "Label has the expected value.");
is(textboxes[3].value, "Disabled", "Label has the expected value.");
// Cert
is(textboxes[4].value, "example.com", "Label has the expected value.");
is(textboxes[5].value, "<Not Available>", "Label has the expected value.");
is(textboxes[6].value, "<Not Available>", "Label has the expected value.");
--- a/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js
+++ b/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js
@@ -28,19 +28,20 @@ add_task(function* () {
yield wait;
info("Selecting secure request.");
RequestsMenu.selectedIndex = 0;
info("Selecting security tab.");
NetworkDetails.widget.selectedIndex = 5;
+ wait = monitor.panelWin.once(EVENTS.NETWORKDETAILSVIEW_POPULATED);
info("Selecting insecure request.");
RequestsMenu.selectedIndex = 1;
info("Waiting for security tab to be updated.");
- yield monitor.panelWin.once(EVENTS.NETWORKDETAILSVIEW_POPULATED);
+ yield wait;
is(NetworkDetails.widget.selectedIndex, 0,
"Selected tab was reset when selected security tab was hidden.");
return teardown(monitor);
});
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -151,24 +151,23 @@ add_task(function* () {
/**
* A function that tests "Summary" contains correct information.
*/
function* testSummary(data) {
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[0];
let { method, uri, details: { status, statusText } } = data;
- is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"),
- uri, "The url summary value is incorrect.");
- is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"),
- method, "The method summary value is incorrect.");
- is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("data-code"),
- status, "The status summary code is incorrect.");
- is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"),
- status + " " + statusText, "The status summary value is incorrect.");
+ let summaryValues = tabpanel.querySelectorAll(".tabpanel-summary-value.textbox-input");
+ is(summaryValues[0].value, uri, "The url summary value is incorrect.");
+ is(summaryValues[1].value, method, "The method summary value is incorrect.");
+ is(tabpanel.querySelector(".requests-menu-status-icon").dataset.code, status,
+ "The status summary code is incorrect.");
+ is(summaryValues[3].value, status + " " + statusText,
+ "The status summary value is incorrect.");
}
/**
* 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();
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -655,20 +655,16 @@
.tabpanel-content {
background-color: var(--theme-sidebar-background);
}
.theme-dark .tabpanel-content {
color: var(--theme-selection-color);
}
-#headers-tabpanel {
- background-color: var(--theme-toolbar-background);
-}
-
.theme-firebug .variables-view-scope:focus > .title {
color: var(--theme-body-color);
}
/* Summary tabpanel */
.tabpanel-summary-container {
padding: 1px;
@@ -687,16 +683,20 @@
}
.theme-dark .tabpanel-summary-value {
color: var(--theme-selection-color);
}
/* Headers tabpanel */
+#headers-tabpanel .summary {
+ background-color: var(--theme-toolbar-background);
+}
+
#headers-summary-status,
#headers-summary-version {
padding-bottom: 2px;
}
#headers-summary-size {
padding-top: 2px;
}
@@ -1097,42 +1097,43 @@
content: "";
}
/* Layout additional warning icon in tree value cell */
.security-info-value {
display: flex;
}
-.treeTable .textbox-input {
+.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;
}
+#headers-tabpanel .properties-view {
+ /* FIXME: Minus 24px * 2 + 87.5 for toolbox height + panel height + headers summary
+ * Give a fixed panel container height in order to force tree view scrollable */
+ height: calc(100vh - 135.5px);
+}
+
.properties-view .searchbox-section {
flex: 0 1 auto;
}
.properties-view .devtools-searchbox {
padding: 0;
}
@@ -1172,43 +1173,104 @@
}
.tree-container .treeTable .treeValueCell {
/* FIXME: Make value cell can be reduced to shorter width */
max-width: 0;
padding-inline-end: 5px;
}
+.headers-summary input:not([type="button"]) {
+ width: 100%;
+ background: none;
+ border: none;
+ color: inherit;
+ margin-inline-end: 2px;
+}
+
+.headers-summary input:not([type="button"]):focus {
+ outline: none;
+ box-shadow: var(--theme-focus-box-shadow-textbox);
+ transition: all 0.2s ease-in-out;
+}
+
+.headers-summary-label,
.tree-container .objectBox {
white-space: nowrap;
}
+.headers-summary,
+.response-summary {
+ display: flex;
+ align-items: center;
+}
+
+.headers-summary .tool-button {
+ background-color: rgba(0,0,0,0.2);
+ border: 1px solid transparent;
+ color: var(--theme-body-color);
+ transition: background 0.05s ease-in-out;
+ margin-inline-end: 6px;
+ padding: 0 5px;
+}
+
+.headers-summary .tool-button:hover {
+ background-color: rgba(0,0,0,0.3);
+}
+
+.headers-summary .tool-button:hover:active {
+ background-color: rgba(0,0,0,0.4);
+}
+
+.headers-summary .requests-menu-status-icon {
+ min-width: 10px;
+}
+
+.headers-summary .raw-headers-container {
+ flex: 1;
+ margin-inline-end: 8px;
+}
+
+.headers-summary .raw-headers-container textarea {
+ width: 100%;
+ height: 50vh;
+ margin: 5px 4px;
+ font: message-box;
+}
+
+.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-headers-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-headers-tabpanel-hook,
#react-preview-tabpanel-hook,
#react-security-tabpanel-hook,
#react-timings-tabpanel-hook,
#primed-cache-chart,
#empty-cache-chart {
-moz-box-orient: vertical;
}