error-messages.js (6538B)
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 nsIConsoleListenerWatcher = require("resource://devtools/server/actors/resources/utils/nsi-console-listener-watcher.js"); 8 const { 9 createStringGrip, 10 makeDebuggeeValue, 11 createValueGripForTarget, 12 } = require("resource://devtools/server/actors/object/utils.js"); 13 const { 14 getActorIdForInternalSourceId, 15 } = require("resource://devtools/server/actors/utils/dbg-source.js"); 16 const { 17 WebConsoleUtils, 18 } = require("resource://devtools/server/actors/webconsole/utils.js"); 19 20 const Targets = require("resource://devtools/server/actors/targets/index.js"); 21 22 const { MESSAGE_CATEGORY } = require("resource://devtools/shared/constants.js"); 23 24 loader.lazyRequireGetter( 25 this, 26 "ErrorDocs", 27 "resource://devtools/server/actors/errordocs.js" 28 ); 29 30 const PLATFORM_SPECIFIC_CATEGORIES = [ 31 "XPConnect JavaScript", 32 "component javascript", 33 "chrome javascript", 34 "chrome registration", 35 ]; 36 37 class ErrorMessageWatcher extends nsIConsoleListenerWatcher { 38 shouldHandleMessage(targetActor, message, isCachedMessage = false) { 39 // The listener we use can be called either with a nsIConsoleMessage or a nsIScriptError. 40 // In this file, we only want to handle nsIScriptError. 41 if ( 42 // We only care about nsIScriptError 43 !(message instanceof Ci.nsIScriptError) || 44 !this.isCategoryAllowed(targetActor, message.category) || 45 // Block any error that was triggered by eager evaluation 46 message.sourceName === "debugger eager eval code" 47 ) { 48 return false; 49 } 50 51 // Filter specific to CONTENT PROCESS targets 52 if (this.isProcessTarget(targetActor)) { 53 // Don't want to display cached messages from private windows. 54 const isCachedFromPrivateWindow = 55 isCachedMessage && message.isFromPrivateWindow; 56 if (isCachedFromPrivateWindow) { 57 return false; 58 } 59 60 // `ContentChild` forwards all errors to the parent process (via IPC) all errors up 61 // the parent process and sets a `isForwardedFromContentProcess` property on them. 62 // Ignore these forwarded messages as the original ones will be logged either in a 63 // content process target (if window-less message) or frame target (if related to a window) 64 if (message.isForwardedFromContentProcess) { 65 return false; 66 } 67 68 // Ignore all messages related to a given window for content process targets 69 // These messages will be handled by Watchers instantiated for the related frame targets 70 if ( 71 targetActor.targetType == Targets.TYPES.PROCESS && 72 message.innerWindowID 73 ) { 74 return false; 75 } 76 77 return true; 78 } 79 80 if (!message.innerWindowID) { 81 return false; 82 } 83 84 const ids = targetActor.windows.map(window => 85 WebConsoleUtils.getInnerWindowId(window) 86 ); 87 return ids.includes(message.innerWindowID); 88 } 89 90 /** 91 * Check if the given message category is allowed to be tracked or not. 92 * We ignore chrome-originating errors as we only care about content. 93 * 94 * @param string category 95 * The message category you want to check. 96 * @return boolean 97 * True if the category is allowed to be logged, false otherwise. 98 */ 99 isCategoryAllowed(targetActor, category) { 100 // CSS Parser errors will be handled by the CSSMessageWatcher. 101 if (!category || category === MESSAGE_CATEGORY.CSS_PARSER) { 102 return false; 103 } 104 105 // We listen for everything on Process targets 106 if (this.isProcessTarget(targetActor)) { 107 return true; 108 } 109 110 // Don't restrict any categories in the Browser Toolbox/Browser Console 111 if (targetActor.sessionContext.type == "all") { 112 return true; 113 } 114 115 // For non-process targets in other toolboxes, we filter-out platform-specific errors. 116 return !PLATFORM_SPECIFIC_CATEGORIES.includes(category); 117 } 118 119 /** 120 * Prepare an nsIScriptError to be sent to the client. 121 * 122 * @param nsIScriptError error 123 * The page error we need to send to the client. 124 * @return object 125 * The object you can send to the remote client. 126 */ 127 buildResource(targetActor, error) { 128 const stack = this.prepareStackForRemote(targetActor, error.stack); 129 130 const notesArray = this.prepareNotesForRemote(targetActor, error.notes); 131 132 // If there is no location information in the error but we have a stack, 133 // fill in the location with the first frame on the stack. 134 let { sourceName, sourceId, lineNumber, columnNumber } = error; 135 if (!sourceName && !sourceId && !lineNumber && !columnNumber && stack) { 136 sourceName = stack[0].filename; 137 sourceId = stack[0].sourceId; 138 // The line is 1-based. 139 lineNumber = stack[0].lineNumber; 140 // The column is also 1-based as it ultimately derivates from SavedFrame's 1-based column 141 columnNumber = stack[0].columnNumber; 142 } 143 144 const pageError = { 145 errorMessage: createStringGrip(targetActor, error.errorMessage), 146 errorMessageName: error.errorMessageName, 147 exceptionDocURL: ErrorDocs.GetURL(error), 148 sourceName, 149 sourceId: getActorIdForInternalSourceId(targetActor, sourceId), 150 lineNumber, 151 columnNumber, 152 category: error.category, 153 innerWindowID: error.innerWindowID, 154 timeStamp: error.microSecondTimeStamp / 1000, 155 warning: !!(error.flags & error.warningFlag), 156 error: !(error.flags & (error.warningFlag | error.infoFlag)), 157 info: !!(error.flags & error.infoFlag), 158 private: error.isFromPrivateWindow, 159 stacktrace: stack, 160 notes: notesArray, 161 chromeContext: error.isFromChromeContext, 162 isPromiseRejection: error.isPromiseRejection, 163 isForwardedFromContentProcess: error.isForwardedFromContentProcess, 164 }; 165 166 // If the pageError does have an exception object, we want to return the grip for it, 167 // but only if we do manage to get the grip, as we're checking the property on the 168 // client to render things differently. 169 if (error.hasException) { 170 try { 171 const obj = makeDebuggeeValue(targetActor, error.exception); 172 if (obj?.class !== "DeadObject") { 173 pageError.exception = createValueGripForTarget(targetActor, obj); 174 pageError.hasException = true; 175 } 176 } catch (e) {} 177 } 178 179 return { 180 pageError, 181 }; 182 } 183 } 184 module.exports = ErrorMessageWatcher;