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
DOMDataStore(DOMData * domData)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
~DOMDataStore()99 DOMDataStore::~DOMDataStore()
100 {
101 WTF::MutexLocker locker(DOMDataStore::allStoresMutex());
102 DOMDataStore::allStores().remove(DOMDataStore::allStores().find(this));
103 }
104
allStores()105 DOMDataList& DOMDataStore::allStores()
106 {
107 DEFINE_STATIC_LOCAL(DOMDataList, staticDOMDataList, ());
108 return staticDOMDataList;
109 }
110
allStoresMutex()111 WTF::Mutex& DOMDataStore::allStoresMutex()
112 {
113 DEFINE_STATIC_LOCAL(WTF::Mutex, staticDOMDataListMutex, ());
114 return staticDOMDataListMutex;
115 }
116
forgetDelayedObject(DOMData * domData,void * object)117 void DOMDataStore::forgetDelayedObject(DOMData* domData, void* object)
118 {
119 domData->forgetDelayedObject(object);
120 }
121
getDOMWrapperMap(DOMWrapperMapType type)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.
weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object,void * domObject)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
weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object,void * domObject)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
weakNodeCallback(v8::Persistent<v8::Value> v8Object,void * domObject)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
removeIfPresent(Node * obj,v8::Persistent<v8::Data> value)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
weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object,void * domObject)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
weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object,void * domObject)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