Chromium Code Reviews| Index: Source/bindings/core/v8/V8Initializer.cpp |
| diff --git a/Source/bindings/core/v8/V8Initializer.cpp b/Source/bindings/core/v8/V8Initializer.cpp |
| index f73978540a245000a78a8a178ab3b90a6fb93a5a..f78495db3857812b80a4685cef56a38b1cc646fd 100644 |
| --- a/Source/bindings/core/v8/V8Initializer.cpp |
| +++ b/Source/bindings/core/v8/V8Initializer.cpp |
| @@ -30,6 +30,8 @@ |
| #include "bindings/core/v8/ScriptCallStackFactory.h" |
| #include "bindings/core/v8/ScriptController.h" |
| #include "bindings/core/v8/ScriptProfiler.h" |
| +#include "bindings/core/v8/ScriptValue.h" |
| +#include "bindings/core/v8/ScriptValueTraits.h" |
| #include "bindings/core/v8/V8Binding.h" |
| #include "bindings/core/v8/V8DOMException.h" |
| #include "bindings/core/v8/V8ErrorEvent.h" |
| @@ -41,6 +43,7 @@ |
| #include "bindings/core/v8/V8Window.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExceptionCode.h" |
| +#include "core/dom/Microtask.h" |
| #include "core/frame/ConsoleTypes.h" |
| #include "core/frame/LocalDOMWindow.h" |
| #include "core/frame/LocalFrame.h" |
| @@ -155,6 +158,125 @@ static void messageHandlerInMainThread(v8::Handle<v8::Message> message, v8::Hand |
| } |
| } |
| +namespace { |
| + |
| +struct PromiseRejectMessage { |
|
yurys
2014/10/03 15:08:06
class?
aandrey
2014/10/06 10:52:57
Done.
|
| + PromiseRejectMessage(const ScriptValue& promise, const ScriptValue& rejectValue) |
| + : m_promise(promise) |
| + , m_rejectValue(rejectValue) |
| + { |
| + } |
| + |
| + ScriptValue m_promise; |
| + ScriptValue m_rejectValue; |
| +}; |
| + |
| +} // namespace |
| + |
| +typedef WillBeHeapHashMap<ScriptValue, OwnPtr<PromiseRejectMessage>, ScriptValueHash, ScriptValueHashTraits> PromiseRejectMessageMap; |
| +static bool promiseEndOfTurnTaskScheduled = false; |
| + |
| +static PromiseRejectMessageMap& promiseRejectMessageQueue() |
| +{ |
| + DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<PromiseRejectMessageMap>, map, (adoptPtrWillBeNoop(new PromiseRejectMessageMap()))); |
| + return *map; |
| +} |
| + |
| +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
|
| +{ |
| + ASSERT(isMainThread()); |
| + |
| + promiseEndOfTurnTaskScheduled = false; |
| + v8::Isolate::GetCurrent()->RemoveCallCompletedCallback(&firePromiseRejectMessages); |
|
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
|
| + |
| + // FIXME: Send more data from V8. |
| + int scriptId = 0; |
| + AccessControlStatus corsStatus = SharableCrossOrigin; |
| + Vector<ScriptCallFrame> callFrames; |
| + RefPtrWillBeRawPtr<ScriptCallStack> callStack = ScriptCallStack::create(callFrames); |
|
yurys
2014/10/03 15:08:05
= nullptr?
aandrey
2014/10/06 10:52:57
Done.
|
| + |
| + PromiseRejectMessageMap& map = promiseRejectMessageQueue(); |
| + while (!map.isEmpty()) { |
| + PromiseRejectMessageMap::iterator it = map.begin(); |
| + ScriptValue promise = it->value->m_promise; |
| + ScriptValue rejectValue = it->value->m_rejectValue; |
| + map.remove(it); |
| + |
| + ScriptState* scriptState = promise.scriptState(); |
| + if (scriptState->contextIsValid()) |
| + continue; |
| + ScriptState::Scope scope(scriptState); |
| + |
| + RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::createSanitizedError(&scriptState->world()); |
| + event->setUnsanitizedMessage("Uncaught promise reject"); |
| + |
| + 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.
|
| + if (!data.IsEmpty() && V8DOMWrapper::isDOMWrapper(data)) { |
| + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(data); |
| + const WrapperTypeInfo* type = toWrapperTypeInfo(obj); |
| + if (V8DOMException::wrapperTypeInfo.isSubclass(type)) { |
| + DOMException* exception = V8DOMException::toImpl(obj); |
| + if (exception && !exception->messageForConsole().isEmpty()) |
| + event->setUnsanitizedMessage("Uncaught promise reject " + exception->toStringForConsole()); |
| + } |
| + } |
| + |
| + if (scriptState->world().isPrivateScriptIsolatedWorld()) { |
| + // We allow a private script to dispatch error events even in a EventDispatchForbiddenScope scope. |
| + // Without having this ability, it's hard to debug the private script 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.
|
| + // in the private script are not reported to console (the private script just crashes silently). |
| + // Allowing error events in private scripts is safe because error events don't propagate to |
| + // other isolated worlds (which means that the error events won't fire any event listeners |
| + // in user's scripts). |
| + EventDispatchForbiddenScope::AllowUserAgentEvents allowUserAgentEvents; |
| + scriptState->executionContext()->reportException(event.release(), scriptId, callStack, corsStatus, true); |
| + } else { |
| + scriptState->executionContext()->reportException(event.release(), scriptId, callStack, corsStatus, true); |
| + } |
| + } |
| +} |
| + |
| +static void promiseRejectHandlerInMainThread(v8::Handle<v8::Promise> promise, v8::Handle<v8::Value> value, v8::PromiseRejectEvent event) |
| +{ |
| + ASSERT(isMainThread()); |
| + |
| + // It's possible that promiseRejectHandlerInMainThread() is invoked while we're initializing a window. |
| + // In that half-baked situation, we don't have a valid context nor a valid world, |
| + // so just return immediately. |
| + if (DOMWrapperWorld::windowIsBeingInitialized()) |
| + return; |
| + |
| + v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| + |
| + // If called during context initialization, there will be no entered window. |
| + LocalDOMWindow* enteredWindow = enteredDOMWindow(isolate); |
| + if (!enteredWindow || !enteredWindow->isCurrentlyDisplayedInFrame()) |
| + return; |
| + |
| + ScriptState* scriptState = ScriptState::current(isolate); |
| + ScriptValue promiseValue(scriptState, promise); |
| + PromiseRejectMessageMap& map = promiseRejectMessageQueue(); |
| + if (event == v8::kPromiseRejectWithNoHandler) { |
| + ASSERT(!map.contains(promiseValue)); |
| + PromiseRejectMessage* data = new PromiseRejectMessage(promiseValue, ScriptValue(scriptState, value)); |
|
yurys
2014/10/03 15:08:05
new should be wrapped with adoptPtr
aandrey
2014/10/06 10:52:57
Done.
|
| + map.set(promiseValue, adoptPtr(data)); |
| + } else if (event == v8::kPromiseHandlerAddedAfterReject) { |
| + PromiseRejectMessageMap::iterator it = map.find(promiseValue); |
| + if (it != map.end()) { |
| + map.remove(it); |
| + } else { |
| + // FIXME: Report revoke message to console. |
| + } |
| + } else { |
| + ASSERT_NOT_REACHED(); |
| + } |
| + |
| + if (!promiseEndOfTurnTaskScheduled && !map.isEmpty()) { |
| + promiseEndOfTurnTaskScheduled = true; |
| + isolate->AddCallCompletedCallback(&firePromiseRejectMessages); |
| + } |
| +} |
| + |
| static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data) |
| { |
| v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| @@ -218,6 +340,7 @@ void V8Initializer::initializeMainThreadIfNeeded() |
| v8::V8::SetAllowCodeGenerationFromStringsCallback(codeGenerationCheckCallbackInMainThread); |
| isolate->SetEventLogger(timerTraceProfilerInMainThread); |
| + isolate->SetPromiseRejectCallback(promiseRejectHandlerInMainThread); |
| ScriptProfiler::initialize(); |
| } |