1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
/* 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 nsIConsoleListenerWatcher = require("devtools/server/actors/resources/utils/nsi-console-listener-watcher");
const { Ci } = require("chrome");
const { DevToolsServer } = require("devtools/server/devtools-server");
const ErrorDocs = require("devtools/server/actors/errordocs");
const {
createStringGrip,
makeDebuggeeValue,
createValueGripForTarget,
} = require("devtools/server/actors/object/utils");
const {
getActorIdForInternalSourceId,
} = require("devtools/server/actors/utils/dbg-source");
const {
TYPES: { ERROR_MESSAGE },
} = require("devtools/server/actors/resources/index");
const { MESSAGE_CATEGORY } = require("devtools/shared/constants");
const PLATFORM_SPECIFIC_CATEGORIES = [
"XPConnect JavaScript",
"component javascript",
"chrome javascript",
"chrome registration",
];
class ErrorMessageWatcher extends nsIConsoleListenerWatcher {
shouldHandleMessage(targetActor, message) {
// The listener we use can be called either with a nsIConsoleMessage or a nsIScriptError.
// In this file, we only want to handle nsIScriptError.
if (
// We only care about nsIScriptError
!(message instanceof Ci.nsIScriptError) ||
!this.isCategoryAllowed(targetActor, message.category) ||
// Block any error that was triggered by eager evaluation
message.sourceName === "debugger eager eval code"
) {
return false;
}
// Process targets listen for everything but messages from private windows
if (this.isProcessTarget(targetActor)) {
return !message.isFromPrivateWindow;
}
if (!message.innerWindowID) {
return false;
}
const { window } = targetActor;
const win = window?.WindowGlobalChild?.getByInnerWindowId(
message.innerWindowID
);
return targetActor.browserId === win?.browsingContext?.browserId;
}
/**
* Check if the given message category is allowed to be tracked or not.
* We ignore chrome-originating errors as we only care about content.
*
* @param string category
* The message category you want to check.
* @return boolean
* True if the category is allowed to be logged, false otherwise.
*/
isCategoryAllowed(targetActor, category) {
// CSS Parser errors will be handled by the CSSMessageWatcher.
if (!category || category === MESSAGE_CATEGORY.CSS_PARSER) {
return false;
}
// We listen for everything on Process targets
if (this.isProcessTarget(targetActor)) {
return true;
}
// For non-process targets, we filter-out platform-specific errors.
return !PLATFORM_SPECIFIC_CATEGORIES.includes(category);
}
/**
* Prepare an nsIScriptError to be sent to the client.
*
* @param nsIScriptError error
* The page error we need to send to the client.
* @return object
* The object you can send to the remote client.
*/
buildResource(targetActor, error) {
const stack = this.prepareStackForRemote(targetActor, error.stack);
let lineText = error.sourceLine;
if (
lineText &&
lineText.length > DevToolsServer.LONG_STRING_INITIAL_LENGTH
) {
lineText = lineText.substr(0, DevToolsServer.LONG_STRING_INITIAL_LENGTH);
}
const notesArray = this.prepareNotesForRemote(targetActor, error.notes);
// If there is no location information in the error but we have a stack,
// fill in the location with the first frame on the stack.
let { sourceName, sourceId, lineNumber, columnNumber } = error;
if (!sourceName && !sourceId && !lineNumber && !columnNumber && stack) {
sourceName = stack[0].filename;
sourceId = stack[0].sourceId;
lineNumber = stack[0].lineNumber;
columnNumber = stack[0].columnNumber;
}
const pageError = {
errorMessage: createStringGrip(targetActor, error.errorMessage),
errorMessageName: error.errorMessageName,
exceptionDocURL: ErrorDocs.GetURL(error),
sourceName,
sourceId: getActorIdForInternalSourceId(targetActor, sourceId),
lineText,
lineNumber,
columnNumber,
category: error.category,
innerWindowID: error.innerWindowID,
timeStamp: error.timeStamp,
warning: !!(error.flags & error.warningFlag),
error: !(error.flags & (error.warningFlag | error.infoFlag)),
info: !!(error.flags & error.infoFlag),
private: error.isFromPrivateWindow,
stacktrace: stack,
notes: notesArray,
chromeContext: error.isFromChromeContext,
isPromiseRejection: error.isPromiseRejection,
isForwardedFromContentProcess: error.isForwardedFromContentProcess,
};
// If the pageError does have an exception object, we want to return the grip for it,
// but only if we do manage to get the grip, as we're checking the property on the
// client to render things differently.
if (error.hasException) {
try {
const obj = makeDebuggeeValue(targetActor, error.exception);
if (obj?.class !== "DeadObject") {
pageError.exception = createValueGripForTarget(targetActor, obj);
pageError.hasException = true;
}
} catch (e) {}
}
return {
pageError,
resourceType: ERROR_MESSAGE,
};
}
}
module.exports = ErrorMessageWatcher;
|