Source/WebCore: Event listener for active DOM object that is also DOM node can be...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Nov 2011 20:13:53 +0000 (20:13 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Nov 2011 20:13:53 +0000 (20:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=70421

Patch by Eugene Nalimov <enal@google.com> on 2011-11-15
Reviewed by Adam Barth.

Problem demonstrated itself when HTMLAudioElement was changed to become active DOM object.
Before that there were no DOM objects that simultaneously were nodes and active objects.
DOM object could be held in one of 3 maps -- node map, active objects map, and all other
objects map, and HTMLAudioElement should be in 2 maps simultaneously. When it was in the
active DOM objects map only, its event listener could be garbage collected, because special
code that groups listeners with wrappers could handle only wrappers for objects in the node
map. If we put HTMLAudioElement into nodes map, it would not be active DOM node, and can be
garbage collected prematurely itself (see https://bugs.webkit.org/show_bug.cgi?id=66878).
Fix is to introduce 4th map -- active nodes map, and change the code accordingly.

Test: media/audio-garbage-collect.html

* bindings/scripts/CodeGeneratorV8.pm:
(GenerateNamedConstructorCallback):
(GetDomMapFunction):
* bindings/v8/DOMDataStore.cpp:
(WebCore::DOMDataStore::DOMDataStore):
(WebCore::DOMDataStore::getDOMWrapperMap):
(WebCore::DOMDataStore::weakNodeCallback):
* bindings/v8/DOMDataStore.h:
(WebCore::DOMDataStore::activeDomNodeMap):
* bindings/v8/ScopedDOMDataStore.cpp:
(WebCore::ScopedDOMDataStore::ScopedDOMDataStore):
(WebCore::ScopedDOMDataStore::~ScopedDOMDataStore):
* bindings/v8/StaticDOMDataStore.cpp:
(WebCore::StaticDOMDataStore::StaticDOMDataStore):
* bindings/v8/StaticDOMDataStore.h:
* bindings/v8/V8DOMMap.cpp:
(WebCore::getActiveDOMNodeMap):
(WebCore::removeAllDOMObjects):
(WebCore::visitActiveDOMNodes):
* bindings/v8/V8DOMMap.h:
* bindings/v8/V8DOMWrapper.cpp:
(WebCore::V8DOMWrapper::setJSWrapperForDOMNode):
(WebCore::V8DOMWrapper::getWrapperSlow):
* bindings/v8/V8GCController.cpp:
(WebCore::GCPrologueSpecialCase):
(WebCore::void):
(WebCore::Node):
(WebCore::GCPrologueVisitor::visitDOMWrapper):
(WebCore::V8GCController::gcPrologue):
(WebCore::GCEpilogueHelper::GCEpilogueSpecialCase):
(WebCore::GCEpilogueVisitor::visitDOMWrapper):
(WebCore::V8GCController::gcEpilogue):
* dom/Node.h:
(WebCore::Node::isActiveNode):
* html/HTMLAudioElement.h:
(WebCore::HTMLAudioElement::isActiveNode):

LayoutTests: Event listener for active DOM object that is also DOM node can be garbage collected prematurely.
https://bugs.webkit.org/show_bug.cgi?id=70421 and https://bugs.webkit.org/show_bug.cgi?id=66878

Patch by Eugene Nalimov <enal@google.com> on 2011-11-15
Reviewed by Adam Barth.

* media/audio-garbage-collect-expected.txt: Added.
* media/audio-garbage-collect.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@100307 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/media/audio-garbage-collect-expected.txt [new file with mode: 0644]
LayoutTests/media/audio-garbage-collect.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/bindings/scripts/CodeGeneratorV8.pm
Source/WebCore/bindings/v8/DOMDataStore.cpp
Source/WebCore/bindings/v8/DOMDataStore.h
Source/WebCore/bindings/v8/ScopedDOMDataStore.cpp
Source/WebCore/bindings/v8/StaticDOMDataStore.cpp
Source/WebCore/bindings/v8/StaticDOMDataStore.h
Source/WebCore/bindings/v8/V8DOMMap.cpp
Source/WebCore/bindings/v8/V8DOMMap.h
Source/WebCore/bindings/v8/V8DOMWrapper.cpp
Source/WebCore/bindings/v8/V8GCController.cpp
Source/WebCore/dom/Node.h
Source/WebCore/html/HTMLAudioElement.h

index 4516b7aa6048ffe93f6ffe54929d1964aac329ed..d285d3501117523f504142f5218fd3e0052b100c 100644 (file)
@@ -1,3 +1,13 @@
+2011-11-15  Eugene Nalimov  <enal@google.com>
+
+        Event listener for active DOM object that is also DOM node can be garbage collected prematurely.
+        https://bugs.webkit.org/show_bug.cgi?id=70421 and https://bugs.webkit.org/show_bug.cgi?id=66878
+
+        Reviewed by Adam Barth.
+
+        * media/audio-garbage-collect-expected.txt: Added.
+        * media/audio-garbage-collect.html: Added.
+
 2011-11-15  Robert Hogan  <robert@webkit.org>
 
         Mac results for r100177
diff --git a/LayoutTests/media/audio-garbage-collect-expected.txt b/LayoutTests/media/audio-garbage-collect-expected.txt
new file mode 100644 (file)
index 0000000..99d9ebc
--- /dev/null
@@ -0,0 +1,11 @@
+Tests that we don't garbage collect playing audio object or event listener.
+
+According to http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html,
+"4.8.10.8 Playing the media resource",
+"Media elements must not stop playing just because all references to them have been removed; only once a media element is in a state where no further audio could ever be played by that element may the element be garbage collected."
+
+(see https://bugs.webkit.org/show_bug.cgi?id=66878, https://bugs.webkit.org/show_bug.cgi?id=70421, and http://crbug.com/62604 for more details).
+
+PASS
+
+
diff --git a/LayoutTests/media/audio-garbage-collect.html b/LayoutTests/media/audio-garbage-collect.html
new file mode 100644 (file)
index 0000000..7218f60
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+
+<html>
+<body>
+
+<p>Tests that we don't garbage collect playing audio object or event listener.</p>
+<p>According to http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html,<br />
+"4.8.10.8 Playing the media resource",<br />
+"Media elements must not stop playing just because all references to them have
+been removed; only once a media element is in a state where no further audio
+could ever be played by that element may the element be garbage collected."<br /><br />
+(see https://bugs.webkit.org/show_bug.cgi?id=66878, https://bugs.webkit.org/show_bug.cgi?id=70421, and http://crbug.com/62604 for more details).</p>
+<p id="result">
+FAIL: Test either still running or stopped prematurely.
+</p>
+
+<script src=../resources/gc.js></script>
+<script src=media-file.js></script>
+<script src=video-test.js></script>
+<script type="text/javascript">
+
+var num_players = 4;
+var play_times = 5;
+
+function finish() {
+    document.getElementById("result").innerText = "PASS";
+    if (window.layoutTestController) {
+        layoutTestController.notifyDone();
+    }
+}
+
+function start() {
+    var num_played = 0;
+    var audioFile = findMediaFile("audio", "content/silence");
+    var a = new Audio(audioFile);
+    a.addEventListener('ended', function() {
+        num_played ++;
+        if (num_played < play_times) {
+            a.currentTime = a.duration - 0.35;
+            a.play();
+            if (num_played == play_times - 1) {
+                a = null;
+                gc();
+            }
+        } else {
+            num_players --;
+            if (num_players == 0)
+                start();
+            else
+                finish();
+        }
+    });
+    a.addEventListener('canplaythrough', function() {
+        a.currentTime = a.duration - 0.35;
+        a.play();
+    });
+}
+
+start();
+
+</script>
+</body>
+</html>
index 3ba7b6fdafeec2b4cce7367671d19830d21bda1b..55839e273ec53efc74229ef43f74885dc7f1de77 100644 (file)
@@ -1,3 +1,59 @@
+2011-11-15  Eugene Nalimov  <enal@google.com>
+
+        Event listener for active DOM object that is also DOM node can be garbage collected prematurely.
+        https://bugs.webkit.org/show_bug.cgi?id=70421 
+
+        Reviewed by Adam Barth.
+
+        Problem demonstrated itself when HTMLAudioElement was changed to become active DOM object.
+        Before that there were no DOM objects that simultaneously were nodes and active objects.
+        DOM object could be held in one of 3 maps -- node map, active objects map, and all other
+        objects map, and HTMLAudioElement should be in 2 maps simultaneously. When it was in the
+        active DOM objects map only, its event listener could be garbage collected, because special
+        code that groups listeners with wrappers could handle only wrappers for objects in the node
+        map. If we put HTMLAudioElement into nodes map, it would not be active DOM node, and can be
+        garbage collected prematurely itself (see https://bugs.webkit.org/show_bug.cgi?id=66878).
+        Fix is to introduce 4th map -- active nodes map, and change the code accordingly.
+
+        Test: media/audio-garbage-collect.html
+
+        * bindings/scripts/CodeGeneratorV8.pm:
+        (GenerateNamedConstructorCallback):
+        (GetDomMapFunction):
+        * bindings/v8/DOMDataStore.cpp:
+        (WebCore::DOMDataStore::DOMDataStore):
+        (WebCore::DOMDataStore::getDOMWrapperMap):
+        (WebCore::DOMDataStore::weakNodeCallback):
+        * bindings/v8/DOMDataStore.h:
+        (WebCore::DOMDataStore::activeDomNodeMap):
+        * bindings/v8/ScopedDOMDataStore.cpp:
+        (WebCore::ScopedDOMDataStore::ScopedDOMDataStore):
+        (WebCore::ScopedDOMDataStore::~ScopedDOMDataStore):
+        * bindings/v8/StaticDOMDataStore.cpp:
+        (WebCore::StaticDOMDataStore::StaticDOMDataStore):
+        * bindings/v8/StaticDOMDataStore.h:
+        * bindings/v8/V8DOMMap.cpp:
+        (WebCore::getActiveDOMNodeMap):
+        (WebCore::removeAllDOMObjects):
+        (WebCore::visitActiveDOMNodes):
+        * bindings/v8/V8DOMMap.h:
+        * bindings/v8/V8DOMWrapper.cpp:
+        (WebCore::V8DOMWrapper::setJSWrapperForDOMNode):
+        (WebCore::V8DOMWrapper::getWrapperSlow):
+        * bindings/v8/V8GCController.cpp:
+        (WebCore::GCPrologueSpecialCase):
+        (WebCore::void):
+        (WebCore::Node):
+        (WebCore::GCPrologueVisitor::visitDOMWrapper):
+        (WebCore::V8GCController::gcPrologue):
+        (WebCore::GCEpilogueHelper::GCEpilogueSpecialCase):
+        (WebCore::GCEpilogueVisitor::visitDOMWrapper):
+        (WebCore::V8GCController::gcEpilogue):
+        * dom/Node.h:
+        (WebCore::Node::isActiveNode):
+        * html/HTMLAudioElement.h:
+        (WebCore::HTMLAudioElement::isActiveNode):
+
 2011-11-15  David Kilzer  <ddkilzer@apple.com>
 
         Remove useless const modifier from KURL::init
index c80523f84799358351edee2fda7d3deeacc3c844..80a8babda2a559587eef09be0009c35984d4eed7 100644 (file)
@@ -1777,11 +1777,12 @@ END
     }
 
     my $DOMObject = "DOMObject";
-    # A DOMObject that is an ActiveDOMObject and also a DOMNode should be treated as an ActiveDOMObject.
-    if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) {
-        $DOMObject = "ActiveDOMObject";
-    } elsif (IsNodeSubType($dataNode)) {
+    # A DOMObject that is an ActiveDOMObject and also a DOMNode should be treated as an DOMNode here.
+    # setJSWrapperForDOMNode() will look if node is active and choose correct map to add node to.
+    if (IsNodeSubType($dataNode)) {
         $DOMObject = "DOMNode";
+    } elsif ($dataNode->extendedAttributes->{"ActiveDOMObject"}) {
+        $DOMObject = "ActiveDOMObject";
     }
     push(@implContent, <<END);
 
@@ -3105,6 +3106,7 @@ sub GetDomMapFunction
     my $dataNode = shift;
     my $type = shift;
     return "getDOMSVGElementInstanceMap()" if $type eq "SVGElementInstance";
+    return "getActiveDOMNodeMap()" if (IsNodeSubType($dataNode) && $dataNode->extendedAttributes->{"ActiveDOMObject"});
     return "getDOMNodeMap()" if (IsNodeSubType($dataNode));
     return "getActiveDOMObjectMap()" if $dataNode->extendedAttributes->{"ActiveDOMObject"};
     return "getDOMObjectMap()";
index 7b84e0adfffc1b369fd05909172a270411be1d0c..f421f69057b6f6312be60c48a1a7a40784a14ccc 100644 (file)
@@ -86,6 +86,7 @@ namespace WebCore {
 
 DOMDataStore::DOMDataStore()
     : m_domNodeMap(0)
+    , m_activeDomNodeMap(0)
     , m_domObjectMap(0)
     , m_activeDomObjectMap(0)
 #if ENABLE(SVG)
@@ -108,6 +109,8 @@ void* DOMDataStore::getDOMWrapperMap(DOMWrapperMapType type)
     switch (type) {
     case DOMNodeMap:
         return m_domNodeMap;
+    case ActiveDOMNodeMap:
+        return m_activeDomNodeMap;
     case DOMObjectMap:
         return m_domObjectMap;
     case ActiveDOMObjectMap:
@@ -149,7 +152,8 @@ void DOMDataStore::weakNodeCallback(v8::Persistent<v8::Value> value, void* domOb
     DOMDataList& list = DOMDataStore::allStores();
     for (size_t i = 0; i < list.size(); ++i) {
         DOMDataStore* store = list[i];
-        if (store->domNodeMap().removeIfPresent(node, v8Object)) {
+        DOMNodeMapping& nodeMap = node->isActiveNode() ? store->activeDomNodeMap() : store->domNodeMap();
+        if (nodeMap.removeIfPresent(node, v8Object)) {
             node->deref(); // Nobody overrides Node::deref so it's safe
             return; // There might be at most one wrapper for the node in world's maps
         }
index ebcc300be651c0e06a7e515c1b2a428b61e3ac9a..c57f8b7298fcc5eba2b2f2281d054933aa9d4d6b 100644 (file)
@@ -65,6 +65,7 @@ namespace WebCore {
     public:
         enum DOMWrapperMapType {
             DOMNodeMap,
+            ActiveDOMNodeMap,
             DOMObjectMap,
             ActiveDOMObjectMap,
 #if ENABLE(SVG)
@@ -81,6 +82,7 @@ namespace WebCore {
         void* getDOMWrapperMap(DOMWrapperMapType);
 
         DOMNodeMapping& domNodeMap() { return *m_domNodeMap; }
+        DOMNodeMapping& activeDomNodeMap() { return *m_activeDomNodeMap; }
         DOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; }
         DOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjectMap; }
 #if ENABLE(SVG)
@@ -89,15 +91,16 @@ namespace WebCore {
 
         // Need by V8GCController.
         static void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
+        static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
 
     protected:
-        static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
         static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
 #if ENABLE(SVG)
         static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
 #endif
         
         DOMNodeMapping* m_domNodeMap;
+        DOMNodeMapping* m_activeDomNodeMap;
         DOMWrapperMap<void>* m_domObjectMap;
         DOMWrapperMap<void>* m_activeDomObjectMap;
 #if ENABLE(SVG)
index db23eeadc87297ef87692c4755a183f37bdf1ab5..666d0f7b78372e530c48fe5a1ec1abcf08b357dd 100644 (file)
@@ -37,6 +37,7 @@ ScopedDOMDataStore::ScopedDOMDataStore()
     : DOMDataStore()
 {
     m_domNodeMap = new DOMWrapperMap<Node>(&DOMDataStore::weakNodeCallback);
+    m_activeDomNodeMap = new DOMWrapperMap<Node>(&DOMDataStore::weakNodeCallback);
     m_domObjectMap = new DOMWrapperMap<void>(&DOMDataStore::weakDOMObjectCallback);
     m_activeDomObjectMap = new DOMWrapperMap<void>(&DOMDataStore::weakActiveDOMObjectCallback);
 #if ENABLE(SVG)
@@ -47,6 +48,7 @@ ScopedDOMDataStore::ScopedDOMDataStore()
 ScopedDOMDataStore::~ScopedDOMDataStore()
 {
     delete m_domNodeMap;
+    delete m_activeDomNodeMap;
     delete m_domObjectMap;
     delete m_activeDomObjectMap;
 #if ENABLE(SVG)
index da4e9107d966b049a2639a3807b4746a57170630..8eb630d88ab0a26cf0c0183e0be15a5efecc7d2a 100644 (file)
@@ -37,6 +37,7 @@ namespace WebCore {
 StaticDOMDataStore::StaticDOMDataStore()
     : DOMDataStore()
     , m_staticDomNodeMap(&DOMDataStore::weakNodeCallback)
+    , m_staticActiveDomNodeMap(&DOMDataStore::weakNodeCallback)
     , m_staticDomObjectMap(&DOMDataStore::weakDOMObjectCallback)
     , m_staticActiveDomObjectMap(&DOMDataStore::weakActiveDOMObjectCallback)
 #if ENABLE(SVG)
@@ -44,6 +45,7 @@ StaticDOMDataStore::StaticDOMDataStore()
 #endif
 {
     m_domNodeMap = &m_staticDomNodeMap;
+    m_activeDomNodeMap = &m_staticActiveDomNodeMap;
     m_domObjectMap = &m_staticDomObjectMap;
     m_activeDomObjectMap = &m_staticActiveDomObjectMap;
 #if ENABLE(SVG)
index ce2c8f86c9952a2d42be9fa0a4cfd0a5863beb08..6c65d44fd1dc9b41e128be8e9996580bd7966ec6 100644 (file)
@@ -51,6 +51,7 @@ public:
 
 private:
     IntrusiveDOMWrapperMap m_staticDomNodeMap;
+    IntrusiveDOMWrapperMap m_staticActiveDomNodeMap;
     DOMWrapperMap<void> m_staticDomObjectMap;
     DOMWrapperMap<void> m_staticActiveDomObjectMap;
 #if ENABLE(SVG)
index cf12176cde4e0c3f11fbad85b6a442e00cffe286..93d5a1b9fae082717d054e1ccdf8e50c0deb68dd 100644 (file)
@@ -64,6 +64,11 @@ DOMNodeMapping& getDOMNodeMap()
     return getDOMDataStore().domNodeMap();
 }
 
+DOMNodeMapping& getActiveDOMNodeMap()
+{
+    return getDOMDataStore().activeDomNodeMap();
+}
+
 DOMWrapperMap<void>& getDOMObjectMap()
 {
     return getDOMDataStore().domObjectMap();
@@ -94,6 +99,9 @@ void removeAllDOMObjects()
         // Remove all DOM nodes.
         DOMData::removeObjectsFromWrapperMap<Node>(&store, store.domNodeMap());
 
+        // Remove all active DOM nodes.
+        DOMData::removeObjectsFromWrapperMap<Node>(&store, store.activeDomNodeMap());
+
 #if ENABLE(SVG)
         // Remove all SVG element instances in the wrapper map.
         DOMData::removeObjectsFromWrapperMap<SVGElementInstance>(&store, store.domSvgElementInstanceMap());
@@ -119,6 +127,18 @@ void visitDOMNodes(DOMWrapperMap<Node>::Visitor* visitor)
     }
 }
 
+void visitActiveDOMNodes(DOMWrapperMap<Node>::Visitor* visitor)
+{
+    v8::HandleScope scope;
+
+    DOMDataList& list = DOMDataStore::allStores();
+    for (size_t i = 0; i < list.size(); ++i) {
+        DOMDataStore* store = list[i];
+
+        store->activeDomNodeMap().visit(store, visitor);
+    }
+}
+
 void visitDOMObjects(DOMWrapperMap<void>::Visitor* visitor)
 {
     v8::HandleScope scope;
index 84cf754658e9a06978e6d75a579099bcced78a5a..4fa48e45e43ac770b47884abd772d1b3a3c01795 100644 (file)
@@ -158,7 +158,9 @@ namespace WebCore {
 
     // A map from DOM node to its JS wrapper.
     DOMNodeMapping& getDOMNodeMap();
+    DOMNodeMapping& getActiveDOMNodeMap();
     void visitDOMNodes(DOMWrapperMap<Node>::Visitor*);
+    void visitActiveDOMNodes(DOMWrapperMap<Node>::Visitor*);
 
     // 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).
     DOMWrapperMap<void>& getDOMObjectMap();
index 3ac75fbc8db775c49b9e526f66e4387b93d5458d..7ad60eaf87a49380ec5cbd1df7384b7515e67a32 100644 (file)
@@ -92,7 +92,10 @@ void V8DOMWrapper::setJSWrapperForActiveDOMObject(void* object, v8::Persistent<v
 void V8DOMWrapper::setJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrapper)
 {
     ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper));
-    getDOMNodeMap().set(node, wrapper);
+    if (node->isActiveNode())
+        getActiveDOMNodeMap().set(node, wrapper);
+    else
+        getDOMNodeMap().set(node, wrapper);
 }
 
 v8::Local<v8::Function> V8DOMWrapper::getConstructor(WrapperTypeInfo* type, v8::Handle<v8::Value> objectPrototype)
@@ -295,7 +298,8 @@ v8::Handle<v8::Object> V8DOMWrapper::getWrapperSlow(Node* node)
             return v8::Handle<v8::Object>();
         return *wrapper;
     }
-    DOMNodeMapping& domNodeMap = context->world()->domDataStore()->domNodeMap();
+    DOMDataStore* store = context->world()->domDataStore();
+    DOMNodeMapping& domNodeMap = node->isActiveNode() ? store->activeDomNodeMap() : store->domNodeMap();
     return domNodeMap.get(node);
 }
 
index 3b2d0e01d5b723475ee7f0e735be1713fbe4fac8..9b7d144df770ad713a16ba997e77014d67d5f5b6 100644 (file)
@@ -143,12 +143,10 @@ public:
 
 #endif // NDEBUG
 
-class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor {
+class SpecialCasePrologueObjectHandler {
 public:
-    void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper)
+    static bool process(void* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo)
     {
-        WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);  
-
         // Additional handling of message port ensuring that entangled ports also
         // have their wrappers entangled. This should ideally be handled when the
         // ports are actually entangled in MessagePort::entangle, but to avoid
@@ -161,7 +159,31 @@ public:
             MessagePort* port1 = static_cast<MessagePort*>(object);
             if (port1->isEntangled() || port1->hasPendingActivity())
                 wrapper.ClearWeak();
-        } else {
+            return true;
+        }
+        return false;
+    }
+};
+
+class SpecialCasePrologueNodeHandler {
+public:
+    static bool process(Node* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo)
+    {
+        UNUSED_PARAM(object);
+        UNUSED_PARAM(wrapper);
+        UNUSED_PARAM(typeInfo);
+        return false;
+    }
+};
+
+template<typename T, typename S>
+class GCPrologueVisitor : public DOMWrapperMap<T>::Visitor {
+public:
+    void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper)
+    {
+        WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);  
+
+        if (!S::process(object, wrapper, typeInfo)) {
             ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper);
             if (activeDOMObject && activeDOMObject->hasPendingActivity())
                 wrapper.ClearWeak();
@@ -373,12 +395,15 @@ void V8GCController::gcPrologue()
 
     // Run through all objects with possible pending activity making their
     // wrappers non weak if there is pending activity.
-    GCPrologueVisitor prologueVisitor;
-    visitActiveDOMObjects(&prologueVisitor);
+    GCPrologueVisitor<void, SpecialCasePrologueObjectHandler> prologueObjectVisitor;
+    visitActiveDOMObjects(&prologueObjectVisitor);
+    GCPrologueVisitor<Node, SpecialCasePrologueNodeHandler> prologueNodeVisitor;
+    visitActiveDOMNodes(&prologueNodeVisitor);
 
     // Create object groups.
     GrouperVisitor grouperVisitor;
     visitDOMNodes(&grouperVisitor);
+    visitActiveDOMNodes(&grouperVisitor);
     visitDOMObjects(&grouperVisitor);
     grouperVisitor.applyGrouping();
 
@@ -387,11 +412,10 @@ void V8GCController::gcPrologue()
     data->stringCache()->clearOnGC();
 }
 
-class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor {
+class SpecialCaseEpilogueObjectHandler {
 public:
-    void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper)
+    static bool process(void* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo)
     {
-        WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);
         if (V8MessagePort::info.equals(typeInfo)) {
             MessagePort* port1 = static_cast<MessagePort*>(object);
             // We marked this port as reachable in GCPrologueVisitor.  Undo this now since the
@@ -399,7 +423,30 @@ public:
             // GCPrologueVisitor expects to see all handles marked as weak).
             if ((!wrapper.IsWeak() && !wrapper.IsNearDeath()) || port1->hasPendingActivity())
                 wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback);
-        } else {
+            return true;
+        }
+        return false;
+    }
+};
+
+class SpecialCaseEpilogueNodeHandler {
+public:
+    static bool process(Node* object, v8::Persistent<v8::Object> wrapper, WrapperTypeInfo* typeInfo)
+    {
+        UNUSED_PARAM(object);
+        UNUSED_PARAM(wrapper);
+        UNUSED_PARAM(typeInfo);
+        return false;
+    }
+};
+
+template<typename T, typename S, v8::WeakReferenceCallback callback>
+class GCEpilogueVisitor : public DOMWrapperMap<T>::Visitor {
+public:
+    void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper)
+    {
+        WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper);
+        if (!S::process(object, wrapper, typeInfo)) {
             ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper);
             if (activeDOMObject && activeDOMObject->hasPendingActivity()) {
                 ASSERT(!wrapper.IsWeak());
@@ -408,7 +455,7 @@ public:
                 // may be a different pointer (in case ActiveDOMObject is not
                 // the main base class of the object's class) and pointer
                 // identity is required by DOM map functions.
-                wrapper.MakeWeak(object, &DOMDataStore::weakActiveDOMObjectCallback);
+                wrapper.MakeWeak(object, callback);
             }
         }
     }
@@ -444,8 +491,10 @@ void V8GCController::gcEpilogue()
 
     // Run through all objects with pending activity making their wrappers weak
     // again.
-    GCEpilogueVisitor epilogueVisitor;
-    visitActiveDOMObjects(&epilogueVisitor);
+    GCEpilogueVisitor<void, SpecialCaseEpilogueObjectHandler, &DOMDataStore::weakActiveDOMObjectCallback> epilogueObjectVisitor;
+    visitActiveDOMObjects(&epilogueObjectVisitor);
+    GCEpilogueVisitor<Node, SpecialCaseEpilogueNodeHandler, &DOMDataStore::weakNodeCallback> epilogueNodeVisitor;
+    visitActiveDOMNodes(&epilogueNodeVisitor);
 
     workingSetEstimateMB = getActualMemoryUsageInMB();
 
index 28bb8c38dbbfbab34cd550550e50b1bde8a7dbb1..ce0cd4a49f64a59506f62ea6e9d9b4f056e6dd26 100644 (file)
@@ -190,6 +190,8 @@ public:
     
     Node* lastDescendant() const;
     Node* firstDescendant() const;
+
+    virtual bool isActiveNode() const { return false; }
     
     // Other methods (not part of DOM)
 
index 41d3b24ad27af2201def174a20e1b443a9d6cc88..d96f2ffe3ba35cc4faebbe7cb81be0e0680517ec 100644 (file)
@@ -42,6 +42,8 @@ public:
 
     virtual bool hasPendingActivity() const { return isPlaying() || HTMLMediaElement::hasPendingActivity(); }
 
+    virtual bool isActiveNode() const { return true; }
+
 private:
     HTMLAudioElement(const QualifiedName&, Document*);