1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef ScriptPromiseResolverWithContext_h 6 #define ScriptPromiseResolverWithContext_h 7 8 #include "bindings/v8/ScopedPersistent.h" 9 #include "bindings/v8/ScriptPromise.h" 10 #include "bindings/v8/ScriptPromiseResolver.h" 11 #include "bindings/v8/ScriptState.h" 12 #include "bindings/v8/V8Binding.h" 13 #include "core/dom/ActiveDOMObject.h" 14 #include "core/dom/ExecutionContext.h" 15 #include "platform/Timer.h" 16 #include "wtf/RefCounted.h" 17 #include "wtf/Vector.h" 18 #include <v8.h> 19 20 namespace WebCore { 21 22 // This class wraps ScriptPromiseResolver and provides the following 23 // functionalities in addition to ScriptPromiseResolver's. 24 // - A ScriptPromiseResolverWithContext retains a ScriptState. A caller 25 // can call resolve or reject from outside of a V8 context. 26 // - This class is an ActiveDOMObject and keeps track of the associated 27 // ExecutionContext state. When the ExecutionContext is suspended, 28 // resolve or reject will be delayed. When it is stopped, resolve or reject 29 // will be ignored. 30 class ScriptPromiseResolverWithContext : public ActiveDOMObject, public RefCounted<ScriptPromiseResolverWithContext> { 31 WTF_MAKE_NONCOPYABLE(ScriptPromiseResolverWithContext); 32 33 public: create(ScriptState * scriptState)34 static PassRefPtr<ScriptPromiseResolverWithContext> create(ScriptState* scriptState) 35 { 36 RefPtr<ScriptPromiseResolverWithContext> resolver = adoptRef(new ScriptPromiseResolverWithContext(scriptState)); 37 resolver->suspendIfNeeded(); 38 return resolver.release(); 39 } 40 ~ScriptPromiseResolverWithContext()41 virtual ~ScriptPromiseResolverWithContext() 42 { 43 // This assertion fails if: 44 // - promise() is called at least once and 45 // - this resolver is destructed before it is resolved, rejected or 46 // the associated ExecutionContext is stopped. 47 ASSERT(m_state == ResolvedOrRejected || !m_isPromiseCalled); 48 } 49 50 // Anything that can be passed to toV8Value can be passed to this function. 51 template <typename T> resolve(T value)52 void resolve(T value) 53 { 54 resolveOrReject(value, Resolving); 55 } 56 57 // Anything that can be passed to toV8Value can be passed to this function. 58 template <typename T> reject(T value)59 void reject(T value) 60 { 61 resolveOrReject(value, Rejecting); 62 } 63 scriptState()64 ScriptState* scriptState() { return m_scriptState.get(); } 65 66 // Note that an empty ScriptPromise will be returned after resolve or 67 // reject is called. promise()68 ScriptPromise promise() 69 { 70 #if ASSERT_ENABLED 71 m_isPromiseCalled = true; 72 #endif 73 return m_resolver ? m_resolver->promise() : ScriptPromise(); 74 } 75 scriptState()76 ScriptState* scriptState() const { return m_scriptState.get(); } 77 78 // ActiveDOMObject implementation. 79 virtual void suspend() OVERRIDE; 80 virtual void resume() OVERRIDE; 81 virtual void stop() OVERRIDE; 82 83 // Once this function is called this resolver stays alive while the 84 // promise is pending and the associated ExecutionContext isn't stopped. 85 void keepAliveWhilePending(); 86 87 protected: 88 // You need to call suspendIfNeeded after the construction because 89 // this is an ActiveDOMObject. 90 explicit ScriptPromiseResolverWithContext(ScriptState*); 91 92 private: 93 enum ResolutionState { 94 Pending, 95 Resolving, 96 Rejecting, 97 ResolvedOrRejected, 98 }; 99 enum LifetimeMode { 100 Default, 101 KeepAliveWhilePending, 102 }; 103 104 template<typename T> toV8Value(const T & value)105 v8::Handle<v8::Value> toV8Value(const T& value) 106 { 107 return ToV8Value<ScriptPromiseResolverWithContext, v8::Handle<v8::Object> >::toV8Value(value, m_scriptState->context()->Global(), m_scriptState->isolate()); 108 } 109 110 template <typename T> resolveOrReject(T value,ResolutionState newState)111 void resolveOrReject(T value, ResolutionState newState) 112 { 113 if (m_state != Pending || !executionContext() || executionContext()->activeDOMObjectsAreStopped()) 114 return; 115 ASSERT(newState == Resolving || newState == Rejecting); 116 m_state = newState; 117 // Retain this object until it is actually resolved or rejected. 118 // |deref| will be called in |clear|. 119 ref(); 120 121 ScriptState::Scope scope(m_scriptState.get()); 122 m_value.set(m_scriptState->isolate(), toV8Value(value)); 123 if (!executionContext()->activeDOMObjectsAreSuspended()) 124 resolveOrRejectImmediately(); 125 } 126 127 void resolveOrRejectImmediately(); 128 void onTimerFired(Timer<ScriptPromiseResolverWithContext>*); 129 void clear(); 130 131 ResolutionState m_state; 132 const RefPtr<ScriptState> m_scriptState; 133 LifetimeMode m_mode; 134 Timer<ScriptPromiseResolverWithContext> m_timer; 135 RefPtr<ScriptPromiseResolver> m_resolver; 136 ScopedPersistent<v8::Value> m_value; 137 #if ASSERT_ENABLED 138 // True if promise() is called. 139 bool m_isPromiseCalled; 140 #endif 141 }; 142 143 } // namespace WebCore 144 145 #endif // #ifndef ScriptPromiseResolverWithContext_h 146