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 #ifndef DOMDataStore_h 32 #define DOMDataStore_h 33 34 #include "DOMObjectsInclude.h" 35 #include "V8Node.h" 36 37 #include <v8.h> 38 #include <wtf/HashMap.h> 39 #include <wtf/MainThread.h> 40 #include <wtf/Noncopyable.h> 41 #include <wtf/OwnPtr.h> 42 #include <wtf/StdLibExtras.h> 43 #include <wtf/Threading.h> 44 #include <wtf/ThreadSpecific.h> 45 #include <wtf/Vector.h> 46 47 namespace WebCore { 48 49 class DOMData; 50 51 typedef WTF::Vector<DOMDataStore*> DOMDataList; 52 53 template <class T, int CHUNK_SIZE, class Traits> 54 class ChunkedTable { 55 public: 56 ChunkedTable() : m_chunks(0), m_current(0), m_last(0) { } 57 58 T* add(T element) 59 { 60 if (m_current == m_last) { 61 m_chunks = new Chunk(m_chunks); 62 m_current = m_chunks->m_entries; 63 m_last = m_current + CHUNK_SIZE; 64 } 65 ASSERT((m_chunks->m_entries <= m_current) && (m_current < m_last)); 66 T* p = m_current++; 67 *p = element; 68 return p; 69 } 70 71 void remove(T* element) 72 { 73 ASSERT(element); 74 ASSERT(m_current > m_chunks->m_entries); 75 m_current--; 76 if (element != m_current) 77 Traits::move(element, m_current); 78 if (m_current == m_chunks->m_entries) { 79 Chunk* toDelete = m_chunks; 80 m_chunks = toDelete->m_previous; 81 m_current = m_last = m_chunks ? m_chunks->m_entries + CHUNK_SIZE : 0; 82 delete toDelete; 83 } 84 ASSERT(!m_chunks || ((m_chunks->m_entries < m_current) && (m_current <= m_last))); 85 } 86 87 void clear() 88 { 89 if (!m_chunks) 90 return; 91 92 clearEntries(m_chunks->m_entries, m_current); 93 Chunk* last = m_chunks; 94 while (true) { 95 Chunk* previous = last->m_previous; 96 if (!previous) 97 break; 98 delete last; 99 clearEntries(previous->m_entries, previous->m_entries + CHUNK_SIZE); 100 last = previous; 101 } 102 103 m_chunks = last; 104 m_current = m_chunks->m_entries; 105 m_last = m_current + CHUNK_SIZE; 106 } 107 108 void visit(typename Traits::Visitor* visitor) 109 { 110 if (!m_chunks) 111 return; 112 113 visitEntries(m_chunks->m_entries, m_current, visitor); 114 for (Chunk* chunk = m_chunks->m_previous; chunk; chunk = chunk->m_previous) 115 visitEntries(chunk->m_entries, chunk->m_entries + CHUNK_SIZE, visitor); 116 } 117 118 private: 119 struct Chunk { 120 explicit Chunk(Chunk* previous) : m_previous(previous) { } 121 Chunk* const m_previous; 122 T m_entries[CHUNK_SIZE]; 123 }; 124 125 static void clearEntries(T* first, T* last) 126 { 127 for (T* entry = first; entry < last; entry++) 128 Traits::clear(entry); 129 } 130 131 static void visitEntries(T* first, T* last, typename Traits::Visitor* visitor) 132 { 133 for (T* entry = first; entry < last; entry++) 134 Traits::visit(entry, visitor); 135 } 136 137 Chunk* m_chunks; 138 T* m_current; 139 T* m_last; 140 }; 141 142 // DOMDataStore 143 // 144 // DOMDataStore is the backing store that holds the maps between DOM objects 145 // and JavaScript objects. In general, each thread can have multiple backing 146 // stores, one per isolated world. 147 // 148 // This class doesn't manage the lifetime of the store. The data store 149 // lifetime is managed by subclasses. 150 // 151 class DOMDataStore : public Noncopyable { 152 public: 153 enum DOMWrapperMapType { 154 DOMNodeMap, 155 DOMObjectMap, 156 ActiveDOMObjectMap, 157 #if ENABLE(SVG) 158 DOMSVGElementInstanceMap, 159 DOMSVGObjectWithContextMap 160 #endif 161 }; 162 163 template <class KeyType> 164 class InternalDOMWrapperMap : public DOMWrapperMap<KeyType> { 165 public: 166 InternalDOMWrapperMap(DOMData* domData, v8::WeakReferenceCallback callback) 167 : DOMWrapperMap<KeyType>(callback), m_domData(domData) { } 168 169 virtual void forget(KeyType* object) 170 { 171 DOMWrapperMap<KeyType>::forget(object); 172 forgetDelayedObject(m_domData, object); 173 } 174 175 private: 176 DOMData* m_domData; 177 }; 178 179 class IntrusiveDOMWrapperMap : public AbstractWeakReferenceMap<Node, v8::Object> { 180 public: 181 IntrusiveDOMWrapperMap(v8::WeakReferenceCallback callback) 182 : AbstractWeakReferenceMap<Node, v8::Object>(callback) { } 183 184 virtual v8::Persistent<v8::Object> get(Node* obj) 185 { 186 v8::Persistent<v8::Object>* wrapper = obj->wrapper(); 187 return wrapper ? *wrapper : v8::Persistent<v8::Object>(); 188 } 189 190 virtual void set(Node* obj, v8::Persistent<v8::Object> wrapper) 191 { 192 ASSERT(obj); 193 ASSERT(!obj->wrapper()); 194 v8::Persistent<v8::Object>* entry = m_table.add(wrapper); 195 obj->setWrapper(entry); 196 wrapper.MakeWeak(obj, weakReferenceCallback()); 197 } 198 199 virtual bool contains(Node* obj) 200 { 201 return obj->wrapper(); 202 } 203 204 virtual void visit(Visitor* visitor) 205 { 206 m_table.visit(visitor); 207 } 208 209 virtual bool removeIfPresent(Node* key, v8::Persistent<v8::Data> value); 210 211 virtual void clear() 212 { 213 m_table.clear(); 214 } 215 216 private: 217 static int const numberOfEntries = (1 << 10) - 1; 218 219 struct ChunkedTableTraits { 220 typedef IntrusiveDOMWrapperMap::Visitor Visitor; 221 222 static void move(v8::Persistent<v8::Object>* target, v8::Persistent<v8::Object>* source) 223 { 224 *target = *source; 225 Node* node = V8Node::toNative(*target); 226 ASSERT(node); 227 node->setWrapper(target); 228 } 229 230 static void clear(v8::Persistent<v8::Object>* entry) 231 { 232 Node* node = V8Node::toNative(*entry); 233 ASSERT(node->wrapper() == entry); 234 235 node->clearWrapper(); 236 entry->Dispose(); 237 } 238 239 static void visit(v8::Persistent<v8::Object>* entry, Visitor* visitor) 240 { 241 Node* node = V8Node::toNative(*entry); 242 ASSERT(node->wrapper() == entry); 243 244 visitor->visitDOMWrapper(node, *entry); 245 } 246 }; 247 248 typedef ChunkedTable<v8::Persistent<v8::Object>, numberOfEntries, ChunkedTableTraits> Table; 249 Table m_table; 250 }; 251 252 DOMDataStore(DOMData*); 253 virtual ~DOMDataStore(); 254 255 // A list of all DOMDataStore objects. Traversed during GC to find a thread-specific map that 256 // contains the object - so we can schedule the object to be deleted on the thread which created it. 257 static DOMDataList& allStores(); 258 // Mutex to protect against concurrent access of DOMDataList. 259 static WTF::Mutex& allStoresMutex(); 260 261 // Helper function to avoid circular includes. 262 static void forgetDelayedObject(DOMData*, void* object); 263 264 DOMData* domData() const { return m_domData; } 265 266 void* getDOMWrapperMap(DOMWrapperMapType); 267 268 DOMNodeMapping& domNodeMap() { return *m_domNodeMap; } 269 InternalDOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; } 270 InternalDOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjectMap; } 271 #if ENABLE(SVG) 272 InternalDOMWrapperMap<SVGElementInstance>& domSvgElementInstanceMap() { return *m_domSvgElementInstanceMap; } 273 InternalDOMWrapperMap<void>& domSvgObjectWithContextMap() { return *m_domSvgObjectWithContextMap; } 274 #endif 275 276 // Need by V8GCController. 277 static void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 278 279 protected: 280 static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 281 static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 282 #if ENABLE(SVG) 283 static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 284 // SVG non-node elements may have a reference to a context node which should be notified when the element is change. 285 static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 286 #endif 287 288 DOMNodeMapping* m_domNodeMap; 289 InternalDOMWrapperMap<void>* m_domObjectMap; 290 InternalDOMWrapperMap<void>* m_activeDomObjectMap; 291 #if ENABLE(SVG) 292 InternalDOMWrapperMap<SVGElementInstance>* m_domSvgElementInstanceMap; 293 InternalDOMWrapperMap<void>* m_domSvgObjectWithContextMap; 294 #endif 295 296 private: 297 // A back-pointer to the DOMData to which we belong. 298 DOMData* m_domData; 299 }; 300 301 } // namespace WebCore 302 303 #endif // DOMDataStore_h 304