Title: [98163] trunk
Revision
98163
Author
ad...@chromium.org
Date
2011-10-21 15:52:29 -0700 (Fri, 21 Oct 2011)

Log Message

[MutationObservers] Implement basic subtree observation
https://bugs.webkit.org/show_bug.cgi?id=70436

Reviewed by Ryosuke Niwa.

Source/WebCore:

Note that this patch only implements "basic" subtree semantics,
not the fully robust semantics described in
http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1622.html.
Most importantly, this change does not handle the case where mutations
occur in a temporarily detached subtree.

The plan is to implement those semantics in a followup to avoid
blocking other parts of the MutationObserver spec that rely on
the existence of subtree observation but not its specific
implementation.

Test: fast/mutation/observe-subtree.html

* dom/Node.cpp:
(WebCore::addMatchingObservers): Static helper method for registeredMutationObserversOfType().
(WebCore::Node::registeredMutationObserversOfType): Walk up the tree looking for observers.
* dom/NodeRareData.h:
(WebCore::MutationObserverEntry::hasAllOptions): A stricter, renamed from matches().

LayoutTests:

* fast/mutation/observe-subtree-expected.txt: Added.
* fast/mutation/observe-subtree.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (98162 => 98163)


--- trunk/LayoutTests/ChangeLog	2011-10-21 22:33:35 UTC (rev 98162)
+++ trunk/LayoutTests/ChangeLog	2011-10-21 22:52:29 UTC (rev 98163)
@@ -1,3 +1,13 @@
+2011-10-21  Adam Klein  <ad...@chromium.org>
+
+        [MutationObservers] Implement basic subtree observation
+        https://bugs.webkit.org/show_bug.cgi?id=70436
+
+        Reviewed by Ryosuke Niwa.
+
+        * fast/mutation/observe-subtree-expected.txt: Added.
+        * fast/mutation/observe-subtree.html: Added.
+
 2011-10-21  Joshua Bell  <jsb...@chromium.org>
 
         IndexedDB: objectStore.transaction property should be readonly

Added: trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt (0 => 98163)


--- trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt	2011-10-21 22:52:29 UTC (rev 98163)
@@ -0,0 +1,39 @@
+Test WebKitMutationObserver.observe on a subtree
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing basic aspects of subtree observation.
+...attribute and characterData changes in subtree
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].attributeNamespace is null
+PASS mutations[1].type is "characterData"
+PASS mutations[1].target is subDiv.firstChild
+
+Testing two observers at different depths.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].attributeNamespace is null
+PASS mutations2.length is 1
+PASS mutations2[0].type is "attributes"
+PASS mutations2[0].target is subDiv
+PASS mutations2[0].attributeName is "foo"
+PASS mutations2[0].attributeNamespace is null
+
+Testing one observer at two different depths.
+PASS calls is 1
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].attributeNamespace is null
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/mutation/observe-subtree.html (0 => 98163)


--- trunk/LayoutTests/fast/mutation/observe-subtree.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mutation/observe-subtree.html	2011-10-21 22:52:29 UTC (rev 98163)
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="stylesheet" href=""
+<script src=""
+<title></title>
+</head>
+<body>
+<p id=description></p>
+<div id="console"></div>
+<script>
+
+window.jsTestIsAsync = true;
+var mutations;
+var mutations2;
+var div;
+var subDiv;
+var calls;
+
+function testBasic() {
+    var observer;
+
+    function start() {
+        debug('Testing basic aspects of subtree observation.');
+
+        mutations = null;
+        div = document.createElement('div');
+        subDiv = div.appendChild(document.createElement('div'));
+        subDiv.innerHTML = 'hello, world';
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+
+        observer.observe(div, {attributes: true, characterData: true, subtree: true});
+        subDiv.setAttribute('foo', 'bar');
+        subDiv.firstChild.textContent = 'goodbye!';
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        debug('...attribute and characterData changes in subtree');
+
+        shouldBe('mutations.length', '2');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].target', 'subDiv');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+        shouldBe('mutations[1].type', '"characterData"');
+        shouldBe('mutations[1].target', 'subDiv.firstChild');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testMultipleObservers() {
+    var observer;
+    var observer2;
+
+    function start() {
+        debug('Testing two observers at different depths.');
+
+        mutations = null;
+        mutations2 = null;
+        div = document.createElement('div');
+        subDiv = div.appendChild(document.createElement('div'));
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+        observer2 = new WebKitMutationObserver(function(mutations) {
+            window.mutations2 = mutations;
+        });
+
+        observer.observe(div, {attributes: true, subtree: true});
+        observer2.observe(subDiv, {attributes: true});
+        subDiv.setAttribute('foo', 'bar');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].target', 'subDiv');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+        shouldBe('mutations2.length', '1');
+        shouldBe('mutations2[0].type', '"attributes"');
+        shouldBe('mutations2[0].target', 'subDiv');
+        shouldBe('mutations2[0].attributeName', '"foo"');
+        shouldBe('mutations2[0].attributeNamespace', 'null');
+        observer.disconnect();
+        observer2.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testMultipleObservations() {
+    var observer;
+
+    function start() {
+        debug('Testing one observer at two different depths.');
+
+        mutations = null;
+        calls = 0;
+        div = document.createElement('div');
+        subDiv = div.appendChild(document.createElement('div'));
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+            ++calls;
+        });
+
+        observer.observe(div, {attributes: true, subtree: true});
+        observer.observe(subDiv, {attributes: true, subtree: true});
+        subDiv.setAttribute('foo', 'bar');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('calls', '1');
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].target', 'subDiv');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+var tests = [testBasic, testMultipleObservers, testMultipleObservations];
+var testIndex = 0;
+
+function runNextTest() {
+    if (testIndex < tests.length)
+        tests[testIndex++]();
+    else
+        finishJSTest();
+}
+
+description('Test WebKitMutationObserver.observe on a subtree');
+
+if (!window.WebKitMutationObserver)
+    testFailed('This test requires ENABLE(MUTATION_OBSERVERS)');
+else
+    runNextTest();
+
+var successfullyParsed = true;
+
+</script>
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (98162 => 98163)


--- trunk/Source/WebCore/ChangeLog	2011-10-21 22:33:35 UTC (rev 98162)
+++ trunk/Source/WebCore/ChangeLog	2011-10-21 22:52:29 UTC (rev 98163)
@@ -1,3 +1,29 @@
+2011-10-21  Adam Klein  <ad...@chromium.org>
+
+        [MutationObservers] Implement basic subtree observation
+        https://bugs.webkit.org/show_bug.cgi?id=70436
+
+        Reviewed by Ryosuke Niwa.
+
+        Note that this patch only implements "basic" subtree semantics,
+        not the fully robust semantics described in
+        http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1622.html.
+        Most importantly, this change does not handle the case where mutations
+        occur in a temporarily detached subtree.
+
+        The plan is to implement those semantics in a followup to avoid
+        blocking other parts of the MutationObserver spec that rely on
+        the existence of subtree observation but not its specific
+        implementation.
+
+        Test: fast/mutation/observe-subtree.html
+
+        * dom/Node.cpp:
+        (WebCore::addMatchingObservers): Static helper method for registeredMutationObserversOfType().
+        (WebCore::Node::registeredMutationObserversOfType): Walk up the tree looking for observers.
+        * dom/NodeRareData.h:
+        (WebCore::MutationObserverEntry::hasAllOptions): A stricter, renamed from matches().
+
 2011-10-21  Joshua Bell  <jsb...@chromium.org>
 
         IndexedDB: objectStore.transaction property should be readonly

Modified: trunk/Source/WebCore/dom/Node.cpp (98162 => 98163)


--- trunk/Source/WebCore/dom/Node.cpp	2011-10-21 22:33:35 UTC (rev 98162)
+++ trunk/Source/WebCore/dom/Node.cpp	2011-10-21 22:52:29 UTC (rev 98163)
@@ -2698,18 +2698,31 @@
     return hasRareData() ? rareData()->mutationObserverEntries() : 0;
 }
 
-void Node::registeredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type)
+static void addMatchingObservers(HashSet<WebKitMutationObserver*>& observerSet, Vector<MutationObserverEntry>* observerEntries, MutationObserverOptions options)
 {
-    Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
-    if (!observerEntries || observerEntries->isEmpty())
+    if (!observerEntries)
         return;
 
-    for (size_t i = 0; i < observerEntries->size(); ++i) {
-        if ((*observerEntries)[i].matches(type))
-            observers.append((*observerEntries)[i].observer.get());
+    const size_t size = observerEntries->size();
+    for (size_t i = 0; i < size; ++i) {
+        MutationObserverEntry& entry = observerEntries->at(i);
+        if (entry.hasAllOptions(options))
+            observerSet.add(entry.observer.get());
     }
 }
 
+void Node::registeredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type)
+{
+    HashSet<WebKitMutationObserver*> observerSet;
+    addMatchingObservers(observerSet, mutationObserverEntries(), type);
+    for (Node* node = parentNode(); node; node = node->parentNode())
+        addMatchingObservers(observerSet, node->mutationObserverEntries(), type | WebKitMutationObserver::Subtree);
+
+    // FIXME: this method should output a HashSet instead of a Vector.
+    if (!observerSet.isEmpty())
+        copyToVector(observerSet, observers);
+}
+
 Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options)
 {
     Vector<MutationObserverEntry>* observerEntries = ensureRareData()->ensureMutationObserverEntries();

Modified: trunk/Source/WebCore/dom/NodeRareData.h (98162 => 98163)


--- trunk/Source/WebCore/dom/NodeRareData.h	2011-10-21 22:33:35 UTC (rev 98162)
+++ trunk/Source/WebCore/dom/NodeRareData.h	2011-10-21 22:52:29 UTC (rev 98163)
@@ -101,15 +101,14 @@
         return observer == other.observer;
     }
 
-    bool matches(MutationObserverOptions options) const
+    bool hasAllOptions(MutationObserverOptions options) const
     {
-        return this->options & options;
+        return (this->options & options) == options;
     }
 
     RefPtr<WebKitMutationObserver> observer;
     MutationObserverOptions options;
 };
-
 #endif // ENABLE(MUTATION_OBSERVERS)
 
 class NodeRareData {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to