Changeset 97659 in webkit


Ignore:
Timestamp:
Oct 17, 2011 3:31:25 PM (13 years ago)
Author:
adamk@chromium.org
Message:

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

Patch by Rafael Weinstein <rafaelw@chromium.org> on 2011-10-17
Reviewed by Ryosuke Niwa.

Source/WebCore:

Test: fast/mutation/observe-attributes.html

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

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

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

  • bindings/v8/V8Proxy.cpp:

(WebCore::V8Proxy::didLeaveScriptContext):

  • dom/Element.cpp:

(WebCore::enqueueAttributesMutationRecord):
(WebCore::Element::setAttribute):

  • dom/MutationRecord.cpp:

(WebCore::MutationRecord::createAttributes):

  • dom/MutationRecord.h:
  • dom/MutationRecord.idl:
  • dom/Node.cpp:

(WebCore::Node::clearRareData):
(WebCore::Node::mutationObserverData):
(WebCore::Node::ensureMutationObserverData):
(WebCore::Node::registeredMutationObserversOfType):
(WebCore::Node::registerMutationObserver):
(WebCore::Node::deregisterMutationObserver):

  • dom/Node.h:
  • dom/NodeRareData.h:

(WebCore::MutationObserverRegistration::MutationObserverRegistration):
(WebCore::MutationObserverRegistration::operator==):
(WebCore::MutationObserverData::MutationObserverData):
(WebCore::MutationObserverData::~MutationObserverData):
(WebCore::NodeRareData::mutationObserverData):
(WebCore::NodeRareData::ensureMutationObserverData):

  • dom/WebKitMutationObserver.cpp:

(WebCore::WebKitMutationObserver::observe):
(WebCore::WebKitMutationObserver::disconnect):
(WebCore::WebKitMutationObserver::wasDeregistered):
(WebCore::activeMutationObservers):
(WebCore::WebKitMutationObserver::enqueueMutationRecord):
(WebCore::WebKitMutationObserver::deliverAllMutations):
(WebCore::WebKitMutationObserver::deliver):

  • dom/WebKitMutationObserver.h:

LayoutTests:

  • fast/mutation/observe-attributes-expected.txt: Added.
  • fast/mutation/observe-attributes.html: Added.
Location:
trunk
Files:
2 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r97657 r97659  
     12011-10-17  Rafael Weinstein  <rafaelw@chromium.org>
     2
     3        [MutationObservers] Implement WebKitMutationObserver.observe for attributes
     4        https://bugs.webkit.org/show_bug.cgi?id=68956
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        * fast/mutation/observe-attributes-expected.txt: Added.
     9        * fast/mutation/observe-attributes.html: Added.
     10
    1112011-10-17  Dirk Pranke  <dpranke@chromium.org>
    212
  • trunk/Source/WebCore/ChangeLog

    r97658 r97659  
     12011-10-17  Rafael Weinstein  <rafaelw@chromium.org>
     2
     3        [MutationObservers] Implement WebKitMutationObserver.observe for attributes
     4        https://bugs.webkit.org/show_bug.cgi?id=68956
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Test: fast/mutation/observe-attributes.html
     9
     10        This adds an initial implementation for registering mutation observers on nodes,
     11        delivering mutation records at the end of the outer-most script invokation and
     12        observing mutations to element attributes.
     13
     14        Note that the outer-most script invokation only works in V8.
     15
     16        Note also that support for observing changes to the style attribute when updated
     17        via the style property is not implemented here.
     18
     19        * bindings/v8/V8Proxy.cpp:
     20        (WebCore::V8Proxy::didLeaveScriptContext):
     21        * dom/Element.cpp:
     22        (WebCore::enqueueAttributesMutationRecord):
     23        (WebCore::Element::setAttribute):
     24        * dom/MutationRecord.cpp:
     25        (WebCore::MutationRecord::createAttributes):
     26        * dom/MutationRecord.h:
     27        * dom/MutationRecord.idl:
     28        * dom/Node.cpp:
     29        (WebCore::Node::clearRareData):
     30        (WebCore::Node::mutationObserverData):
     31        (WebCore::Node::ensureMutationObserverData):
     32        (WebCore::Node::registeredMutationObserversOfType):
     33        (WebCore::Node::registerMutationObserver):
     34        (WebCore::Node::deregisterMutationObserver):
     35        * dom/Node.h:
     36        * dom/NodeRareData.h:
     37        (WebCore::MutationObserverRegistration::MutationObserverRegistration):
     38        (WebCore::MutationObserverRegistration::operator==):
     39        (WebCore::MutationObserverData::MutationObserverData):
     40        (WebCore::MutationObserverData::~MutationObserverData):
     41        (WebCore::NodeRareData::mutationObserverData):
     42        (WebCore::NodeRareData::ensureMutationObserverData):
     43        * dom/WebKitMutationObserver.cpp:
     44        (WebCore::WebKitMutationObserver::observe):
     45        (WebCore::WebKitMutationObserver::disconnect):
     46        (WebCore::WebKitMutationObserver::wasDeregistered):
     47        (WebCore::activeMutationObservers):
     48        (WebCore::WebKitMutationObserver::enqueueMutationRecord):
     49        (WebCore::WebKitMutationObserver::deliverAllMutations):
     50        (WebCore::WebKitMutationObserver::deliver):
     51        * dom/WebKitMutationObserver.h:
     52
    1532011-10-17  Andreas Kling  <kling@webkit.org>
    254
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r97637 r97659  
    54675467                C6F0900A14327B6100685849 /* MutationCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900114327B6100685849 /* MutationCallback.h */; };
    54685468                C6F0900E14327B6100685849 /* WebKitMutationObserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6F0900514327B6100685849 /* WebKitMutationObserver.cpp */; };
    5469                 C6F0900F14327B6100685849 /* WebKitMutationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900614327B6100685849 /* WebKitMutationObserver.h */; };
     5469                C6F0900F14327B6100685849 /* WebKitMutationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900614327B6100685849 /* WebKitMutationObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
    54705470                C6F0901114327B6100685849 /* MutationObserverOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900814327B6100685849 /* MutationObserverOptions.h */; };
    54715471                C6F0902814327D4F00685849 /* JSMutationCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6F0902014327D4F00685849 /* JSMutationCallback.cpp */; };
  • trunk/Source/WebCore/bindings/v8/V8Proxy.cpp

    r97280 r97659  
    6666#include "V8XMLHttpRequestException.h"
    6767#include "V8XPathException.h"
     68#include "WebKitMutationObserver.h"
    6869#include "WorkerContext.h"
    6970#include "WorkerContextExecutionProxy.h"
     
    621622    if (page->group().hasLocalStorage())
    622623        page->group().localStorage()->unlock();
     624
     625#if ENABLE(MUTATION_OBSERVERS)
     626    WebCore::WebKitMutationObserver::deliverAllMutations();
     627#endif
    623628}
    624629
  • trunk/Source/WebCore/dom/Element.cpp

    r97638 r97659  
    4949#include "HTMLParserIdioms.h"
    5050#include "InspectorInstrumentation.h"
     51#include "MutationRecord.h"
    5152#include "NodeList.h"
    5253#include "NodeRenderStyle.h"
     
    6061#include "Text.h"
    6162#include "TextIterator.h"
     63#include "WebKitMutationObserver.h"
    6264#include "WebKitAnimationList.h"
    6365#include "XMLNames.h"
     
    611613}
    612614
     615#if ENABLE(MUTATION_OBSERVERS)
     616static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name)
     617{
     618    Vector<WebKitMutationObserver*> observers;
     619    element->registeredMutationObserversOfType(observers, WebKitMutationObserver::Attributes);
     620    if (observers.isEmpty())
     621        return;
     622
     623    RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name);
     624    for (Vector<WebKitMutationObserver*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
     625        (*iter)->enqueueMutationRecord(mutation);
     626}
     627#endif
     628
    613629void Element::setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode& ec)
    614630{
     
    630646
    631647    document()->incDOMTreeVersion();
     648
     649#if ENABLE(MUTATION_OBSERVERS)
     650    // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
     651    enqueueAttributesMutationRecord(this, attributeName);
     652#endif
    632653
    633654    if (isIdAttributeName(old ? old->name() : attributeName))
     
    663684    // Allocate attribute map if necessary.
    664685    Attribute* old = attributes(false)->getAttributeItem(name);
     686
     687#if ENABLE(MUTATION_OBSERVERS)
     688    // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now.
     689    enqueueAttributesMutationRecord(this, name);
     690#endif
    665691
    666692    if (isIdAttributeName(name))
  • trunk/Source/WebCore/dom/MutationRecord.cpp

    r96064 r97659  
    3737#include "Node.h"
    3838#include "NodeList.h"
     39#include "QualifiedName.h"
    3940#include <wtf/Assertions.h>
    4041#include <wtf/StdLibExtras.h>
     
    7071class AttributesRecord : public MutationRecord {
    7172public:
    72     AttributesRecord(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace)
     73    AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name)
    7374        : MutationRecord(target)
    74         , m_attributeName(attributeName)
    75         , m_attributeNamespace(attributeNamespace)
     75        , m_attributeName(name.localName())
     76        , m_attributeNamespace(name.namespaceURI())
    7677    {
    7778    }
     
    8081    virtual const AtomicString& type();
    8182    virtual const AtomicString& attributeName() { return m_attributeName; }
    82     virtual const AtomicString& attributeNamespace() { return m_attributeName; }
     83    virtual const AtomicString& attributeNamespace() { return m_attributeNamespace; }
    8384    virtual String oldValue() { return m_oldValue; }
    8485    virtual void setOldValue(const String& value) { m_oldValue = value; }
     
    129130}
    130131
    131 PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace)
     132PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name)
    132133{
    133     return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, attributeName, attributeNamespace)));
     134    return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name)));
    134135}
    135136
  • trunk/Source/WebCore/dom/MutationRecord.h

    r96064 r97659  
    4343class Node;
    4444class NodeList;
     45class QualifiedName;
    4546
    4647class MutationRecord : public RefCounted<MutationRecord> {
    4748public:
    4849    static PassRefPtr<MutationRecord> createChildList(PassRefPtr<Node> target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling);
    49     static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace);
     50    static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const QualifiedName&);
    5051    static PassRefPtr<MutationRecord> createCharacterData(PassRefPtr<Node> target);
    5152
  • trunk/Source/WebCore/dom/MutationRecord.idl

    r96064 r97659  
    4242
    4343        readonly attribute DOMString attributeName;
    44         readonly attribute DOMString attributeNamespace;
     44        readonly attribute [ConvertNullStringTo=Null] DOMString attributeNamespace;
    4545
    46         readonly attribute DOMString oldValue;
     46        readonly attribute [ConvertNullStringTo=Null] DOMString oldValue;
    4747    };
    4848}
  • trunk/Source/WebCore/dom/Node.cpp

    r97574 r97659  
    101101#include <wtf/RefCountedLeakCounter.h>
    102102#include <wtf/UnusedParam.h>
     103#include <wtf/Vector.h>
    103104#include <wtf/text/CString.h>
    104105#include <wtf/text/StringBuilder.h>
     
    547548        treeScope()->removeNodeListCache();
    548549
     550#if ENABLE(MUTATION_OBSERVERS)
     551    Vector<MutationObserverEntry>* observerEnties = mutationObserverEntries();
     552    if (observerEnties) {
     553        for (Vector<MutationObserverEntry>::iterator iter = observerEnties->begin(); iter != observerEnties->end(); ++iter)
     554            iter->observer->observedNodeDestructed(this);
     555    }
     556#endif
     557
    549558    NodeRareData::NodeRareDataMap& dataMap = NodeRareData::rareDataMap();
    550559    NodeRareData::NodeRareDataMap::iterator it = dataMap.find(this);
     
    26742683}
    26752684
     2685#if ENABLE(MUTATION_OBSERVERS)
     2686Vector<MutationObserverEntry>* Node::mutationObserverEntries()
     2687{
     2688    return hasRareData() ? rareData()->mutationObserverEntries() : 0;
     2689}
     2690
     2691Vector<MutationObserverEntry>* Node::ensureMutationObserverEntries()
     2692{
     2693    return ensureRareData()->ensureMutationObserverEntries();
     2694}
     2695
     2696void Node::registeredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type)
     2697{
     2698    Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
     2699    if (!observerEntries || observerEntries->isEmpty())
     2700        return;
     2701
     2702    for (Vector<MutationObserverEntry>::iterator iter = observerEntries->begin(); iter != observerEntries->end(); ++iter) {
     2703        if (iter->matches(type))
     2704            observers.append(iter->observer.get());
     2705    }
     2706}
     2707
     2708Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, unsigned char options)
     2709{
     2710    Vector<MutationObserverEntry>* observerEntries = ensureMutationObserverEntries();
     2711    MutationObserverEntry entry(observer, options);
     2712
     2713    size_t index = observerEntries->find(entry);
     2714    if (index == notFound) {
     2715        observerEntries->append(entry);
     2716        return MutationObserverRegistered;
     2717    }
     2718
     2719    (*observerEntries)[index].options = entry.options;
     2720    return MutationRegistrationOptionsReset;
     2721}
     2722
     2723void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer)
     2724{
     2725    Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
     2726    ASSERT(observerEntries);
     2727    if (!observerEntries)
     2728        return;
     2729
     2730    MutationObserverEntry entry(observer, 0);
     2731    size_t index = observerEntries->find(entry);
     2732    ASSERT(index != notFound);
     2733    if (index == notFound)
     2734        return;
     2735
     2736    observerEntries->remove(index);
     2737}
     2738#endif // ENABLE(MUTATION_OBSERVERS)
     2739
     2740
    26762741void Node::handleLocalEvents(Event* event)
    26772742{
  • trunk/Source/WebCore/dom/Node.h

    r97471 r97659  
    3232#include "ScriptWrappable.h"
    3333#include "TreeShared.h"
     34#include "WebKitMutationObserver.h"
    3435#include <wtf/Forward.h>
    3536#include <wtf/ListHashSet.h>
     
    5960class IntRect;
    6061class KeyboardEvent;
     62class MutationObserverEntry;
    6163class NSResolver;
    6264class NamedNodeMap;
     
    586588#endif
    587589
     590#if ENABLE(MUTATION_OBSERVERS)
     591    Vector<MutationObserverEntry>* mutationObserverEntries();
     592    Vector<MutationObserverEntry>* ensureMutationObserverEntries();
     593
     594    void registeredMutationObserversOfType(Vector<WebKitMutationObserver*>&, WebKitMutationObserver::MutationType);
     595
     596    // Returns true if the observer wasn't already registered on this node.
     597    enum MutationRegistrationResult {
     598        MutationObserverRegistered,
     599        MutationRegistrationOptionsReset
     600    };
     601    MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, unsigned char options);
     602
     603    void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>);
     604#endif // ENABLE(MUTATION_OBSERVERS)
     605
    588606private:
    589607    enum NodeFlags {
  • trunk/Source/WebCore/dom/NodeRareData.h

    r97471 r97659  
    3333#include "QualifiedName.h"
    3434#include "TagNodeList.h"
     35#include "WebKitMutationObserver.h"
    3536#include <wtf/HashSet.h>
    3637#include <wtf/OwnPtr.h>
     
    8788    NodeListsNodeData() : m_labelsNodeListCache(0) {}
    8889};
    89    
     90
     91#if ENABLE(MUTATION_OBSERVERS)
     92struct MutationObserverEntry {
     93    MutationObserverEntry(PassRefPtr<WebKitMutationObserver> observer, unsigned char options)
     94        : observer(observer)
     95        , options(options)
     96    {
     97    }
     98
     99    bool operator==(const MutationObserverEntry& other) const
     100    {
     101        return observer == other.observer;
     102    }
     103
     104    bool matches(unsigned char options) const
     105    {
     106        return this->options & options;
     107    }
     108
     109    RefPtr<WebKitMutationObserver> observer;
     110    unsigned char options;
     111};
     112
     113#endif // ENABLE(MUTATION_OBSERVERS)
     114
    90115class NodeRareData {
    91116    WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED;
     
    137162    }
    138163
     164#if ENABLE(MUTATION_OBSERVERS)
     165    Vector<MutationObserverEntry>* mutationObserverEntries() { return m_mutationObservers.get(); }
     166    Vector<MutationObserverEntry>* ensureMutationObserverEntries()
     167    {
     168        if (!m_mutationObservers)
     169            m_mutationObservers = adoptPtr(new Vector<MutationObserverEntry>);
     170        return m_mutationObservers.get();
     171    }
     172#endif
     173
    139174    bool isFocused() const { return m_isFocused; }
    140175    void setFocused(bool focused) { m_isFocused = focused; }
     
    153188    bool m_isFocused : 1;
    154189    bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1;
     190
     191#if ENABLE(MUTATION_OBSERVERS)
     192    OwnPtr<Vector<MutationObserverEntry> > m_mutationObservers;
     193#endif
    155194};
    156195
  • trunk/Source/WebCore/dom/WebKitMutationObserver.cpp

    r97159 r97659  
    3535#include "WebKitMutationObserver.h"
    3636
     37#include "MutationObserverOptions.h"
    3738#include "MutationCallback.h"
    38 #include "NotImplemented.h"
     39#include "MutationRecord.h"
     40#include "Node.h"
     41#include <wtf/ListHashSet.h>
    3942
    4043namespace WebCore {
     
    5457}
    5558
    56 void WebKitMutationObserver::observe(Node*, MutationObserverOptions*)
     59void WebKitMutationObserver::observe(Node* node, MutationObserverOptions* options)
    5760{
    58     notImplemented();
     61    unsigned char optionFlags = 0;
     62
     63    // FIXME: Push composition of the optionFlags into the custom binding.
     64    if (options->childList())
     65        optionFlags |= ChildList;
     66    if (options->attributes())
     67        optionFlags |= Attributes;
     68    if (options->characterData())
     69        optionFlags |= CharacterData;
     70    // FIXME: More options composition work needs to be done here.
     71
     72    if (node->registerMutationObserver(this, optionFlags) == Node::MutationObserverRegistered)
     73        m_observedNodes.append(node);
    5974}
    6075
    6176void WebKitMutationObserver::disconnect()
    6277{
    63     notImplemented();
     78    for (Vector<Node*>::iterator iter = m_observedNodes.begin(); iter !=  m_observedNodes.end(); ++iter)
     79        (*iter)->unregisterMutationObserver(this);
     80
     81    m_observedNodes.clear();
     82}
     83
     84void WebKitMutationObserver::observedNodeDestructed(Node* node)
     85{
     86    size_t index = m_observedNodes.find(node);
     87    ASSERT(index != notFound);
     88    if (index == notFound)
     89        return;
     90
     91    m_observedNodes.remove(index);
     92}
     93
     94typedef ListHashSet<RefPtr<WebKitMutationObserver> > MutationObserverSet;
     95
     96static MutationObserverSet& activeMutationObservers()
     97{
     98    DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ());
     99    return activeObservers;
     100}
     101
     102void WebKitMutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation)
     103{
     104    m_records.append(mutation);
     105    activeMutationObservers().add(this);
     106}
     107
     108void WebKitMutationObserver::deliver()
     109{
     110    MutationRecordArray records;
     111    records.swap(m_records);
     112    m_callback->handleEvent(&records, this);
     113}
     114
     115void WebKitMutationObserver::deliverAllMutations()
     116{
     117    while (!activeMutationObservers().isEmpty()) {
     118        MutationObserverSet::iterator iter = activeMutationObservers().begin();
     119        RefPtr<WebKitMutationObserver> observer = *iter;
     120        activeMutationObservers().remove(iter);
     121        observer->deliver();
     122    }
    64123}
    65124
  • trunk/Source/WebCore/dom/WebKitMutationObserver.h

    r97159 r97659  
    3737#include <wtf/RefCounted.h>
    3838#include <wtf/RefPtr.h>
     39#include <wtf/Vector.h>
    3940
    4041namespace WebCore {
     
    4243class MutationCallback;
    4344class MutationObserverOptions;
     45class MutationRecord;
    4446class Node;
    4547
    4648class WebKitMutationObserver : public RefCounted<WebKitMutationObserver> {
    4749public:
     50    enum MutationType {
     51        ChildList = 1 << 0,
     52        Attributes = 1 << 1,
     53        CharacterData = 1 << 2
     54    };
     55
     56    enum OptionFlags {
     57        Subtree = 1 << 3,
     58        AttributeOldValue = 1 << 4,
     59        CharacterDataOldValue = 1 << 5,
     60        AttributeFilter = 1 << 6
     61    };
     62
    4863    static PassRefPtr<WebKitMutationObserver> create(PassRefPtr<MutationCallback>);
     64    static void deliverAllMutations();
    4965
    5066    ~WebKitMutationObserver();
     
    5268    void observe(Node*, MutationObserverOptions*);
    5369    void disconnect();
     70    void observedNodeDestructed(Node*);
     71    void enqueueMutationRecord(PassRefPtr<MutationRecord>);
    5472
    5573private:
    5674    WebKitMutationObserver(PassRefPtr<MutationCallback>);
    5775
     76    void deliver();
     77
    5878    RefPtr<MutationCallback> m_callback;
     79    Vector<RefPtr<MutationRecord> > m_records;
     80    Vector<Node*> m_observedNodes; // NodeRareData has a RefPtr to this, so use a weak pointer to avoid a cycle.
    5981};
    6082
Note: See TracChangeset for help on using the changeset viewer.