Source code

Revision control

Copy as Markdown

Other Tools

/* 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 CONSOLE_WORKER_IDS = (exports.CONSOLE_WORKER_IDS = new Set([
"SharedWorker",
"ServiceWorker",
"Worker",
]));
var WebConsoleUtils = {
/**
* Given a message, return one of CONSOLE_WORKER_IDS if it matches
* one of those.
*
* @return string
*/
getWorkerType(message) {
const innerID = message?.innerID;
return CONSOLE_WORKER_IDS.has(innerID) ? innerID : null;
},
/**
* Gets the ID of the inner window of this DOM window.
*
* @param nsIDOMWindow window
* @return integer|null
* Inner ID for the given window, null if we can't access it.
*/
getInnerWindowId(window) {
// Might throw with SecurityError: Permission denied to access property
// "windowGlobalChild" on cross-origin object.
try {
return window.windowGlobalChild.innerWindowId;
} catch (e) {
return null;
}
},
/**
* Recursively gather a list of inner window ids given a
* top level window.
*
* @param nsIDOMWindow window
* @return Array
* list of inner window ids.
*/
getInnerWindowIDsForFrames(window) {
const innerWindowID = this.getInnerWindowId(window);
if (innerWindowID === null) {
return [];
}
let ids = [innerWindowID];
if (window.frames) {
for (let i = 0; i < window.frames.length; i++) {
const frame = window.frames[i];
ids = ids.concat(this.getInnerWindowIDsForFrames(frame));
}
}
return ids;
},
/**
* Create a grip for the given value. If the value is an object,
* an object wrapper will be created.
*
* @param mixed value
* The value you want to create a grip for, before sending it to the
* client.
* @param function objectWrapper
* If the value is an object then the objectWrapper function is
* invoked to give us an object grip. See this.getObjectGrip().
* @return mixed
* The value grip.
*/
createValueGrip(value, objectWrapper) {
switch (typeof value) {
case "boolean":
return value;
case "string":
return objectWrapper(value);
case "number":
if (value === Infinity) {
return { type: "Infinity" };
} else if (value === -Infinity) {
return { type: "-Infinity" };
} else if (Number.isNaN(value)) {
return { type: "NaN" };
} else if (!value && 1 / value === -Infinity) {
return { type: "-0" };
}
return value;
case "undefined":
return { type: "undefined" };
case "object":
if (value === null) {
return { type: "null" };
}
// Fall through.
case "function":
case "record":
case "tuple":
return objectWrapper(value);
default:
console.error(
"Failed to provide a grip for value of " + typeof value + ": " + value
);
return null;
}
},
/**
* Remove any frames in a stack that are above a debugger-triggered evaluation
* and will correspond with devtools server code, which we never want to show
* to the user.
*
* @param array stack
* An array of frames, with the topmost first, and each of which has a
* 'filename' property.
* @return array
* An array of stack frames with any devtools server frames removed.
* The original array is not modified.
*/
removeFramesAboveDebuggerEval(stack) {
const debuggerEvalFilename = "debugger eval code";
// Remove any frames for server code above the last debugger eval frame.
const evalIndex = stack.findIndex(({ filename }, idx, arr) => {
const nextFrame = arr[idx + 1];
return (
filename == debuggerEvalFilename &&
(!nextFrame || nextFrame.filename !== debuggerEvalFilename)
);
});
if (evalIndex != -1) {
return stack.slice(0, evalIndex + 1);
}
// In some cases (e.g. evaluated expression with SyntaxError), we might not have a
// "debugger eval code" frame but still have internal ones. If that's the case, we
// return null as the end user shouldn't see those frames.
if (
stack.some(
({ filename }) =>
filename && filename.startsWith("resource://devtools/")
)
) {
return null;
}
return stack;
},
};
exports.WebConsoleUtils = WebConsoleUtils;