Changeset 98791 in webkit


Ignore:
Timestamp:
Oct 28, 2011 6:31:04 PM (12 years ago)
Author:
adamk@chromium.org
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:
Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r98789 r98791  
     12011-10-28  Adam Klein  <adamk@chromium.org>
     2
     3        [MutationObservers] Support attributeOldValue for attribute mutations
     4        https://bugs.webkit.org/show_bug.cgi?id=70861
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Added test cases for attributeOldValue to existing tests.
     9
     10        * fast/mutation/observe-attributes-expected.txt:
     11        * fast/mutation/observe-attributes.html:
     12
    1132011-10-28  Sheriff Bot  <webkit.review.bot@gmail.com>
    214
  • trunk/LayoutTests/fast/mutation/observe-attributes-expected.txt

    r98121 r98791  
    5959PASS mutations[1].attributeName is "baz"
    6060
     61Testing basic oldValue delivery.
     62PASS mutations.length is 2
     63PASS mutations[0].type is "attributes"
     64PASS mutations[0].attributeName is "foo"
     65PASS mutations[0].oldValue is null
     66PASS mutations[1].type is "attributes"
     67PASS mutations[1].attributeName is "foo"
     68PASS mutations[1].oldValue is "bar"
     69
     70Testing that oldValue is delivered as requested (or not).
     71PASS mutationsWithOldValue.length is 1
     72PASS mutationsWithOldValue[0].type is "attributes"
     73PASS mutationsWithOldValue[0].attributeName is "foo"
     74PASS mutationsWithOldValue[0].oldValue is "bar"
     75PASS mutations.length is 1
     76PASS mutations[0].type is "attributes"
     77PASS mutations[0].attributeName is "foo"
     78PASS mutations[0].oldValue is null
     79
     80An observer with multiple observations will get attributeOldValue if any entries request it.
     81PASS mutations.length is 1
     82PASS mutations[0].type is "attributes"
     83PASS mutations[0].attributeName is "foo"
     84PASS mutations[0].oldValue is "bar"
     85
     86Testing setting an attribute via reflected IDL attribute.
     87PASS mutations.length is 2
     88PASS mutations[0].type is "attributes"
     89PASS mutations[0].attributeName is "id"
     90PASS mutations[0].oldValue is null
     91PASS mutations[1].type is "attributes"
     92PASS mutations[1].attributeName is "id"
     93PASS mutations[1].oldValue is "foo"
     94
    6195PASS successfullyParsed is true
    6296
  • trunk/LayoutTests/fast/mutation/observe-attributes.html

    r98407 r98791  
    1111
    1212window.jsTestIsAsync = true;
    13 var mutations, mutations2;
     13var mutations, mutations2, mutationsWithOldValue;
    1414var calls;
    1515
     
    295295    start();
    296296}
    297 var tests = [testBasic, testWrongType, testMultipleRegistration, testMultipleObservers, testNamespaceURI, testPropertyAccess, testOrderingWrtDOMSubtreeModified];
     297
     298function testOldValue() {
     299    var div;
     300    var observer;
     301
     302    function start() {
     303        debug('Testing basic oldValue delivery.');
     304        mutations = null;
     305        div = document.createElement('div');
     306        observer = new WebKitMutationObserver(function(mutations) {
     307            window.mutations = mutations;
     308        });
     309        observer.observe(div, { attributes: true, attributeOldValue: true });
     310        div.setAttribute('foo', 'bar');
     311        div.setAttribute('foo', 'baz');
     312        setTimeout(finish, 0);
     313    }
     314
     315    function finish() {
     316        shouldBe('mutations.length', '2');
     317        shouldBe('mutations[0].type', '"attributes"');
     318        shouldBe('mutations[0].attributeName', '"foo"');
     319        shouldBe('mutations[0].oldValue', 'null');
     320        shouldBe('mutations[1].type', '"attributes"');
     321        shouldBe('mutations[1].attributeName', '"foo"');
     322        shouldBe('mutations[1].oldValue', '"bar"');
     323        observer.disconnect();
     324        debug('');
     325        runNextTest();
     326    }
     327
     328    start();
     329}
     330
     331function testOldValueAsRequested() {
     332    var div;
     333    var observerWithOldValue;
     334    var observer;
     335
     336    function start() {
     337        debug('Testing that oldValue is delivered as requested (or not).');
     338        mutationsWithOldValue = null;
     339        mutations = null;
     340        div = document.createElement('div');
     341        div.setAttribute('foo', 'bar');
     342        observerWithOldValue = new WebKitMutationObserver(function(mutations) {
     343            window.mutationsWithOldValue = mutations;
     344        });
     345        observer = new WebKitMutationObserver(function(mutations) {
     346            window.mutations = mutations;
     347        });
     348        observerWithOldValue.observe(div, { attributes: true, attributeOldValue: true });
     349        observer.observe(div, { attributes: true });
     350        div.setAttribute('foo', 'baz');
     351        setTimeout(finish, 0);
     352    }
     353
     354    function finish() {
     355        shouldBe('mutationsWithOldValue.length', '1');
     356        shouldBe('mutationsWithOldValue[0].type', '"attributes"');
     357        shouldBe('mutationsWithOldValue[0].attributeName', '"foo"');
     358        shouldBe('mutationsWithOldValue[0].oldValue', '"bar"');
     359        shouldBe('mutations.length', '1');
     360        shouldBe('mutations[0].type', '"attributes"');
     361        shouldBe('mutations[0].attributeName', '"foo"');
     362        shouldBe('mutations[0].oldValue', 'null');
     363        observerWithOldValue.disconnect();
     364        observer.disconnect();
     365        debug('');
     366        runNextTest();
     367    }
     368
     369    start();
     370}
     371
     372function testOldValueUnionMultipleObservations() {
     373    var div;
     374    var span;
     375    var observer;
     376
     377    function start() {
     378        debug('An observer with multiple observations will get attributeOldValue if any entries request it.');
     379        mutations = null;
     380        div = document.createElement('div');
     381        span = div.appendChild(document.createElement('span'));
     382        span.setAttribute('foo', 'bar');
     383        observer = new WebKitMutationObserver(function(mutations) {
     384            window.mutations = mutations;
     385        });
     386        observer.observe(div, { attributes: true, attributeOldValue: true, subtree: true });
     387        observer.observe(span, { attributes: true });
     388        span.setAttribute('foo', 'baz');
     389        setTimeout(finish, 0);
     390    }
     391
     392    function finish() {
     393        shouldBe('mutations.length', '1');
     394        shouldBe('mutations[0].type', '"attributes"');
     395        shouldBe('mutations[0].attributeName', '"foo"');
     396        shouldBe('mutations[0].oldValue', '"bar"');
     397        observer.disconnect();
     398        debug('');
     399        runNextTest();
     400    }
     401
     402    start();
     403}
     404
     405function testIDLAttribute() {
     406    var div;
     407    var observer;
     408
     409    function start() {
     410        debug('Testing setting an attribute via reflected IDL attribute.');
     411        mutations = null;
     412        div = document.createElement('div');
     413        observer = new WebKitMutationObserver(function(mutations) {
     414            window.mutations = mutations;
     415        });
     416        observer.observe(div, { attributes: true, attributeOldValue: true });
     417        div.id = 'foo';
     418        div.id = 'bar';
     419        setTimeout(finish, 0);
     420    }
     421
     422    function finish() {
     423        shouldBe('mutations.length', '2');
     424        shouldBe('mutations[0].type', '"attributes"');
     425        shouldBe('mutations[0].attributeName', '"id"');
     426        shouldBe('mutations[0].oldValue', 'null');
     427        shouldBe('mutations[1].type', '"attributes"');
     428        shouldBe('mutations[1].attributeName', '"id"');
     429        shouldBe('mutations[1].oldValue', '"foo"');
     430        observer.disconnect();
     431        debug('');
     432        runNextTest();
     433    }
     434
     435    start();
     436}
     437
     438var tests = [
     439    testBasic,
     440    testWrongType,
     441    testMultipleRegistration,
     442    testMultipleObservers,
     443    testNamespaceURI,
     444    testPropertyAccess,
     445    testOrderingWrtDOMSubtreeModified,
     446    testOldValue,
     447    testOldValueAsRequested,
     448    testOldValueUnionMultipleObservations,
     449    testIDLAttribute
     450];
    298451var testIndex = 0;
    299  
     452
    300453function runNextTest() {
    301454    if (testIndex < tests.length)
  • trunk/Source/WebCore/ChangeLog

    r98790 r98791  
     12011-10-28  Adam Klein  <adamk@chromium.org>
     2
     3        [MutationObservers] Support attributeOldValue for attribute mutations
     4        https://bugs.webkit.org/show_bug.cgi?id=70861
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Respect 'attributeOldValue' when passed to WebKitMutationObserver.observe().
     9
     10        If multiple observers have different attributeOldValue settings in
     11        their registrations, two different MutationRecords are created (one is
     12        a wrapper around the other).
     13
     14        If a single observer has multiple registrations that apply to a single
     15        mutation, and those registrations have different values for
     16        attributeOldValue, the observer is passed the oldValue.
     17
     18        * dom/Element.cpp:
     19        (WebCore::hasOldValue):
     20        (WebCore::enqueueAttributesMutationRecord):
     21        (WebCore::Element::setAttribute):
     22        * dom/MutationRecord.cpp:
     23        (WebCore::MutationRecord::createAttributes):
     24        (WebCore::MutationRecord::createWithNullOldValue):
     25        * dom/MutationRecord.h:
     26        (WebCore::MutationRecord::oldValue):
     27
    1282011-10-28  Adam Barth  <abarth@webkit.org>
    229
  • trunk/Source/WebCore/dom/Element.cpp

    r98659 r98791  
    618618
    619619#if ENABLE(MUTATION_OBSERVERS)
    620 static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name)
     620static inline bool hasOldValue(MutationObserverOptions options)
     621{
     622    return options & WebKitMutationObserver::AttributeOldValue;
     623}
     624
     625static inline bool isOldValueRequested(const HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers)
     626{
     627    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::const_iterator iter = observers.begin(); iter != observers.end(); ++iter) {
     628        if (hasOldValue(iter->second))
     629            return true;
     630    }
     631    return false;
     632}
     633
     634static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name, const AtomicString& oldValue)
    621635{
    622636    HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
     
    625639        return;
    626640
    627     RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name);
    628     for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
    629         iter->first->enqueueMutationRecord(mutation);
     641    RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name, isOldValueRequested(observers) ? oldValue : nullAtom);
     642    RefPtr<MutationRecord> mutationWithNullOldValue;
     643    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter) {
     644        WebKitMutationObserver* observer = iter->first;
     645        if (hasOldValue(iter->second)) {
     646            observer->enqueueMutationRecord(mutation);
     647            continue;
     648        }
     649        if (!mutationWithNullOldValue) {
     650            if (mutation->oldValue().isNull())
     651                mutationWithNullOldValue = mutation;
     652            else
     653                mutationWithNullOldValue = MutationRecord::createWithNullOldValue(mutation).get();
     654        }
     655        observer->enqueueMutationRecord(mutationWithNullOldValue);
     656    }
    630657}
    631658#endif
     
    653680#if ENABLE(MUTATION_OBSERVERS)
    654681    // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
    655     enqueueAttributesMutationRecord(this, attributeName);
     682    enqueueAttributesMutationRecord(this, attributeName, old ? old->value() : nullAtom);
    656683#endif
    657684
     
    691718#if ENABLE(MUTATION_OBSERVERS)
    692719    // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
    693     enqueueAttributesMutationRecord(this, name);
     720    enqueueAttributesMutationRecord(this, name, old ? old->value() : nullAtom);
    694721#endif
    695722
  • trunk/Source/WebCore/dom/MutationRecord.cpp

    r97659 r98791  
    4848public:
    4949    ChildListRecord(PassRefPtr<Node> target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling)
    50         : MutationRecord(target)
     50        : m_target(target)
    5151        , m_addedNodes(added)
    5252        , m_removedNodes(removed)
     
    5757
    5858private:
    59     virtual const AtomicString& type();
    60     virtual NodeList* addedNodes() { return m_addedNodes.get(); }
    61     virtual NodeList* removedNodes() { return m_removedNodes.get(); }
    62     virtual Node* previousSibling() { return m_previousSibling.get(); }
    63     virtual Node* nextSibling() { return m_nextSibling.get(); }
     59    virtual const AtomicString& type() OVERRIDE;
     60    virtual Node* target() OVERRIDE { return m_target.get(); }
     61    virtual NodeList* addedNodes() OVERRIDE { return m_addedNodes.get(); }
     62    virtual NodeList* removedNodes() OVERRIDE { return m_removedNodes.get(); }
     63    virtual Node* previousSibling() OVERRIDE { return m_previousSibling.get(); }
     64    virtual Node* nextSibling() OVERRIDE { return m_nextSibling.get(); }
    6465
     66    RefPtr<Node> m_target;
    6567    RefPtr<NodeList> m_addedNodes;
    6668    RefPtr<NodeList> m_removedNodes;
     
    7173class AttributesRecord : public MutationRecord {
    7274public:
    73     AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name)
    74         : MutationRecord(target)
     75    AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name, const AtomicString& oldValue)
     76        : m_target(target)
    7577        , m_attributeName(name.localName())
    7678        , m_attributeNamespace(name.namespaceURI())
     79        , m_oldValue(oldValue)
    7780    {
    7881    }
    7982
    8083private:
    81     virtual const AtomicString& type();
    82     virtual const AtomicString& attributeName() { return m_attributeName; }
    83     virtual const AtomicString& attributeNamespace() { return m_attributeNamespace; }
    84     virtual String oldValue() { return m_oldValue; }
    85     virtual void setOldValue(const String& value) { m_oldValue = value; }
     84    virtual const AtomicString& type() OVERRIDE;
     85    virtual Node* target() OVERRIDE { return m_target.get(); }
     86    virtual const AtomicString& attributeName() OVERRIDE { return m_attributeName; }
     87    virtual const AtomicString& attributeNamespace() OVERRIDE { return m_attributeNamespace; }
     88    virtual String oldValue() OVERRIDE { return m_oldValue; }
    8689
     90    RefPtr<Node> m_target;
    8791    AtomicString m_attributeName;
    8892    AtomicString m_attributeNamespace;
    89     String m_oldValue;
     93    AtomicString m_oldValue;
    9094};
    9195
     
    9397public:
    9498    CharacterDataRecord(PassRefPtr<Node> target)
    95         : MutationRecord(target)
     99        : m_target(target)
    96100    {
    97101    }
    98102
    99103private:
    100     virtual const AtomicString& type();
    101     virtual String oldValue() { return m_oldValue; }
    102     virtual void setOldValue(const String& value) { m_oldValue = value; }
     104    virtual const AtomicString& type() OVERRIDE;
     105    virtual Node* target() OVERRIDE { return m_target.get(); }
    103106
    104     String m_oldValue;
     107    RefPtr<Node> m_target;
     108};
     109
     110class MutationRecordWithNullOldValue : public MutationRecord {
     111public:
     112    MutationRecordWithNullOldValue(PassRefPtr<MutationRecord> record)
     113        : m_record(record)
     114    {
     115    }
     116
     117private:
     118    virtual const AtomicString& type() OVERRIDE { return m_record->type(); }
     119    virtual Node* target() OVERRIDE { return m_record->target(); }
     120    virtual NodeList* addedNodes() OVERRIDE { return m_record->addedNodes(); }
     121    virtual NodeList* removedNodes() OVERRIDE { return m_record->removedNodes(); }
     122    virtual Node* previousSibling() OVERRIDE { return m_record->previousSibling(); }
     123    virtual Node* nextSibling() OVERRIDE { return m_record->nextSibling(); }
     124    virtual const AtomicString& attributeName() OVERRIDE { return m_record->attributeName(); }
     125    virtual const AtomicString& attributeNamespace() OVERRIDE { return m_record->attributeNamespace(); }
     126
     127    virtual String oldValue() OVERRIDE { return String(); }
     128
     129    RefPtr<MutationRecord> m_record;
    105130};
    106131
     
    130155}
    131156
    132 PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name)
     157PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name, const AtomicString& oldValue)
    133158{
    134     return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name)));
     159    return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name, oldValue)));
    135160}
    136161
     
    140165}
    141166
    142 MutationRecord::MutationRecord(PassRefPtr<Node> target)
    143     : m_target(target)
     167PassRefPtr<MutationRecord> MutationRecord::createWithNullOldValue(PassRefPtr<MutationRecord> record)
    144168{
     169    return adoptRef(static_cast<MutationRecord*>(new MutationRecordWithNullOldValue(record)));
    145170}
    146171
  • trunk/Source/WebCore/dom/MutationRecord.h

    r97659 r98791  
    4848public:
    4949    static PassRefPtr<MutationRecord> createChildList(PassRefPtr<Node> target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling);
    50     static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const QualifiedName&);
     50    static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const QualifiedName&, const AtomicString& oldValue);
    5151    static PassRefPtr<MutationRecord> createCharacterData(PassRefPtr<Node> target);
     52
     53    static PassRefPtr<MutationRecord> createWithNullOldValue(PassRefPtr<MutationRecord>);
    5254
    5355    virtual ~MutationRecord();
    5456
    5557    virtual const AtomicString& type() = 0;
    56     Node* target() { return m_target.get(); }
     58    virtual Node* target() = 0;
    5759
    5860    virtual NodeList* addedNodes() { return 0; }
     
    6567
    6668    virtual String oldValue() { return String(); }
    67     virtual void setOldValue(const String&) { }
    68 
    69 protected:
    70     explicit MutationRecord(PassRefPtr<Node> target);
    71 
    72 private:
    73     RefPtr<Node> m_target;
    7469};
    75 
    7670
    7771} // namespace WebCore
Note: See TracChangeset for help on using the changeset viewer.