Using next-line is less error prone for refactorings than wrapping methods with enable/disable blocks. Differential Revision: https://phabricator.services.mozilla.com/D51782
680 lines
20 KiB
JavaScript
680 lines
20 KiB
JavaScript
/* 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 l10n = require("devtools/client/webconsole/utils/l10n");
|
|
const {
|
|
getUrlDetails,
|
|
} = require("devtools/client/netmonitor/src/utils/request-utils");
|
|
|
|
// URL Regex, common idioms:
|
|
//
|
|
// Lead-in (URL):
|
|
// ( Capture because we need to know if there was a lead-in
|
|
// character so we can include it as part of the text
|
|
// preceding the match. We lack look-behind matching.
|
|
// ^| The URL can start at the beginning of the string.
|
|
// [\s(,;'"`“] Or whitespace or some punctuation that does not imply
|
|
// a context which would preclude a URL.
|
|
// )
|
|
//
|
|
// We do not need a trailing look-ahead because our regex's will terminate
|
|
// because they run out of characters they can eat.
|
|
|
|
// What we do not attempt to have the regexp do:
|
|
// - Avoid trailing '.' and ')' characters. We let our greedy match absorb
|
|
// these, but have a separate regex for extra characters to leave off at the
|
|
// end.
|
|
//
|
|
// The Regex (apart from lead-in/lead-out):
|
|
// ( Begin capture of the URL
|
|
// (?: (potential detect beginnings)
|
|
// https?:\/\/| Start with "http" or "https"
|
|
// www\d{0,3}[.][a-z0-9.\-]{2,249}|
|
|
// Start with "www", up to 3 numbers, then "." then
|
|
// something that looks domain-namey. We differ from the
|
|
// next case in that we do not constrain the top-level
|
|
// domain as tightly and do not require a trailing path
|
|
// indicator of "/". This is IDN root compatible.
|
|
// [a-z0-9.\-]{2,250}[.][a-z]{2,4}\/
|
|
// Detect a non-www domain, but requiring a trailing "/"
|
|
// to indicate a path. This only detects IDN domains
|
|
// with a non-IDN root. This is reasonable in cases where
|
|
// there is no explicit http/https start us out, but
|
|
// unreasonable where there is. Our real fix is the bug
|
|
// to port the Thunderbird/gecko linkification logic.
|
|
//
|
|
// Domain names can be up to 253 characters long, and are
|
|
// limited to a-zA-Z0-9 and '-'. The roots don't have
|
|
// hyphens unless they are IDN roots. Root zones can be
|
|
// found here: http://www.iana.org/domains/root/db
|
|
// )
|
|
// [-\w.!~*'();,/?:@&=+$#%]*
|
|
// path onwards. We allow the set of characters that
|
|
// encodeURI does not escape plus the result of escaping
|
|
// (so also '%')
|
|
// )
|
|
// eslint-disable-next-line max-len
|
|
const urlRegex = /(^|[\s(,;'"`“])((?:https?:\/\/|www\d{0,3}[.][a-z0-9.\-]{2,249}|[a-z0-9.\-]{2,250}[.][a-z]{2,4}\/)[-\w.!~*'();,/?:@&=+$#%]*)/im;
|
|
|
|
// Set of terminators that are likely to have been part of the context rather
|
|
// than part of the URL and so should be uneaten. This is '(', ',', ';', plus
|
|
// quotes and question end-ing punctuation and the potential permutations with
|
|
// parentheses (english-specific).
|
|
const uneatLastUrlCharsRegex = /(?:[),;.!?`'"]|[.!?]\)|\)[.!?])$/;
|
|
|
|
const { MESSAGE_SOURCE, MESSAGE_TYPE, MESSAGE_LEVEL } = require("../constants");
|
|
const { ConsoleMessage, NetworkEventMessage } = require("../types");
|
|
|
|
function prepareMessage(packet, idGenerator) {
|
|
if (!packet.source) {
|
|
packet = transformPacket(packet);
|
|
}
|
|
|
|
if (packet.allowRepeating) {
|
|
packet.repeatId = getRepeatId(packet);
|
|
}
|
|
packet.id = idGenerator.getNextId(packet);
|
|
return packet;
|
|
}
|
|
|
|
/**
|
|
* 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": {
|
|
return transformConsoleAPICallPacket(packet);
|
|
}
|
|
|
|
case "will-navigate": {
|
|
return transformNavigationMessagePacket(packet);
|
|
}
|
|
|
|
case "logMessage": {
|
|
return transformLogMessagePacket(packet);
|
|
}
|
|
|
|
case "pageError": {
|
|
return transformPageErrorPacket(packet);
|
|
}
|
|
|
|
case "networkEvent": {
|
|
return transformNetworkEventPacket(packet);
|
|
}
|
|
|
|
case "evaluationResult":
|
|
default: {
|
|
return transformEvaluationResultPacket(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
// eslint-disable-next-line complexity
|
|
function transformConsoleAPICallPacket(packet) {
|
|
const { 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":
|
|
case "countReset":
|
|
// Chrome RDP doesn't have a special type for count.
|
|
type = MESSAGE_TYPE.LOG;
|
|
const { counter } = message;
|
|
|
|
if (!counter) {
|
|
// We don't show anything if we don't have counter data.
|
|
type = MESSAGE_TYPE.NULL_MESSAGE;
|
|
} else if (counter.error) {
|
|
messageText = l10n.getFormatStr(counter.error, [counter.label]);
|
|
level = MESSAGE_LEVEL.WARN;
|
|
parameters = null;
|
|
} else {
|
|
const label = counter.label
|
|
? counter.label
|
|
: l10n.getStr("noCounterLabel");
|
|
messageText = `${label}: ${counter.count}`;
|
|
parameters = null;
|
|
}
|
|
break;
|
|
case "timeStamp":
|
|
type = MESSAGE_TYPE.NULL_MESSAGE;
|
|
break;
|
|
case "time":
|
|
parameters = null;
|
|
if (timer && timer.error) {
|
|
messageText = l10n.getFormatStr(timer.error, [timer.name]);
|
|
level = MESSAGE_LEVEL.WARN;
|
|
} else {
|
|
// We don't show anything for console.time calls to match Chrome's behaviour.
|
|
type = MESSAGE_TYPE.NULL_MESSAGE;
|
|
}
|
|
break;
|
|
case "timeLog":
|
|
case "timeEnd":
|
|
if (timer && timer.error) {
|
|
parameters = null;
|
|
messageText = l10n.getFormatStr(timer.error, [timer.name]);
|
|
level = MESSAGE_LEVEL.WARN;
|
|
} else if (timer) {
|
|
// We show the duration to users when calls console.timeLog/timeEnd is called,
|
|
// if corresponding console.time() was called before.
|
|
const duration = Math.round(timer.duration * 100) / 100;
|
|
if (type === "timeEnd") {
|
|
messageText = l10n.getFormatStr("console.timeEnd", [
|
|
timer.name,
|
|
duration,
|
|
]);
|
|
parameters = null;
|
|
} else if (type === "timeLog") {
|
|
const [, ...rest] = parameters;
|
|
parameters = [
|
|
l10n.getFormatStr("timeLog", [timer.name, duration]),
|
|
...rest,
|
|
];
|
|
}
|
|
} 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;
|
|
if (parameters.length === 0) {
|
|
parameters = [l10n.getStr("noGroupLabel")];
|
|
}
|
|
break;
|
|
case "groupCollapsed":
|
|
type = MESSAGE_TYPE.START_GROUP_COLLAPSED;
|
|
if (parameters.length === 0) {
|
|
parameters = [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,
|
|
sourceId: message.sourceId,
|
|
line: message.lineNumber,
|
|
column: message.columnNumber,
|
|
}
|
|
: null;
|
|
|
|
if (type === "logPointError" || type === "logPoint") {
|
|
frame.options = { logPoint: true };
|
|
}
|
|
|
|
return new ConsoleMessage({
|
|
source: MESSAGE_SOURCE.CONSOLE_API,
|
|
type,
|
|
level,
|
|
parameters,
|
|
messageText,
|
|
stacktrace: message.stacktrace ? message.stacktrace : null,
|
|
frame,
|
|
timeStamp: message.timeStamp,
|
|
userProvidedStyles: message.styles,
|
|
prefix: message.prefix,
|
|
private: message.private,
|
|
executionPoint: message.executionPoint,
|
|
logpointId: message.logpointId,
|
|
chromeContext: message.chromeContext,
|
|
});
|
|
}
|
|
|
|
function transformNavigationMessagePacket(packet) {
|
|
const { url } = packet;
|
|
return new ConsoleMessage({
|
|
source: MESSAGE_SOURCE.CONSOLE_API,
|
|
type: MESSAGE_TYPE.NAVIGATION_MARKER,
|
|
level: MESSAGE_LEVEL.LOG,
|
|
messageText: l10n.getFormatStr("webconsole.navigated", [url]),
|
|
timeStamp: Date.now(),
|
|
});
|
|
}
|
|
|
|
function transformLogMessagePacket(packet) {
|
|
const { message, timeStamp } = packet;
|
|
|
|
return new ConsoleMessage({
|
|
source: MESSAGE_SOURCE.CONSOLE_API,
|
|
type: MESSAGE_TYPE.LOG,
|
|
level: MESSAGE_LEVEL.LOG,
|
|
messageText: message,
|
|
timeStamp,
|
|
private: message.private,
|
|
chromeContext: message.chromeContext,
|
|
});
|
|
}
|
|
|
|
function transformPageErrorPacket(packet) {
|
|
const { 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,
|
|
sourceId: pageError.sourceId,
|
|
line: pageError.lineNumber,
|
|
column: pageError.columnNumber,
|
|
}
|
|
: null;
|
|
|
|
const matchesCSS = pageError.category == "CSS Parser";
|
|
const messageSource = matchesCSS
|
|
? MESSAGE_SOURCE.CSS
|
|
: MESSAGE_SOURCE.JAVASCRIPT;
|
|
return new ConsoleMessage({
|
|
innerWindowID: pageError.innerWindowID,
|
|
source: messageSource,
|
|
type: MESSAGE_TYPE.LOG,
|
|
level,
|
|
category: pageError.category,
|
|
messageText: pageError.errorMessage,
|
|
stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
|
|
frame,
|
|
errorMessageName: pageError.errorMessageName,
|
|
exceptionDocURL: pageError.exceptionDocURL,
|
|
timeStamp: pageError.timeStamp,
|
|
notes: pageError.notes,
|
|
private: pageError.private,
|
|
executionPoint: pageError.executionPoint,
|
|
chromeContext: pageError.chromeContext,
|
|
// Backward compatibility: cssSelectors might not be available when debugging
|
|
// Firefox 67 or older.
|
|
// Remove `|| ""` when Firefox 68 is on the release channel.
|
|
cssSelectors: pageError.cssSelectors || "",
|
|
});
|
|
}
|
|
|
|
function transformNetworkEventPacket(packet) {
|
|
const { networkEvent } = packet;
|
|
|
|
return new NetworkEventMessage({
|
|
actor: networkEvent.actor,
|
|
isXHR: networkEvent.isXHR,
|
|
request: networkEvent.request,
|
|
response: networkEvent.response,
|
|
timeStamp: networkEvent.timeStamp,
|
|
totalTime: networkEvent.totalTime,
|
|
url: networkEvent.request.url,
|
|
urlDetails: getUrlDetails(networkEvent.request.url),
|
|
method: networkEvent.request.method,
|
|
updates: networkEvent.updates,
|
|
cause: networkEvent.cause,
|
|
private: networkEvent.private,
|
|
securityState: networkEvent.securityState,
|
|
chromeContext: networkEvent.chromeContext,
|
|
});
|
|
}
|
|
|
|
function transformEvaluationResultPacket(packet) {
|
|
let {
|
|
exceptionMessage,
|
|
errorMessageName,
|
|
exceptionDocURL,
|
|
exception,
|
|
exceptionStack,
|
|
frame,
|
|
result,
|
|
helperResult,
|
|
timestamp: timeStamp,
|
|
notes,
|
|
} = packet;
|
|
|
|
const parameter =
|
|
helperResult && helperResult.object ? helperResult.object : result;
|
|
|
|
if (helperResult && helperResult.type === "error") {
|
|
try {
|
|
exceptionMessage = l10n.getStr(helperResult.message);
|
|
} catch (ex) {
|
|
exceptionMessage = helperResult.message;
|
|
}
|
|
} else if (typeof exception === "string") {
|
|
// Wrap thrown strings in Error objects, so `throw "foo"` outputs "Error: foo"
|
|
exceptionMessage = new Error(exceptionMessage).toString();
|
|
}
|
|
|
|
const level =
|
|
typeof exceptionMessage !== "undefined" && exceptionMessage !== null
|
|
? MESSAGE_LEVEL.ERROR
|
|
: MESSAGE_LEVEL.LOG;
|
|
|
|
return new ConsoleMessage({
|
|
source: MESSAGE_SOURCE.JAVASCRIPT,
|
|
type: MESSAGE_TYPE.RESULT,
|
|
helperType: helperResult ? helperResult.type : null,
|
|
level,
|
|
messageText: exceptionMessage,
|
|
parameters: [parameter],
|
|
errorMessageName,
|
|
exceptionDocURL,
|
|
stacktrace: exceptionStack,
|
|
frame,
|
|
timeStamp,
|
|
notes,
|
|
private: packet.private,
|
|
allowRepeating: false,
|
|
});
|
|
}
|
|
|
|
// Helpers
|
|
function getRepeatId(message) {
|
|
return JSON.stringify({
|
|
frame: message.frame,
|
|
groupId: message.groupId,
|
|
indent: message.indent,
|
|
level: message.level,
|
|
messageText: message.messageText,
|
|
parameters: message.parameters,
|
|
source: message.source,
|
|
type: message.type,
|
|
userProvidedStyles: message.userProvidedStyles,
|
|
private: message.private,
|
|
stacktrace: message.stacktrace,
|
|
executionPoint: message.executionPoint,
|
|
});
|
|
}
|
|
|
|
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 (packet._type === "NetworkEvent") {
|
|
convertPacket.networkEvent = packet;
|
|
convertPacket.type = "networkEvent";
|
|
} else if (packet._type === "LogMessage") {
|
|
convertPacket = {
|
|
...packet,
|
|
type: "logMessage",
|
|
};
|
|
} else {
|
|
throw new Error("Unexpected packet type: " + 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,
|
|
logPointError: 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_DEBUG,
|
|
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_LOG,
|
|
};
|
|
|
|
return levelMap[type] || MESSAGE_TYPE.LOG;
|
|
}
|
|
|
|
function isGroupType(type) {
|
|
return [
|
|
MESSAGE_TYPE.START_GROUP,
|
|
MESSAGE_TYPE.START_GROUP_COLLAPSED,
|
|
].includes(type);
|
|
}
|
|
|
|
function getInitialMessageCountForViewport(win) {
|
|
const minMessageHeight = 20;
|
|
return Math.ceil(win.innerHeight / minMessageHeight);
|
|
}
|
|
|
|
function isPacketPrivate(packet) {
|
|
return (
|
|
packet.private === true ||
|
|
(packet.message && packet.message.private === true) ||
|
|
(packet.pageError && packet.pageError.private === true) ||
|
|
(packet.networkEvent && packet.networkEvent.private === true)
|
|
);
|
|
}
|
|
|
|
function createWarningGroupMessage(id, type, firstMessage) {
|
|
return new ConsoleMessage({
|
|
id,
|
|
level: MESSAGE_LEVEL.WARN,
|
|
source: MESSAGE_SOURCE.CONSOLE_FRONTEND,
|
|
type,
|
|
messageText: getWarningGroupLabel(firstMessage),
|
|
timeStamp: firstMessage.timeStamp,
|
|
innerWindowID: firstMessage.innerWindowID,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Given the a regular warning message, compute the label of the warning group the message
|
|
* could be in.
|
|
* For example, if the message text is:
|
|
* The resource at “http://evil.com” was blocked because content blocking is enabled
|
|
*
|
|
* it may be turned into
|
|
*
|
|
* The resource at “<URL>” was blocked because content blocking is enabled
|
|
*
|
|
* @param {ConsoleMessage} firstMessage
|
|
* @returns {String} The computed label
|
|
*/
|
|
function getWarningGroupLabel(firstMessage) {
|
|
if (
|
|
isContentBlockingMessage(firstMessage) ||
|
|
isTrackingProtectionMessage(firstMessage)
|
|
) {
|
|
return replaceURL(firstMessage.messageText, "<URL>");
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Replace any URL in the provided text by the provided replacement text, or an empty
|
|
* string.
|
|
*
|
|
* @param {String} text
|
|
* @param {String} replacementText
|
|
* @returns {String}
|
|
*/
|
|
function replaceURL(text, replacementText = "") {
|
|
let result = "";
|
|
let currentIndex = 0;
|
|
let contentStart;
|
|
while (true) {
|
|
const url = urlRegex.exec(text);
|
|
// Pick the regexp with the earlier content; index will always be zero.
|
|
if (!url) {
|
|
break;
|
|
}
|
|
contentStart = url.index + url[1].length;
|
|
if (contentStart > 0) {
|
|
const nonUrlText = text.substring(0, contentStart);
|
|
result += nonUrlText;
|
|
}
|
|
|
|
// There are some final characters for a URL that are much more likely
|
|
// to have been part of the enclosing text rather than the end of the
|
|
// URL.
|
|
let useUrl = url[2];
|
|
const uneat = uneatLastUrlCharsRegex.exec(useUrl);
|
|
if (uneat) {
|
|
useUrl = useUrl.substring(0, uneat.index);
|
|
}
|
|
|
|
if (useUrl) {
|
|
result += replacementText;
|
|
}
|
|
|
|
currentIndex = currentIndex + contentStart;
|
|
|
|
currentIndex = currentIndex + useUrl.length;
|
|
text = text.substring(url.index + url[1].length + useUrl.length);
|
|
}
|
|
|
|
return result + text;
|
|
}
|
|
|
|
/**
|
|
* Get the warningGroup type in which the message could be in.
|
|
* @param {ConsoleMessage} message
|
|
* @returns {String|null} null if the message can't be part of a warningGroup.
|
|
*/
|
|
function getWarningGroupType(message) {
|
|
if (isContentBlockingMessage(message)) {
|
|
return MESSAGE_TYPE.CONTENT_BLOCKING_GROUP;
|
|
}
|
|
|
|
if (isTrackingProtectionMessage(message)) {
|
|
return MESSAGE_TYPE.TRACKING_PROTECTION_GROUP;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns a computed id given a message
|
|
*
|
|
* @param {ConsoleMessage} type: the message type, from MESSAGE_TYPE.
|
|
* @param {Integer} innerWindowID: the message innerWindowID.
|
|
* @returns {String}
|
|
*/
|
|
function getParentWarningGroupMessageId(message) {
|
|
const warningGroupType = getWarningGroupType(message);
|
|
if (!warningGroupType) {
|
|
return null;
|
|
}
|
|
|
|
return `${warningGroupType}-${message.innerWindowID}`;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the message is a warningGroup message (i.e. the "Header").
|
|
* @param {ConsoleMessage} message
|
|
* @returns {Boolean}
|
|
*/
|
|
function isWarningGroup(message) {
|
|
return (
|
|
message.type === MESSAGE_TYPE.CONTENT_BLOCKING_GROUP ||
|
|
message.type === MESSAGE_TYPE.TRACKING_PROTECTION_GROUP ||
|
|
message.type === MESSAGE_TYPE.CORS_GROUP ||
|
|
message.type === MESSAGE_TYPE.CSP_GROUP
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns true if the message is a content blocking message.
|
|
* @param {ConsoleMessage} message
|
|
* @returns {Boolean}
|
|
*/
|
|
function isContentBlockingMessage(message) {
|
|
const { category } = message;
|
|
return (
|
|
category == "cookieBlockedPermission" ||
|
|
category == "cookieBlockedTracker" ||
|
|
category == "cookieBlockedAll" ||
|
|
category == "cookieBlockedForeign"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns true if the message is a tracking protection message.
|
|
* @param {ConsoleMessage} message
|
|
* @returns {Boolean}
|
|
*/
|
|
function isTrackingProtectionMessage(message) {
|
|
const { category } = message;
|
|
return category == "Tracking Protection";
|
|
}
|
|
|
|
module.exports = {
|
|
createWarningGroupMessage,
|
|
getInitialMessageCountForViewport,
|
|
getParentWarningGroupMessageId,
|
|
getWarningGroupType,
|
|
isContentBlockingMessage,
|
|
isGroupType,
|
|
isPacketPrivate,
|
|
isWarningGroup,
|
|
l10n,
|
|
prepareMessage,
|
|
// Export for use in testing.
|
|
getRepeatId,
|
|
};
|