Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "DOMDataStore.h"
     33 
     34 #include "DOMData.h"
     35 
     36 namespace WebCore {
     37 
     38 // DOM binding algorithm:
     39 //
     40 // There are two kinds of DOM objects:
     41 // 1. DOM tree nodes, such as Document, HTMLElement, ...
     42 //    there classes implement TreeShared<T> interface;
     43 // 2. Non-node DOM objects, such as CSSRule, Location, etc.
     44 //    these classes implement a ref-counted scheme.
     45 //
     46 // A DOM object may have a JS wrapper object. If a tree node
     47 // is alive, its JS wrapper must be kept alive even it is not
     48 // reachable from JS roots.
     49 // However, JS wrappers of non-node objects can go away if
     50 // not reachable from other JS objects. It works like a cache.
     51 //
     52 // DOM objects are ref-counted, and JS objects are traced from
     53 // a set of root objects. They can create a cycle. To break
     54 // cycles, we do following:
     55 //   Handles from DOM objects to JS wrappers are always weak,
     56 // so JS wrappers of non-node object cannot create a cycle.
     57 //   Before starting a global GC, we create a virtual connection
     58 // between nodes in the same tree in the JS heap. If the wrapper
     59 // of one node in a tree is alive, wrappers of all nodes in
     60 // the same tree are considered alive. This is done by creating
     61 // object groups in GC prologue callbacks. The mark-compact
     62 // collector will remove these groups after each GC.
     63 //
     64 // DOM objects should be deref-ed from the owning thread, not the GC thread
     65 // that does not own them. In V8, GC can kick in from any thread. To ensure
     66 // that DOM objects are always deref-ed from the owning thread when running
     67 // V8 in multi-threading environment, we do following:
     68 // 1. Maintain a thread specific DOM wrapper map for each object map.
     69 //    (We're using TLS support from WTF instead of base since V8Bindings
     70 //     does not depend on base. We further assume that all child threads
     71 //     running V8 instances are created by WTF and thus a destructor will
     72 //     be called to clean up all thread specific data.)
     73 // 2. When GC happens:
     74 //    2.1. If the dead object is in GC thread's map, remove the JS reference
     75 //         and deref the DOM object.
     76 //    2.2. Otherwise, go through all thread maps to find the owning thread.
     77 //         Remove the JS reference from the owning thread's map and move the
     78 //         DOM object to a delayed queue. Post a task to the owning thread
     79 //         to have it deref-ed from the owning thread at later time.
     80 // 3. When a thread is tearing down, invoke a cleanup routine to go through
     81 //    all objects in the delayed queue and the thread map and deref all of
     82 //    them.
     83 
     84 
     85 DOMDataStore::DOMDataStore(DOMData* domData)
     86     : m_domNodeMap(0)
     87     , m_domObjectMap(0)
     88     , m_activeDomObjectMap(0)
     89 #if ENABLE(SVG)
     90     , m_domSvgElementInstanceMap(0)
     91     , m_domSvgObjectWithContextMap(0)
     92 #endif
     93     , m_domData(domData)
     94 {
     95     WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
     96     DOMDataStore::allStores().append(this);
     97 }
     98 
     99 DOMDataStore::~DOMDataStore()
    100 {
    101     WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
    102     DOMDataStore::allStores().remove(DOMDataStore::allStores().find(this));
    103 }
    104 
    105 DOMDataList& DOMDataStore::allStores()
    106 {
    107   DEFINE_STATIC_LOCAL(DOMDataList, staticDOMDataList, ());
    108   return staticDOMDataList;
    109 }
    110 
    111 WTF::Mutex& DOMDataStore::allStoresMutex()
    112 {
    113     DEFINE_STATIC_LOCAL(WTF::Mutex, staticDOMDataListMutex, ());
    114     return staticDOMDataListMutex;
    115 }
    116 
    117 void DOMDataStore::forgetDelayedObject(DOMData* domData, void* object)
    118 {
    119     domData->forgetDelayedObject(object);
    120 }
    121 
    122 void* DOMDataStore::getDOMWrapperMap(DOMWrapperMapType type)
    123 {
    124     switch (type) {
    125     case DOMNodeMap:
    126         return m_domNodeMap;
    127     case DOMObjectMap:
    128         return m_domObjectMap;
    129     case ActiveDOMObjectMap:
    130         return m_activeDomObjectMap;
    131 #if ENABLE(SVG)
    132     case DOMSVGElementInstanceMap:
    133         return m_domSvgElementInstanceMap;
    134     case DOMSVGObjectWithContextMap:
    135         return m_domSvgObjectWithContextMap;
    136 #endif
    137     }
    138 
    139     ASSERT_NOT_REACHED();
    140     return 0;
    141 }
    142 
    143 // Called when the object is near death (not reachable from JS roots).
    144 // It is time to remove the entry from the table and dispose the handle.
    145 void DOMDataStore::weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
    146 {
    147     v8::HandleScope scope;
    148     ASSERT(v8Object->IsObject());
    149     DOMData::handleWeakObject(DOMDataStore::DOMObjectMap, v8::Persistent<v8::Object>::Cast(v8Object), domObject);
    150 }
    151 
    152 void DOMDataStore::weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
    153 {
    154     v8::HandleScope scope;
    155     ASSERT(v8Object->IsObject());
    156     DOMData::handleWeakObject(DOMDataStore::ActiveDOMObjectMap, v8::Persistent<v8::Object>::Cast(v8Object), domObject);
    157 }
    158 
    159 void DOMDataStore::weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
    160 {
    161     ASSERT(WTF::isMainThread());
    162 
    163     Node* node = static_cast<Node*>(domObject);
    164 
    165     WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
    166     DOMDataList& list = DOMDataStore::allStores();
    167     for (size_t i = 0; i < list.size(); ++i) {
    168         DOMDataStore* store = list[i];
    169         if (store->domNodeMap().removeIfPresent(node, v8Object)) {
    170             ASSERT(store->domData()->owningThread() == WTF::currentThread());
    171             node->deref();  // Nobody overrides Node::deref so it's safe
    172             break;  // There might be at most one wrapper for the node in world's maps
    173         }
    174     }
    175 }
    176 
    177 bool DOMDataStore::IntrusiveDOMWrapperMap::removeIfPresent(Node* obj, v8::Persistent<v8::Data> value)
    178 {
    179     ASSERT(obj);
    180     v8::Persistent<v8::Object>* entry = obj->wrapper();
    181     if (!entry)
    182         return false;
    183     if (*entry != value)
    184         return false;
    185     obj->clearWrapper();
    186     m_table.remove(entry);
    187     value.Dispose();
    188     return true;
    189 }
    190 
    191 #if ENABLE(SVG)
    192 
    193 void DOMDataStore::weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
    194 {
    195     v8::HandleScope scope;
    196     ASSERT(v8Object->IsObject());
    197     DOMData::handleWeakObject(DOMDataStore::DOMSVGElementInstanceMap, v8::Persistent<v8::Object>::Cast(v8Object), static_cast<SVGElementInstance*>(domObject));
    198 }
    199 
    200 void DOMDataStore::weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
    201 {
    202     v8::HandleScope scope;
    203     ASSERT(v8Object->IsObject());
    204     DOMData::handleWeakObject(DOMDataStore::DOMSVGObjectWithContextMap, v8::Persistent<v8::Object>::Cast(v8Object), domObject);
    205 }
    206 
    207 #endif  // ENABLE(SVG)
    208 
    209 } // namespace WebCore
    210