diff options
Diffstat (limited to 'devtools/client/webconsole/new-console-output/utils/messages.js')
-rw-r--r-- | devtools/client/webconsole/new-console-output/utils/messages.js | 283 |
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; |