Title: [98791] trunk
Revision
98791
Author
[email protected]
Date
2011-10-28 18:31:04 -0700 (Fri, 28 Oct 2011)

Log Message

[MutationObservers] Support attributeOldValue for attribute mutations
https://bugs.webkit.org/show_bug.cgi?id=70861

Reviewed by Ryosuke Niwa.

Source/WebCore:

Respect 'attributeOldValue' when passed to WebKitMutationObserver.observe().

If multiple observers have different attributeOldValue settings in
their registrations, two different MutationRecords are created (one is
a wrapper around the other).

If a single observer has multiple registrations that apply to a single
mutation, and those registrations have different values for
attributeOldValue, the observer is passed the oldValue.

* dom/Element.cpp:
(WebCore::hasOldValue):
(WebCore::enqueueAttributesMutationRecord):
(WebCore::Element::setAttribute):
* dom/MutationRecord.cpp:
(WebCore::MutationRecord::createAttributes):
(WebCore::MutationRecord::createWithNullOldValue):
* dom/MutationRecord.h:
(WebCore::MutationRecord::oldValue):

LayoutTests:

Added test cases for attributeOldValue to existing tests.

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

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (98790 => 98791)


--- trunk/LayoutTests/ChangeLog	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/LayoutTests/ChangeLog	2011-10-29 01:31:04 UTC (rev 98791)
@@ -1,3 +1,15 @@
+2011-10-28  Adam Klein  <[email protected]>
+
+        [MutationObservers] Support attributeOldValue for attribute mutations
+        https://bugs.webkit.org/show_bug.cgi?id=70861
+
+        Reviewed by Ryosuke Niwa.
+
+        Added test cases for attributeOldValue to existing tests.
+
+        * fast/mutation/observe-attributes-expected.txt:
+        * fast/mutation/observe-attributes.html:
+
 2011-10-28  Sheriff Bot  <[email protected]>
 
         Unreviewed, rolling out r98776.

Modified: trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt (98790 => 98791)


--- trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt	2011-10-29 01:31:04 UTC (rev 98791)
@@ -58,6 +58,40 @@
 PASS mutations[1].type is "attributes"
 PASS mutations[1].attributeName is "baz"
 
+Testing basic oldValue delivery.
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].oldValue is null
+PASS mutations[1].type is "attributes"
+PASS mutations[1].attributeName is "foo"
+PASS mutations[1].oldValue is "bar"
+
+Testing that oldValue is delivered as requested (or not).
+PASS mutationsWithOldValue.length is 1
+PASS mutationsWithOldValue[0].type is "attributes"
+PASS mutationsWithOldValue[0].attributeName is "foo"
+PASS mutationsWithOldValue[0].oldValue is "bar"
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].oldValue is null
+
+An observer with multiple observations will get attributeOldValue if any entries request it.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "foo"
+PASS mutations[0].oldValue is "bar"
+
+Testing setting an attribute via reflected IDL attribute.
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].attributeName is "id"
+PASS mutations[0].oldValue is null
+PASS mutations[1].type is "attributes"
+PASS mutations[1].attributeName is "id"
+PASS mutations[1].oldValue is "foo"
+
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/mutation/observe-attributes.html (98790 => 98791)


--- trunk/LayoutTests/fast/mutation/observe-attributes.html	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/LayoutTests/fast/mutation/observe-attributes.html	2011-10-29 01:31:04 UTC (rev 98791)
@@ -10,7 +10,7 @@
 <script>
 
 window.jsTestIsAsync = true;
-var mutations, mutations2;
+var mutations, mutations2, mutationsWithOldValue;
 var calls;
 
 function testBasic() {
@@ -294,9 +294,162 @@
 
     start();
 }
-var tests = [testBasic, testWrongType, testMultipleRegistration, testMultipleObservers, testNamespaceURI, testPropertyAccess, testOrderingWrtDOMSubtreeModified];
+
+function testOldValue() {
+    var div;
+    var observer;
+
+    function start() {
+        debug('Testing basic oldValue delivery.');
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+        observer.observe(div, { attributes: true, attributeOldValue: true });
+        div.setAttribute('foo', 'bar');
+        div.setAttribute('foo', 'baz');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '2');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].oldValue', 'null');
+        shouldBe('mutations[1].type', '"attributes"');
+        shouldBe('mutations[1].attributeName', '"foo"');
+        shouldBe('mutations[1].oldValue', '"bar"');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testOldValueAsRequested() {
+    var div;
+    var observerWithOldValue;
+    var observer;
+
+    function start() {
+        debug('Testing that oldValue is delivered as requested (or not).');
+        mutationsWithOldValue = null;
+        mutations = null;
+        div = document.createElement('div');
+        div.setAttribute('foo', 'bar');
+        observerWithOldValue = new WebKitMutationObserver(function(mutations) {
+            window.mutationsWithOldValue = mutations;
+        });
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+        observerWithOldValue.observe(div, { attributes: true, attributeOldValue: true });
+        observer.observe(div, { attributes: true });
+        div.setAttribute('foo', 'baz');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutationsWithOldValue.length', '1');
+        shouldBe('mutationsWithOldValue[0].type', '"attributes"');
+        shouldBe('mutationsWithOldValue[0].attributeName', '"foo"');
+        shouldBe('mutationsWithOldValue[0].oldValue', '"bar"');
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].oldValue', 'null');
+        observerWithOldValue.disconnect();
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testOldValueUnionMultipleObservations() {
+    var div;
+    var span;
+    var observer;
+
+    function start() {
+        debug('An observer with multiple observations will get attributeOldValue if any entries request it.');
+        mutations = null;
+        div = document.createElement('div');
+        span = div.appendChild(document.createElement('span'));
+        span.setAttribute('foo', 'bar');
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+        observer.observe(div, { attributes: true, attributeOldValue: true, subtree: true });
+        observer.observe(span, { attributes: true });
+        span.setAttribute('foo', 'baz');
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '1');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"foo"');
+        shouldBe('mutations[0].oldValue', '"bar"');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+function testIDLAttribute() {
+    var div;
+    var observer;
+
+    function start() {
+        debug('Testing setting an attribute via reflected IDL attribute.');
+        mutations = null;
+        div = document.createElement('div');
+        observer = new WebKitMutationObserver(function(mutations) {
+            window.mutations = mutations;
+        });
+        observer.observe(div, { attributes: true, attributeOldValue: true });
+        div.id = 'foo';
+        div.id = 'bar';
+        setTimeout(finish, 0);
+    }
+
+    function finish() {
+        shouldBe('mutations.length', '2');
+        shouldBe('mutations[0].type', '"attributes"');
+        shouldBe('mutations[0].attributeName', '"id"');
+        shouldBe('mutations[0].oldValue', 'null');
+        shouldBe('mutations[1].type', '"attributes"');
+        shouldBe('mutations[1].attributeName', '"id"');
+        shouldBe('mutations[1].oldValue', '"foo"');
+        observer.disconnect();
+        debug('');
+        runNextTest();
+    }
+
+    start();
+}
+
+var tests = [
+    testBasic,
+    testWrongType,
+    testMultipleRegistration,
+    testMultipleObservers,
+    testNamespaceURI,
+    testPropertyAccess,
+    testOrderingWrtDOMSubtreeModified,
+    testOldValue,
+    testOldValueAsRequested,
+    testOldValueUnionMultipleObservations,
+    testIDLAttribute
+];
 var testIndex = 0;
- 
+
 function runNextTest() {
     if (testIndex < tests.length)
         tests[testIndex++]();

Modified: trunk/Source/WebCore/ChangeLog (98790 => 98791)


--- trunk/Source/WebCore/ChangeLog	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/Source/WebCore/ChangeLog	2011-10-29 01:31:04 UTC (rev 98791)
@@ -1,3 +1,30 @@
+2011-10-28  Adam Klein  <[email protected]>
+
+        [MutationObservers] Support attributeOldValue for attribute mutations
+        https://bugs.webkit.org/show_bug.cgi?id=70861
+
+        Reviewed by Ryosuke Niwa.
+
+        Respect 'attributeOldValue' when passed to WebKitMutationObserver.observe().
+
+        If multiple observers have different attributeOldValue settings in
+        their registrations, two different MutationRecords are created (one is
+        a wrapper around the other).
+
+        If a single observer has multiple registrations that apply to a single
+        mutation, and those registrations have different values for
+        attributeOldValue, the observer is passed the oldValue.
+
+        * dom/Element.cpp:
+        (WebCore::hasOldValue):
+        (WebCore::enqueueAttributesMutationRecord):
+        (WebCore::Element::setAttribute):
+        * dom/MutationRecord.cpp:
+        (WebCore::MutationRecord::createAttributes):
+        (WebCore::MutationRecord::createWithNullOldValue):
+        * dom/MutationRecord.h:
+        (WebCore::MutationRecord::oldValue):
+
 2011-10-28  Adam Barth  <[email protected]>
 
         Delete ExceptionCode.cpp, which is empty

Modified: trunk/Source/WebCore/dom/Element.cpp (98790 => 98791)


--- trunk/Source/WebCore/dom/Element.cpp	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/Source/WebCore/dom/Element.cpp	2011-10-29 01:31:04 UTC (rev 98791)
@@ -617,16 +617,43 @@
 }
 
 #if ENABLE(MUTATION_OBSERVERS)
-static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name)
+static inline bool hasOldValue(MutationObserverOptions options)
 {
+    return options & WebKitMutationObserver::AttributeOldValue;
+}
+
+static inline bool isOldValueRequested(const HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers)
+{
+    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::const_iterator iter = observers.begin(); iter != observers.end(); ++iter) {
+        if (hasOldValue(iter->second))
+            return true;
+    }
+    return false;
+}
+
+static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name, const AtomicString& oldValue)
+{
     HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
     element->getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::Attributes);
     if (observers.isEmpty())
         return;
 
-    RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name);
-    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
-        iter->first->enqueueMutationRecord(mutation);
+    RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name, isOldValueRequested(observers) ? oldValue : nullAtom);
+    RefPtr<MutationRecord> mutationWithNullOldValue;
+    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter) {
+        WebKitMutationObserver* observer = iter->first;
+        if (hasOldValue(iter->second)) {
+            observer->enqueueMutationRecord(mutation);
+            continue;
+        }
+        if (!mutationWithNullOldValue) {
+            if (mutation->oldValue().isNull())
+                mutationWithNullOldValue = mutation;
+            else
+                mutationWithNullOldValue = MutationRecord::createWithNullOldValue(mutation).get();
+        }
+        observer->enqueueMutationRecord(mutationWithNullOldValue);
+    }
 }
 #endif
 
@@ -652,7 +679,7 @@
 
 #if ENABLE(MUTATION_OBSERVERS)
     // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
-    enqueueAttributesMutationRecord(this, attributeName);
+    enqueueAttributesMutationRecord(this, attributeName, old ? old->value() : nullAtom);
 #endif
 
     if (isIdAttributeName(old ? old->name() : attributeName))
@@ -690,7 +717,7 @@
 
 #if ENABLE(MUTATION_OBSERVERS)
     // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
-    enqueueAttributesMutationRecord(this, name);
+    enqueueAttributesMutationRecord(this, name, old ? old->value() : nullAtom);
 #endif
 
     if (isIdAttributeName(name))

Modified: trunk/Source/WebCore/dom/MutationRecord.cpp (98790 => 98791)


--- trunk/Source/WebCore/dom/MutationRecord.cpp	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/Source/WebCore/dom/MutationRecord.cpp	2011-10-29 01:31:04 UTC (rev 98791)
@@ -47,7 +47,7 @@
 class ChildListRecord : public MutationRecord {
 public:
     ChildListRecord(PassRefPtr<Node> target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling)
-        : MutationRecord(target)
+        : m_target(target)
         , m_addedNodes(added)
         , m_removedNodes(removed)
         , m_previousSibling(previousSibling)
@@ -56,12 +56,14 @@
     }
 
 private:
-    virtual const AtomicString& type();
-    virtual NodeList* addedNodes() { return m_addedNodes.get(); }
-    virtual NodeList* removedNodes() { return m_removedNodes.get(); }
-    virtual Node* previousSibling() { return m_previousSibling.get(); }
-    virtual Node* nextSibling() { return m_nextSibling.get(); }
+    virtual const AtomicString& type() OVERRIDE;
+    virtual Node* target() OVERRIDE { return m_target.get(); }
+    virtual NodeList* addedNodes() OVERRIDE { return m_addedNodes.get(); }
+    virtual NodeList* removedNodes() OVERRIDE { return m_removedNodes.get(); }
+    virtual Node* previousSibling() OVERRIDE { return m_previousSibling.get(); }
+    virtual Node* nextSibling() OVERRIDE { return m_nextSibling.get(); }
 
+    RefPtr<Node> m_target;
     RefPtr<NodeList> m_addedNodes;
     RefPtr<NodeList> m_removedNodes;
     RefPtr<Node> m_previousSibling;
@@ -70,40 +72,63 @@
 
 class AttributesRecord : public MutationRecord {
 public:
-    AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name)
-        : MutationRecord(target)
+    AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name, const AtomicString& oldValue)
+        : m_target(target)
         , m_attributeName(name.localName())
         , m_attributeNamespace(name.namespaceURI())
+        , m_oldValue(oldValue)
     {
     }
 
 private:
-    virtual const AtomicString& type();
-    virtual const AtomicString& attributeName() { 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; }
+    virtual const AtomicString& type() OVERRIDE;
+    virtual Node* target() OVERRIDE { return m_target.get(); }
+    virtual const AtomicString& attributeName() OVERRIDE { return m_attributeName; }
+    virtual const AtomicString& attributeNamespace() OVERRIDE { return m_attributeNamespace; }
+    virtual String oldValue() OVERRIDE { return m_oldValue; }
 
+    RefPtr<Node> m_target;
     AtomicString m_attributeName;
     AtomicString m_attributeNamespace;
-    String m_oldValue;
+    AtomicString m_oldValue;
 };
 
 class CharacterDataRecord : public MutationRecord {
 public:
     CharacterDataRecord(PassRefPtr<Node> target)
-        : MutationRecord(target)
+        : m_target(target)
     {
     }
 
 private:
-    virtual const AtomicString& type();
-    virtual String oldValue() { return m_oldValue; }
-    virtual void setOldValue(const String& value) { m_oldValue = value; }
+    virtual const AtomicString& type() OVERRIDE;
+    virtual Node* target() OVERRIDE { return m_target.get(); }
 
-    String m_oldValue;
+    RefPtr<Node> m_target;
 };
 
+class MutationRecordWithNullOldValue : public MutationRecord {
+public:
+    MutationRecordWithNullOldValue(PassRefPtr<MutationRecord> record)
+        : m_record(record)
+    {
+    }
+
+private:
+    virtual const AtomicString& type() OVERRIDE { return m_record->type(); }
+    virtual Node* target() OVERRIDE { return m_record->target(); }
+    virtual NodeList* addedNodes() OVERRIDE { return m_record->addedNodes(); }
+    virtual NodeList* removedNodes() OVERRIDE { return m_record->removedNodes(); }
+    virtual Node* previousSibling() OVERRIDE { return m_record->previousSibling(); }
+    virtual Node* nextSibling() OVERRIDE { return m_record->nextSibling(); }
+    virtual const AtomicString& attributeName() OVERRIDE { return m_record->attributeName(); }
+    virtual const AtomicString& attributeNamespace() OVERRIDE { return m_record->attributeNamespace(); }
+
+    virtual String oldValue() OVERRIDE { return String(); }
+
+    RefPtr<MutationRecord> m_record;
+};
+
 const AtomicString& ChildListRecord::type()
 {
     DEFINE_STATIC_LOCAL(AtomicString, childList, ("childList"));
@@ -129,9 +154,9 @@
     return adoptRef(static_cast<MutationRecord*>(new ChildListRecord(target, added, removed, previousSibling, nextSibling)));
 }
 
-PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name)
+PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name, const AtomicString& oldValue)
 {
-    return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name)));
+    return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name, oldValue)));
 }
 
 PassRefPtr<MutationRecord> MutationRecord::createCharacterData(PassRefPtr<Node> target)
@@ -139,9 +164,9 @@
     return adoptRef(static_cast<MutationRecord*>(new CharacterDataRecord(target)));
 }
 
-MutationRecord::MutationRecord(PassRefPtr<Node> target)
-    : m_target(target)
+PassRefPtr<MutationRecord> MutationRecord::createWithNullOldValue(PassRefPtr<MutationRecord> record)
 {
+    return adoptRef(static_cast<MutationRecord*>(new MutationRecordWithNullOldValue(record)));
 }
 
 MutationRecord::~MutationRecord()

Modified: trunk/Source/WebCore/dom/MutationRecord.h (98790 => 98791)


--- trunk/Source/WebCore/dom/MutationRecord.h	2011-10-29 01:14:41 UTC (rev 98790)
+++ trunk/Source/WebCore/dom/MutationRecord.h	2011-10-29 01:31:04 UTC (rev 98791)
@@ -47,13 +47,15 @@
 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 QualifiedName&);
+    static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const QualifiedName&, const AtomicString& oldValue);
     static PassRefPtr<MutationRecord> createCharacterData(PassRefPtr<Node> target);
 
+    static PassRefPtr<MutationRecord> createWithNullOldValue(PassRefPtr<MutationRecord>);
+
     virtual ~MutationRecord();
 
     virtual const AtomicString& type() = 0;
-    Node* target() { return m_target.get(); }
+    virtual Node* target() = 0;
 
     virtual NodeList* addedNodes() { return 0; }
     virtual NodeList* removedNodes() { return 0; }
@@ -64,16 +66,8 @@
     virtual const AtomicString& attributeNamespace() { return nullAtom; }
 
     virtual String oldValue() { return String(); }
-    virtual void setOldValue(const String&) { }
-
-protected:
-    explicit MutationRecord(PassRefPtr<Node> target);
-
-private:
-    RefPtr<Node> m_target;
 };
 
-
 } // namespace WebCore
 
 #endif // ENABLE(MUTATION_OBSERVERS)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to