/* 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 Actions = require("../../actions/index"); const { getSelectedRequest } = require("../../selectors/index"); const { writeHeaderText } = require("../../request-utils"); const { getFormattedSize } = require("../../utils/format-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, request: PropTypes.object, }, getInitialState() { return { rawHeadersOpened: false, }; }, getProperties(headers, title) { if (headers && headers.headers.length) { return { [`${title} (${getFormattedSize(headers.headersSize, 3)})`]: headers.headers.reduce((acc, { name, value }) => name ? Object.assign(acc, { [name]: value }) : acc , {}) }; } return null; }, toggleRawHeaders() { this.setState({ rawHeadersOpened: !this.state.rawHeadersOpened, }); }, renderSummary(label, value) { return ( div({ className: "tabpanel-summary-container headers-summary" }, div({ className: "tabpanel-summary-label headers-summary-label", }, label), input({ className: "tabpanel-summary-value textbox-input devtools-monospace", readOnly: true, value, }), ) ); }, render() { const { cloneSelectedRequest, request: { fromCache, fromServiceWorker, httpVersion, method, remoteAddress, remotePort, requestHeaders, requestHeadersFromUploadStream: uploadHeaders, responseHeaders, status, statusText, urlDetails, }, } = this.props; if ((!requestHeaders || !requestHeaders.headers.length) && (!uploadHeaders || !uploadHeaders.headers.length) && (!responseHeaders || !responseHeaders.headers.length)) { return div({ className: "empty-notice" }, HEADERS_EMPTY_TEXT ); } let object = Object.assign({}, this.getProperties(responseHeaders, RESPONSE_HEADERS), this.getProperties(requestHeaders, REQUEST_HEADERS), this.getProperties(uploadHeaders, REQUEST_HEADERS_FROM_UPLOAD), ); let summaryUrl = urlDetails.unicodeUrl ? this.renderSummary(SUMMARY_URL, urlDetails.unicodeUrl) : null; let summaryMethod = method ? this.renderSummary(SUMMARY_METHOD, method) : null; let summaryAddress = remoteAddress ? this.renderSummary(SUMMARY_ADDRESS, remotePort ? `${remoteAddress}:${remotePort}` : remoteAddress) : null; 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 = httpVersion ? this.renderSummary(SUMMARY_VERSION, httpVersion) : null; // display Status-Line above other response headers let statusLine = `${httpVersion} ${status} ${statusText}\n`; let summaryRawHeaders; if (this.state.rawHeadersOpened) { summaryRawHeaders = ( div({ className: "tabpanel-summary-container headers-summary" }, div({ className: "raw-headers-container" }, div({ className: "raw-headers" }, div({ className: "tabpanel-summary-label" }, RAW_HEADERS_REQUEST), textarea({ value: writeHeaderText(requestHeaders.headers), readOnly: true, }), ), div({ className: "raw-headers" }, div({ className: "tabpanel-summary-label" }, RAW_HEADERS_RESPONSE), textarea({ value: statusLine + writeHeaderText(responseHeaders.headers), readOnly: true, }), ), ) ) ); } return ( div({ className: "panel-container" }, div({ className: "summary" }, summaryUrl, summaryMethod, summaryAddress, summaryStatus, summaryVersion, summaryRawHeaders, ), PropertiesView({ object, filterPlaceHolder: HEADERS_FILTER_TEXT, sectionNames: Object.keys(object), }), ) ); } }); module.exports = connect( (state) => ({ request: getSelectedRequest(state) || {}, }), (dispatch) => ({ cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()), }), )(HeadersPanel);