--- a/devtools/client/netmonitor/details-view.js
+++ b/devtools/client/netmonitor/details-view.js
@@ -19,22 +19,22 @@ const { EVENTS } = require("./events");
const { L10N } = require("./l10n");
const { Filters } = require("./filter-predicates");
const {
decodeUnicodeUrl,
formDataURI,
getFormDataSections,
getUrlBaseName,
getUrlQuery,
- getUrlHost,
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 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,
@@ -88,16 +88,23 @@ DetailsView.prototype = {
},
/**
* Initialization function, called when the network monitor is started.
*/
initialize: function (store) {
dumpn("Initializing the DetailsView");
+ this._securityPanelNode = $("#react-security-tabpanel-hook");
+
+ ReactDOM.render(Provider(
+ { store },
+ SecurityPanel()
+ ), this._securityPanelNode);
+
this._timingsPanelNode = $("#react-timings-tabpanel-hook");
ReactDOM.render(Provider(
{ store },
TimingsPanel()
), this._timingsPanelNode);
this.widget = $("#event-details-pane");
@@ -140,16 +147,17 @@ DetailsView.prototype = {
$("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._securityPanelNode);
ReactDOM.unmountComponentAtNode(this._timingsPanelNode);
this.sidebar.destroy();
$("tabpanels", this.widget).removeEventListener("select",
this._onTabSelect);
},
/**
* Populates this view with the specified data.
@@ -250,20 +258,16 @@ DetailsView.prototype = {
src.requestHeaders,
src.requestHeadersFromUploadStream,
src.requestPostData);
break;
// "Response"
case 3:
yield view._setResponseBody(src.url, src.responseContent);
break;
- // "Security"
- case 5:
- yield view._setSecurityInfo(src.securityInfo, src.url);
- break;
// "Preview"
case 6:
yield view._setHtmlPreview(src.responseContent);
break;
}
viewState.updating[tab] = false;
}).then(() => {
if (tab == this.widget.selectedIndex) {
@@ -712,122 +716,16 @@ DetailsView.prototype = {
let iframe = $("#response-preview");
iframe.contentDocument.docShell.allowJavascript = false;
iframe.contentDocument.documentElement.innerHTML = responseBody;
window.emit(EVENTS.RESPONSE_HTML_PREVIEW_DISPLAYED);
return undefined;
}),
- /**
- * Sets the security information shown in this view.
- *
- * @param object securityInfo
- * The data received from server
- * @param string url
- * The URL of this request
- * @return object
- * A promise that is resolved when the security info is rendered.
- */
- _setSecurityInfo: Task.async(function* (securityInfo, url) {
- if (!securityInfo) {
- // We don't have security info. This could mean one of two things:
- // 1) This connection is not secure and this tab is not visible and thus
- // we shouldn't be here.
- // 2) We have already received securityState and the tab is visible BUT
- // the rest of the information is still on its way. Once it arrives
- // this method is called again.
- return;
- }
-
- /**
- * A helper that sets value and tooltiptext attributes of an element to
- * specified value.
- *
- * @param string selector
- * A selector for the element.
- * @param string value
- * The value to set. If this evaluates to false a placeholder string
- * <Not Available> is used instead.
- */
- function setValue(selector, value) {
- let label = $(selector);
- if (!value) {
- label.setAttribute("value", L10N.getStr(
- "netmonitor.security.notAvailable"));
- label.setAttribute("tooltiptext", label.getAttribute("value"));
- } else {
- label.setAttribute("value", value);
- label.setAttribute("tooltiptext", value);
- }
- }
-
- let errorbox = $("#security-error");
- let infobox = $("#security-information");
-
- if (securityInfo.state === "secure" || securityInfo.state === "weak") {
- infobox.hidden = false;
- errorbox.hidden = true;
-
- // Warning icons
- let cipher = $("#security-warning-cipher");
-
- if (securityInfo.state === "weak") {
- cipher.hidden = securityInfo.weaknessReasons.indexOf("cipher") === -1;
- } else {
- cipher.hidden = true;
- }
-
- let enabledLabel = L10N.getStr("netmonitor.security.enabled");
- let disabledLabel = L10N.getStr("netmonitor.security.disabled");
-
- // Connection parameters
- setValue("#security-protocol-version-value",
- securityInfo.protocolVersion);
- setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
-
- // Host header
- let domain = getUrlHost(url);
- let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader",
- domain);
- setValue("#security-info-host-header", hostHeader);
-
- // Parameters related to the domain
- setValue("#security-http-strict-transport-security-value",
- securityInfo.hsts ? enabledLabel : disabledLabel);
-
- setValue("#security-public-key-pinning-value",
- securityInfo.hpkp ? enabledLabel : disabledLabel);
-
- // Certificate parameters
- let cert = securityInfo.cert;
- setValue("#security-cert-subject-cn", cert.subject.commonName);
- setValue("#security-cert-subject-o", cert.subject.organization);
- setValue("#security-cert-subject-ou", cert.subject.organizationalUnit);
-
- setValue("#security-cert-issuer-cn", cert.issuer.commonName);
- setValue("#security-cert-issuer-o", cert.issuer.organization);
- setValue("#security-cert-issuer-ou", cert.issuer.organizationalUnit);
-
- setValue("#security-cert-validity-begins", cert.validity.start);
- setValue("#security-cert-validity-expires", cert.validity.end);
-
- setValue("#security-cert-sha1-fingerprint", cert.fingerprint.sha1);
- setValue("#security-cert-sha256-fingerprint", cert.fingerprint.sha256);
- } else {
- infobox.hidden = true;
- errorbox.hidden = false;
-
- // Strip any HTML from the message.
- let plain = new DOMParser().parseFromString(securityInfo.errorMessage,
- "text/html");
- setValue("#security-error-message", plain.body.textContent);
- }
- }),
-
_dataSrc: null,
_headers: null,
_cookies: null,
_params: null,
_json: null,
_paramsQueryString: "",
_paramsFormData: "",
_paramsPostPayload: "",
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -262,214 +262,18 @@
</tabpanel>
<tabpanel id="timings-tabpanel"
class="tabpanel-content">
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-timings-tabpanel-hook"/>
</tabpanel>
<tabpanel id="security-tabpanel"
class="tabpanel-content">
- <vbox id="security-error"
- class="tabpanel-summary-container"
- flex="1">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.error"/>
- <hbox class="security-info-section"
- flex="1">
- <textbox id="security-error-message"
- class="plain"
- flex="1"
- multiline="true"
- readonly="true"/>
- </hbox>
- </vbox>
- <vbox id="security-information"
- flex="1">
- <vbox id="security-info-connection"
- class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.connection"/>
- <vbox class="security-info-section">
- <hbox id="security-protocol-version"
- class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.protocolVersion"/>
- <textbox id="security-protocol-version-value"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox id="security-ciphersuite"
- class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.cipherSuite"/>
- <textbox id="security-ciphersuite-value"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- <image class="security-warning-icon"
- id="security-warning-cipher"
- data-localization="tooltiptext=netmonitor.security.warning.cipher" />
- </hbox>
- </vbox>
- </vbox>
- <vbox id="security-info-domain"
- class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- id="security-info-host-header"/>
- <vbox class="security-info-section">
- <hbox id="security-http-strict-transport-security"
- class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.hsts"/>
- <textbox id="security-http-strict-transport-security-value"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox id="security-public-key-pinning"
- class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.hpkp"/>
- <textbox id="security-public-key-pinning-value"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- </vbox>
- </vbox>
- <vbox id="security-info-certificate"
- class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- data-localization="content=netmonitor.security.certificate"/>
- <vbox class="security-info-section">
- <vbox class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.subjectinfo.label" flex="1"/>
- </vbox>
- <vbox class="security-info-section">
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.cn"/>
- <textbox id="security-cert-subject-cn"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.o"/>
- <textbox id="security-cert-subject-o"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.ou"/>
- <textbox id="security-cert-subject-ou"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- </vbox>
- <vbox class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.issuerinfo.label"
- flex="1"/>
- </vbox>
- <vbox class="security-info-section">
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.cn"/>
- <textbox id="security-cert-issuer-cn"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.o"/>
- <textbox id="security-cert-issuer-o"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.ou"/>
- <textbox id="security-cert-issuer-ou"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- </vbox>
- <vbox class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.periodofvalidity.label"
- flex="1"/>
- </vbox>
- <vbox class="security-info-section">
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.begins"/>
- <textbox id="security-cert-validity-begins"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.expires"/>
- <textbox id="security-cert-validity-expires"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- </vbox>
- <vbox class="tabpanel-summary-container">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.fingerprints.label"
- flex="1"/>
- </vbox>
- <vbox class="security-info-section">
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.sha256fingerprint"/>
- <textbox id="security-cert-sha256-fingerprint"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- <hbox class="tabpanel-summary-container"
- align="baseline">
- <label class="plain tabpanel-summary-label"
- data-localization="content=certmgr.certdetail.sha1fingerprint"/>
- <textbox id="security-cert-sha1-fingerprint"
- class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
- flex="1"
- readonly="true"/>
- </hbox>
- </vbox>
- </vbox>
- </vbox>
- </vbox>
+ <html:div xmlns="http://www.w3.org/1999/xhtml"
+ id="react-security-tabpanel-hook"/>
</tabpanel>
<tabpanel id="preview-tabpanel"
class="tabpanel-content">
<html:iframe id="response-preview"
frameborder="0"
sandbox=""/>
</tabpanel>
</tabpanels>
--- a/devtools/client/netmonitor/shared/components/moz.build
+++ b/devtools/client/netmonitor/shared/components/moz.build
@@ -1,7 +1,8 @@
# 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(
+ 'security-panel.js',
'timings-panel.js',
)
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/shared/components/security-panel.js
@@ -0,0 +1,176 @@
+/* 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 { DOM, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view"));
+const { L10N } = require("../../l10n");
+const { getUrlHost } = require("../../request-utils");
+const { getSelectedRequest } = require("../../selectors/index");
+
+const { div, input } = DOM;
+
+/*
+ * Security panel component
+ * If the site is being served over HTTPS, you get an extra tab labeled "Security".
+ * This contains details about the secure connection used including the protocol,
+ * the cipher suite, and certificate details
+ */
+function SecurityPanel({
+ securityInfo,
+ url,
+}) {
+ if (!securityInfo || !url) {
+ return div();
+ }
+
+ const notAvailable = L10N.getStr("netmonitor.security.notAvailable");
+ let object;
+
+ if ((securityInfo.state === "secure" || securityInfo.state === "weak")) {
+ const { subject, issuer, validity, fingerprint } = securityInfo.cert;
+ const enabledLabel = L10N.getStr("netmonitor.security.enabled");
+ const disabledLabel = L10N.getStr("netmonitor.security.disabled");
+
+ object = {
+ [L10N.getStr("netmonitor.security.connection")]: {
+ [L10N.getStr("netmonitor.security.protocolVersion")]:
+ securityInfo.protocolVersion || notAvailable,
+ [L10N.getStr("netmonitor.security.cipherSuite")]:
+ securityInfo.cipherSuite || notAvailable,
+ },
+ [L10N.getFormatStr("netmonitor.security.hostHeader", getUrlHost(url))]: {
+ [L10N.getStr("netmonitor.security.hsts")]:
+ securityInfo.hsts ? enabledLabel : disabledLabel,
+ [L10N.getStr("netmonitor.security.hpkp")]:
+ securityInfo.hpkp ? enabledLabel : disabledLabel,
+ },
+ [L10N.getStr("netmonitor.security.certificate")]: {
+ [L10N.getStr("certmgr.subjectinfo.label")]: {
+ [L10N.getStr("certmgr.certdetail.cn")]:
+ subject.commonName || notAvailable,
+ [L10N.getStr("certmgr.certdetail.o")]:
+ subject.organization || notAvailable,
+ [L10N.getStr("certmgr.certdetail.ou")]:
+ subject.organizationUnit || notAvailable,
+ },
+ [L10N.getStr("certmgr.issuerinfo.label")]: {
+ [L10N.getStr("certmgr.certdetail.cn")]:
+ issuer.commonName || notAvailable,
+ [L10N.getStr("certmgr.certdetail.o")]:
+ issuer.organization || notAvailable,
+ [L10N.getStr("certmgr.certdetail.ou")]:
+ issuer.organizationUnit || notAvailable,
+ },
+ [L10N.getStr("certmgr.periodofvalidity.label")]: {
+ [L10N.getStr("certmgr.begins")]:
+ validity.start || notAvailable,
+ [L10N.getStr("certmgr.expires")]:
+ validity.end || notAvailable,
+ },
+ [L10N.getStr("certmgr.fingerprints.label")]: {
+ [L10N.getStr("certmgr.certdetail.sha256fingerprint")]:
+ fingerprint.sha256 || notAvailable,
+ [L10N.getStr("certmgr.certdetail.sha1fingerprint")]:
+ fingerprint.sha1 || notAvailable,
+ },
+ },
+ };
+ } else {
+ object = {
+ [L10N.getStr("netmonitor.security.error")]:
+ new DOMParser().parseFromString(securityInfo.errorMessage, "text/html")
+ .body.textContent || notAvailable
+ };
+ }
+
+ return div({ id: "security-information" },
+ TreeView({
+ object,
+ columns: [{
+ id: "value",
+ width: "100%",
+ }],
+ renderValue: renderValue.bind(null, securityInfo.weaknessReasons),
+ expandedNodes: getExpandedNodes(object),
+ expandableStrings: false,
+ })
+ );
+}
+
+SecurityPanel.displayName = "SecurityPanel";
+
+SecurityPanel.propTypes = {
+ securityInfo: PropTypes.object.isRequired,
+ url: PropTypes.string.isRequired,
+};
+
+function renderValue(weaknessReasons = [], props) {
+ const { member, value } = props;
+
+ // Hide object summary
+ if (typeof member.value === "object") {
+ return null;
+ }
+
+ return div({ className: "security-info-value" },
+ member.name === L10N.getStr("netmonitor.security.error") ?
+ // Display multiline text for security error
+ value
+ :
+ // Display one line selectable text for security details
+ input({
+ className: "textbox-input",
+ readonly: "true",
+ value,
+ })
+ ,
+ weaknessReasons.indexOf("cipher") !== -1 &&
+ member.name === L10N.getStr("netmonitor.security.cipherSuite") ?
+ // Display an extra warning icon after the cipher suite
+ div({
+ id: "security-warning-cipher",
+ className: "security-warning-icon",
+ title: L10N.getStr("netmonitor.security.warning.cipher"),
+ })
+ :
+ null
+ );
+}
+
+function getExpandedNodes(object, path = "", level = 0) {
+ if (typeof object !== "object") {
+ return null;
+ }
+
+ let expandedNodes = new Set();
+ for (let prop in object) {
+ let nodePath = path + "/" + prop;
+ expandedNodes.add(nodePath);
+
+ let nodes = getExpandedNodes(object[prop], nodePath, level + 1);
+ if (nodes) {
+ expandedNodes = new Set([...expandedNodes, ...nodes]);
+ }
+ }
+ return expandedNodes;
+}
+
+module.exports = connect(
+ (state) => {
+ const selectedRequest = getSelectedRequest(state);
+
+ if (selectedRequest) {
+ const { securityInfo, url} = selectedRequest;
+ return {
+ securityInfo,
+ url,
+ };
+ }
+
+ return {};
+ }
+)(SecurityPanel);
--- a/devtools/client/netmonitor/test/browser_net_security-details.js
+++ b/devtools/client/netmonitor/test/browser_net_security-details.js
@@ -4,17 +4,17 @@
"use strict";
/**
* Test that Security details tab contains the expected data.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
- let { $, EVENTS, NetMonitorView } = monitor.panelWin;
+ let { $, $all, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
info("Performing a secure request.");
const REQUESTS_URL = "https://example.com" + CORS_SJS_PATH;
let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, REQUESTS_URL, function* (url) {
content.wrappedJSObject.performRequests(1, url);
@@ -31,72 +31,55 @@ add_task(function* () {
NetworkDetails.widget.selectedIndex = 5;
info("Waiting for security tab to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
let errorbox = $("#security-error");
let infobox = $("#security-information");
- is(errorbox.hidden, true, "Error box is hidden.");
- is(infobox.hidden, false, "Information box visible.");
+ is(errorbox, null, "Error box is hidden.");
+ ok(infobox, "Information box visible.");
+ let textboxes = $all(".textbox-input");
// Connection
// The protocol will be TLS but the exact version depends on which protocol
// the test server example.com supports.
- let protocol = $("#security-protocol-version-value").value;
+ 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 = $("#security-ciphersuite-value").value;
+ let suite = textboxes[1].value;
ok(suite.startsWith("TLS_"), "The suite " + suite + " seems valid.");
// Host
- checkLabel("#security-info-host-header", "Host example.com:");
- checkLabel("#security-http-strict-transport-security-value", "Disabled");
- checkLabel("#security-public-key-pinning-value", "Disabled");
+ let hostLabel = $all(".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
- checkLabel("#security-cert-subject-cn", "example.com");
- checkLabel("#security-cert-subject-o", "<Not Available>");
- checkLabel("#security-cert-subject-ou", "<Not Available>");
+ 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.");
- checkLabel("#security-cert-issuer-cn", "Temporary Certificate Authority");
- checkLabel("#security-cert-issuer-o", "Mozilla Testing");
- checkLabel("#security-cert-issuer-ou", "<Not Available>");
+ is(textboxes[7].value, "Temporary Certificate Authority", "Label has the expected value.");
+ is(textboxes[8].value, "Mozilla Testing", "Label has the expected value.");
+ is(textboxes[9].value, "Profile Guided Optimization", "Label has the expected value.");
// Locale sensitive and varies between timezones. Cant't compare equality or
// the test fails depending on which part of the world the test is executed.
- checkLabelNotEmpty("#security-cert-validity-begins");
- checkLabelNotEmpty("#security-cert-validity-expires");
- checkLabelNotEmpty("#security-cert-sha1-fingerprint");
- checkLabelNotEmpty("#security-cert-sha256-fingerprint");
- yield teardown(monitor);
-
- /**
- * A helper that compares value attribute of a label with given selector to the
- * expected value.
- */
- function checkLabel(selector, expected) {
- info("Checking label " + selector);
-
- let element = $(selector);
+ // cert validity begins
+ isnot(textboxes[10].value, "", "Label was not empty.");
+ // cert validity expires
+ isnot(textboxes[11].value, "", "Label was not empty.");
- ok(element, "Selector matched an element.");
- is(element.value, expected, "Label has the expected value.");
- }
+ // cert sha1 fingerprint
+ isnot(textboxes[12].value, "", "Label was not empty.");
+ // cert sha256 fingerprint
+ isnot(textboxes[13].value, "", "Label was not empty.");
- /**
- * A helper that checks the label with given selector is not an empty string.
- */
- function checkLabelNotEmpty(selector) {
- info("Checking that label " + selector + " is non-empty.");
-
- let element = $(selector);
-
- ok(element, "Selector matched an element.");
- isnot(element.value, "", "Label was not empty.");
- }
+ yield teardown(monitor);
});
--- a/devtools/client/netmonitor/test/browser_net_security-error.js
+++ b/devtools/client/netmonitor/test/browser_net_security-error.js
@@ -28,24 +28,19 @@ add_task(function* () {
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
info("Selecting security tab.");
NetworkDetails.widget.selectedIndex = 5;
info("Waiting for security tab to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
- let errorbox = $("#security-error");
- let errormsg = $("#security-error-message");
- let infobox = $("#security-information");
+ let errormsg = $(".security-info-value");
- is(errorbox.hidden, false, "Error box is visble.");
- is(infobox.hidden, true, "Information box is hidden.");
-
- isnot(errormsg.value, "", "Error message is not empty.");
+ isnot(errormsg.textContent, "", "Error message is not empty.");
return teardown(monitor);
/**
* Returns a promise that's resolved once a request with security issues is
* completed.
*/
function waitForSecurityBrokenNetworkEvent() {
--- a/devtools/client/netmonitor/test/browser_net_security-warnings.js
+++ b/devtools/client/netmonitor/test/browser_net_security-warnings.js
@@ -6,17 +6,17 @@
/**
* Test that warning indicators are shown when appropriate.
*/
const TEST_CASES = [
{
desc: "no warnings",
uri: "https://example.com" + CORS_SJS_PATH,
- warnCipher: false,
+ warnCipher: null,
},
];
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@@ -42,15 +42,15 @@ add_task(function* () {
if (NetworkDetails.widget.selectedIndex !== 5) {
info("Selecting security tab.");
NetworkDetails.widget.selectedIndex = 5;
info("Waiting for details pane to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
}
- is(cipher.hidden, !test.warnCipher, "Cipher suite warning is hidden.");
+ is(cipher, test.warnCipher, "Cipher suite warning is hidden.");
RequestsMenu.clear();
}
return teardown(monitor);
});
--- a/devtools/client/shared/components/tree/tree-view.js
+++ b/devtools/client/shared/components/tree/tree-view.js
@@ -92,30 +92,33 @@ define(function (require, exports, modul
// Set of expanded nodes
expandedNodes: PropTypes.object,
// Custom filtering callback
onFilter: PropTypes.func,
// Custom sorting callback
onSort: PropTypes.func,
// A header is displayed if set to true
header: PropTypes.bool,
+ // Long string is expandable by a toggle button
+ expandableStrings: PropTypes.bool,
// Array of columns
columns: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
title: PropTypes.string,
width: PropTypes.string
}))
},
getDefaultProps: function () {
return {
object: null,
renderRow: null,
provider: ObjectProvider,
expandedNodes: new Set(),
+ expandableStrings: true,
columns: []
};
},
getInitialState: function () {
return {
expandedNodes: this.props.expandedNodes,
columns: ensureDefaultColumn(this.props.columns)
@@ -174,17 +177,17 @@ define(function (require, exports, modul
getMembers: function (parent, level, path) {
// Strings don't have children. Note that 'long' strings are using
// the expander icon (+/-) to display the entire original value,
// but there are no child items.
if (typeof parent == "string") {
return [];
}
- let provider = this.props.provider;
+ let { expandableStrings, provider } = this.props;
let children = provider.getChildren(parent) || [];
// If the return value is non-array, the children
// are being loaded asynchronously.
if (!Array.isArray(children)) {
return children;
}
@@ -196,17 +199,17 @@ define(function (require, exports, modul
let type = provider.getType(child);
let hasChildren = provider.hasChildren(child);
// Value with no column specified is used for optimization.
// The row is re-rendered only if this value changes.
// Value for actual column is get when a cell is rendered.
let value = provider.getValue(child);
- if (isLongString(value)) {
+ if (expandableStrings && isLongString(value)) {
hasChildren = true;
}
// Return value is a 'member' object containing meta-data about
// tree node. It describes node label, value, type, etc.
return {
// An object associated with this node.
object: child,
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1,13 +1,14 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
+@import "resource://devtools/client/shared/components/tree/tree-view.css";
+
#toolbar-labels {
overflow: hidden;
display: flex;
flex: auto;
}
.devtools-toolbar-container {
display: flex;
@@ -781,16 +782,17 @@
}
.theme-dark #security-error-message {
color: var(--theme-selection-color);
}
#security-tabpanel {
overflow: auto;
+ -moz-user-select: text;
}
.security-warning-icon {
background-image: url(images/alerticon-warning.png);
background-size: 13px 12px;
margin-inline-start: 5px;
vertical-align: top;
width: 13px;
@@ -1079,17 +1081,66 @@
/* Responsive sidebar */
@media (max-width: 700px) {
:root[platform="linux"] .requests-menu-header-button {
font-size: 85%;
}
}
+/* Overwrite tree-view cell colon and use l10n string instead */
+.treeTable .treeLabelCell::after {
+ content: "";
+}
+
+/* Layout additional warning icon in tree value cell */
+.security-info-value {
+ display: flex;
+}
+
+.security-info-value .textbox-input {
+ text-overflow: ellipsis;
+ border: none;
+ background: none;
+ color: inherit;
+ width: 100%;
+ margin-inline-end: 2px;
+}
+
+.security-info-value .textbox-input:focus {
+ outline: 0;
+ box-shadow: var(--theme-focus-box-shadow-textbox);
+}
+
+.treeTable .treeLabel {
+ font-weight: 600;
+}
+
+/* Customize default tree table style to align with devtools theme */
+.theme-light .treeTable .treeLabel,
+.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+ color: var(--theme-highlight-red);
+}
+
+.theme-dark .treeTable .treeLabel,
+.theme-dark .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+ color: var(--theme-highlight-purple);
+}
+
+.theme-firebug .treeTable .treeLabel {
+ color: var(--theme-body-color);
+}
+
+.treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+ cursor: default;
+ text-decoration: none;
+}
+
/*
* FIXME: normal html block element cannot fill outer XUL element
* This workaround should be removed after sidebar is migrated to react
*/
+#react-security-tabpanel-hook,
#react-timings-tabpanel-hook {
display: -moz-box;
-moz-box-orient: vertical;
-moz-box-flex: 1;
}