Home | History | Annotate | Download | only in inspector
      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 "core/inspector/PromiseTracker.h"
      7 
      8 #include "bindings/core/v8/ScopedPersistent.h"
      9 #include "bindings/core/v8/ScriptCallStackFactory.h"
     10 #include "bindings/core/v8/ScriptState.h"
     11 #include "bindings/core/v8/ScriptValue.h"
     12 #include "wtf/PassOwnPtr.h"
     13 #include "wtf/WeakPtr.h"
     14 
     15 using blink::TypeBuilder::Array;
     16 using blink::TypeBuilder::Console::CallFrame;
     17 using blink::TypeBuilder::Debugger::PromiseDetails;
     18 
     19 namespace blink {
     20 
     21 class PromiseTracker::PromiseData FINAL : public RefCountedWillBeGarbageCollectedFinalized<PromiseData> {
     22 public:
     23     static PassRefPtrWillBeRawPtr<PromiseData> create(ScriptState* scriptState, int promiseHash, int promiseId, v8::Handle<v8::Object> promise)
     24     {
     25         return adoptRefWillBeNoop(new PromiseData(scriptState, promiseHash, promiseId, promise));
     26     }
     27 
     28     int promiseHash() const { return m_promiseHash; }
     29     ScopedPersistent<v8::Object>& promise() { return m_promise; }
     30 
     31 #if ENABLE(OILPAN)
     32     void dispose()
     33     {
     34         m_promise.clear();
     35         m_parentPromise.clear();
     36     }
     37 #else
     38     WeakPtr<PromiseData> createWeakPtr()
     39     {
     40         return m_weakPtrFactory.createWeakPtr();
     41     }
     42 #endif
     43 
     44     void trace(Visitor* visitor)
     45     {
     46         visitor->trace(m_callStack);
     47     }
     48 
     49 private:
     50     friend class PromiseTracker;
     51 
     52     PromiseData(ScriptState* scriptState, int promiseHash, int promiseId, v8::Handle<v8::Object> promise)
     53         : m_scriptState(scriptState)
     54         , m_promiseHash(promiseHash)
     55         , m_promiseId(promiseId)
     56         , m_promise(scriptState->isolate(), promise)
     57         , m_parentPromiseId(0)
     58         , m_status(0)
     59 #if !ENABLE(OILPAN)
     60         , m_weakPtrFactory(this)
     61 #endif
     62     {
     63     }
     64 
     65     RefPtr<ScriptState> m_scriptState;
     66     int m_promiseHash;
     67     int m_promiseId;
     68     ScopedPersistent<v8::Object> m_promise;
     69     int m_parentPromiseId;
     70     int m_status;
     71     RefPtrWillBeMember<ScriptCallStack> m_callStack;
     72     ScopedPersistent<v8::Object> m_parentPromise;
     73 #if !ENABLE(OILPAN)
     74     WeakPtrFactory<PromiseData> m_weakPtrFactory;
     75 #endif
     76 };
     77 
     78 static int indexOf(PromiseTracker::PromiseDataVector* vector, const ScopedPersistent<v8::Object>& promise)
     79 {
     80     for (size_t index = 0; index < vector->size(); ++index) {
     81         if (vector->at(index)->promise() == promise)
     82             return index;
     83     }
     84     return -1;
     85 }
     86 
     87 namespace {
     88 
     89 class PromiseDataWrapper FINAL : public NoBaseWillBeGarbageCollected<PromiseDataWrapper> {
     90 public:
     91     static PassOwnPtrWillBeRawPtr<PromiseDataWrapper> create(PromiseTracker::PromiseData* data, PromiseTracker* tracker)
     92     {
     93 #if ENABLE(OILPAN)
     94         return new PromiseDataWrapper(data, tracker);
     95 #else
     96         return adoptPtr(new PromiseDataWrapper(data->createWeakPtr(), tracker));
     97 #endif
     98     }
     99 
    100 #if ENABLE(OILPAN)
    101     static void didRemovePromise(const v8::WeakCallbackData<v8::Object, Persistent<PromiseDataWrapper> >& data)
    102 #else
    103     static void didRemovePromise(const v8::WeakCallbackData<v8::Object, PromiseDataWrapper>& data)
    104 #endif
    105     {
    106 #if ENABLE(OILPAN)
    107         OwnPtr<Persistent<PromiseDataWrapper> > persistentWrapper = adoptPtr(data.GetParameter());
    108         RawPtr<PromiseDataWrapper> wrapper = *persistentWrapper;
    109 #else
    110         OwnPtr<PromiseDataWrapper> wrapper = adoptPtr(data.GetParameter());
    111 #endif
    112         WeakPtrWillBeRawPtr<PromiseTracker::PromiseData> promiseData = wrapper->m_data;
    113         if (!promiseData || !wrapper->m_tracker)
    114             return;
    115 
    116 #if ENABLE(OILPAN)
    117         // Oilpan: let go of ScopedPersistent<>s right here (and not wait until the
    118         // PromiseDataWrapper is GCed later.) The v8 weak callback handling expects
    119         // to see the callback data upon return.
    120         promiseData->dispose();
    121 #endif
    122         PromiseTracker::PromiseDataMap& map = wrapper->m_tracker->promiseDataMap();
    123         int promiseHash = promiseData->promiseHash();
    124 
    125         PromiseTracker::PromiseDataMap::iterator it = map.find(promiseHash);
    126         // The PromiseTracker may have been disabled (and, possibly, re-enabled later),
    127         // leaving the promiseHash as unmapped.
    128         if (it == map.end())
    129             return;
    130 
    131         PromiseTracker::PromiseDataVector* vector = &it->value;
    132         int index = indexOf(vector, promiseData->promise());
    133         ASSERT(index >= 0);
    134         vector->remove(index);
    135         if (vector->isEmpty())
    136             map.remove(promiseHash);
    137     }
    138 
    139     void trace(Visitor* visitor)
    140     {
    141 #if ENABLE(OILPAN)
    142         visitor->trace(m_data);
    143         visitor->trace(m_tracker);
    144 #endif
    145     }
    146 
    147 private:
    148     PromiseDataWrapper(WeakPtrWillBeRawPtr<PromiseTracker::PromiseData> data, PromiseTracker* tracker)
    149         : m_data(data)
    150         , m_tracker(tracker)
    151     {
    152     }
    153 
    154     WeakPtrWillBeWeakMember<PromiseTracker::PromiseData> m_data;
    155     RawPtrWillBeWeakMember<PromiseTracker> m_tracker;
    156 };
    157 
    158 }
    159 
    160 PromiseTracker::PromiseTracker()
    161     : m_circularSequentialId(0)
    162     , m_isEnabled(false)
    163 {
    164 }
    165 
    166 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(PromiseTracker);
    167 
    168 void PromiseTracker::trace(Visitor* visitor)
    169 {
    170 #if ENABLE(OILPAN)
    171     visitor->trace(m_promiseDataMap);
    172 #endif
    173 }
    174 
    175 void PromiseTracker::setEnabled(bool enabled)
    176 {
    177     m_isEnabled = enabled;
    178     if (!enabled)
    179         clear();
    180 }
    181 
    182 void PromiseTracker::clear()
    183 {
    184     m_promiseDataMap.clear();
    185 }
    186 
    187 int PromiseTracker::circularSequentialId()
    188 {
    189     ++m_circularSequentialId;
    190     if (m_circularSequentialId <= 0)
    191         m_circularSequentialId = 1;
    192     return m_circularSequentialId;
    193 }
    194 
    195 PassRefPtrWillBeRawPtr<PromiseTracker::PromiseData> PromiseTracker::createPromiseDataIfNeeded(ScriptState* scriptState, v8::Handle<v8::Object> promise)
    196 {
    197     int promiseHash = promise->GetIdentityHash();
    198     RawPtr<PromiseDataVector> vector = nullptr;
    199     PromiseDataMap::iterator it = m_promiseDataMap.find(promiseHash);
    200     if (it != m_promiseDataMap.end())
    201         vector = &it->value;
    202     else
    203         vector = &m_promiseDataMap.add(promiseHash, PromiseDataVector()).storedValue->value;
    204 
    205     int index = indexOf(vector, ScopedPersistent<v8::Object>(scriptState->isolate(), promise));
    206     if (index != -1)
    207         return vector->at(index);
    208 
    209     // FIXME: Consider using the ScriptState's DOMWrapperWorld instead
    210     // to handle the lifetime of PromiseDataWrapper, avoiding all this
    211     // manual labor to achieve the same, with and without Oilpan.
    212     RefPtrWillBeRawPtr<PromiseData> data = PromiseData::create(scriptState, promiseHash, circularSequentialId(), promise);
    213     OwnPtrWillBeRawPtr<PromiseDataWrapper> dataWrapper = PromiseDataWrapper::create(data.get(), this);
    214 #if ENABLE(OILPAN)
    215     OwnPtr<Persistent<PromiseDataWrapper> > wrapper = adoptPtr(new Persistent<PromiseDataWrapper>(dataWrapper));
    216 #else
    217     OwnPtr<PromiseDataWrapper> wrapper = dataWrapper.release();
    218 #endif
    219     data->m_promise.setWeak(wrapper.leakPtr(), &PromiseDataWrapper::didRemovePromise);
    220     vector->append(data);
    221 
    222     return data.release();
    223 }
    224 
    225 void PromiseTracker::didReceiveV8PromiseEvent(ScriptState* scriptState, v8::Handle<v8::Object> promise, v8::Handle<v8::Value> parentPromise, int status)
    226 {
    227     ASSERT(isEnabled());
    228 
    229     RefPtrWillBeRawPtr<PromiseData> data = createPromiseDataIfNeeded(scriptState, promise);
    230     if (!parentPromise.IsEmpty() && parentPromise->IsObject()) {
    231         v8::Handle<v8::Object> handle = parentPromise->ToObject();
    232         RefPtrWillBeRawPtr<PromiseData> parentData = createPromiseDataIfNeeded(scriptState, handle);
    233         data->m_parentPromiseId = parentData->m_promiseId;
    234         data->m_parentPromise.set(scriptState->isolate(), handle);
    235     } else {
    236         data->m_status = status;
    237         if (!status && !data->m_callStack) {
    238             RefPtrWillBeRawPtr<ScriptCallStack> stack = createScriptCallStack(1, true);
    239             if (stack && stack->size())
    240                 data->m_callStack = stack;
    241         }
    242     }
    243 }
    244 
    245 PassRefPtr<Array<PromiseDetails> > PromiseTracker::promises()
    246 {
    247     ASSERT(isEnabled());
    248 
    249     RefPtr<Array<PromiseDetails> > result = Array<PromiseDetails>::create();
    250     for (PromiseDataMap::iterator it = m_promiseDataMap.begin(); it != m_promiseDataMap.end(); ++it) {
    251         PromiseDataVector* vector = &it->value;
    252         for (size_t index = 0; index < vector->size(); ++index) {
    253             RefPtrWillBeRawPtr<PromiseData> data = vector->at(index);
    254             PromiseDetails::Status::Enum status;
    255             if (!data->m_status)
    256                 status = PromiseDetails::Status::Pending;
    257             else if (data->m_status == 1)
    258                 status = PromiseDetails::Status::Resolved;
    259             else
    260                 status = PromiseDetails::Status::Rejected;
    261             RefPtr<PromiseDetails> promiseDetails = PromiseDetails::create()
    262                 .setId(data->m_promiseId)
    263                 .setStatus(status);
    264             if (data->m_parentPromiseId)
    265                 promiseDetails->setParentId(data->m_parentPromiseId);
    266             if (data->m_callStack)
    267                 promiseDetails->setCallFrame(data->m_callStack->at(0).buildInspectorObject());
    268             result->addItem(promiseDetails);
    269         }
    270     }
    271 
    272     return result.release();
    273 }
    274 
    275 ScriptValue PromiseTracker::promiseById(int promiseId) const
    276 {
    277     ASSERT(isEnabled());
    278 
    279     for (PromiseDataMap::const_iterator it = m_promiseDataMap.begin(); it != m_promiseDataMap.end(); ++it) {
    280         const PromiseDataVector* vector = &it->value;
    281         for (size_t index = 0; index < vector->size(); ++index) {
    282             RefPtrWillBeRawPtr<PromiseData> data = vector->at(index);
    283             if (data->m_promiseId == promiseId) {
    284                 ScriptState* scriptState = data->m_scriptState.get();
    285                 return ScriptValue(scriptState, data->m_promise.newLocal(scriptState->isolate()));
    286             }
    287         }
    288     }
    289 
    290     return ScriptValue();
    291 }
    292 
    293 } // namespace blink
    294