/* 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 DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js"); loader.lazyRequireGetter( this, "ObjectUtils", "resource://devtools/server/actors/object/utils.js" ); const { formatDisplayName, } = require("resource://devtools/server/actors/frame.js"); const { TYPES, getResourceWatcher, } = require("resource://devtools/server/actors/resources/index.js"); // Get a string message to display when a frame evaluation throws. function getThrownMessage(completion) { try { if (completion.throw.getOwnPropertyDescriptor) { return completion.throw.getOwnPropertyDescriptor("message").value; } else if (completion.toString) { return completion.toString(); } } catch (ex) { // ignore } return "Unknown exception"; } module.exports.getThrownMessage = getThrownMessage; function evalAndLogEvent({ threadActor, frame, level, expression, bindings }) { const frameLocation = threadActor.sourcesManager.getFrameLocation(frame); const { sourceActor, line } = frameLocation; const displayName = formatDisplayName(frame); // TODO remove this branch when (#1749668) lands (#1609540) if (isWorker) { threadActor.targetActor._consoleActor.evaluateJS({ text: `console.log(...${expression})`, bindings: { displayName, ...bindings }, url: sourceActor.url, lineNumber: line, disableBreaks: true, }); return undefined; } let completion; // Ensure disabling all types of breakpoints for all sources while evaluating the log points threadActor.insideClientEvaluation = { disableBreaks: true }; try { completion = frame.evalWithBindings( expression, { displayName, ...bindings, }, { hideFromDebugger: true } ); } finally { threadActor.insideClientEvaluation = null; } let value; if (!completion) { // The evaluation was killed (possibly by the slow script dialog). value = ["Evaluation failed"]; } else if ("return" in completion) { value = []; const length = ObjectUtils.getArrayLength(completion.return); for (let i = 0; i < length; i++) { value.push(DevToolsUtils.getProperty(completion.return, i)); } } else { value = [getThrownMessage(completion)]; level = `${level}Error`; } ChromeUtils.addProfilerMarker("Debugger log point", undefined, value); emitConsoleMessage(threadActor, frameLocation, value, level); return undefined; } function logEvent({ threadActor, frame }) { const frameLocation = threadActor.sourcesManager.getFrameLocation(frame); const { sourceActor, line } = frameLocation; // TODO remove this branch when (#1749668) lands (#1609540) if (isWorker) { const bindings = {}; for (let i = 0; i < frame.arguments.length; i++) { bindings[`x${i}`] = frame.arguments[i]; } threadActor.targetActor._consoleActor.evaluateJS({ text: `console.log(${Object.keys(bindings).join(",")})`, bindings, url: sourceActor.url, lineNumber: line, disableBreaks: true, }); return undefined; } emitConsoleMessage(threadActor, frameLocation, frame.arguments, "logPoint"); return undefined; } function emitConsoleMessage(threadActor, frameLocation, args, level) { const targetActor = threadActor.targetActor; const { sourceActor, line, column } = frameLocation; const message = { filename: sourceActor.url, // The line is 1-based lineNumber: line, // `frameLocation` comes from the SourcesManager which uses 0-base column // whereas CONSOLE_MESSAGE resources emits 1-based columns. columnNumber: column + 1, arguments: args, level, timeStamp: ChromeUtils.dateNow(), chromeContext: targetActor.actorID && /conn\d+\.parentProcessTarget\d+/.test(targetActor.actorID), // The 'prepareConsoleMessageForRemote' method in webconsoleActor expects internal source ID, // thus we can't set sourceId directly to sourceActorID. sourceId: sourceActor.internalSourceId, }; // Note that only WindowGlobalTarget actor support resource watcher // This is still missing for worker and content processes const consoleMessageWatcher = getResourceWatcher( targetActor, TYPES.CONSOLE_MESSAGE ); if (consoleMessageWatcher) { consoleMessageWatcher.emitMessages([message], false); } else { // Bug 1642296: Once we enable ConsoleMessage resource on the server, we should remove onConsoleAPICall // from the WebConsoleActor, and only support the ConsoleMessageWatcher codepath. message.arguments = message.arguments.map(arg => arg && typeof arg.unsafeDereference === "function" ? arg.unsafeDereference() : arg ); targetActor._consoleActor.onConsoleAPICall(message); } } module.exports.evalAndLogEvent = evalAndLogEvent; module.exports.logEvent = logEvent;