Changeset 100307 in webkit
- Timestamp:
- Nov 15, 2011 12:13:53 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r100305 r100307 1 2011-11-15 Eugene Nalimov <enal@google.com> 2 3 Event listener for active DOM object that is also DOM node can be garbage collected prematurely. 4 https://bugs.webkit.org/show_bug.cgi?id=70421 and https://bugs.webkit.org/show_bug.cgi?id=66878 5 6 Reviewed by Adam Barth. 7 8 * media/audio-garbage-collect-expected.txt: Added. 9 * media/audio-garbage-collect.html: Added. 10 1 11 2011-11-15 Robert Hogan <robert@webkit.org> 2 12 -
trunk/Source/WebCore/ChangeLog
r100298 r100307 1 2011-11-15 Eugene Nalimov <enal@google.com> 2 3 Event listener for active DOM object that is also DOM node can be garbage collected prematurely. 4 https://bugs.webkit.org/show_bug.cgi?id=70421 5 6 Reviewed by Adam Barth. 7 8 Problem demonstrated itself when HTMLAudioElement was changed to become active DOM object. 9 Before that there were no DOM objects that simultaneously were nodes and active objects. 10 DOM object could be held in one of 3 maps -- node map, active objects map, and all other 11 objects map, and HTMLAudioElement should be in 2 maps simultaneously. When it was in the 12 active DOM objects map only, its event listener could be garbage collected, because special 13 code that groups listeners with wrappers could handle only wrappers for objects in the node 14 map. If we put HTMLAudioElement into nodes map, it would not be active DOM node, and can be 15 garbage collected prematurely itself (see https://bugs.webkit.org/show_bug.cgi?id=66878). 16 Fix is to introduce 4th map -- active nodes map, and change the code accordingly. 17 18 Test: media/audio-garbage-collect.html 19 20 * bindings/scripts/CodeGeneratorV8.pm: 21 (GenerateNamedConstructorCallback): 22 (GetDomMapFunction): 23 * bindings/v8/DOMDataStore.cpp: 24 (WebCore::DOMDataStore::DOMDataStore): 25 (WebCore::DOMDataStore::getDOMWrapperMap): 26 (WebCore::DOMDataStore::weakNodeCallback): 27 * bindings/v8/DOMDataStore.h: 28 (WebCore::DOMDataStore::activeDomNodeMap): 29 * bindings/v8/ScopedDOMDataStore.cpp: 30 (WebCore::ScopedDOMDataStore::ScopedDOMDataStore): 31 (WebCore::ScopedDOMDataStore::~ScopedDOMDataStore): 32 * bindings/v8/StaticDOMDataStore.cpp: 33 (WebCore::StaticDOMDataStore::StaticDOMDataStore): 34 * bindings/v8/StaticDOMDataStore.h: 35 * bindings/v8/V8DOMMap.cpp: 36 (WebCore::getActiveDOMNodeMap): 37 (WebCore::removeAllDOMObjects): 38 (WebCore::visitActiveDOMNodes): 39 * bindings/v8/V8DOMMap.h: 40 * bindings/v8/V8DOMWrapper.cpp: 41 (WebCore::V8DOMWrapper::setJSWrapperForDOMNode): 42 (WebCore::V8DOMWrapper::getWrapperSlow): 43 * bindings/v8/V8GCController.cpp: 44 (WebCore::GCPrologueSpecialCase): 45 (WebCore::void): 46 (WebCore::Node): 47 (WebCore::GCPrologueVisitor::visitDOMWrapper): 48 (WebCore::V8GCController::gcPrologue): 49 (WebCore::GCEpilogueHelper::GCEpilogueSpecialCase): 50 (WebCore::GCEpilogueVisitor::visitDOMWrapper): 51 (WebCore::V8GCController::gcEpilogue): 52 * dom/Node.h: 53 (WebCore::Node::isActiveNode): 54 * html/HTMLAudioElement.h: 55 (WebCore::HTMLAudioElement::isActiveNode): 56 1 57 2011-11-15 David Kilzer <ddkilzer@apple.com> 2 58 -
trunk/Source/WebCore/bindings/scripts/CodeGeneratorV8.pm
r100229 r100307 1778 1778 1779 1779 my $DOMObject = "DOMObject"; 1780 # A DOMObject that is an ActiveDOMObject and also a DOMNode should be treated as an ActiveDOMObject. 1781 if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { 1780 # A DOMObject that is an ActiveDOMObject and also a DOMNode should be treated as an DOMNode here. 1781 # setJSWrapperForDOMNode() will look if node is active and choose correct map to add node to. 1782 if (IsNodeSubType($dataNode)) { 1783 $DOMObject = "DOMNode"; 1784 } elsif ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { 1782 1785 $DOMObject = "ActiveDOMObject"; 1783 } elsif (IsNodeSubType($dataNode)) {1784 $DOMObject = "DOMNode";1785 1786 } 1786 1787 push(@implContent, <<END); … … 3106 3107 my $type = shift; 3107 3108 return "getDOMSVGElementInstanceMap()" if $type eq "SVGElementInstance"; 3109 return "getActiveDOMNodeMap()" if (IsNodeSubType($dataNode) && $dataNode->extendedAttributes->{"ActiveDOMObject"}); 3108 3110 return "getDOMNodeMap()" if (IsNodeSubType($dataNode)); 3109 3111 return "getActiveDOMObjectMap()" if $dataNode->extendedAttributes->{"ActiveDOMObject"}; -
trunk/Source/WebCore/bindings/v8/DOMDataStore.cpp
r95901 r100307 87 87 DOMDataStore::DOMDataStore() 88 88 : m_domNodeMap(0) 89 , m_activeDomNodeMap(0) 89 90 , m_domObjectMap(0) 90 91 , m_activeDomObjectMap(0) … … 109 110 case DOMNodeMap: 110 111 return m_domNodeMap; 112 case ActiveDOMNodeMap: 113 return m_activeDomNodeMap; 111 114 case DOMObjectMap: 112 115 return m_domObjectMap; … … 150 153 for (size_t i = 0; i < list.size(); ++i) { 151 154 DOMDataStore* store = list[i]; 152 if (store->domNodeMap().removeIfPresent(node, v8Object)) { 155 DOMNodeMapping& nodeMap = node->isActiveNode() ? store->activeDomNodeMap() : store->domNodeMap(); 156 if (nodeMap.removeIfPresent(node, v8Object)) { 153 157 node->deref(); // Nobody overrides Node::deref so it's safe 154 158 return; // There might be at most one wrapper for the node in world's maps -
trunk/Source/WebCore/bindings/v8/DOMDataStore.h
r95901 r100307 66 66 enum DOMWrapperMapType { 67 67 DOMNodeMap, 68 ActiveDOMNodeMap, 68 69 DOMObjectMap, 69 70 ActiveDOMObjectMap, … … 82 83 83 84 DOMNodeMapping& domNodeMap() { return *m_domNodeMap; } 85 DOMNodeMapping& activeDomNodeMap() { return *m_activeDomNodeMap; } 84 86 DOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; } 85 87 DOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjectMap; } … … 90 92 // Need by V8GCController. 91 93 static void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 94 static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 92 95 93 96 protected: 94 static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject);95 97 static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject); 96 98 #if ENABLE(SVG) … … 99 101 100 102 DOMNodeMapping* m_domNodeMap; 103 DOMNodeMapping* m_activeDomNodeMap; 101 104 DOMWrapperMap<void>* m_domObjectMap; 102 105 DOMWrapperMap<void>* m_activeDomObjectMap; -
trunk/Source/WebCore/bindings/v8/ScopedDOMDataStore.cpp
r95901 r100307 38 38 { 39 39 m_domNodeMap = new DOMWrapperMap<Node>(&DOMDataStore::weakNodeCallback); 40 m_activeDomNodeMap = new DOMWrapperMap<Node>(&DOMDataStore::weakNodeCallback); 40 41 m_domObjectMap = new DOMWrapperMap<void>(&DOMDataStore::weakDOMObjectCallback); 41 42 m_activeDomObjectMap = new DOMWrapperMap<void>(&DOMDataStore::weakActiveDOMObjectCallback); … … 48 49 { 49 50 delete m_domNodeMap; 51 delete m_activeDomNodeMap; 50 52 delete m_domObjectMap; 51 53 delete m_activeDomObjectMap; -
trunk/Source/WebCore/bindings/v8/StaticDOMDataStore.cpp
r95901 r100307 38 38 : DOMDataStore() 39 39 , m_staticDomNodeMap(&DOMDataStore::weakNodeCallback) 40 , m_staticActiveDomNodeMap(&DOMDataStore::weakNodeCallback) 40 41 , m_staticDomObjectMap(&DOMDataStore::weakDOMObjectCallback) 41 42 , m_staticActiveDomObjectMap(&DOMDataStore::weakActiveDOMObjectCallback) … … 45 46 { 46 47 m_domNodeMap = &m_staticDomNodeMap; 48 m_activeDomNodeMap = &m_staticActiveDomNodeMap; 47 49 m_domObjectMap = &m_staticDomObjectMap; 48 50 m_activeDomObjectMap = &m_staticActiveDomObjectMap; -
trunk/Source/WebCore/bindings/v8/StaticDOMDataStore.h
r95901 r100307 52 52 private: 53 53 IntrusiveDOMWrapperMap m_staticDomNodeMap; 54 IntrusiveDOMWrapperMap m_staticActiveDomNodeMap; 54 55 DOMWrapperMap<void> m_staticDomObjectMap; 55 56 DOMWrapperMap<void> m_staticActiveDomObjectMap; -
trunk/Source/WebCore/bindings/v8/V8DOMMap.cpp
r95901 r100307 65 65 } 66 66 67 DOMNodeMapping& getActiveDOMNodeMap() 68 { 69 return getDOMDataStore().activeDomNodeMap(); 70 } 71 67 72 DOMWrapperMap<void>& getDOMObjectMap() 68 73 { … … 95 100 DOMData::removeObjectsFromWrapperMap<Node>(&store, store.domNodeMap()); 96 101 102 // Remove all active DOM nodes. 103 DOMData::removeObjectsFromWrapperMap<Node>(&store, store.activeDomNodeMap()); 104 97 105 #if ENABLE(SVG) 98 106 // Remove all SVG element instances in the wrapper map. … … 117 125 118 126 store->domNodeMap().visit(store, visitor); 127 } 128 } 129 130 void visitActiveDOMNodes(DOMWrapperMap<Node>::Visitor* visitor) 131 { 132 v8::HandleScope scope; 133 134 DOMDataList& list = DOMDataStore::allStores(); 135 for (size_t i = 0; i < list.size(); ++i) { 136 DOMDataStore* store = list[i]; 137 138 store->activeDomNodeMap().visit(store, visitor); 119 139 } 120 140 } -
trunk/Source/WebCore/bindings/v8/V8DOMMap.h
r95901 r100307 159 159 // A map from DOM node to its JS wrapper. 160 160 DOMNodeMapping& getDOMNodeMap(); 161 DOMNodeMapping& getActiveDOMNodeMap(); 161 162 void visitDOMNodes(DOMWrapperMap<Node>::Visitor*); 163 void visitActiveDOMNodes(DOMWrapperMap<Node>::Visitor*); 162 164 163 165 // A map from a DOM object (non-node) to its JS wrapper. This map does not contain the DOM objects which can have pending activity (active dom objects). -
trunk/Source/WebCore/bindings/v8/V8DOMWrapper.cpp
r98426 r100307 93 93 { 94 94 ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); 95 getDOMNodeMap().set(node, wrapper); 95 if (node->isActiveNode()) 96 getActiveDOMNodeMap().set(node, wrapper); 97 else 98 getDOMNodeMap().set(node, wrapper); 96 99 } 97 100 … … 296 299 return *wrapper; 297 300 } 298 DOMNodeMapping& domNodeMap = context->world()->domDataStore()->domNodeMap(); 301 DOMDataStore* store = context->world()->domDataStore(); 302 DOMNodeMapping& domNodeMap = node->isActiveNode() ? store->activeDomNodeMap() : store->domNodeMap(); 299 303 return domNodeMap.get(node); 300 304 } -
trunk/Source/WebCore/bindings/v8/V8GCController.cpp
r97557 r100307 144 144 #endif // NDEBUG 145 145 146 class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor { 147 public: 148 void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper) 149 { 150 WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); 151 146 class SpecialCasePrologueObjectHandler { 147 public: 148 static bool process(void* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo) 149 { 152 150 // Additional handling of message port ensuring that entangled ports also 153 151 // have their wrappers entangled. This should ideally be handled when the … … 162 160 if (port1->isEntangled() || port1->hasPendingActivity()) 163 161 wrapper.ClearWeak(); 164 } else { 162 return true; 163 } 164 return false; 165 } 166 }; 167 168 class SpecialCasePrologueNodeHandler { 169 public: 170 static bool process(Node* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo) 171 { 172 UNUSED_PARAM(object); 173 UNUSED_PARAM(wrapper); 174 UNUSED_PARAM(typeInfo); 175 return false; 176 } 177 }; 178 179 template<typename T, typename S> 180 class GCPrologueVisitor : public DOMWrapperMap<T>::Visitor { 181 public: 182 void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper) 183 { 184 WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); 185 186 if (!S::process(object, wrapper, typeInfo)) { 165 187 ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper); 166 188 if (activeDOMObject && activeDOMObject->hasPendingActivity()) … … 374 396 // Run through all objects with possible pending activity making their 375 397 // wrappers non weak if there is pending activity. 376 GCPrologueVisitor prologueVisitor; 377 visitActiveDOMObjects(&prologueVisitor); 398 GCPrologueVisitor<void, SpecialCasePrologueObjectHandler> prologueObjectVisitor; 399 visitActiveDOMObjects(&prologueObjectVisitor); 400 GCPrologueVisitor<Node, SpecialCasePrologueNodeHandler> prologueNodeVisitor; 401 visitActiveDOMNodes(&prologueNodeVisitor); 378 402 379 403 // Create object groups. 380 404 GrouperVisitor grouperVisitor; 381 405 visitDOMNodes(&grouperVisitor); 406 visitActiveDOMNodes(&grouperVisitor); 382 407 visitDOMObjects(&grouperVisitor); 383 408 grouperVisitor.applyGrouping(); … … 388 413 } 389 414 390 class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor { 391 public: 392 void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper) 393 { 394 WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); 415 class SpecialCaseEpilogueObjectHandler { 416 public: 417 static bool process(void* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo) 418 { 395 419 if (V8MessagePort::info.equals(typeInfo)) { 396 420 MessagePort* port1 = static_cast<MessagePort*>(object); … … 400 424 if ((!wrapper.IsWeak() && !wrapper.IsNearDeath()) || port1->hasPendingActivity()) 401 425 wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback); 402 } else { 426 return true; 427 } 428 return false; 429 } 430 }; 431 432 class SpecialCaseEpilogueNodeHandler { 433 public: 434 static bool process(Node* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo) 435 { 436 UNUSED_PARAM(object); 437 UNUSED_PARAM(wrapper); 438 UNUSED_PARAM(typeInfo); 439 return false; 440 } 441 }; 442 443 template<typename T, typename S, v8::WeakReferenceCallback callback> 444 class GCEpilogueVisitor : public DOMWrapperMap<T>::Visitor { 445 public: 446 void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper) 447 { 448 WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); 449 if (!S::process(object, wrapper, typeInfo)) { 403 450 ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper); 404 451 if (activeDOMObject && activeDOMObject->hasPendingActivity()) { … … 409 456 // the main base class of the object's class) and pointer 410 457 // identity is required by DOM map functions. 411 wrapper.MakeWeak(object, &DOMDataStore::weakActiveDOMObjectCallback);458 wrapper.MakeWeak(object, callback); 412 459 } 413 460 } … … 445 492 // Run through all objects with pending activity making their wrappers weak 446 493 // again. 447 GCEpilogueVisitor epilogueVisitor; 448 visitActiveDOMObjects(&epilogueVisitor); 494 GCEpilogueVisitor<void, SpecialCaseEpilogueObjectHandler, &DOMDataStore::weakActiveDOMObjectCallback> epilogueObjectVisitor; 495 visitActiveDOMObjects(&epilogueObjectVisitor); 496 GCEpilogueVisitor<Node, SpecialCaseEpilogueNodeHandler, &DOMDataStore::weakNodeCallback> epilogueNodeVisitor; 497 visitActiveDOMNodes(&epilogueNodeVisitor); 449 498 450 499 workingSetEstimateMB = getActualMemoryUsageInMB(); -
trunk/Source/WebCore/dom/Node.h
r99992 r100307 191 191 Node* lastDescendant() const; 192 192 Node* firstDescendant() const; 193 194 virtual bool isActiveNode() const { return false; } 193 195 194 196 // Other methods (not part of DOM) -
trunk/Source/WebCore/html/HTMLAudioElement.h
r98005 r100307 43 43 virtual bool hasPendingActivity() const { return isPlaying() || HTMLMediaElement::hasPendingActivity(); } 44 44 45 virtual bool isActiveNode() const { return true; } 46 45 47 private: 46 48 HTMLAudioElement(const QualifiedName&, Document*);
Note: See TracChangeset
for help on using the changeset viewer.