summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/components/request-details/TimingsPanel.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/netmonitor/src/components/request-details/TimingsPanel.js')
-rw-r--r--devtools/client/netmonitor/src/components/request-details/TimingsPanel.js229
1 files changed, 229 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/request-details/TimingsPanel.js b/devtools/client/netmonitor/src/components/request-details/TimingsPanel.js
new file mode 100644
index 0000000000..30053f09ea
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/request-details/TimingsPanel.js
@@ -0,0 +1,229 @@
+/* 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 {
+ connect,
+} = require("resource://devtools/client/shared/redux/visibility-handler-connect.js");
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const {
+ L10N,
+} = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
+const {
+ getNetMonitorTimingsURL,
+} = require("resource://devtools/client/netmonitor/src/utils/doc-utils.js");
+const {
+ fetchNetworkUpdatePacket,
+} = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
+const {
+ getFormattedTime,
+} = require("resource://devtools/client/netmonitor/src/utils/format-utils.js");
+const {
+ TIMING_KEYS,
+} = require("resource://devtools/client/netmonitor/src/constants.js");
+
+// Components
+const MDNLink = require("resource://devtools/client/shared/components/MdnLink.js");
+
+const { div, span } = dom;
+
+const TIMINGS_END_PADDING = "80px";
+
+/**
+ * Timings panel component
+ * Display timeline bars that shows the total wait time for various stages
+ */
+class TimingsPanel extends Component {
+ static get propTypes() {
+ return {
+ connector: PropTypes.object.isRequired,
+ request: PropTypes.object.isRequired,
+ firstRequestStartedMs: PropTypes.number.isRequired,
+ };
+ }
+
+ componentDidMount() {
+ const { connector, request } = this.props;
+ fetchNetworkUpdatePacket(connector.requestData, request, ["eventTimings"]);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ const { connector, request } = nextProps;
+ fetchNetworkUpdatePacket(connector.requestData, request, ["eventTimings"]);
+ }
+
+ renderServerTimings() {
+ const { serverTimings, totalTime } = this.props.request.eventTimings;
+
+ if (!serverTimings?.length) {
+ return null;
+ }
+
+ return div(
+ {},
+ div(
+ { className: "label-separator" },
+ L10N.getStr("netmonitor.timings.serverTiming")
+ ),
+ ...serverTimings.map(({ name, duration, description }, index) => {
+ const color = name === "total" ? "total" : (index % 3) + 1;
+
+ return div(
+ {
+ key: index,
+ className: "tabpanel-summary-container timings-container server",
+ },
+ span(
+ { className: "tabpanel-summary-label timings-label" },
+ description || name
+ ),
+ div(
+ { className: "requests-list-timings-container" },
+ span({
+ className: "requests-list-timings-offset",
+ style: {
+ width: `calc(${
+ (totalTime - duration) / totalTime
+ } * (100% - ${TIMINGS_END_PADDING})`,
+ },
+ }),
+ span({
+ className: `requests-list-timings-box server-timings-color-${color}`,
+ style: {
+ width: `calc(${
+ duration / totalTime
+ } * (100% - ${TIMINGS_END_PADDING}))`,
+ },
+ }),
+ span(
+ { className: "requests-list-timings-total" },
+ getFormattedTime(duration)
+ )
+ )
+ );
+ })
+ );
+ }
+
+ render() {
+ const { eventTimings, totalTime, startedMs } = this.props.request;
+ const { firstRequestStartedMs } = this.props;
+
+ if (!eventTimings) {
+ return div(
+ {
+ className:
+ "tabpanel-summary-container timings-container empty-notice",
+ },
+ L10N.getStr("netmonitor.timings.noTimings")
+ );
+ }
+
+ const { timings, offsets } = eventTimings;
+ let queuedAt, startedAt, downloadedAt;
+ const isFirstRequestStartedAvailable = firstRequestStartedMs !== null;
+
+ if (isFirstRequestStartedAvailable) {
+ queuedAt = startedMs - firstRequestStartedMs;
+ startedAt = queuedAt + timings.blocked;
+ downloadedAt = queuedAt + totalTime;
+ }
+
+ const timelines = TIMING_KEYS.map((type, idx) => {
+ // Determine the relative offset for each timings box. For example, the
+ // offset of third timings box will be 0 + blocked offset + dns offset
+ // If offsets sent from the backend aren't available calculate it
+ // from the timing info.
+ const offset = offsets
+ ? offsets[type]
+ : TIMING_KEYS.slice(0, idx).reduce(
+ (acc, cur) => acc + timings[cur] || 0,
+ 0
+ );
+
+ const offsetScale = offset / totalTime || 0;
+ const timelineScale = timings[type] / totalTime || 0;
+
+ return div(
+ {
+ key: type,
+ id: `timings-summary-${type}`,
+ className: "tabpanel-summary-container timings-container request",
+ },
+ span(
+ { className: "tabpanel-summary-label timings-label" },
+ L10N.getStr(`netmonitor.timings.${type}`)
+ ),
+ div(
+ { className: "requests-list-timings-container" },
+ span({
+ className: "requests-list-timings-offset",
+ style: {
+ width: `calc(${offsetScale} * (100% - ${TIMINGS_END_PADDING})`,
+ },
+ }),
+ span({
+ className: `requests-list-timings-box ${type}`,
+ style: {
+ width: `calc(${timelineScale} * (100% - ${TIMINGS_END_PADDING}))`,
+ },
+ }),
+ span(
+ { className: "requests-list-timings-total" },
+ getFormattedTime(timings[type])
+ )
+ )
+ );
+ });
+
+ return div(
+ { className: "panel-container" },
+ isFirstRequestStartedAvailable &&
+ div(
+ { className: "timings-overview" },
+ span(
+ { className: "timings-overview-item" },
+ L10N.getFormatStr(
+ "netmonitor.timings.queuedAt",
+ getFormattedTime(queuedAt)
+ )
+ ),
+ span(
+ { className: "timings-overview-item" },
+ L10N.getFormatStr(
+ "netmonitor.timings.startedAt",
+ getFormattedTime(startedAt)
+ )
+ ),
+ span(
+ { className: "timings-overview-item" },
+ L10N.getFormatStr(
+ "netmonitor.timings.downloadedAt",
+ getFormattedTime(downloadedAt)
+ )
+ )
+ ),
+ div(
+ { className: "label-separator" },
+ L10N.getStr("netmonitor.timings.requestTiming")
+ ),
+ timelines,
+ this.renderServerTimings(),
+ MDNLink({
+ url: getNetMonitorTimingsURL(),
+ title: L10N.getStr("netmonitor.timings.learnMore"),
+ })
+ );
+ }
+}
+
+module.exports = connect(state => ({
+ firstRequestStartedMs: state.requests ? state.requests.firstStartedMs : null,
+}))(TimingsPanel);