Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: Source/bindings/core/v8/V8Initializer.cpp

Issue 625943002: Catch uncaught promise rejections from V8 and log to console. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved. 2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 12 matching lines...) Expand all
23 * THE POSSIBILITY OF SUCH DAMAGE. 23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 #include "config.h" 26 #include "config.h"
27 #include "bindings/core/v8/V8Initializer.h" 27 #include "bindings/core/v8/V8Initializer.h"
28 28
29 #include "bindings/core/v8/DOMWrapperWorld.h" 29 #include "bindings/core/v8/DOMWrapperWorld.h"
30 #include "bindings/core/v8/ScriptCallStackFactory.h" 30 #include "bindings/core/v8/ScriptCallStackFactory.h"
31 #include "bindings/core/v8/ScriptController.h" 31 #include "bindings/core/v8/ScriptController.h"
32 #include "bindings/core/v8/ScriptProfiler.h" 32 #include "bindings/core/v8/ScriptProfiler.h"
33 #include "bindings/core/v8/ScriptValue.h"
34 #include "bindings/core/v8/ScriptValueTraits.h"
33 #include "bindings/core/v8/V8Binding.h" 35 #include "bindings/core/v8/V8Binding.h"
34 #include "bindings/core/v8/V8DOMException.h" 36 #include "bindings/core/v8/V8DOMException.h"
35 #include "bindings/core/v8/V8ErrorEvent.h" 37 #include "bindings/core/v8/V8ErrorEvent.h"
36 #include "bindings/core/v8/V8ErrorHandler.h" 38 #include "bindings/core/v8/V8ErrorHandler.h"
37 #include "bindings/core/v8/V8GCController.h" 39 #include "bindings/core/v8/V8GCController.h"
38 #include "bindings/core/v8/V8History.h" 40 #include "bindings/core/v8/V8History.h"
39 #include "bindings/core/v8/V8Location.h" 41 #include "bindings/core/v8/V8Location.h"
40 #include "bindings/core/v8/V8PerContextData.h" 42 #include "bindings/core/v8/V8PerContextData.h"
41 #include "bindings/core/v8/V8Window.h" 43 #include "bindings/core/v8/V8Window.h"
42 #include "core/dom/Document.h" 44 #include "core/dom/Document.h"
43 #include "core/dom/ExceptionCode.h" 45 #include "core/dom/ExceptionCode.h"
46 #include "core/dom/Microtask.h"
44 #include "core/frame/ConsoleTypes.h" 47 #include "core/frame/ConsoleTypes.h"
45 #include "core/frame/LocalDOMWindow.h" 48 #include "core/frame/LocalDOMWindow.h"
46 #include "core/frame/LocalFrame.h" 49 #include "core/frame/LocalFrame.h"
47 #include "core/frame/csp/ContentSecurityPolicy.h" 50 #include "core/frame/csp/ContentSecurityPolicy.h"
48 #include "core/inspector/ScriptCallStack.h" 51 #include "core/inspector/ScriptCallStack.h"
49 #include "platform/EventDispatchForbiddenScope.h" 52 #include "platform/EventDispatchForbiddenScope.h"
50 #include "platform/TraceEvent.h" 53 #include "platform/TraceEvent.h"
51 #include "public/platform/Platform.h" 54 #include "public/platform/Platform.h"
52 #include "wtf/RefPtr.h" 55 #include "wtf/RefPtr.h"
53 #include "wtf/text/WTFString.h" 56 #include "wtf/text/WTFString.h"
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 // Allowing error events in private scripts is safe because error events don't propagate to 151 // Allowing error events in private scripts is safe because error events don't propagate to
149 // other isolated worlds (which means that the error events won't fire a ny event listeners 152 // other isolated worlds (which means that the error events won't fire a ny event listeners
150 // in user's scripts). 153 // in user's scripts).
151 EventDispatchForbiddenScope::AllowUserAgentEvents allowUserAgentEvents; 154 EventDispatchForbiddenScope::AllowUserAgentEvents allowUserAgentEvents;
152 enteredWindow->document()->reportException(event.release(), scriptId, ca llStack, corsStatus); 155 enteredWindow->document()->reportException(event.release(), scriptId, ca llStack, corsStatus);
153 } else { 156 } else {
154 enteredWindow->document()->reportException(event.release(), scriptId, ca llStack, corsStatus); 157 enteredWindow->document()->reportException(event.release(), scriptId, ca llStack, corsStatus);
155 } 158 }
156 } 159 }
157 160
161 namespace {
162
163 struct PromiseRejectMessage {
yurys 2014/10/03 15:08:06 class?
aandrey 2014/10/06 10:52:57 Done.
164 PromiseRejectMessage(const ScriptValue& promise, const ScriptValue& rejectVa lue)
165 : m_promise(promise)
166 , m_rejectValue(rejectValue)
167 {
168 }
169
170 ScriptValue m_promise;
171 ScriptValue m_rejectValue;
172 };
173
174 } // namespace
175
176 typedef WillBeHeapHashMap<ScriptValue, OwnPtr<PromiseRejectMessage>, ScriptValue Hash, ScriptValueHashTraits> PromiseRejectMessageMap;
177 static bool promiseEndOfTurnTaskScheduled = false;
178
179 static PromiseRejectMessageMap& promiseRejectMessageQueue()
180 {
181 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<PromiseRejectMessageMap>, map, (a doptPtrWillBeNoop(new PromiseRejectMessageMap())));
182 return *map;
183 }
184
185 static void firePromiseRejectMessages()
yurys 2014/10/03 15:08:06 Please wrap the new code into a class.
aandrey 2014/10/06 10:52:57 I'd rather put this code together with the excepti
186 {
187 ASSERT(isMainThread());
188
189 promiseEndOfTurnTaskScheduled = false;
190 v8::Isolate::GetCurrent()->RemoveCallCompletedCallback(&firePromiseRejectMes sages);
yurys 2014/10/03 15:08:06 Why not add this callback once on the isolate init
aandrey 2014/10/06 10:52:57 So that we should not affect common use cases: we
191
192 // FIXME: Send more data from V8.
193 int scriptId = 0;
194 AccessControlStatus corsStatus = SharableCrossOrigin;
195 Vector<ScriptCallFrame> callFrames;
196 RefPtrWillBeRawPtr<ScriptCallStack> callStack = ScriptCallStack::create(call Frames);
yurys 2014/10/03 15:08:05 = nullptr?
aandrey 2014/10/06 10:52:57 Done.
197
198 PromiseRejectMessageMap& map = promiseRejectMessageQueue();
199 while (!map.isEmpty()) {
200 PromiseRejectMessageMap::iterator it = map.begin();
201 ScriptValue promise = it->value->m_promise;
202 ScriptValue rejectValue = it->value->m_rejectValue;
203 map.remove(it);
204
205 ScriptState* scriptState = promise.scriptState();
206 if (scriptState->contextIsValid())
207 continue;
208 ScriptState::Scope scope(scriptState);
209
210 RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::createSanitizedError( &scriptState->world());
211 event->setUnsanitizedMessage("Uncaught promise reject");
212
213 v8::Handle<v8::Value> data = rejectValue.v8Value();
yurys 2014/10/03 15:08:06 The whole idea we agreed on was to pass rejected v
aandrey 2014/10/06 10:52:57 Done. Passing ScriptArguments.
214 if (!data.IsEmpty() && V8DOMWrapper::isDOMWrapper(data)) {
215 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(data);
216 const WrapperTypeInfo* type = toWrapperTypeInfo(obj);
217 if (V8DOMException::wrapperTypeInfo.isSubclass(type)) {
218 DOMException* exception = V8DOMException::toImpl(obj);
219 if (exception && !exception->messageForConsole().isEmpty())
220 event->setUnsanitizedMessage("Uncaught promise reject " + ex ception->toStringForConsole());
221 }
222 }
223
224 if (scriptState->world().isPrivateScriptIsolatedWorld()) {
225 // We allow a private script to dispatch error events even in a Even tDispatchForbiddenScope scope.
226 // Without having this ability, it's hard to debug the private scrip t because syntax errors
yurys 2014/10/03 15:08:05 This code is shared between regular exception hand
aandrey 2014/10/06 10:52:57 Acknowledged.
227 // in the private script are not reported to console (the private sc ript just crashes silently).
228 // Allowing error events in private scripts is safe because error ev ents don't propagate to
229 // other isolated worlds (which means that the error events won't fi re any event listeners
230 // in user's scripts).
231 EventDispatchForbiddenScope::AllowUserAgentEvents allowUserAgentEven ts;
232 scriptState->executionContext()->reportException(event.release(), sc riptId, callStack, corsStatus, true);
233 } else {
234 scriptState->executionContext()->reportException(event.release(), sc riptId, callStack, corsStatus, true);
235 }
236 }
237 }
238
239 static void promiseRejectHandlerInMainThread(v8::Handle<v8::Promise> promise, v8 ::Handle<v8::Value> value, v8::PromiseRejectEvent event)
240 {
241 ASSERT(isMainThread());
242
243 // It's possible that promiseRejectHandlerInMainThread() is invoked while we 're initializing a window.
244 // In that half-baked situation, we don't have a valid context nor a valid w orld,
245 // so just return immediately.
246 if (DOMWrapperWorld::windowIsBeingInitialized())
247 return;
248
249 v8::Isolate* isolate = v8::Isolate::GetCurrent();
250
251 // If called during context initialization, there will be no entered window.
252 LocalDOMWindow* enteredWindow = enteredDOMWindow(isolate);
253 if (!enteredWindow || !enteredWindow->isCurrentlyDisplayedInFrame())
254 return;
255
256 ScriptState* scriptState = ScriptState::current(isolate);
257 ScriptValue promiseValue(scriptState, promise);
258 PromiseRejectMessageMap& map = promiseRejectMessageQueue();
259 if (event == v8::kPromiseRejectWithNoHandler) {
260 ASSERT(!map.contains(promiseValue));
261 PromiseRejectMessage* data = new PromiseRejectMessage(promiseValue, Scri ptValue(scriptState, value));
yurys 2014/10/03 15:08:05 new should be wrapped with adoptPtr
aandrey 2014/10/06 10:52:57 Done.
262 map.set(promiseValue, adoptPtr(data));
263 } else if (event == v8::kPromiseHandlerAddedAfterReject) {
264 PromiseRejectMessageMap::iterator it = map.find(promiseValue);
265 if (it != map.end()) {
266 map.remove(it);
267 } else {
268 // FIXME: Report revoke message to console.
269 }
270 } else {
271 ASSERT_NOT_REACHED();
272 }
273
274 if (!promiseEndOfTurnTaskScheduled && !map.isEmpty()) {
275 promiseEndOfTurnTaskScheduled = true;
276 isolate->AddCallCompletedCallback(&firePromiseRejectMessages);
277 }
278 }
279
158 static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8 ::AccessType type, v8::Local<v8::Value> data) 280 static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8 ::AccessType type, v8::Local<v8::Value> data)
159 { 281 {
160 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 282 v8::Isolate* isolate = v8::Isolate::GetCurrent();
161 LocalFrame* target = findFrame(host, data, isolate); 283 LocalFrame* target = findFrame(host, data, isolate);
162 if (!target) 284 if (!target)
163 return; 285 return;
164 LocalDOMWindow* targetWindow = target->domWindow(); 286 LocalDOMWindow* targetWindow = target->domWindow();
165 287
166 // FIXME: We should modify V8 to pass in more contextual information (contex t, property, and object). 288 // FIXME: We should modify V8 to pass in more contextual information (contex t, property, and object).
167 ExceptionState exceptionState(ExceptionState::UnknownContext, 0, 0, isolate- >GetCurrentContext()->Global(), isolate); 289 ExceptionState exceptionState(ExceptionState::UnknownContext, 0, 0, isolate- >GetCurrentContext()->Global(), isolate);
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 v8::Isolate* isolate = V8PerIsolateData::initialize(); 333 v8::Isolate* isolate = V8PerIsolateData::initialize();
212 334
213 initializeV8Common(isolate); 335 initializeV8Common(isolate);
214 336
215 v8::V8::SetFatalErrorHandler(reportFatalErrorInMainThread); 337 v8::V8::SetFatalErrorHandler(reportFatalErrorInMainThread);
216 v8::V8::AddMessageListener(messageHandlerInMainThread); 338 v8::V8::AddMessageListener(messageHandlerInMainThread);
217 v8::V8::SetFailedAccessCheckCallbackFunction(failedAccessCheckCallbackInMain Thread); 339 v8::V8::SetFailedAccessCheckCallbackFunction(failedAccessCheckCallbackInMain Thread);
218 v8::V8::SetAllowCodeGenerationFromStringsCallback(codeGenerationCheckCallbac kInMainThread); 340 v8::V8::SetAllowCodeGenerationFromStringsCallback(codeGenerationCheckCallbac kInMainThread);
219 341
220 isolate->SetEventLogger(timerTraceProfilerInMainThread); 342 isolate->SetEventLogger(timerTraceProfilerInMainThread);
343 isolate->SetPromiseRejectCallback(promiseRejectHandlerInMainThread);
221 344
222 ScriptProfiler::initialize(); 345 ScriptProfiler::initialize();
223 } 346 }
224 347
225 static void reportFatalErrorInWorker(const char* location, const char* message) 348 static void reportFatalErrorInWorker(const char* location, const char* message)
226 { 349 {
227 // FIXME: We temporarily deal with V8 internal error situations such as out- of-memory by crashing the worker. 350 // FIXME: We temporarily deal with V8 internal error situations such as out- of-memory by crashing the worker.
228 CRASH(); 351 CRASH();
229 } 352 }
230 353
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
266 initializeV8Common(isolate); 389 initializeV8Common(isolate);
267 390
268 v8::V8::AddMessageListener(messageHandlerInWorker); 391 v8::V8::AddMessageListener(messageHandlerInWorker);
269 v8::V8::SetFatalErrorHandler(reportFatalErrorInWorker); 392 v8::V8::SetFatalErrorHandler(reportFatalErrorInWorker);
270 393
271 uint32_t here; 394 uint32_t here;
272 isolate->SetStackLimit(reinterpret_cast<uintptr_t>(&here - kWorkerMaxStackSi ze / sizeof(uint32_t*))); 395 isolate->SetStackLimit(reinterpret_cast<uintptr_t>(&here - kWorkerMaxStackSi ze / sizeof(uint32_t*)));
273 } 396 }
274 397
275 } // namespace blink 398 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698