Home | History | Annotate | Download | only in v8
      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 #include "config.h"
      6 #include "bindings/core/v8/ScriptPromisePropertyBase.h"
      7 
      8 #include "bindings/core/v8/ScopedPersistent.h"
      9 #include "bindings/core/v8/ScriptState.h"
     10 #include "bindings/core/v8/V8Binding.h"
     11 #include "bindings/core/v8/V8HiddenValue.h"
     12 #include "core/dom/ExecutionContext.h"
     13 
     14 namespace blink {
     15 
     16 ScriptPromisePropertyBase::ScriptPromisePropertyBase(ExecutionContext* executionContext, Name name)
     17     : ContextLifecycleObserver(executionContext)
     18     , m_isolate(toIsolate(executionContext))
     19     , m_name(name)
     20     , m_state(Pending)
     21 {
     22 }
     23 
     24 ScriptPromisePropertyBase::~ScriptPromisePropertyBase()
     25 {
     26     clearWrappers();
     27 }
     28 
     29 static void clearHandle(const v8::WeakCallbackData<v8::Object, ScopedPersistent<v8::Object> >& data)
     30 {
     31     data.GetParameter()->clear();
     32 }
     33 
     34 ScriptPromise ScriptPromisePropertyBase::promise(DOMWrapperWorld& world)
     35 {
     36     if (!executionContext())
     37         return ScriptPromise();
     38 
     39     v8::HandleScope handleScope(m_isolate);
     40     v8::Handle<v8::Context> context = toV8Context(executionContext(), world);
     41     if (context.IsEmpty())
     42         return ScriptPromise();
     43     ScriptState* scriptState = ScriptState::from(context);
     44     ScriptState::Scope scope(scriptState);
     45 
     46     v8::Handle<v8::Object> wrapper = ensureHolderWrapper(scriptState);
     47     ASSERT(wrapper->CreationContext() == context);
     48 
     49     v8::Handle<v8::Value> cachedPromise = V8HiddenValue::getHiddenValue(m_isolate, wrapper, promiseName());
     50     if (!cachedPromise.IsEmpty())
     51         return ScriptPromise(scriptState, cachedPromise);
     52 
     53     // Create and cache the Promise
     54     v8::Handle<v8::Promise::Resolver> resolver = v8::Promise::Resolver::New(m_isolate);
     55     v8::Handle<v8::Promise> promise = resolver->GetPromise();
     56     V8HiddenValue::setHiddenValue(m_isolate, wrapper, promiseName(), promise);
     57 
     58     switch (m_state) {
     59     case Pending:
     60         // Cache the resolver too
     61         V8HiddenValue::setHiddenValue(m_isolate, wrapper, resolverName(), resolver);
     62         break;
     63     case Resolved:
     64     case Rejected:
     65         resolveOrRejectInternal(resolver);
     66         break;
     67     }
     68 
     69     return ScriptPromise(scriptState, promise);
     70 }
     71 
     72 void ScriptPromisePropertyBase::resolveOrReject(State targetState)
     73 {
     74     ASSERT(executionContext());
     75     ASSERT(m_state == Pending);
     76     ASSERT(targetState == Resolved || targetState == Rejected);
     77 
     78     m_state = targetState;
     79 
     80     v8::HandleScope handleScope(m_isolate);
     81     size_t i = 0;
     82     while (i < m_wrappers.size()) {
     83         const OwnPtr<ScopedPersistent<v8::Object> >& persistent = m_wrappers[i];
     84         if (persistent->isEmpty()) {
     85             // wrapper has died.
     86             // Since v8 GC can run during the iteration and clear the reference,
     87             // we can't move this check out of the loop.
     88             m_wrappers.remove(i);
     89             continue;
     90         }
     91         v8::Local<v8::Object> wrapper = persistent->newLocal(m_isolate);
     92         ScriptState::Scope scope(ScriptState::from(wrapper->CreationContext()));
     93 
     94         v8::Local<v8::Promise::Resolver> resolver = V8HiddenValue::getHiddenValue(m_isolate, wrapper, resolverName()).As<v8::Promise::Resolver>();
     95 
     96         V8HiddenValue::deleteHiddenValue(m_isolate, wrapper, resolverName());
     97         resolveOrRejectInternal(resolver);
     98         ++i;
     99     }
    100 }
    101 
    102 void ScriptPromisePropertyBase::resetBase()
    103 {
    104     clearWrappers();
    105     m_state = Pending;
    106 }
    107 
    108 void ScriptPromisePropertyBase::resolveOrRejectInternal(v8::Handle<v8::Promise::Resolver> resolver)
    109 {
    110     switch (m_state) {
    111     case Pending:
    112         ASSERT_NOT_REACHED();
    113         break;
    114     case Resolved:
    115         resolver->Resolve(resolvedValue(resolver->CreationContext()->Global(), m_isolate));
    116         break;
    117     case Rejected:
    118         resolver->Reject(rejectedValue(resolver->CreationContext()->Global(), m_isolate));
    119         break;
    120     }
    121 }
    122 
    123 v8::Local<v8::Object> ScriptPromisePropertyBase::ensureHolderWrapper(ScriptState* scriptState)
    124 {
    125     v8::Local<v8::Context> context = scriptState->context();
    126     size_t i = 0;
    127     while (i < m_wrappers.size()) {
    128         const OwnPtr<ScopedPersistent<v8::Object> >& persistent = m_wrappers[i];
    129         if (persistent->isEmpty()) {
    130             // wrapper has died.
    131             // Since v8 GC can run during the iteration and clear the reference,
    132             // we can't move this check out of the loop.
    133             m_wrappers.remove(i);
    134             continue;
    135         }
    136 
    137         v8::Local<v8::Object> wrapper = persistent->newLocal(m_isolate);
    138         if (wrapper->CreationContext() == context)
    139             return wrapper;
    140         ++i;
    141     }
    142     v8::Local<v8::Object> wrapper = holder(context->Global(), m_isolate);
    143     OwnPtr<ScopedPersistent<v8::Object> > weakPersistent = adoptPtr(new ScopedPersistent<v8::Object>);
    144     weakPersistent->set(m_isolate, wrapper);
    145     weakPersistent->setWeak(weakPersistent.get(), &clearHandle);
    146     m_wrappers.append(weakPersistent.release());
    147     return wrapper;
    148 }
    149 
    150 void ScriptPromisePropertyBase::clearWrappers()
    151 {
    152     v8::HandleScope handleScope(m_isolate);
    153     for (WeakPersistentSet::iterator i = m_wrappers.begin(); i != m_wrappers.end(); ++i) {
    154         v8::Local<v8::Object> wrapper = (*i)->newLocal(m_isolate);
    155         if (!wrapper.IsEmpty()) {
    156             wrapper->DeleteHiddenValue(resolverName());
    157             wrapper->DeleteHiddenValue(promiseName());
    158         }
    159     }
    160     m_wrappers.clear();
    161 }
    162 
    163 v8::Handle<v8::String> ScriptPromisePropertyBase::promiseName()
    164 {
    165     switch (m_name) {
    166 #define P(Name)                                           \
    167     case Name:                                            \
    168         return V8HiddenValue::Name ## Promise(m_isolate);
    169 
    170         SCRIPT_PROMISE_PROPERTIES(P)
    171 
    172 #undef P
    173     }
    174     ASSERT_NOT_REACHED();
    175     return v8::Handle<v8::String>();
    176 }
    177 
    178 v8::Handle<v8::String> ScriptPromisePropertyBase::resolverName()
    179 {
    180     switch (m_name) {
    181 #define P(Name)                                            \
    182     case Name:                                             \
    183         return V8HiddenValue::Name ## Resolver(m_isolate);
    184 
    185         SCRIPT_PROMISE_PROPERTIES(P)
    186 
    187 #undef P
    188     }
    189     ASSERT_NOT_REACHED();
    190     return v8::Handle<v8::String>();
    191 }
    192 
    193 } // namespace blink
    194