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
36namespace 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
85DOMDataStore::DOMDataStore(DOMData* domData)
86    : m_domNodeMap(0)
87    , m_domObjectMap(0)
88    , m_activeDomObjectMap(0)
89#if ENABLE(SVG)
90    , m_domSvgElementInstanceMap(0)
91#endif
92    , m_domData(domData)
93{
94    WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
95    DOMDataStore::allStores().append(this);
96}
97
98DOMDataStore::~DOMDataStore()
99{
100    WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
101    DOMDataStore::allStores().remove(DOMDataStore::allStores().find(this));
102}
103
104DOMDataList& DOMDataStore::allStores()
105{
106  DEFINE_STATIC_LOCAL(DOMDataList, staticDOMDataList, ());
107  return staticDOMDataList;
108}
109
110WTF::Mutex& DOMDataStore::allStoresMutex()
111{
112    DEFINE_STATIC_LOCAL(WTF::Mutex, staticDOMDataListMutex, ());
113    return staticDOMDataListMutex;
114}
115
116void* DOMDataStore::getDOMWrapperMap(DOMWrapperMapType type)
117{
118    switch (type) {
119    case DOMNodeMap:
120        return m_domNodeMap;
121    case DOMObjectMap:
122        return m_domObjectMap;
123    case ActiveDOMObjectMap:
124        return m_activeDomObjectMap;
125#if ENABLE(SVG)
126    case DOMSVGElementInstanceMap:
127        return m_domSvgElementInstanceMap;
128#endif
129    }
130
131    ASSERT_NOT_REACHED();
132    return 0;
133}
134
135// Called when the object is near death (not reachable from JS roots).
136// It is time to remove the entry from the table and dispose the handle.
137void DOMDataStore::weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
138{
139    v8::HandleScope scope;
140    ASSERT(v8Object->IsObject());
141    DOMData::handleWeakObject(DOMDataStore::DOMObjectMap, v8::Persistent<v8::Object>::Cast(v8Object), domObject);
142}
143
144void DOMDataStore::weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
145{
146    v8::HandleScope scope;
147    ASSERT(v8Object->IsObject());
148    DOMData::handleWeakObject(DOMDataStore::ActiveDOMObjectMap, v8::Persistent<v8::Object>::Cast(v8Object), domObject);
149}
150
151void DOMDataStore::weakNodeCallback(v8::Persistent<v8::Value> value, void* domObject)
152{
153    ASSERT(WTF::isMainThread());
154
155    Node* node = static_cast<Node*>(domObject);
156    // Node wrappers must be JS objects.
157    v8::Persistent<v8::Object> v8Object = v8::Persistent<v8::Object>::Cast(value);
158
159    WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
160    DOMDataList& list = DOMDataStore::allStores();
161    for (size_t i = 0; i < list.size(); ++i) {
162        DOMDataStore* store = list[i];
163        if (store->domNodeMap().removeIfPresent(node, v8Object)) {
164            ASSERT(store->domData()->owningThread() == WTF::currentThread());
165            node->deref(); // Nobody overrides Node::deref so it's safe
166            return; // There might be at most one wrapper for the node in world's maps
167        }
168    }
169
170    // If not found, it means map for the wrapper has been already destroyed, just dispose the
171    // handle and deref the object to fight memory leak.
172    v8Object.Dispose();
173    node->deref(); // Nobody overrides Node::deref so it's safe
174}
175
176#if ENABLE(SVG)
177
178void DOMDataStore::weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
179{
180    v8::HandleScope scope;
181    ASSERT(v8Object->IsObject());
182    DOMData::handleWeakObject(DOMDataStore::DOMSVGElementInstanceMap, v8::Persistent<v8::Object>::Cast(v8Object), static_cast<SVGElementInstance*>(domObject));
183}
184
185#endif  // ENABLE(SVG)
186
187} // namespace WebCore
188