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)