Title: [99593] trunk
Revision
99593
Author
[email protected]
Date
2011-11-08 10:29:54 -0800 (Tue, 08 Nov 2011)

Log Message

Only walk up the tree in search of MutationObservers if one has been added
https://bugs.webkit.org/show_bug.cgi?id=71499

Reviewed by Ojan Vafai.

Source/WebCore:

Analogous to m_listenerTypes, added an m_subtreeMutationObserverTypes field
to Document that keeps track of which observer types have been added.
This allows us to avoid doing any extra work if MutationObservers are
not attached to a document.

This could be improved upon to keep a count of each type, as removing
an observer currently has no effect on m_subtreeMutationObserverTypes.
But that would require a more complex implementation: one counter per
mutation type. And it would be easier to get wrong: if the counter
gets out of sync with the actual state of the DOM, we could start
dropping mutation notifications.

Test: fast/mutation/cross-document.html

* dom/Document.h:
(WebCore::Document::hasSubtreeMutationObserverOfType):
(WebCore::Document::hasSubtreeMutationObserver):
(WebCore::Document::addSubtreeMutationObserverTypes):
* dom/MutationObserverRegistration.h:
(WebCore::MutationObserverRegistration::isSubtree):
(WebCore::MutationObserverRegistration::deliveryOptions):
(WebCore::MutationObserverRegistration::mutationTypes):
* dom/Node.cpp:
(WebCore::Node::didMoveToNewOwnerDocument): Update mutationObserverTypes when a Node is moved to a new document.
(WebCore::Node::getRegisteredMutationObserversOfType): Exit early if it's known that no observers of |type| are registered.
(WebCore::Node::notifyMutationObserversNodeWillDetach): Exit early if it's known no subtree observers of any type are registered.
* dom/WebKitMutationObserver.cpp:
(WebCore::WebKitMutationObserver::observe): Update mutationObserverTypes when an observation occurs.
* dom/WebKitMutationObserver.h: Add WebKitMutationObserver::AllMutationTypes to enum

LayoutTests:

Created a basic test showing that MutationObservers
are preserved when a Node switches documents.

* fast/mutation/cross-document-expected.txt: Added.
* fast/mutation/cross-document.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (99592 => 99593)


--- trunk/LayoutTests/ChangeLog	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/LayoutTests/ChangeLog	2011-11-08 18:29:54 UTC (rev 99593)
@@ -1,3 +1,16 @@
+2011-11-08  Adam Klein  <[email protected]>
+
+        Only walk up the tree in search of MutationObservers if one has been added
+        https://bugs.webkit.org/show_bug.cgi?id=71499
+
+        Reviewed by Ojan Vafai.
+
+        Created a basic test showing that MutationObservers
+        are preserved when a Node switches documents.
+
+        * fast/mutation/cross-document-expected.txt: Added.
+        * fast/mutation/cross-document.html: Added.
+
 2011-11-08  Ojan Vafai  <[email protected]>
 
         Move regressions from V8 roll to from the downstream expectations file.

Added: trunk/LayoutTests/fast/mutation/cross-document-expected.txt (0 => 99593)


--- trunk/LayoutTests/fast/mutation/cross-document-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/mutation/cross-document-expected.txt	2011-11-08 18:29:54 UTC (rev 99593)
@@ -0,0 +1,23 @@
+Test that MutationObservers move correctly across documents
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing basic aspects of cross-document observation.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is div
+PASS mutations[0].attributeName is "id"
+PASS mutations[0].attributeNamespace is null
+
+Testing that subtree observation works after node is moved.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "id"
+PASS mutations[0].attributeNamespace is null
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/mutation/cross-document.html (0 => 99593)


--- trunk/LayoutTests/fast/mutation/cross-document.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mutation/cross-document.html	2011-11-08 18:29:54 UTC (rev 99593)
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+
+window.jsTestIsAsync = true;
+var mutations;
+var div, subDiv;
+
+function testBasic() {
+    var observer;
+
+    function start() {
+        debug('Testing basic aspects of cross-document observation.');
+
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+
+        observer.observe(div, {attributes: true});
+        var newDoc = document.implementation.createDocument('', '', null);
+        newDoc.appendChild(div);
+        div.id = 'foo';
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].target', 'div');
+        shouldBe('mutations[0].attributeName', '"id"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testSubtree() {
+    var observer;
+
+    function start() {
+        debug('Testing that subtree observation works after node is moved.');
+
+        mutations = null;
+        div = document.createElement('div');
+        subDiv = div.appendChild(document.createElement('div'));
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+
+        observer.observe(div, {attributes: true, subtree: true});
+        var newDoc = document.implementation.createDocument('', '', null);
+        newDoc.appendChild(div);
+        subDiv.id = 'foo';
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].target', 'subDiv');
+        shouldBe('mutations[0].attributeName', '"id"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+var tests = [testBasic, testSubtree];
+var testIndex = 0;
+
+function runNextTest() {
+    if (testIndex < tests.length)
+        tests[testIndex++]();
+    else
+        finishJSTest();
+}
+
+description('Test that MutationObservers move correctly across documents');
+
+if (!window.WebKitMutationObserver)
+    testFailed('This test requires ENABLE(MUTATION_OBSERVERS)');
+else
+    runNextTest();
+
+</script>
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (99592 => 99593)


--- trunk/Source/WebCore/ChangeLog	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/ChangeLog	2011-11-08 18:29:54 UTC (rev 99593)
@@ -1,3 +1,40 @@
+2011-11-08  Adam Klein  <[email protected]>
+
+        Only walk up the tree in search of MutationObservers if one has been added
+        https://bugs.webkit.org/show_bug.cgi?id=71499
+
+        Reviewed by Ojan Vafai.
+
+        Analogous to m_listenerTypes, added an m_subtreeMutationObserverTypes field
+        to Document that keeps track of which observer types have been added.
+        This allows us to avoid doing any extra work if MutationObservers are
+        not attached to a document.
+
+        This could be improved upon to keep a count of each type, as removing
+        an observer currently has no effect on m_subtreeMutationObserverTypes.
+        But that would require a more complex implementation: one counter per
+        mutation type. And it would be easier to get wrong: if the counter
+        gets out of sync with the actual state of the DOM, we could start
+        dropping mutation notifications.
+
+        Test: fast/mutation/cross-document.html
+
+        * dom/Document.h:
+        (WebCore::Document::hasSubtreeMutationObserverOfType):
+        (WebCore::Document::hasSubtreeMutationObserver):
+        (WebCore::Document::addSubtreeMutationObserverTypes):
+        * dom/MutationObserverRegistration.h:
+        (WebCore::MutationObserverRegistration::isSubtree):
+        (WebCore::MutationObserverRegistration::deliveryOptions):
+        (WebCore::MutationObserverRegistration::mutationTypes):
+        * dom/Node.cpp:
+        (WebCore::Node::didMoveToNewOwnerDocument): Update mutationObserverTypes when a Node is moved to a new document.
+        (WebCore::Node::getRegisteredMutationObserversOfType): Exit early if it's known that no observers of |type| are registered.
+        (WebCore::Node::notifyMutationObserversNodeWillDetach): Exit early if it's known no subtree observers of any type are registered.
+        * dom/WebKitMutationObserver.cpp:
+        (WebCore::WebKitMutationObserver::observe): Update mutationObserverTypes when an observation occurs.
+        * dom/WebKitMutationObserver.h: Add WebKitMutationObserver::AllMutationTypes to enum
+
 2011-11-08  Andreas Kling  <[email protected]>
 
         Devirtualize CSSValue.

Modified: trunk/Source/WebCore/dom/Document.cpp (99592 => 99593)


--- trunk/Source/WebCore/dom/Document.cpp	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/dom/Document.cpp	2011-11-08 18:29:54 UTC (rev 99593)
@@ -360,6 +360,9 @@
     , m_compatibilityMode(NoQuirksMode)
     , m_compatibilityModeLocked(false)
     , m_domTreeVersion(++s_globalTreeVersion)
+#if ENABLE(MUTATION_OBSERVERS)
+    , m_subtreeMutationObserverTypes(0)
+#endif
     , m_styleSheets(StyleSheetList::create(this))
     , m_readyState(Complete)
     , m_styleRecalcTimer(this, &Document::styleRecalcTimerFired)

Modified: trunk/Source/WebCore/dom/Document.h (99592 => 99593)


--- trunk/Source/WebCore/dom/Document.h	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/dom/Document.h	2011-11-08 18:29:54 UTC (rev 99593)
@@ -46,6 +46,7 @@
 #include "Timer.h"
 #include "TreeScope.h"
 #include "ViewportArguments.h"
+#include "WebKitMutationObserver.h"
 #include <wtf/Deque.h>
 #include <wtf/FixedArray.h>
 #include <wtf/OwnPtr.h>
@@ -776,6 +777,15 @@
     void addListenerType(ListenerType listenerType) { m_listenerTypes = m_listenerTypes | listenerType; }
     void addListenerTypeIfNeeded(const AtomicString& eventType);
 
+#if ENABLE(MUTATION_OBSERVERS)
+    bool hasSubtreeMutationObserverOfType(WebKitMutationObserver::MutationType type) const
+    {
+        return m_subtreeMutationObserverTypes & type;
+    }
+    bool hasSubtreeMutationObserver() const { return m_subtreeMutationObserverTypes; }
+    void addSubtreeMutationObserverTypes(MutationObserverOptions types) { m_subtreeMutationObserverTypes |= types; }
+#endif
+
     CSSStyleDeclaration* getOverrideStyle(Element*, const String& pseudoElt);
 
     int nodeAbsIndex(Node*);
@@ -1238,6 +1248,10 @@
 
     unsigned short m_listenerTypes;
 
+#if ENABLE(MUTATION_OBSERVERS)
+    MutationObserverOptions m_subtreeMutationObserverTypes;
+#endif
+
     RefPtr<StyleSheetList> m_styleSheets; // All of the stylesheets that are currently in effect for our media type and stylesheet set.
     
     typedef ListHashSet<Node*, 32> StyleSheetCandidateListHashSet;

Modified: trunk/Source/WebCore/dom/MutationObserverRegistration.h (99592 => 99593)


--- trunk/Source/WebCore/dom/MutationObserverRegistration.h	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/dom/MutationObserverRegistration.h	2011-11-08 18:29:54 UTC (rev 99593)
@@ -53,9 +53,11 @@
     void unregister();
 
     bool shouldReceiveMutationFrom(Node*, WebKitMutationObserver::MutationType);
+    bool isSubtree() const { return m_options & WebKitMutationObserver::Subtree; }
 
     WebKitMutationObserver* observer() { return m_observer.get(); }
-    MutationRecordDeliveryOptions deliveryOptions() { return m_options & (WebKitMutationObserver::AttributeOldValue | WebKitMutationObserver::CharacterDataOldValue); }
+    MutationRecordDeliveryOptions deliveryOptions() const { return m_options & (WebKitMutationObserver::AttributeOldValue | WebKitMutationObserver::CharacterDataOldValue); }
+    MutationObserverOptions mutationTypes() const { return m_options & WebKitMutationObserver::AllMutationTypes; }
 
 private:
     MutationObserverRegistration(PassRefPtr<WebKitMutationObserver>, Node*);

Modified: trunk/Source/WebCore/dom/Node.cpp (99592 => 99593)


--- trunk/Source/WebCore/dom/Node.cpp	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/dom/Node.cpp	2011-11-08 18:29:54 UTC (rev 99593)
@@ -2544,6 +2544,24 @@
 {
     ASSERT(!didMoveToNewOwnerDocumentWasCalled);
     setDidMoveToNewOwnerDocumentWasCalled(true);
+
+    // FIXME: Event listener types for this node should be set on the new owner document here.
+
+#if ENABLE(MUTATION_OBSERVERS)
+    if (Vector<OwnPtr<MutationObserverRegistration> >* registry = mutationObserverRegistry()) {
+        for (size_t i = 0; i < registry->size(); ++i) {
+            if (registry->at(i)->isSubtree())
+                document()->addSubtreeMutationObserverTypes(registry->at(i)->mutationTypes());
+        }
+    }
+
+    if (HashSet<MutationObserverRegistration*>* transientRegistry = transientMutationObserverRegistry()) {
+        for (HashSet<MutationObserverRegistration*>::iterator iter = transientRegistry->begin(); iter != transientRegistry->end(); ++iter) {
+            if ((*iter)->isSubtree())
+                document()->addSubtreeMutationObserverTypes((*iter)->mutationTypes());
+        }
+    }
+#endif
 }
 
 #if ENABLE(SVG)
@@ -2734,7 +2752,12 @@
 
 void Node::getRegisteredMutationObserversOfType(HashMap<WebKitMutationObserver*, MutationRecordDeliveryOptions>& observers, WebKitMutationObserver::MutationType type)
 {
-    for (Node* node = this; node; node = node->parentNode())
+    collectMatchingObserversForMutation(observers, this, type);
+
+    if (!document()->hasSubtreeMutationObserverOfType(type))
+        return;
+
+    for (Node* node = parentNode(); node; node = node->parentNode())
         collectMatchingObserversForMutation(observers, node, type);
 }
 
@@ -2785,6 +2808,9 @@
 
 void Node::notifyMutationObserversNodeWillDetach()
 {
+    if (!document()->hasSubtreeMutationObserver())
+        return;
+
     for (Node* node = parentNode(); node; node = node->parentNode()) {
         if (Vector<OwnPtr<MutationObserverRegistration> >* registry = node->mutationObserverRegistry()) {
             const size_t size = registry->size();

Modified: trunk/Source/WebCore/dom/WebKitMutationObserver.cpp (99592 => 99593)


--- trunk/Source/WebCore/dom/WebKitMutationObserver.cpp	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/dom/WebKitMutationObserver.cpp	2011-11-08 18:29:54 UTC (rev 99593)
@@ -34,6 +34,7 @@
 
 #include "WebKitMutationObserver.h"
 
+#include "Document.h"
 #include "MutationCallback.h"
 #include "MutationObserverRegistration.h"
 #include "MutationRecord.h"
@@ -61,6 +62,9 @@
 {
     MutationObserverRegistration* registration = node->registerMutationObserver(this);
     registration->resetObservation(options);
+
+    if (registration->isSubtree())
+        node->document()->addSubtreeMutationObserverTypes(registration->mutationTypes());
 }
 
 void WebKitMutationObserver::disconnect()

Modified: trunk/Source/WebCore/dom/WebKitMutationObserver.h (99592 => 99593)


--- trunk/Source/WebCore/dom/WebKitMutationObserver.h	2011-11-08 18:27:07 UTC (rev 99592)
+++ trunk/Source/WebCore/dom/WebKitMutationObserver.h	2011-11-08 18:29:54 UTC (rev 99593)
@@ -55,7 +55,9 @@
     enum MutationType {
         ChildList = 1 << 0,
         Attributes = 1 << 1,
-        CharacterData = 1 << 2
+        CharacterData = 1 << 2,
+
+        AllMutationTypes = ChildList | Attributes | CharacterData
     };
 
     enum ObservationFlags  {
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to