Title: [97659] trunk
Revision
97659
Author
[email protected]
Date
2011-10-17 15:31:25 -0700 (Mon, 17 Oct 2011)

Log Message

[MutationObservers] Implement WebKitMutationObserver.observe for attributes
https://bugs.webkit.org/show_bug.cgi?id=68956

Patch by Rafael Weinstein <[email protected]> on 2011-10-17
Reviewed by Ryosuke Niwa.

Source/WebCore:

Test: fast/mutation/observe-attributes.html

This adds an initial implementation for registering mutation observers on nodes,
delivering mutation records at the end of the outer-most script invokation and
observing mutations to element attributes.

Note that the outer-most script invokation only works in V8.

Note also that support for observing changes to the style attribute when updated
via the style property is not implemented here.

* bindings/v8/V8Proxy.cpp:
(WebCore::V8Proxy::didLeaveScriptContext):
* dom/Element.cpp:
(WebCore::enqueueAttributesMutationRecord):
(WebCore::Element::setAttribute):
* dom/MutationRecord.cpp:
(WebCore::MutationRecord::createAttributes):
* dom/MutationRecord.h:
* dom/MutationRecord.idl:
* dom/Node.cpp:
(WebCore::Node::clearRareData):
(WebCore::Node::mutationObserverData):
(WebCore::Node::ensureMutationObserverData):
(WebCore::Node::registeredMutationObserversOfType):
(WebCore::Node::registerMutationObserver):
(WebCore::Node::deregisterMutationObserver):
* dom/Node.h:
* dom/NodeRareData.h:
(WebCore::MutationObserverRegistration::MutationObserverRegistration):
(WebCore::MutationObserverRegistration::operator==):
(WebCore::MutationObserverData::MutationObserverData):
(WebCore::MutationObserverData::~MutationObserverData):
(WebCore::NodeRareData::mutationObserverData):
(WebCore::NodeRareData::ensureMutationObserverData):
* dom/WebKitMutationObserver.cpp:
(WebCore::WebKitMutationObserver::observe):
(WebCore::WebKitMutationObserver::disconnect):
(WebCore::WebKitMutationObserver::wasDeregistered):
(WebCore::activeMutationObservers):
(WebCore::WebKitMutationObserver::enqueueMutationRecord):
(WebCore::WebKitMutationObserver::deliverAllMutations):
(WebCore::WebKitMutationObserver::deliver):
* dom/WebKitMutationObserver.h:

LayoutTests:

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

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (97658 => 97659)


--- trunk/LayoutTests/ChangeLog	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/LayoutTests/ChangeLog	2011-10-17 22:31:25 UTC (rev 97659)
@@ -1,3 +1,13 @@
+2011-10-17  Rafael Weinstein  <[email protected]>
+
+        [MutationObservers] Implement WebKitMutationObserver.observe for attributes
+        https://bugs.webkit.org/show_bug.cgi?id=68956
+
+        Reviewed by Ryosuke Niwa.
+
+        * fast/mutation/observe-attributes-expected.txt: Added.
+        * fast/mutation/observe-attributes.html: Added.
+
 2011-10-17  Dirk Pranke  <[email protected]>
 
         Update chromium gpu baselines for fast/canvas/canvas-composite-transformclip.html .

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


--- trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt	2011-10-17 22:31:25 UTC (rev 97659)
@@ -0,0 +1,64 @@
+Test the constructor of WebKitMutationObserver
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing basic aspects of attribute observation.
+...can attribute changes be observed at all
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].attributeNamespace is null
+...observer.disconnect() should prevent further delivery of mutations.
+PASS mutations is null
+...re-observing after disconnect works with the same observer.
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].attributeNamespace is null
+PASS mutations[1].type is "attributes"
+PASS mutations[1].attributeName is "bar"
+PASS mutations[1].attributeNamespace is null
+
+Testing that observing without specifying "attributes" does not result in hearing about attribute changes.
+PASS mutations is null
+
+Testing that re-observing the same node with the same observing has the effect of resetting the options.
+PASS calls is 1
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations is null
+
+Testing that multiple observers can be registered to a given node and both receive mutations.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations2.length is 1
+PASS mutations2[0].type is "attributes"
+PASS mutations2[0].attributeName is "foo"
+
+Testing that "attributeNamespace" value is delivered properly.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].attributeNamespace is "http://www.foo.com/bar"
+
+Testing that modifications to node properties which delegate to attribute storage deliver mutations.
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "src"
+PASS mutations[1].type is "attributes"
+PASS mutations[1].attributeName is "href"
+
+Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched.
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[1].type is "attributes"
+PASS mutations[1].attributeName is "baz"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Property changes on: trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt
___________________________________________________________________

Added: svn:eol-style

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


--- trunk/LayoutTests/fast/mutation/observe-attributes.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mutation/observe-attributes.html	2011-10-17 22:31:25 UTC (rev 97659)
@@ -0,0 +1,322 @@
+<!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 calls;
+
+function testBasic() {
+    var div;
+    var observer;
+
+    function start() {
+        debug('Testing basic aspects of attribute observation.');
+
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+        });
+
+        observer.observe(div, { attributes: true, characterData: true });
+        div.setAttribute('foo', 'bar');
+        setTimeout(checkDisconnectAndMutate, 0);
+    }
+
+    function checkDisconnectAndMutate() {
+        debug('...can attribute changes be observed at all');
+
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+
+        mutations = null;
+        observer.disconnect();
+        div.setAttribute('foo', 'baz');
+        setTimeout(checkNotDeliveredAndMutateMultiple, 0);
+    }
+
+    function checkNotDeliveredAndMutateMultiple() {
+        debug('...observer.disconnect() should prevent further delivery of mutations.');
+
+        shouldBe('mutations', 'null');
+        observer.observe(div, { attributes: true });
+        div.setAttribute('foo', 'bat');
+        div.setAttribute('bar', 'foo');
+        setTimeout(finish);
+    }
+
+    function finish() {
+        debug('...re-observing after disconnect works with the same observer.');
+
+        shouldBe('mutations.length', '2');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].attributeNamespace', 'null');
+        shouldBe('mutations[1].type', '"attributes"');
+        shouldBe('mutations[1].attributeName', '"bar"');
+        shouldBe('mutations[1].attributeNamespace', 'null');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testWrongType() {
+    var div;
+    var observer;
+
+    function start() {
+        debug('Testing that observing without specifying "attributes" does not result in hearing about attribute changes.');
+
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+        });
+
+        observer.observe(div, { childList: true, characterData: true });
+        div.setAttribute('foo', 'bar');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations', 'null');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testMultipleRegistration() {
+    var div;
+    var observer;
+
+    function start() {
+        debug('Testing that re-observing the same node with the same observing has the effect of resetting the options.');
+    
+		calls = 0;
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+			calls++;
+        });
+
+        observer.observe(div, { attributes: true, characterData: true });
+        observer.observe(div, { attributes: true });
+        div.setAttribute('foo', 'bar');
+        setTimeout(checkDisconnectAndMutate, 0);
+    }
+
+    function checkDisconnectAndMutate() {
+        shouldBe('calls', '1');
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        mutations = null;
+        observer.observe(div, { attributes: true, characterData: true });
+        observer.observe(div, { childList: true });
+        div.setAttribute('foo', 'baz');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations', 'null');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testMultipleObservers() {
+    var div;
+    var observer;
+    var observer2;
+
+    function start() {
+        debug('Testing that multiple observers can be registered to a given node and both receive mutations.');
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+        });
+        observer2 = new WebKitMutationObserver(function(m) {
+            mutations2 = m;
+        });
+        observer.observe(div, { attributes: true });
+        observer2.observe(div, { attributes: true });
+        div.setAttribute('foo', 'bar');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations2.length', '1');
+        shouldBe('mutations2[0].type', '"attributes"');
+        shouldBe('mutations2[0].attributeName', '"foo"');
+        observer.disconnect();
+        observer2.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testNamespaceURI() {
+    var div;
+    var observer;
+
+    function start() {
+        debug('Testing that "attributeNamespace" value is delivered properly.');
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+        });
+
+        observer.observe(div, { attributes: true, childList: true });
+        div.setAttributeNS('http://www.foo.com/bar', 'foo', 'bar');
+        setTimeout(finish, 0);    
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].attributeNamespace', '"http://www.foo.com/bar"');        
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testPropertyAccess() {
+    var img, a;
+    var observer;
+
+    function start() {
+        debug('Testing that modifications to node properties which delegate to attribute storage deliver mutations.');
+        mutations = null;
+        img = document.createElement('img');
+        a = document.createElement('a');
+
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+        });
+
+        observer.observe(img, { attributes: true });
+        observer.observe(a, { attributes: true });
+
+        img.src = '';
+        a.href = '';
+
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '2');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"src"');
+        shouldBe('mutations[1].type', '"attributes"');
+        shouldBe('mutations[1].attributeName', '"href"');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testOrderingWrtDOMSubtreeModified() {
+    var div, div2, subDiv;
+    var observer;
+    var listener;
+
+    function start() {
+        debug('Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched.');
+
+        mutations = null;
+        div = document.body.appendChild(document.createElement('div'));
+        div2 = document.body.appendChild(document.createElement('div'));
+
+        subDiv = div.appendChild(document.createElement('div'));
+
+        observer = new WebKitMutationObserver(function(m) {
+            mutations = m;
+        });
+
+        listener = function(e) {
+            div2.setAttribute('baz', 'bat');
+        }
+
+        div.addEventListener('DOMSubtreeModified', listener);
+        observer.observe(subDiv, { attributes: true });
+        observer.observe(div2, { attributes: true });
+
+        subDiv.setAttribute('foo', 'bar');
+
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '2');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[1].type', '"attributes"');
+        shouldBe('mutations[1].attributeName', '"baz"');
+        div.removeEventListener(listener);
+        document.body.removeChild(div);
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+var tests = [testBasic, testWrongType, testMultipleRegistration, testMultipleObservers, testNamespaceURI, testPropertyAccess, testOrderingWrtDOMSubtreeModified];
+var testIndex = 0;
+ 
+function runNextTest() {
+    if (testIndex < tests.length)
+        tests[testIndex++]();
+    else
+        finishJSTest();
+}
+
+description('Test the constructor of WebKitMutationObserver');
+
+if (!window.WebKitMutationObserver)
+    testFailed('This test requires ENABLE(MUTATION_OBSERVERS)');
+else
+    runNextTest();
+
+var successfullyParsed = true;
+
+</script>
+<script src=""
+</body>
+</html>
Property changes on: trunk/LayoutTests/fast/mutation/observe-attributes.html
___________________________________________________________________

Added: svn:eol-style

Modified: trunk/Source/WebCore/ChangeLog (97658 => 97659)


--- trunk/Source/WebCore/ChangeLog	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/ChangeLog	2011-10-17 22:31:25 UTC (rev 97659)
@@ -1,3 +1,55 @@
+2011-10-17  Rafael Weinstein  <[email protected]>
+
+        [MutationObservers] Implement WebKitMutationObserver.observe for attributes
+        https://bugs.webkit.org/show_bug.cgi?id=68956
+
+        Reviewed by Ryosuke Niwa.
+
+        Test: fast/mutation/observe-attributes.html
+
+        This adds an initial implementation for registering mutation observers on nodes,
+        delivering mutation records at the end of the outer-most script invokation and
+        observing mutations to element attributes.
+
+        Note that the outer-most script invokation only works in V8.
+
+        Note also that support for observing changes to the style attribute when updated
+        via the style property is not implemented here.
+
+        * bindings/v8/V8Proxy.cpp:
+        (WebCore::V8Proxy::didLeaveScriptContext):
+        * dom/Element.cpp:
+        (WebCore::enqueueAttributesMutationRecord):
+        (WebCore::Element::setAttribute):
+        * dom/MutationRecord.cpp:
+        (WebCore::MutationRecord::createAttributes):
+        * dom/MutationRecord.h:
+        * dom/MutationRecord.idl:
+        * dom/Node.cpp:
+        (WebCore::Node::clearRareData):
+        (WebCore::Node::mutationObserverData):
+        (WebCore::Node::ensureMutationObserverData):
+        (WebCore::Node::registeredMutationObserversOfType):
+        (WebCore::Node::registerMutationObserver):
+        (WebCore::Node::deregisterMutationObserver):
+        * dom/Node.h:
+        * dom/NodeRareData.h:
+        (WebCore::MutationObserverRegistration::MutationObserverRegistration):
+        (WebCore::MutationObserverRegistration::operator==):
+        (WebCore::MutationObserverData::MutationObserverData):
+        (WebCore::MutationObserverData::~MutationObserverData):
+        (WebCore::NodeRareData::mutationObserverData):
+        (WebCore::NodeRareData::ensureMutationObserverData):
+        * dom/WebKitMutationObserver.cpp:
+        (WebCore::WebKitMutationObserver::observe):
+        (WebCore::WebKitMutationObserver::disconnect):
+        (WebCore::WebKitMutationObserver::wasDeregistered):
+        (WebCore::activeMutationObservers):
+        (WebCore::WebKitMutationObserver::enqueueMutationRecord):
+        (WebCore::WebKitMutationObserver::deliverAllMutations):
+        (WebCore::WebKitMutationObserver::deliver):
+        * dom/WebKitMutationObserver.h:
+
 2011-10-17  Andreas Kling  <[email protected]>
 
         HTMLKeygenElement: Don't cache keytype and challenge attributes.

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (97658 => 97659)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2011-10-17 22:31:25 UTC (rev 97659)
@@ -5466,7 +5466,7 @@
 		C6F08FCA1431000D00685849 /* JSMutationRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F08FC81431000D00685849 /* JSMutationRecord.h */; };
 		C6F0900A14327B6100685849 /* MutationCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900114327B6100685849 /* MutationCallback.h */; };
 		C6F0900E14327B6100685849 /* WebKitMutationObserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6F0900514327B6100685849 /* WebKitMutationObserver.cpp */; };
-		C6F0900F14327B6100685849 /* WebKitMutationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900614327B6100685849 /* WebKitMutationObserver.h */; };
+		C6F0900F14327B6100685849 /* WebKitMutationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900614327B6100685849 /* WebKitMutationObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		C6F0901114327B6100685849 /* MutationObserverOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900814327B6100685849 /* MutationObserverOptions.h */; };
 		C6F0902814327D4F00685849 /* JSMutationCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6F0902014327D4F00685849 /* JSMutationCallback.cpp */; };
 		C6F0902914327D4F00685849 /* JSMutationCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0902114327D4F00685849 /* JSMutationCallback.h */; };

Modified: trunk/Source/WebCore/bindings/v8/V8Proxy.cpp (97658 => 97659)


--- trunk/Source/WebCore/bindings/v8/V8Proxy.cpp	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/bindings/v8/V8Proxy.cpp	2011-10-17 22:31:25 UTC (rev 97659)
@@ -65,6 +65,7 @@
 #include "V8SQLException.h"
 #include "V8XMLHttpRequestException.h"
 #include "V8XPathException.h"
+#include "WebKitMutationObserver.h"
 #include "WorkerContext.h"
 #include "WorkerContextExecutionProxy.h"
 
@@ -620,6 +621,10 @@
 #endif // ENABLE(INDEXED_DATABASE)
     if (page->group().hasLocalStorage())
         page->group().localStorage()->unlock();
+
+#if ENABLE(MUTATION_OBSERVERS)
+    WebCore::WebKitMutationObserver::deliverAllMutations();
+#endif
 }
 
 void V8Proxy::resetIsolatedWorlds()

Modified: trunk/Source/WebCore/dom/Element.cpp (97658 => 97659)


--- trunk/Source/WebCore/dom/Element.cpp	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/Element.cpp	2011-10-17 22:31:25 UTC (rev 97659)
@@ -48,6 +48,7 @@
 #include "HTMLNames.h"
 #include "HTMLParserIdioms.h"
 #include "InspectorInstrumentation.h"
+#include "MutationRecord.h"
 #include "NodeList.h"
 #include "NodeRenderStyle.h"
 #include "NodeRenderingContext.h"
@@ -59,6 +60,7 @@
 #include "ShadowRoot.h"
 #include "Text.h"
 #include "TextIterator.h"
+#include "WebKitMutationObserver.h"
 #include "WebKitAnimationList.h"
 #include "XMLNames.h"
 #include "htmlediting.h"
@@ -610,6 +612,20 @@
     return getAttribute(QualifiedName(nullAtom, localName, namespaceURI));
 }
 
+#if ENABLE(MUTATION_OBSERVERS)
+static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name)
+{
+    Vector<WebKitMutationObserver*> observers;
+    element->registeredMutationObserversOfType(observers, WebKitMutationObserver::Attributes);
+    if (observers.isEmpty())
+        return;
+
+    RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name);
+    for (Vector<WebKitMutationObserver*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
+        (*iter)->enqueueMutationRecord(mutation);
+}
+#endif
+
 void Element::setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode& ec)
 {
     if (!Document::isValidName(name)) {
@@ -630,6 +646,11 @@
 
     document()->incDOMTreeVersion();
 
+#if ENABLE(MUTATION_OBSERVERS)
+    // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
+    enqueueAttributesMutationRecord(this, attributeName);
+#endif
+
     if (isIdAttributeName(old ? old->name() : attributeName))
         updateId(old ? old->value() : nullAtom, value);
 
@@ -663,6 +684,11 @@
     // Allocate attribute map if necessary.
     Attribute* old = attributes(false)->getAttributeItem(name);
 
+#if ENABLE(MUTATION_OBSERVERS)
+    // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
+    enqueueAttributesMutationRecord(this, name);
+#endif
+
     if (isIdAttributeName(name))
         updateId(old ? old->value() : nullAtom, value);
 

Modified: trunk/Source/WebCore/dom/MutationRecord.cpp (97658 => 97659)


--- trunk/Source/WebCore/dom/MutationRecord.cpp	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/MutationRecord.cpp	2011-10-17 22:31:25 UTC (rev 97659)
@@ -36,6 +36,7 @@
 
 #include "Node.h"
 #include "NodeList.h"
+#include "QualifiedName.h"
 #include <wtf/Assertions.h>
 #include <wtf/StdLibExtras.h>
 
@@ -69,17 +70,17 @@
 
 class AttributesRecord : public MutationRecord {
 public:
-    AttributesRecord(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace)
+    AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name)
         : MutationRecord(target)
-        , m_attributeName(attributeName)
-        , m_attributeNamespace(attributeNamespace)
+        , m_attributeName(name.localName())
+        , m_attributeNamespace(name.namespaceURI())
     {
     }
 
 private:
     virtual const AtomicString& type();
     virtual const AtomicString& attributeName() { return m_attributeName; }
-    virtual const AtomicString& attributeNamespace() { return m_attributeName; }
+    virtual const AtomicString& attributeNamespace() { return m_attributeNamespace; }
     virtual String oldValue() { return m_oldValue; }
     virtual void setOldValue(const String& value) { m_oldValue = value; }
 
@@ -128,9 +129,9 @@
     return adoptRef(static_cast<MutationRecord*>(new ChildListRecord(target, added, removed, previousSibling, nextSibling)));
 }
 
-PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace)
+PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name)
 {
-    return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, attributeName, attributeNamespace)));
+    return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name)));
 }
 
 PassRefPtr<MutationRecord> MutationRecord::createCharacterData(PassRefPtr<Node> target)

Modified: trunk/Source/WebCore/dom/MutationRecord.h (97658 => 97659)


--- trunk/Source/WebCore/dom/MutationRecord.h	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/MutationRecord.h	2011-10-17 22:31:25 UTC (rev 97659)
@@ -42,11 +42,12 @@
 
 class Node;
 class NodeList;
+class QualifiedName;
 
 class MutationRecord : public RefCounted<MutationRecord> {
 public:
     static PassRefPtr<MutationRecord> createChildList(PassRefPtr<Node> target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling);
-    static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace);
+    static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const QualifiedName&);
     static PassRefPtr<MutationRecord> createCharacterData(PassRefPtr<Node> target);
 
     virtual ~MutationRecord();

Modified: trunk/Source/WebCore/dom/MutationRecord.idl (97658 => 97659)


--- trunk/Source/WebCore/dom/MutationRecord.idl	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/MutationRecord.idl	2011-10-17 22:31:25 UTC (rev 97659)
@@ -41,8 +41,8 @@
         readonly attribute Node nextSibling;
 
         readonly attribute DOMString attributeName;
-        readonly attribute DOMString attributeNamespace;
+        readonly attribute [ConvertNullStringTo=Null] DOMString attributeNamespace;
 
-        readonly attribute DOMString oldValue;
+        readonly attribute [ConvertNullStringTo=Null] DOMString oldValue;
     };
 }

Modified: trunk/Source/WebCore/dom/Node.cpp (97658 => 97659)


--- trunk/Source/WebCore/dom/Node.cpp	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/Node.cpp	2011-10-17 22:31:25 UTC (rev 97659)
@@ -100,6 +100,7 @@
 #include <wtf/PassOwnPtr.h>
 #include <wtf/RefCountedLeakCounter.h>
 #include <wtf/UnusedParam.h>
+#include <wtf/Vector.h>
 #include <wtf/text/CString.h>
 #include <wtf/text/StringBuilder.h>
 
@@ -546,6 +547,14 @@
     if (treeScope() && rareData()->nodeLists())
         treeScope()->removeNodeListCache();
 
+#if ENABLE(MUTATION_OBSERVERS)
+    Vector<MutationObserverEntry>* observerEnties = mutationObserverEntries();
+    if (observerEnties) {
+        for (Vector<MutationObserverEntry>::iterator iter = observerEnties->begin(); iter != observerEnties->end(); ++iter)
+            iter->observer->observedNodeDestructed(this);
+    }
+#endif
+
     NodeRareData::NodeRareDataMap& dataMap = NodeRareData::rareDataMap();
     NodeRareData::NodeRareDataMap::iterator it = dataMap.find(this);
     ASSERT(it != dataMap.end());
@@ -2673,6 +2682,62 @@
     return ensureRareData()->ensureEventTargetData();
 }
 
+#if ENABLE(MUTATION_OBSERVERS)
+Vector<MutationObserverEntry>* Node::mutationObserverEntries()
+{
+    return hasRareData() ? rareData()->mutationObserverEntries() : 0;
+}
+
+Vector<MutationObserverEntry>* Node::ensureMutationObserverEntries()
+{
+    return ensureRareData()->ensureMutationObserverEntries();
+}
+
+void Node::registeredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type)
+{
+    Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
+    if (!observerEntries || observerEntries->isEmpty())
+        return;
+
+    for (Vector<MutationObserverEntry>::iterator iter = observerEntries->begin(); iter != observerEntries->end(); ++iter) {
+        if (iter->matches(type))
+            observers.append(iter->observer.get());
+    }
+}
+
+Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, unsigned char options)
+{
+    Vector<MutationObserverEntry>* observerEntries = ensureMutationObserverEntries();
+    MutationObserverEntry entry(observer, options);
+
+    size_t index = observerEntries->find(entry);
+    if (index == notFound) {
+        observerEntries->append(entry);
+        return MutationObserverRegistered;
+    }
+
+    (*observerEntries)[index].options = entry.options;
+    return MutationRegistrationOptionsReset;
+}
+
+void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer)
+{
+    Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
+    ASSERT(observerEntries);
+    if (!observerEntries)
+        return;
+
+    MutationObserverEntry entry(observer, 0);
+    size_t index = observerEntries->find(entry);
+    ASSERT(index != notFound);
+    if (index == notFound)
+        return;
+
+    observerEntries->remove(index);
+}
+#endif // ENABLE(MUTATION_OBSERVERS)
+
+
 void Node::handleLocalEvents(Event* event)
 {
     if (!hasRareData() || !rareData()->eventTargetData())

Modified: trunk/Source/WebCore/dom/Node.h (97658 => 97659)


--- trunk/Source/WebCore/dom/Node.h	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/Node.h	2011-10-17 22:31:25 UTC (rev 97659)
@@ -31,6 +31,7 @@
 #include "RenderStyleConstants.h"
 #include "ScriptWrappable.h"
 #include "TreeShared.h"
+#include "WebKitMutationObserver.h"
 #include <wtf/Forward.h>
 #include <wtf/ListHashSet.h>
 
@@ -58,6 +59,7 @@
 class HTMLInputElement;
 class IntRect;
 class KeyboardEvent;
+class MutationObserverEntry;
 class NSResolver;
 class NamedNodeMap;
 class NameNodeList;
@@ -585,6 +587,22 @@
     void itemTypeAttributeChanged();
 #endif
 
+#if ENABLE(MUTATION_OBSERVERS)
+    Vector<MutationObserverEntry>* mutationObserverEntries();
+    Vector<MutationObserverEntry>* ensureMutationObserverEntries();
+
+    void registeredMutationObserversOfType(Vector<WebKitMutationObserver*>&, WebKitMutationObserver::MutationType);
+
+    // Returns true if the observer wasn't already registered on this node.
+    enum MutationRegistrationResult {
+        MutationObserverRegistered,
+        MutationRegistrationOptionsReset
+    };
+    MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, unsigned char options);
+
+    void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>);
+#endif // ENABLE(MUTATION_OBSERVERS)
+
 private:
     enum NodeFlags {
         IsTextFlag = 1,

Modified: trunk/Source/WebCore/dom/NodeRareData.h (97658 => 97659)


--- trunk/Source/WebCore/dom/NodeRareData.h	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/NodeRareData.h	2011-10-17 22:31:25 UTC (rev 97659)
@@ -32,6 +32,7 @@
 #include "NameNodeList.h"
 #include "QualifiedName.h"
 #include "TagNodeList.h"
+#include "WebKitMutationObserver.h"
 #include <wtf/HashSet.h>
 #include <wtf/OwnPtr.h>
 #include <wtf/PassOwnPtr.h>
@@ -86,7 +87,31 @@
 private:
     NodeListsNodeData() : m_labelsNodeListCache(0) {}
 };
-    
+
+#if ENABLE(MUTATION_OBSERVERS)
+struct MutationObserverEntry {
+    MutationObserverEntry(PassRefPtr<WebKitMutationObserver> observer, unsigned char options)
+        : observer(observer)
+        , options(options)
+    {
+    }
+
+    bool operator==(const MutationObserverEntry& other) const
+    {
+        return observer == other.observer;
+    }
+
+    bool matches(unsigned char options) const
+    {
+        return this->options & options;
+    }
+
+    RefPtr<WebKitMutationObserver> observer;
+    unsigned char options;
+};
+
+#endif // ENABLE(MUTATION_OBSERVERS)
+
 class NodeRareData {
     WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED;
 public:    
@@ -136,6 +161,16 @@
         return m_eventTargetData.get();
     }
 
+#if ENABLE(MUTATION_OBSERVERS)
+    Vector<MutationObserverEntry>* mutationObserverEntries() { return m_mutationObservers.get(); }
+    Vector<MutationObserverEntry>* ensureMutationObserverEntries()
+    {
+        if (!m_mutationObservers)
+            m_mutationObservers = adoptPtr(new Vector<MutationObserverEntry>);
+        return m_mutationObservers.get();
+    }
+#endif
+
     bool isFocused() const { return m_isFocused; }
     void setFocused(bool focused) { m_isFocused = focused; }
 
@@ -152,6 +187,10 @@
     bool m_tabIndexWasSetExplicitly : 1;
     bool m_isFocused : 1;
     bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1;
+
+#if ENABLE(MUTATION_OBSERVERS)
+    OwnPtr<Vector<MutationObserverEntry> > m_mutationObservers;
+#endif
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/WebKitMutationObserver.cpp (97658 => 97659)


--- trunk/Source/WebCore/dom/WebKitMutationObserver.cpp	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/WebKitMutationObserver.cpp	2011-10-17 22:31:25 UTC (rev 97659)
@@ -34,8 +34,11 @@
 
 #include "WebKitMutationObserver.h"
 
+#include "MutationObserverOptions.h"
 #include "MutationCallback.h"
-#include "NotImplemented.h"
+#include "MutationRecord.h"
+#include "Node.h"
+#include <wtf/ListHashSet.h>
 
 namespace WebCore {
 
@@ -53,16 +56,72 @@
 {
 }
 
-void WebKitMutationObserver::observe(Node*, MutationObserverOptions*)
+void WebKitMutationObserver::observe(Node* node, MutationObserverOptions* options)
 {
-    notImplemented();
+    unsigned char optionFlags = 0;
+
+    // FIXME: Push composition of the optionFlags into the custom binding.
+    if (options->childList())
+        optionFlags |= ChildList;
+    if (options->attributes())
+        optionFlags |= Attributes;
+    if (options->characterData())
+        optionFlags |= CharacterData;
+    // FIXME: More options composition work needs to be done here.
+
+    if (node->registerMutationObserver(this, optionFlags) == Node::MutationObserverRegistered)
+        m_observedNodes.append(node);
 }
 
 void WebKitMutationObserver::disconnect()
 {
-    notImplemented();
+    for (Vector<Node*>::iterator iter = m_observedNodes.begin(); iter !=  m_observedNodes.end(); ++iter)
+        (*iter)->unregisterMutationObserver(this);
+
+    m_observedNodes.clear();
 }
 
+void WebKitMutationObserver::observedNodeDestructed(Node* node)
+{
+    size_t index = m_observedNodes.find(node);
+    ASSERT(index != notFound);
+    if (index == notFound)
+        return;
+
+    m_observedNodes.remove(index);
+}
+
+typedef ListHashSet<RefPtr<WebKitMutationObserver> > MutationObserverSet;
+
+static MutationObserverSet& activeMutationObservers()
+{
+    DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ());
+    return activeObservers;
+}
+
+void WebKitMutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation)
+{
+    m_records.append(mutation);
+    activeMutationObservers().add(this);
+}
+
+void WebKitMutationObserver::deliver()
+{
+    MutationRecordArray records;
+    records.swap(m_records);
+    m_callback->handleEvent(&records, this);
+}
+
+void WebKitMutationObserver::deliverAllMutations()
+{
+    while (!activeMutationObservers().isEmpty()) {
+        MutationObserverSet::iterator iter = activeMutationObservers().begin();
+        RefPtr<WebKitMutationObserver> observer = *iter;
+        activeMutationObservers().remove(iter);
+        observer->deliver();
+    }
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MUTATION_OBSERVERS)

Modified: trunk/Source/WebCore/dom/WebKitMutationObserver.h (97658 => 97659)


--- trunk/Source/WebCore/dom/WebKitMutationObserver.h	2011-10-17 22:30:57 UTC (rev 97658)
+++ trunk/Source/WebCore/dom/WebKitMutationObserver.h	2011-10-17 22:31:25 UTC (rev 97659)
@@ -36,26 +36,48 @@
 #include <wtf/PassOwnPtr.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
 class MutationCallback;
 class MutationObserverOptions;
+class MutationRecord;
 class Node;
 
 class WebKitMutationObserver : public RefCounted<WebKitMutationObserver> {
 public:
+    enum MutationType {
+        ChildList = 1 << 0,
+        Attributes = 1 << 1,
+        CharacterData = 1 << 2
+    };
+
+    enum OptionFlags {
+        Subtree = 1 << 3,
+        AttributeOldValue = 1 << 4,
+        CharacterDataOldValue = 1 << 5,
+        AttributeFilter = 1 << 6
+    };
+
     static PassRefPtr<WebKitMutationObserver> create(PassRefPtr<MutationCallback>);
+    static void deliverAllMutations();
 
     ~WebKitMutationObserver();
 
     void observe(Node*, MutationObserverOptions*);
     void disconnect();
+    void observedNodeDestructed(Node*);
+    void enqueueMutationRecord(PassRefPtr<MutationRecord>);
 
 private:
     WebKitMutationObserver(PassRefPtr<MutationCallback>);
 
+    void deliver();
+
     RefPtr<MutationCallback> m_callback;
+    Vector<RefPtr<MutationRecord> > m_records;
+    Vector<Node*> m_observedNodes; // NodeRareData has a RefPtr to this, so use a weak pointer to avoid a cycle.
 };
 
 }
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to