summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/new-console-output/utils/messages.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webconsole/new-console-output/utils/messages.js')
-rw-r--r--devtools/client/webconsole/new-console-output/utils/messages.js283
1 files changed, 283 insertions, 0 deletions
diff --git a/devtools/client/webconsole/new-console-output/utils/messages.js b/devtools/client/webconsole/new-console-output/utils/messages.js
new file mode 100644
index 000000000..f91209e9d
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -0,0 +1,283 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=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";
+
+const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
+const STRINGS_URI = "devtools/client/locales/webconsole.properties";
+const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
+
+const {
+ MESSAGE_SOURCE,
+ MESSAGE_TYPE,
+ MESSAGE_LEVEL,
+} = require("../constants");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("../types");
+
+function prepareMessage(packet, idGenerator) {
+ // This packet is already in the expected packet structure. Simply return.
+ if (!packet.source) {
+ packet = transformPacket(packet);
+ }
+
+ if (packet.allowRepeating) {
+ packet = packet.set("repeatId", getRepeatId(packet));
+ }
+ return packet.set("id", idGenerator.getNextId());
+}
+
+/**
+ * Transforms a packet from Firefox RDP structure to Chrome RDP structure.
+ */
+function transformPacket(packet) {
+ if (packet._type) {
+ packet = convertCachedPacket(packet);
+ }
+
+ switch (packet.type) {
+ case "consoleAPICall": {
+ let { message } = packet;
+
+ let parameters = message.arguments;
+ let type = message.level;
+ let level = getLevelFromType(type);
+ let messageText = null;
+ const timer = message.timer;
+
+ // Special per-type conversion.
+ switch (type) {
+ case "clear":
+ // We show a message to users when calls console.clear() is called.
+ parameters = [l10n.getStr("consoleCleared")];
+ break;
+ case "count":
+ // Chrome RDP doesn't have a special type for count.
+ type = MESSAGE_TYPE.LOG;
+ let {counter} = message;
+ let label = counter.label ? counter.label : l10n.getStr("noCounterLabel");
+ messageText = `${label}: ${counter.count}`;
+ parameters = null;
+ break;
+ case "time":
+ // We don't show anything for console.time calls to match Chrome's behaviour.
+ parameters = null;
+ type = MESSAGE_TYPE.NULL_MESSAGE;
+ break;
+ case "timeEnd":
+ parameters = null;
+ if (timer) {
+ // We show the duration to users when calls console.timeEnd() is called,
+ // if corresponding console.time() was called before.
+ let duration = Math.round(timer.duration * 100) / 100;
+ messageText = l10n.getFormatStr("timeEnd", [timer.name, duration]);
+ } else {
+ // If the `timer` property does not exists, we don't output anything.
+ type = MESSAGE_TYPE.NULL_MESSAGE;
+ }
+ break;
+ case "table":
+ const supportedClasses = [
+ "Array", "Object", "Map", "Set", "WeakMap", "WeakSet"];
+ if (
+ !Array.isArray(parameters) ||
+ parameters.length === 0 ||
+ !supportedClasses.includes(parameters[0].class)
+ ) {
+ // If the class of the first parameter is not supported,
+ // we handle the call as a simple console.log
+ type = "log";
+ }
+ break;
+ case "group":
+ type = MESSAGE_TYPE.START_GROUP;
+ parameters = null;
+ messageText = message.groupName || l10n.getStr("noGroupLabel");
+ break;
+ case "groupCollapsed":
+ type = MESSAGE_TYPE.START_GROUP_COLLAPSED;
+ parameters = null;
+ messageText = message.groupName || l10n.getStr("noGroupLabel");
+ break;
+ case "groupEnd":
+ type = MESSAGE_TYPE.END_GROUP;
+ parameters = null;
+ break;
+ case "dirxml":
+ // Handle console.dirxml calls as simple console.log
+ type = "log";
+ break;
+ }
+
+ const frame = message.filename ? {
+ source: message.filename,
+ line: message.lineNumber,
+ column: message.columnNumber,
+ } : null;
+
+ return new ConsoleMessage({
+ source: MESSAGE_SOURCE.CONSOLE_API,
+ type,
+ level,
+ parameters,
+ messageText,
+ stacktrace: message.stacktrace ? message.stacktrace : null,
+ frame,
+ userProvidedStyles: message.styles,
+ });
+ }
+
+ case "navigationMessage": {
+ let { message } = packet;
+ return new ConsoleMessage({
+ source: MESSAGE_SOURCE.CONSOLE_API,
+ type: MESSAGE_TYPE.LOG,
+ level: MESSAGE_LEVEL.LOG,
+ messageText: "Navigated to " + message.url,
+ });
+ }
+
+ case "pageError": {
+ let { pageError } = packet;
+ let level = MESSAGE_LEVEL.ERROR;
+ if (pageError.warning || pageError.strict) {
+ level = MESSAGE_LEVEL.WARN;
+ } else if (pageError.info) {
+ level = MESSAGE_LEVEL.INFO;
+ }
+
+ const frame = pageError.sourceName ? {
+ source: pageError.sourceName,
+ line: pageError.lineNumber,
+ column: pageError.columnNumber
+ } : null;
+
+ return new ConsoleMessage({
+ source: MESSAGE_SOURCE.JAVASCRIPT,
+ type: MESSAGE_TYPE.LOG,
+ level,
+ messageText: pageError.errorMessage,
+ stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
+ frame,
+ exceptionDocURL: pageError.exceptionDocURL,
+ });
+ }
+
+ case "networkEvent": {
+ let { networkEvent } = packet;
+
+ return new NetworkEventMessage({
+ actor: networkEvent.actor,
+ isXHR: networkEvent.isXHR,
+ request: networkEvent.request,
+ response: networkEvent.response,
+ });
+ }
+
+ case "evaluationResult":
+ default: {
+ let {
+ exceptionMessage: messageText,
+ exceptionDocURL,
+ frame,
+ result: parameters
+ } = packet;
+
+ const level = messageText ? MESSAGE_LEVEL.ERROR : MESSAGE_LEVEL.LOG;
+ return new ConsoleMessage({
+ source: MESSAGE_SOURCE.JAVASCRIPT,
+ type: MESSAGE_TYPE.RESULT,
+ level,
+ messageText,
+ parameters,
+ exceptionDocURL,
+ frame,
+ });
+ }
+ }
+}
+
+// Helpers
+function getRepeatId(message) {
+ message = message.toJS();
+ delete message.repeat;
+ return JSON.stringify(message);
+}
+
+function convertCachedPacket(packet) {
+ // The devtools server provides cached message packets in a different shape, so we
+ // transform them here.
+ let convertPacket = {};
+ if (packet._type === "ConsoleAPI") {
+ convertPacket.message = packet;
+ convertPacket.type = "consoleAPICall";
+ } else if (packet._type === "PageError") {
+ convertPacket.pageError = packet;
+ convertPacket.type = "pageError";
+ } else if ("_navPayload" in packet) {
+ convertPacket.type = "navigationMessage";
+ convertPacket.message = packet;
+ } else if (packet._type === "NetworkEvent") {
+ convertPacket.networkEvent = packet;
+ convertPacket.type = "networkEvent";
+ } else {
+ throw new Error("Unexpected packet type");
+ }
+ return convertPacket;
+}
+
+/**
+ * Maps a Firefox RDP type to its corresponding level.
+ */
+function getLevelFromType(type) {
+ const levels = {
+ LEVEL_ERROR: "error",
+ LEVEL_WARNING: "warn",
+ LEVEL_INFO: "info",
+ LEVEL_LOG: "log",
+ LEVEL_DEBUG: "debug",
+ };
+
+ // A mapping from the console API log event levels to the Web Console levels.
+ const levelMap = {
+ error: levels.LEVEL_ERROR,
+ exception: levels.LEVEL_ERROR,
+ assert: levels.LEVEL_ERROR,
+ warn: levels.LEVEL_WARNING,
+ info: levels.LEVEL_INFO,
+ log: levels.LEVEL_LOG,
+ clear: levels.LEVEL_LOG,
+ trace: levels.LEVEL_LOG,
+ table: levels.LEVEL_LOG,
+ debug: levels.LEVEL_LOG,
+ dir: levels.LEVEL_LOG,
+ dirxml: levels.LEVEL_LOG,
+ group: levels.LEVEL_LOG,
+ groupCollapsed: levels.LEVEL_LOG,
+ groupEnd: levels.LEVEL_LOG,
+ time: levels.LEVEL_LOG,
+ timeEnd: levels.LEVEL_LOG,
+ count: levels.LEVEL_DEBUG,
+ };
+
+ return levelMap[type] || MESSAGE_TYPE.LOG;
+}
+
+function isGroupType(type) {
+ return [
+ MESSAGE_TYPE.START_GROUP,
+ MESSAGE_TYPE.START_GROUP_COLLAPSED
+ ].includes(type);
+}
+
+exports.prepareMessage = prepareMessage;
+// Export for use in testing.
+exports.getRepeatId = getRepeatId;
+
+exports.l10n = l10n;
+exports.isGroupType = isGroupType;