diff options
Diffstat (limited to 'devtools/server/actors/webconsole/utils.js')
-rw-r--r-- | devtools/server/actors/webconsole/utils.js | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/devtools/server/actors/webconsole/utils.js b/devtools/server/actors/webconsole/utils.js new file mode 100644 index 0000000000..2834696944 --- /dev/null +++ b/devtools/server/actors/webconsole/utils.js @@ -0,0 +1,160 @@ +/* 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; |