blob: 143f3ba080d8854438c148fc1e794225cb32b4dd [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ScriptPromiseResolver_h
#define ScriptPromiseResolver_h
#include "bindings/core/v8/ScopedPersistent.h"
#include "bindings/core/v8/ScriptPromise.h"
#include "bindings/core/v8/ScriptState.h"
#include "bindings/core/v8/V8Binding.h"
#include "core/dom/ActiveDOMObject.h"
#include "core/dom/ExecutionContext.h"
#include "platform/Timer.h"
#include "wtf/RefCounted.h"
#include <v8.h>
namespace blink {
// This class wraps v8::Promise::Resolver and provides the following
// functionalities.
// - A ScriptPromiseResolver retains a ScriptState. A caller
// can call resolve or reject from outside of a V8 context.
// - This class is an ActiveDOMObject and keeps track of the associated
// ExecutionContext state. When the ExecutionContext is suspended,
// resolve or reject will be delayed. When it is stopped, resolve or reject
// will be ignored.
class ScriptPromiseResolver : public ActiveDOMObject, public RefCounted<ScriptPromiseResolver> {
WTF_MAKE_NONCOPYABLE(ScriptPromiseResolver);
public:
static PassRefPtr<ScriptPromiseResolver> create(ScriptState* scriptState)
{
RefPtr<ScriptPromiseResolver> resolver = adoptRef(new ScriptPromiseResolver(scriptState));
resolver->suspendIfNeeded();
return resolver.release();
}
virtual ~ScriptPromiseResolver()
{
// This assertion fails if:
// - promise() is called at least once and
// - this resolver is destructed before it is resolved, rejected or
// the associated ExecutionContext is stopped.
ASSERT(m_state == ResolvedOrRejected || !m_isPromiseCalled);
}
// Anything that can be passed to toV8Value can be passed to this function.
template <typename T>
void resolve(T value)
{
resolveOrReject(value, Resolving);
}
// Anything that can be passed to toV8Value can be passed to this function.
template <typename T>
void reject(T value)
{
resolveOrReject(value, Rejecting);
}
void resolve() { resolve(V8UndefinedType()); }
void reject() { reject(V8UndefinedType()); }
ScriptState* scriptState() { return m_scriptState.get(); }
// Note that an empty ScriptPromise will be returned after resolve or
// reject is called.
ScriptPromise promise()
{
#if ENABLE(ASSERT)
m_isPromiseCalled = true;
#endif
return m_resolver.promise();
}
ScriptState* scriptState() const { return m_scriptState.get(); }
// ActiveDOMObject implementation.
virtual void suspend() override;
virtual void resume() override;
virtual void stop() override;
// Once this function is called this resolver stays alive while the
// promise is pending and the associated ExecutionContext isn't stopped.
void keepAliveWhilePending();
protected:
// You need to call suspendIfNeeded after the construction because
// this is an ActiveDOMObject.
explicit ScriptPromiseResolver(ScriptState*);
private:
typedef ScriptPromise::InternalResolver Resolver;
enum ResolutionState {
Pending,
Resolving,
Rejecting,
ResolvedOrRejected,
};
enum LifetimeMode {
Default,
KeepAliveWhilePending,
};
template<typename T>
v8::Handle<v8::Value> toV8Value(const T& value)
{
return V8ValueTraits<T>::toV8Value(value, m_scriptState->context()->Global(), m_scriptState->isolate());
}
template <typename T>
void resolveOrReject(T value, ResolutionState newState)
{
if (m_state != Pending || !executionContext() || executionContext()->activeDOMObjectsAreStopped())
return;
ASSERT(newState == Resolving || newState == Rejecting);
m_state = newState;
// Retain this object until it is actually resolved or rejected.
// |deref| will be called in |clear|.
ref();
ScriptState::Scope scope(m_scriptState.get());
m_value.set(m_scriptState->isolate(), toV8Value(value));
if (!executionContext()->activeDOMObjectsAreSuspended())
resolveOrRejectImmediately();
}
void resolveOrRejectImmediately();
void onTimerFired(Timer<ScriptPromiseResolver>*);
void clear();
ResolutionState m_state;
const RefPtr<ScriptState> m_scriptState;
LifetimeMode m_mode;
Timer<ScriptPromiseResolver> m_timer;
Resolver m_resolver;
ScopedPersistent<v8::Value> m_value;
#if ENABLE(ASSERT)
// True if promise() is called.
bool m_isPromiseCalled;
#endif
};
} // namespace blink
#endif // #ifndef ScriptPromiseResolver_h