Mypal/devtools/client/webconsole/new-console-output/utils/messages.js

287 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- 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,
notes: pageError.notes,
});
}
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,
notes,
} = 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,
notes,
});
}
}
}
// 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;