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.
};
}