summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/components/Output/message-types
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /devtools/client/webconsole/components/Output/message-types
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/webconsole/components/Output/message-types')
-rw-r--r--devtools/client/webconsole/components/Output/message-types/CSSWarning.js173
-rw-r--r--devtools/client/webconsole/components/Output/message-types/ConsoleApiCall.js221
-rw-r--r--devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js105
-rw-r--r--devtools/client/webconsole/components/Output/message-types/DefaultRenderer.js15
-rw-r--r--devtools/client/webconsole/components/Output/message-types/EvaluationResult.js124
-rw-r--r--devtools/client/webconsole/components/Output/message-types/NavigationMarker.js62
-rw-r--r--devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js243
-rw-r--r--devtools/client/webconsole/components/Output/message-types/PageError.js130
-rw-r--r--devtools/client/webconsole/components/Output/message-types/SimpleTable.js134
-rw-r--r--devtools/client/webconsole/components/Output/message-types/WarningGroup.js80
-rw-r--r--devtools/client/webconsole/components/Output/message-types/moz.build17
11 files changed, 1304 insertions, 0 deletions
diff --git a/devtools/client/webconsole/components/Output/message-types/CSSWarning.js b/devtools/client/webconsole/components/Output/message-types/CSSWarning.js
new file mode 100644
index 0000000000..cef91c22be
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/CSSWarning.js
@@ -0,0 +1,173 @@
+/* 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 {
+ Component,
+ createFactory,
+} = 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/webconsole/utils/messages.js");
+const actions = require("resource://devtools/client/webconsole/actions/index.js");
+
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+
+loader.lazyRequireGetter(
+ this,
+ "GripMessageBody",
+ "resource://devtools/client/webconsole/components/Output/GripMessageBody.js"
+);
+
+/**
+ * This component is responsible for rendering CSS warnings in the Console panel.
+ *
+ * CSS warnings are expandable when they have associated CSS selectors so the
+ * user can inspect any matching DOM elements. Not all CSS warnings have
+ * associated selectors (those that don't are not expandable) and not all
+ * selectors match elements in the current page (warnings can appear for styles
+ * which don't apply to the current page).
+ *
+ * @extends Component
+ */
+class CSSWarning extends Component {
+ static get propTypes() {
+ return {
+ dispatch: PropTypes.func.isRequired,
+ inWarningGroup: PropTypes.bool.isRequired,
+ message: PropTypes.object.isRequired,
+ open: PropTypes.bool,
+ cssMatchingElements: PropTypes.object,
+ repeat: PropTypes.any,
+ serviceContainer: PropTypes.object,
+ timestampsVisible: PropTypes.bool.isRequired,
+ setExpanded: PropTypes.func,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ open: false,
+ };
+ }
+
+ static get displayName() {
+ return "CSSWarning";
+ }
+
+ constructor(props) {
+ super(props);
+ this.onToggle = this.onToggle.bind(this);
+ }
+
+ onToggle(messageId) {
+ const { dispatch, message, cssMatchingElements, open } = this.props;
+
+ if (open) {
+ dispatch(actions.messageClose(messageId));
+ } else if (cssMatchingElements) {
+ // If the message already has information about the elements matching
+ // the selectors associated with this CSS warning, just open the message.
+ dispatch(actions.messageOpen(messageId));
+ } else {
+ // Query the server for elements matching the CSS selectors associated
+ // with this CSS warning and populate the message's additional cssMatchingElements with
+ // the result. It's an async operation and potentially expensive, so we only do it
+ // on demand, once, when the component is first expanded.
+ dispatch(actions.messageGetMatchingElements(message));
+ dispatch(actions.messageOpen(messageId));
+ }
+ }
+
+ render() {
+ const {
+ dispatch,
+ message,
+ open,
+ cssMatchingElements,
+ repeat,
+ serviceContainer,
+ timestampsVisible,
+ inWarningGroup,
+ setExpanded,
+ } = this.props;
+
+ const {
+ id: messageId,
+ indent,
+ cssSelectors,
+ source,
+ type,
+ level,
+ messageText,
+ frame,
+ exceptionDocURL,
+ timeStamp,
+ notes,
+ } = message;
+
+ let messageBody;
+ if (typeof messageText === "string") {
+ messageBody = messageText;
+ } else if (
+ typeof messageText === "object" &&
+ messageText.type === "longString"
+ ) {
+ messageBody = `${message.messageText.initial}…`;
+ }
+
+ // Create a message attachment only when the message is open and there is a result
+ // to the query for elements matching the CSS selectors associated with the message.
+ const attachment =
+ open &&
+ cssMatchingElements !== undefined &&
+ dom.div(
+ { className: "devtools-monospace" },
+ dom.div(
+ { className: "elements-label" },
+ l10n.getFormatStr("webconsole.cssWarningElements.label", [
+ cssSelectors,
+ ])
+ ),
+ GripMessageBody({
+ dispatch,
+ escapeWhitespace: false,
+ grip: cssMatchingElements,
+ serviceContainer,
+ setExpanded,
+ })
+ );
+
+ return Message({
+ attachment,
+ collapsible: !!cssSelectors.length,
+ dispatch,
+ exceptionDocURL,
+ frame,
+ indent,
+ inWarningGroup,
+ level,
+ messageBody,
+ messageId,
+ notes,
+ open,
+ onToggle: this.onToggle,
+ repeat,
+ serviceContainer,
+ source,
+ timeStamp,
+ timestampsVisible,
+ topLevelClasses: [],
+ type,
+ message,
+ });
+ }
+}
+
+module.exports = createFactory(CSSWarning);
diff --git a/devtools/client/webconsole/components/Output/message-types/ConsoleApiCall.js b/devtools/client/webconsole/components/Output/message-types/ConsoleApiCall.js
new file mode 100644
index 0000000000..155075731f
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/ConsoleApiCall.js
@@ -0,0 +1,221 @@
+/* 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";
+
+// React & Redux
+const {
+ createFactory,
+} = 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 GripMessageBody = require("resource://devtools/client/webconsole/components/Output/GripMessageBody.js");
+const ConsoleTable = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/ConsoleTable.js")
+);
+const {
+ isGroupType,
+ l10n,
+} = require("resource://devtools/client/webconsole/utils/messages.js");
+
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+
+ConsoleApiCall.displayName = "ConsoleApiCall";
+
+ConsoleApiCall.propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ message: PropTypes.object.isRequired,
+ open: PropTypes.bool,
+ serviceContainer: PropTypes.object.isRequired,
+ timestampsVisible: PropTypes.bool.isRequired,
+ maybeScrollToBottom: PropTypes.func,
+};
+
+ConsoleApiCall.defaultProps = {
+ open: false,
+};
+
+function ConsoleApiCall(props) {
+ const {
+ dispatch,
+ message,
+ open,
+ serviceContainer,
+ timestampsVisible,
+ repeat,
+ maybeScrollToBottom,
+ setExpanded,
+ } = props;
+ const {
+ id: messageId,
+ indent,
+ source,
+ type,
+ level,
+ stacktrace,
+ frame,
+ timeStamp,
+ parameters,
+ messageText,
+ prefix,
+ userProvidedStyles,
+ } = message;
+
+ let messageBody;
+ const messageBodyConfig = {
+ dispatch,
+ messageId,
+ parameters,
+ userProvidedStyles,
+ serviceContainer,
+ type,
+ maybeScrollToBottom,
+ setExpanded,
+ // When the object is a parameter of a console.dir call, we always want to show its
+ // properties, like regular object (i.e. not showing the DOM tree for an Element, or
+ // only showing the message + stacktrace for Error object).
+ customFormat: type !== "dir",
+ };
+
+ if (type === "trace") {
+ const traceParametersBody =
+ Array.isArray(parameters) && parameters.length
+ ? [" "].concat(formatReps(messageBodyConfig))
+ : [];
+
+ messageBody = [
+ dom.span({ className: "cm-variable" }, "console.trace()"),
+ ...traceParametersBody,
+ ];
+ } else if (type === "assert") {
+ const reps = formatReps(messageBodyConfig);
+ messageBody = dom.span({}, "Assertion failed: ", reps);
+ } else if (type === "table") {
+ // TODO: Chrome does not output anything, see if we want to keep this
+ messageBody = dom.span({ className: "cm-variable" }, "console.table()");
+ } else if (parameters) {
+ messageBody = formatReps(messageBodyConfig);
+ if (prefix) {
+ messageBody.unshift(
+ dom.span(
+ {
+ className: "console-message-prefix",
+ },
+ `${prefix}: `
+ )
+ );
+ }
+ } else if (typeof messageText === "string") {
+ messageBody = messageText;
+ } else if (messageText) {
+ messageBody = GripMessageBody({
+ dispatch,
+ messageId,
+ grip: messageText,
+ serviceContainer,
+ useQuotes: false,
+ transformEmptyString: true,
+ setExpanded,
+ type,
+ });
+ }
+
+ let attachment = null;
+ if (type === "table") {
+ attachment = ConsoleTable({
+ dispatch,
+ id: message.id,
+ serviceContainer,
+ parameters: message.parameters,
+ });
+ }
+
+ let collapseTitle = null;
+ if (isGroupType(type)) {
+ collapseTitle = l10n.getStr("groupToggle");
+ }
+
+ const collapsible =
+ isGroupType(type) || (type === "error" && Array.isArray(stacktrace));
+ const topLevelClasses = ["cm-s-mozilla"];
+
+ return Message({
+ messageId,
+ open,
+ collapsible,
+ collapseTitle,
+ source,
+ type,
+ level,
+ topLevelClasses,
+ messageBody,
+ repeat,
+ frame,
+ stacktrace,
+ attachment,
+ serviceContainer,
+ dispatch,
+ indent,
+ timeStamp,
+ timestampsVisible,
+ parameters,
+ message,
+ maybeScrollToBottom,
+ });
+}
+
+function formatReps(options = {}) {
+ const {
+ dispatch,
+ loadedObjectProperties,
+ loadedObjectEntries,
+ messageId,
+ parameters,
+ serviceContainer,
+ userProvidedStyles,
+ type,
+ maybeScrollToBottom,
+ setExpanded,
+ customFormat,
+ } = options;
+
+ const elements = [];
+ const parametersLength = parameters.length;
+ for (let i = 0; i < parametersLength; i++) {
+ elements.push(
+ GripMessageBody({
+ dispatch,
+ messageId,
+ grip: parameters[i],
+ key: i,
+ userProvidedStyle: userProvidedStyles ? userProvidedStyles[i] : null,
+ serviceContainer,
+ useQuotes: false,
+ loadedObjectProperties,
+ loadedObjectEntries,
+ type,
+ maybeScrollToBottom,
+ setExpanded,
+ customFormat,
+ })
+ );
+
+ // We need to interleave a space if we are not on the last element AND
+ // if we are not between 2 messages with user provided style.
+ if (
+ i !== parametersLength - 1 &&
+ (!userProvidedStyles ||
+ userProvidedStyles[i] === undefined ||
+ userProvidedStyles[i + 1] === undefined)
+ ) {
+ elements.push(" ");
+ }
+ }
+
+ return elements;
+}
+
+module.exports = ConsoleApiCall;
diff --git a/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js b/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js
new file mode 100644
index 0000000000..5cfb87113c
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js
@@ -0,0 +1,105 @@
+/* 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";
+
+// React & Redux
+const {
+ createElement,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const { ELLIPSIS } = require("resource://devtools/shared/l10n.js");
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+
+ConsoleCommand.displayName = "ConsoleCommand";
+
+ConsoleCommand.propTypes = {
+ message: PropTypes.object.isRequired,
+ timestampsVisible: PropTypes.bool.isRequired,
+ serviceContainer: PropTypes.object,
+ maybeScrollToBottom: PropTypes.func,
+ open: PropTypes.bool,
+};
+
+ConsoleCommand.defaultProps = {
+ open: false,
+};
+
+/**
+ * Displays input from the console.
+ */
+function ConsoleCommand(props) {
+ const {
+ message,
+ timestampsVisible,
+ serviceContainer,
+ maybeScrollToBottom,
+ dispatch,
+ open,
+ } = props;
+
+ const { indent, source, type, level, timeStamp, id: messageId } = message;
+
+ const messageText = trimCode(message.messageText);
+ const messageLines = messageText.split("\n");
+
+ const collapsible = messageLines.length > 5;
+
+ // Show only first 5 lines if its collapsible and closed
+ const visibleMessageText =
+ collapsible && !open
+ ? `${messageLines.slice(0, 5).join("\n")}${ELLIPSIS}`
+ : messageText;
+
+ // This uses a Custom Element to syntax highlight when possible. If it's not
+ // (no CodeMirror editor), then it will just render text.
+ const messageBody = createElement(
+ "syntax-highlighted",
+ null,
+ visibleMessageText
+ );
+
+ // Enable collapsing the code if it has multiple lines
+
+ return Message({
+ messageId,
+ source,
+ type,
+ level,
+ topLevelClasses: [],
+ messageBody,
+ collapsible,
+ open,
+ dispatch,
+ serviceContainer,
+ indent,
+ timeStamp,
+ timestampsVisible,
+ maybeScrollToBottom,
+ message,
+ });
+}
+
+module.exports = ConsoleCommand;
+
+/**
+ * Trim user input to avoid blank lines before and after messages
+ */
+function trimCode(input) {
+ if (typeof input !== "string") {
+ return input;
+ }
+
+ // Trim on both edges if we have a single line of content
+ if (input.trim().includes("\n") === false) {
+ return input.trim();
+ }
+
+ // For multiline input we want to keep the indentation of the first line
+ // with non-whitespace, so we can't .trim()/.trimStart().
+ return input.replace(/^\s*\n/, "").trimEnd();
+}
diff --git a/devtools/client/webconsole/components/Output/message-types/DefaultRenderer.js b/devtools/client/webconsole/components/Output/message-types/DefaultRenderer.js
new file mode 100644
index 0000000000..893e6b04c6
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/DefaultRenderer.js
@@ -0,0 +1,15 @@
+/* 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 = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+DefaultRenderer.displayName = "DefaultRenderer";
+
+function DefaultRenderer(props) {
+ return dom.div({}, "This message type is not supported yet.");
+}
+
+module.exports = DefaultRenderer;
diff --git a/devtools/client/webconsole/components/Output/message-types/EvaluationResult.js b/devtools/client/webconsole/components/Output/message-types/EvaluationResult.js
new file mode 100644
index 0000000000..60d44a9f99
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/EvaluationResult.js
@@ -0,0 +1,124 @@
+/* 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";
+
+// React & Redux
+const {
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+const GripMessageBody = require("resource://devtools/client/webconsole/components/Output/GripMessageBody.js");
+
+EvaluationResult.displayName = "EvaluationResult";
+
+EvaluationResult.propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ message: PropTypes.object.isRequired,
+ timestampsVisible: PropTypes.bool.isRequired,
+ serviceContainer: PropTypes.object,
+ maybeScrollToBottom: PropTypes.func,
+ open: PropTypes.bool,
+};
+
+EvaluationResult.defaultProps = {
+ open: false,
+};
+
+function EvaluationResult(props) {
+ const {
+ dispatch,
+ message,
+ serviceContainer,
+ timestampsVisible,
+ maybeScrollToBottom,
+ open,
+ setExpanded,
+ } = props;
+
+ const {
+ source,
+ type,
+ helperType,
+ level,
+ id: messageId,
+ indent,
+ hasException,
+ exceptionDocURL,
+ stacktrace,
+ frame,
+ timeStamp,
+ parameters,
+ notes,
+ } = message;
+
+ let messageBody;
+ if (
+ typeof message.messageText !== "undefined" &&
+ message.messageText !== null
+ ) {
+ const messageText = message.messageText?.getGrip
+ ? message.messageText.getGrip()
+ : message.messageText;
+ if (typeof messageText === "string") {
+ messageBody = messageText;
+ } else if (
+ typeof messageText === "object" &&
+ messageText.type === "longString"
+ ) {
+ messageBody = `${messageText.initial}…`;
+ }
+ } else {
+ messageBody = [];
+ if (hasException) {
+ messageBody.push("Uncaught ");
+ }
+ messageBody.push(
+ GripMessageBody({
+ dispatch,
+ messageId,
+ grip: parameters[0],
+ key: "grip",
+ serviceContainer,
+ useQuotes: !hasException,
+ escapeWhitespace: false,
+ type,
+ helperType,
+ maybeScrollToBottom,
+ setExpanded,
+ customFormat: true,
+ })
+ );
+ }
+
+ const topLevelClasses = ["cm-s-mozilla"];
+
+ return Message({
+ dispatch,
+ source,
+ type,
+ level,
+ indent,
+ topLevelClasses,
+ messageBody,
+ messageId,
+ serviceContainer,
+ exceptionDocURL,
+ stacktrace,
+ collapsible: Array.isArray(stacktrace),
+ open,
+ frame,
+ timeStamp,
+ parameters,
+ notes,
+ timestampsVisible,
+ maybeScrollToBottom,
+ message,
+ });
+}
+
+module.exports = EvaluationResult;
diff --git a/devtools/client/webconsole/components/Output/message-types/NavigationMarker.js b/devtools/client/webconsole/components/Output/message-types/NavigationMarker.js
new file mode 100644
index 0000000000..7d14206a6a
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/NavigationMarker.js
@@ -0,0 +1,62 @@
+/* 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";
+
+// React & Redux
+const {
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+
+NavigationMarker.displayName = "NavigationMarker";
+
+NavigationMarker.propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ message: PropTypes.object.isRequired,
+ serviceContainer: PropTypes.object.isRequired,
+ timestampsVisible: PropTypes.bool.isRequired,
+ maybeScrollToBottom: PropTypes.func,
+};
+
+function NavigationMarker(props) {
+ const {
+ dispatch,
+ message,
+ serviceContainer,
+ timestampsVisible,
+ maybeScrollToBottom,
+ } = props;
+ const {
+ id: messageId,
+ indent,
+ source,
+ type,
+ level,
+ timeStamp,
+ messageText,
+ } = message;
+
+ return Message({
+ messageId,
+ source,
+ type,
+ level,
+ messageBody: messageText,
+ serviceContainer,
+ dispatch,
+ indent,
+ timeStamp,
+ timestampsVisible,
+ topLevelClasses: [],
+ message,
+ maybeScrollToBottom,
+ });
+}
+
+module.exports = NavigationMarker;
diff --git a/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js b/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js
new file mode 100644
index 0000000000..ce0961668b
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js
@@ -0,0 +1,243 @@
+/* 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";
+
+// React & Redux
+const {
+ createFactory,
+ createElement,
+} = 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 Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+const actions = require("resource://devtools/client/webconsole/actions/index.js");
+const {
+ isMessageNetworkError,
+ l10n,
+} = require("resource://devtools/client/webconsole/utils/messages.js");
+
+loader.lazyRequireGetter(
+ this,
+ "TabboxPanel",
+ "resource://devtools/client/netmonitor/src/components/TabboxPanel.js"
+);
+const {
+ getHTTPStatusCodeURL,
+} = require("resource://devtools/client/netmonitor/src/utils/doc-utils.js");
+const {
+ getUnicodeUrl,
+} = require("resource://devtools/client/shared/unicode-url.js");
+loader.lazyRequireGetter(
+ this,
+ "BLOCKED_REASON_MESSAGES",
+ "resource://devtools/client/netmonitor/src/constants.js",
+ true
+);
+
+const LEARN_MORE = l10n.getStr("webConsoleMoreInfoLabel");
+
+const isMacOS = Services.appinfo.OS === "Darwin";
+
+NetworkEventMessage.displayName = "NetworkEventMessage";
+
+NetworkEventMessage.propTypes = {
+ message: PropTypes.object.isRequired,
+ serviceContainer: PropTypes.shape({
+ openNetworkPanel: PropTypes.func.isRequired,
+ resendNetworkRequest: PropTypes.func.isRequired,
+ }),
+ timestampsVisible: PropTypes.bool.isRequired,
+ networkMessageUpdate: PropTypes.object.isRequired,
+};
+
+/**
+ * This component is responsible for rendering network messages
+ * in the Console panel.
+ *
+ * Network logs are expandable and the user can inspect it inline
+ * within the Console panel (no need to switch to the Network panel).
+ *
+ * HTTP details are rendered using `TabboxPanel` component used to
+ * render contents of the side bar in the Network panel.
+ *
+ * All HTTP details data are fetched from the backend on-demand
+ * when the user is expanding network log for the first time.
+ */
+function NetworkEventMessage({
+ message = {},
+ serviceContainer,
+ timestampsVisible,
+ networkMessageUpdate = {},
+ networkMessageActiveTabId,
+ dispatch,
+ open,
+ disabled,
+}) {
+ const {
+ id,
+ indent,
+ source,
+ type,
+ level,
+ url,
+ method,
+ isXHR,
+ timeStamp,
+ blockedReason,
+ httpVersion,
+ status,
+ statusText,
+ totalTime,
+ } = message;
+
+ const topLevelClasses = ["cm-s-mozilla"];
+ if (isMessageNetworkError(message)) {
+ topLevelClasses.push("error");
+ }
+
+ let statusCode, statusInfo;
+
+ if (
+ httpVersion &&
+ status &&
+ statusText !== undefined &&
+ totalTime !== undefined
+ ) {
+ const statusCodeDocURL = getHTTPStatusCodeURL(
+ status.toString(),
+ "webconsole"
+ );
+ statusCode = dom.span(
+ {
+ className: "status-code",
+ "data-code": status,
+ title: LEARN_MORE,
+ onClick: e => {
+ e.stopPropagation();
+ e.preventDefault();
+ serviceContainer.openLink(statusCodeDocURL, e);
+ },
+ },
+ status
+ );
+ statusInfo = dom.span(
+ { className: "status-info" },
+ `[${httpVersion} `,
+ statusCode,
+ ` ${statusText} ${totalTime}ms]`
+ );
+ }
+
+ if (blockedReason) {
+ statusInfo = dom.span(
+ { className: "status-info" },
+ BLOCKED_REASON_MESSAGES[blockedReason]
+ );
+ topLevelClasses.push("network-message-blocked");
+ }
+
+ // Message body components.
+ const requestMethod = dom.span({ className: "method" }, method);
+ const xhr = isXHR
+ ? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
+ : null;
+ const unicodeURL = getUnicodeUrl(url);
+ const requestUrl = dom.a(
+ {
+ className: "url",
+ title: unicodeURL,
+ href: url,
+ onClick: e => {
+ // The href of the <a> is the actual URL, so we need to prevent the navigation
+ // within the console panel.
+ // We only want to handle Ctrl/Cmd + click to open the link in a new tab.
+ e.preventDefault();
+ const shouldOpenLink =
+ (isMacOS && e.metaKey) || (!isMacOS && e.ctrlKey);
+ if (shouldOpenLink) {
+ e.stopPropagation();
+ serviceContainer.openLink(url, e);
+ }
+ },
+ },
+ unicodeURL
+ );
+ const statusBody = statusInfo
+ ? dom.a({ className: "status" }, statusInfo)
+ : null;
+
+ const messageBody = [xhr, requestMethod, requestUrl, statusBody];
+
+ // API consumed by Net monitor UI components. Most of the method
+ // are not needed in context of the Console panel (atm) and thus
+ // let's just provide empty implementation.
+ // Individual methods might be implemented step by step as needed.
+ const connector = {
+ viewSourceInDebugger: (srcUrl, line, column) => {
+ serviceContainer.onViewSourceInDebugger({ url: srcUrl, line, column });
+ },
+ getLongString: grip => {
+ return serviceContainer.getLongString(grip);
+ },
+ triggerActivity: () => {},
+ requestData: (requestId, dataType) => {
+ return serviceContainer.requestData(requestId, dataType);
+ },
+ };
+
+ // Only render the attachment if the network-event is
+ // actually opened (performance optimization) and its not disabled.
+ const attachment =
+ open &&
+ !disabled &&
+ dom.div(
+ {
+ className: "network-info network-monitor",
+ },
+ createElement(TabboxPanel, {
+ connector,
+ activeTabId: networkMessageActiveTabId,
+ request: networkMessageUpdate,
+ sourceMapURLService: serviceContainer.sourceMapURLService,
+ openLink: serviceContainer.openLink,
+ selectTab: tabId => {
+ dispatch(actions.selectNetworkMessageTab(tabId));
+ },
+ openNetworkDetails: enabled => {
+ if (!enabled) {
+ dispatch(actions.messageClose(id));
+ }
+ },
+ hideToggleButton: true,
+ showMessagesView: false,
+ })
+ );
+
+ const request = { url, method };
+ return Message({
+ dispatch,
+ messageId: id,
+ source,
+ type,
+ level,
+ indent,
+ collapsible: true,
+ open,
+ disabled,
+ attachment,
+ topLevelClasses,
+ timeStamp,
+ messageBody,
+ serviceContainer,
+ request,
+ timestampsVisible,
+ isBlockedNetworkMessage: !!blockedReason,
+ message,
+ });
+}
+
+module.exports = NetworkEventMessage;
diff --git a/devtools/client/webconsole/components/Output/message-types/PageError.js b/devtools/client/webconsole/components/Output/message-types/PageError.js
new file mode 100644
index 0000000000..01828e968e
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/PageError.js
@@ -0,0 +1,130 @@
+/* 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";
+
+// React & Redux
+const {
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+const GripMessageBody = require("resource://devtools/client/webconsole/components/Output/GripMessageBody.js");
+loader.lazyGetter(this, "REPS", function () {
+ return require("resource://devtools/client/shared/components/reps/index.js")
+ .REPS;
+});
+loader.lazyGetter(this, "MODE", function () {
+ return require("resource://devtools/client/shared/components/reps/index.js")
+ .MODE;
+});
+
+PageError.displayName = "PageError";
+
+PageError.propTypes = {
+ message: PropTypes.object.isRequired,
+ open: PropTypes.bool,
+ timestampsVisible: PropTypes.bool.isRequired,
+ serviceContainer: PropTypes.object,
+ maybeScrollToBottom: PropTypes.func,
+ setExpanded: PropTypes.func,
+ inWarningGroup: PropTypes.bool.isRequired,
+};
+
+PageError.defaultProps = {
+ open: false,
+};
+
+function PageError(props) {
+ const {
+ dispatch,
+ message,
+ open,
+ repeat,
+ serviceContainer,
+ timestampsVisible,
+ maybeScrollToBottom,
+ setExpanded,
+ inWarningGroup,
+ } = props;
+ const {
+ id: messageId,
+ source,
+ type,
+ level,
+ messageText,
+ stacktrace,
+ frame,
+ exceptionDocURL,
+ timeStamp,
+ notes,
+ parameters,
+ hasException,
+ isPromiseRejection,
+ } = message;
+
+ const messageBody = [];
+
+ const repsProps = {
+ useQuotes: false,
+ escapeWhitespace: false,
+ openLink: serviceContainer.openLink,
+ };
+
+ if (hasException) {
+ const prefix = `Uncaught${isPromiseRejection ? " (in promise)" : ""} `;
+ messageBody.push(
+ prefix,
+ GripMessageBody({
+ key: "body",
+ dispatch,
+ messageId,
+ grip: parameters[0],
+ serviceContainer,
+ type,
+ customFormat: true,
+ maybeScrollToBottom,
+ setExpanded,
+ ...repsProps,
+ })
+ );
+ } else {
+ messageBody.push(
+ REPS.StringRep.rep({
+ key: "bodytext",
+ object: messageText,
+ mode: MODE.LONG,
+ ...repsProps,
+ })
+ );
+ }
+
+ return Message({
+ dispatch,
+ messageId,
+ open,
+ collapsible: Array.isArray(stacktrace),
+ source,
+ type,
+ level,
+ topLevelClasses: [],
+ indent: message.indent,
+ inWarningGroup,
+ messageBody,
+ repeat,
+ frame,
+ stacktrace,
+ serviceContainer,
+ exceptionDocURL,
+ timeStamp,
+ notes,
+ timestampsVisible,
+ maybeScrollToBottom,
+ message,
+ });
+}
+
+module.exports = PageError;
diff --git a/devtools/client/webconsole/components/Output/message-types/SimpleTable.js b/devtools/client/webconsole/components/Output/message-types/SimpleTable.js
new file mode 100644
index 0000000000..4f0414e562
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/SimpleTable.js
@@ -0,0 +1,134 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const {
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const GripMessageBody = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/GripMessageBody.js")
+);
+
+loader.lazyRequireGetter(
+ this,
+ "PropTypes",
+ "resource://devtools/client/shared/vendor/react-prop-types.js"
+);
+
+loader.lazyGetter(this, "MODE", function () {
+ return require("resource://devtools/client/shared/components/reps/index.js")
+ .MODE;
+});
+
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+
+SimpleTable.displayName = "SimpleTable";
+
+SimpleTable.propTypes = {
+ columns: PropTypes.object.isRequired,
+ items: PropTypes.array.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ serviceContainer: PropTypes.object.isRequired,
+};
+
+function SimpleTable(props) {
+ const {
+ dispatch,
+ message,
+ serviceContainer,
+ timestampsVisible,
+ badge,
+ open,
+ } = props;
+
+ const {
+ source,
+ type,
+ level,
+ id: messageId,
+ indent,
+ timeStamp,
+ columns,
+ items,
+ } = message;
+
+ // if we don't have any data, don't show anything.
+ if (!items.length) {
+ return null;
+ }
+ const headerItems = [];
+ columns.forEach((value, key) =>
+ headerItems.push(
+ dom.th(
+ {
+ key,
+ title: value,
+ },
+ value
+ )
+ )
+ );
+
+ const rowItems = items.map((item, index) => {
+ const cells = [];
+
+ columns.forEach((_, key) => {
+ const cellValue = item[key];
+ const cellContent =
+ typeof cellValue === "undefined"
+ ? ""
+ : GripMessageBody({
+ grip: cellValue,
+ mode: MODE.SHORT,
+ useQuotes: false,
+ serviceContainer,
+ dispatch,
+ });
+
+ cells.push(
+ dom.td(
+ {
+ key,
+ },
+ cellContent
+ )
+ );
+ });
+ return dom.tr({ key: index }, cells);
+ });
+
+ const attachment = dom.table(
+ {
+ className: "simple-table",
+ role: "grid",
+ },
+ dom.thead({}, dom.tr({ className: "simple-table-header" }, headerItems)),
+ dom.tbody({}, rowItems)
+ );
+
+ const topLevelClasses = ["cm-s-mozilla"];
+ return Message({
+ attachment,
+ badge,
+ dispatch,
+ indent,
+ level,
+ messageId,
+ open,
+ serviceContainer,
+ source,
+ timeStamp,
+ timestampsVisible,
+ topLevelClasses,
+ type,
+ message,
+ messageBody: [],
+ });
+}
+
+module.exports = SimpleTable;
diff --git a/devtools/client/webconsole/components/Output/message-types/WarningGroup.js b/devtools/client/webconsole/components/Output/message-types/WarningGroup.js
new file mode 100644
index 0000000000..d54976dbcd
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/WarningGroup.js
@@ -0,0 +1,80 @@
+/* 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";
+
+// React & Redux
+const {
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const Message = createFactory(
+ require("resource://devtools/client/webconsole/components/Output/Message.js")
+);
+
+const { PluralForm } = require("resource://devtools/shared/plural-form.js");
+const {
+ l10n,
+} = require("resource://devtools/client/webconsole/utils/messages.js");
+const messageCountTooltip = l10n.getStr(
+ "webconsole.warningGroup.messageCount.tooltip"
+);
+
+WarningGroup.displayName = "WarningGroup";
+
+WarningGroup.propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ message: PropTypes.object.isRequired,
+ timestampsVisible: PropTypes.bool.isRequired,
+ serviceContainer: PropTypes.object,
+ badge: PropTypes.number.isRequired,
+};
+
+function WarningGroup(props) {
+ const {
+ dispatch,
+ message,
+ serviceContainer,
+ timestampsVisible,
+ badge,
+ open,
+ } = props;
+
+ const { source, type, level, id: messageId, indent, timeStamp } = message;
+
+ const messageBody = [
+ message.messageText,
+ " ",
+ dom.span(
+ {
+ className: "warning-group-badge",
+ title: PluralForm.get(badge, messageCountTooltip).replace("#1", badge),
+ },
+ badge
+ ),
+ ];
+ const topLevelClasses = ["cm-s-mozilla"];
+
+ return Message({
+ badge,
+ collapsible: true,
+ dispatch,
+ indent,
+ level,
+ messageBody,
+ messageId,
+ open,
+ serviceContainer,
+ source,
+ timeStamp,
+ timestampsVisible,
+ topLevelClasses,
+ type,
+ message,
+ });
+}
+
+module.exports = WarningGroup;
diff --git a/devtools/client/webconsole/components/Output/message-types/moz.build b/devtools/client/webconsole/components/Output/message-types/moz.build
new file mode 100644
index 0000000000..ac1019bf05
--- /dev/null
+++ b/devtools/client/webconsole/components/Output/message-types/moz.build
@@ -0,0 +1,17 @@
+# vim: set filetype=python:
+# 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(
+ "ConsoleApiCall.js",
+ "ConsoleCommand.js",
+ "CSSWarning.js",
+ "DefaultRenderer.js",
+ "EvaluationResult.js",
+ "NavigationMarker.js",
+ "NetworkEventMessage.js",
+ "PageError.js",
+ "SimpleTable.js",
+ "WarningGroup.js",
+)