- 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 {