• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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