logEvent.js (6307B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js"); 8 loader.lazyRequireGetter( 9 this, 10 "ObjectUtils", 11 "resource://devtools/server/actors/object/utils.js" 12 ); 13 const { 14 formatDisplayName, 15 } = require("resource://devtools/server/actors/frame.js"); 16 const { 17 TYPES, 18 getResourceWatcher, 19 } = require("resource://devtools/server/actors/resources/index.js"); 20 loader.lazyRequireGetter( 21 this, 22 ["isValidSavedFrame"], 23 "devtools/server/actors/frame", 24 true 25 ); 26 27 // Get a string message to display when a frame evaluation throws. 28 function getThrownMessage(completion) { 29 try { 30 if (completion.throw.getOwnPropertyDescriptor) { 31 return completion.throw.getOwnPropertyDescriptor("message").value; 32 } else if (completion.toString) { 33 return completion.toString(); 34 } 35 } catch (ex) { 36 // ignore 37 } 38 return "Unknown exception"; 39 } 40 module.exports.getThrownMessage = getThrownMessage; 41 42 function evalAndLogEvent({ 43 threadActor, 44 frame, 45 level, 46 expression, 47 bindings, 48 showStacktrace, 49 }) { 50 const frameLocation = threadActor.sourcesManager.getFrameLocation(frame); 51 const { sourceActor, line } = frameLocation; 52 const displayName = formatDisplayName(frame); 53 const stacktrace = showStacktrace ? [] : undefined; 54 55 if (showStacktrace) { 56 let currentFrame = frame; 57 while (currentFrame) { 58 if (currentFrame.script) { 59 stacktrace.push({ 60 filename: currentFrame.script.url, 61 functionName: currentFrame.script.displayName, 62 lineNumber: currentFrame.script.startLine, 63 columnNumber: currentFrame.script.startColumn, 64 sourceId: currentFrame.script.source.id, 65 }); 66 } else { 67 stacktrace.push({ 68 filename: "unknown", 69 functionName: currentFrame.displayName || "anonymous", 70 lineNumber: 0, 71 columnNumber: 0, 72 sourceId: "", 73 }); 74 } 75 const olderSavedFrame = 76 currentFrame.olderSavedFrame && 77 isValidSavedFrame(threadActor, currentFrame.olderSavedFrame) 78 ? currentFrame.olderSavedFrame 79 : null; 80 currentFrame = currentFrame.older || olderSavedFrame; 81 } 82 } 83 84 // TODO remove this branch when (#1749668) lands (#1609540) 85 if (isWorker) { 86 threadActor.targetActor._consoleActor.evaluateJS({ 87 text: showStacktrace 88 ? `console.trace(...${expression})` 89 : `console.log(...${expression})`, 90 bindings: { displayName, ...bindings }, 91 url: sourceActor.url, 92 lineNumber: line, 93 disableBreaks: true, 94 }); 95 96 return undefined; 97 } 98 99 let completion; 100 // Ensure disabling all types of breakpoints for all sources while evaluating the log points 101 threadActor.insideClientEvaluation = { disableBreaks: true }; 102 try { 103 completion = frame.evalWithBindings( 104 expression, 105 { 106 displayName, 107 ...bindings, 108 }, 109 { hideFromDebugger: true } 110 ); 111 } finally { 112 threadActor.insideClientEvaluation = null; 113 } 114 115 let value; 116 if (!completion) { 117 // The evaluation was killed (possibly by the slow script dialog). 118 value = ["Evaluation failed"]; 119 } else if ("return" in completion) { 120 value = []; 121 const length = ObjectUtils.getArrayLength(completion.return); 122 for (let i = 0; i < length; i++) { 123 value.push(DevToolsUtils.getProperty(completion.return, i)); 124 } 125 } else { 126 value = [getThrownMessage(completion)]; 127 level = `${level}Error`; 128 } 129 130 ChromeUtils.addProfilerMarker("Debugger log point", undefined, value); 131 132 emitConsoleMessage(threadActor, frameLocation, value, level, stacktrace); 133 134 return undefined; 135 } 136 137 function logEvent({ threadActor, frame }) { 138 const frameLocation = threadActor.sourcesManager.getFrameLocation(frame); 139 const { sourceActor, line } = frameLocation; 140 141 // TODO remove this branch when (#1749668) lands (#1609540) 142 if (isWorker) { 143 const bindings = {}; 144 for (let i = 0; i < frame.arguments.length; i++) { 145 bindings[`x${i}`] = frame.arguments[i]; 146 } 147 threadActor.targetActor._consoleActor.evaluateJS({ 148 text: `console.log(${Object.keys(bindings).join(",")})`, 149 bindings, 150 url: sourceActor.url, 151 lineNumber: line, 152 disableBreaks: true, 153 }); 154 155 return undefined; 156 } 157 158 emitConsoleMessage(threadActor, frameLocation, frame.arguments, "logPoint"); 159 160 return undefined; 161 } 162 163 function emitConsoleMessage( 164 threadActor, 165 frameLocation, 166 args, 167 level, 168 stacktrace 169 ) { 170 const targetActor = threadActor.targetActor; 171 const { sourceActor, line, column } = frameLocation; 172 173 const message = { 174 filename: sourceActor.url, 175 176 // The line is 1-based 177 lineNumber: line, 178 // `frameLocation` comes from the SourcesManager which uses 0-base column 179 // whereas CONSOLE_MESSAGE resources emits 1-based columns. 180 columnNumber: column + 1, 181 arguments: args, 182 level, 183 timeStamp: ChromeUtils.dateNow(), 184 chromeContext: 185 targetActor.actorID && 186 /conn\d+\.parentProcessTarget\d+/.test(targetActor.actorID), 187 // The 'prepareConsoleMessageForRemote' method in webconsoleActor expects internal source ID, 188 // thus we can't set sourceId directly to sourceActorID. 189 sourceId: sourceActor.internalSourceId, 190 stacktrace, 191 }; 192 193 // Note that only WindowGlobalTarget actor support resource watcher 194 // This is still missing for worker and content processes 195 const consoleMessageWatcher = getResourceWatcher( 196 targetActor, 197 TYPES.CONSOLE_MESSAGE 198 ); 199 if (consoleMessageWatcher) { 200 consoleMessageWatcher.emitMessages([message], false); 201 } else { 202 // Bug 1642296: Once we enable ConsoleMessage resource on the server, we should remove onConsoleAPICall 203 // from the WebConsoleActor, and only support the ConsoleMessageWatcher codepath. 204 message.arguments = message.arguments.map(arg => 205 arg && typeof arg.unsafeDereference === "function" 206 ? arg.unsafeDereference() 207 : arg 208 ); 209 targetActor._consoleActor.onConsoleAPICall(message); 210 } 211 } 212 213 module.exports.evalAndLogEvent = evalAndLogEvent; 214 module.exports.logEvent = logEvent;