Changeset 98659 in webkit


Ignore:
Timestamp:
Oct 27, 2011 4:38:09 PM (12 years ago)
Author:
commit-queue@webkit.org
Message:

[MutationObservers] Implement subtree observation of transiently disconnected nodes
https://bugs.webkit.org/show_bug.cgi?id=70788

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

Source/WebCore:

This patch adds support for observing all descendant nodes reachable from a subtree
observation until delivery of mutations -- even if they become detached. We do this by
introducing a "transient registration" which can exist for a short time along side
normal registrations on Node. Transient registrations have a reference to the node
which "owns" the subtree observation registration (the "registrationNode"). Transient
registrations are cleared immediately before mutations are delivered to an observer,
or when the observer re-observes at the registrationNode, in-effect resetting the
observation.

New tests added to fast/mutation/observe-subtree.html.

  • dom/CharacterData.cpp:

(WebCore::CharacterData::dispatchModifiedEvent):

  • dom/ChildListMutationScope.cpp:

(WebCore::MutationAccumulationRouter::ChildListMutationAccumulator::ChildListMutationAccumulator):
(WebCore::MutationAccumulationRouter::ChildListMutationAccumulator::enqueueMutationRecord):
(WebCore::MutationAccumulationRouter::MutationAccumulationRouter::incrementScopingLevel):

  • dom/ContainerNode.cpp:

(WebCore::dispatchChildRemovalEvents):

  • dom/Element.cpp:

(WebCore::enqueueAttributesMutationRecord):

  • dom/Node.cpp:

(WebCore::addMatchingObservers):
(WebCore::Node::getRegisteredMutationObserversOfType):
(WebCore::Node::registerMutationObserver):
(WebCore::Node::unregisterMutationObserver):
(WebCore::Node::notifySubtreeObserversOfDisconnection):

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

(WebCore::MutationObserverEntry::MutationObserverEntry):
(WebCore::MutationObserverEntry::operator==):

  • dom/WebKitMutationObserver.cpp:

(WebCore::WebKitMutationObserver::observe):
(WebCore::unregisterTransientEntries):
(WebCore::WebKitMutationObserver::disconnect):
(WebCore::WebKitMutationObserver::observedNodeDestructed):
(WebCore::WebKitMutationObserver::observedSubtreeWillDisconnect):
(WebCore::WebKitMutationObserver::clearTransientEntries):
(WebCore::WebKitMutationObserver::deliver):

  • dom/WebKitMutationObserver.h:

LayoutTests:

Added tests which assert that nodes can be detached from a subtree where observation is registered
and still be observed -- until the end of the current "microtask".

  • fast/mutation/observe-subtree-expected.txt:
  • fast/mutation/observe-subtree.html:
Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r98658 r98659  
     12011-10-27  Rafael Weinstein  <rafaelw@chromium.org>
     2
     3        [MutationObservers] Implement subtree observation of transiently disconnected nodes
     4        https://bugs.webkit.org/show_bug.cgi?id=70788
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Added tests which assert that nodes can be detached from a subtree where observation is registered
     9        and still be observed -- until the end of the current "microtask".
     10
     11        * fast/mutation/observe-subtree-expected.txt:
     12        * fast/mutation/observe-subtree.html:
     13
    1142011-10-27  Filip Pizlo  <fpizlo@apple.com>
    215
  • trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt

    r98163 r98659  
    3434PASS mutations[0].attributeNamespace is null
    3535
     36Testing that transiently detached nodes are still observed via subtree.
     37...both changes should be received. Change detached subDiv again.
     38PASS mutations.length is 2
     39PASS mutations[0].type is "attributes"
     40PASS mutations[0].target is subDiv
     41PASS mutations[0].attributeName is "foo"
     42PASS mutations[1].type is "attributes"
     43PASS mutations[1].target is subDiv
     44PASS mutations[1].attributeName is "test"
     45...transient subtree observation was stopped after delivery, so subDiv change should not be received. Reattach and change again.
     46PASS mutations is null
     47...reattached subtree should now be observable. Try detaching and re-observing.
     48PASS mutations.length is 1
     49PASS mutations[0].type is "attributes"
     50PASS mutations[0].target is subDiv
     51PASS mutations[0].attributeName is "foo"
     52...The change made before re-observing should be received, but not the one after.
     53PASS mutations.length is 1
     54PASS mutations[0].type is "characterData"
     55PASS mutations[0].target is subDiv.firstChild
     56
     57Testing correct behavior of transient observation with complex movement .
     58...All changes should be received except for setting the "d" attribute on subDiv3 before it was reachable from div.
     59PASS mutations.length is 6
     60PASS mutations[0].type is "attributes"
     61PASS mutations[0].target is subDiv
     62PASS mutations[0].attributeName is "a"
     63PASS mutations[1].type is "attributes"
     64PASS mutations[1].target is subDiv2
     65PASS mutations[1].attributeName is "b"
     66PASS mutations[2].type is "characterData"
     67PASS mutations[2].target is text
     68PASS mutations[3].type is "attributes"
     69PASS mutations[3].target is subDiv2
     70PASS mutations[3].attributeName is "e"
     71PASS mutations[4].type is "attributes"
     72PASS mutations[4].target is subDiv3
     73PASS mutations[4].attributeName is "f"
     74PASS mutations[5].type is "attributes"
     75PASS mutations[5].target is subDiv2
     76PASS mutations[5].attributeName is "g"
     77
    3678PASS successfullyParsed is true
    3779
  • trunk/LayoutTests/fast/mutation/observe-subtree.html

    r98407 r98659  
    1616var mutations2;
    1717var div;
    18 var subDiv;
     18var subDiv, subDiv2, subDiv3, text;
    1919var calls;
    2020
     
    136136    start();
    137137}
    138 var tests = [testBasic, testMultipleObservers, testMultipleObservations];
     138
     139function testTransientDetachedBasic() {
     140    var observer;
     141
     142    function start() {
     143        debug('Testing that transiently detached nodes are still observed via subtree.');
     144
     145        mutations = null;
     146        div = document.createElement('div');
     147        subDiv = div.appendChild(document.createElement('div'));
     148        subDiv.innerHTML = 'hello, world';
     149        observer = new WebKitMutationObserver(function(mutations) {
     150            window.mutations = mutations;
     151        });
     152
     153        observer.observe(div, {attributes: true, characterData: true, subtree: true});
     154        subDiv.setAttribute('foo', 'bar');
     155        div.removeChild(subDiv);
     156        subDiv.setAttribute('test', 'test');
     157        setTimeout(checkDeliveredAndChangeAgain, 0);
     158    }
     159
     160    function checkDeliveredAndChangeAgain() {
     161        debug('...both changes should be received. Change detached subDiv again.');
     162
     163        shouldBe('mutations.length', '2');
     164        shouldBe('mutations[0].type', '"attributes"');
     165        shouldBe('mutations[0].target', 'subDiv');
     166        shouldBe('mutations[0].attributeName', '"foo"');
     167        shouldBe('mutations[1].type', '"attributes"');
     168        shouldBe('mutations[1].target', 'subDiv');
     169        shouldBe('mutations[1].attributeName', '"test"');
     170
     171        mutations = null;
     172        subDiv.setAttribute('foo', 'baz');
     173
     174        setTimeout(checkNotDeliveredAndReattach);
     175    }
     176
     177    function checkNotDeliveredAndReattach() {
     178        debug('...transient subtree observation was stopped after delivery, so subDiv change should not be received. Reattach and change again.');
     179
     180        shouldBe('mutations', 'null');
     181
     182        mutations = null
     183        div.appendChild(subDiv);
     184        subDiv.setAttribute('foo', 'bat');
     185
     186        setTimeout(checkDeliveredAndReobserve);
     187    }
     188
     189    function checkDeliveredAndReobserve() {
     190        debug('...reattached subtree should now be observable. Try detaching and re-observing.');
     191
     192        shouldBe('mutations.length', '1');
     193        shouldBe('mutations[0].type', '"attributes"');
     194        shouldBe('mutations[0].target', 'subDiv');
     195        shouldBe('mutations[0].attributeName', '"foo"');
     196
     197        mutations = null;
     198        div.removeChild(subDiv);
     199        subDiv.firstChild.textContent = 'badbye';
     200        observer.observe(div, {attributes: true, characterData: true, subtree: true});
     201        subDiv.setAttribute('foo', 'boo');
     202
     203        setTimeout(finish);
     204    }
     205
     206
     207    function finish() {
     208        debug('...The change made before re-observing should be received, but not the one after.');
     209
     210        shouldBe('mutations.length', '1');
     211        shouldBe('mutations[0].type', '"characterData"');
     212        shouldBe('mutations[0].target', 'subDiv.firstChild');
     213
     214        observer.disconnect();
     215        debug('');
     216        runNextTest();
     217    }
     218    start();
     219}
     220
     221function testTransientDetachedDetailed() {
     222    var observer;
     223
     224    function start() {
     225        debug('Testing correct behavior of transient observation with complex movement .');
     226
     227        mutations = null;
     228        div = document.createElement('div');
     229        subDiv = div.appendChild(document.createElement('div'));
     230        subDiv2 = subDiv.appendChild(document.createElement('div'));
     231        subDiv2.innerHTML = 'hello, world';
     232        subDiv3 = document.createElement('div');
     233
     234        observer = new WebKitMutationObserver(function(mutations) {
     235            window.mutations = mutations;
     236        });
     237
     238        observer.observe(div, {attributes: true, characterData: true, subtree: true});
     239        div.removeChild(subDiv);
     240        subDiv.removeChild(subDiv2);
     241        text = subDiv2.removeChild(subDiv2.firstChild);
     242
     243        subDiv.setAttribute('a', 'a');
     244        subDiv2.setAttribute('b', 'b');
     245        text.textContent = 'c';
     246        subDiv3.appendChild(subDiv2);
     247        subDiv3.setAttribute('d', 'd');
     248        subDiv2.setAttribute('e', 'e');
     249        div.appendChild(subDiv3);
     250        subDiv3.setAttribute('f', 'f');
     251        subDiv2.setAttribute('g', 'g');
     252
     253        setTimeout(finish, 0);
     254    }
     255
     256    function finish() {
     257        debug('...All changes should be received except for setting the "d" attribute on subDiv3 before it was reachable from div.');
     258
     259        shouldBe('mutations.length', '6');
     260        shouldBe('mutations[0].type', '"attributes"');
     261        shouldBe('mutations[0].target', 'subDiv');
     262        shouldBe('mutations[0].attributeName', '"a"');
     263
     264        shouldBe('mutations[1].type', '"attributes"');
     265        shouldBe('mutations[1].target', 'subDiv2');
     266        shouldBe('mutations[1].attributeName', '"b"');
     267
     268        shouldBe('mutations[2].type', '"characterData"');
     269        shouldBe('mutations[2].target', 'text');
     270
     271        shouldBe('mutations[3].type', '"attributes"');
     272        shouldBe('mutations[3].target', 'subDiv2');
     273        shouldBe('mutations[3].attributeName', '"e"');
     274
     275        shouldBe('mutations[4].type', '"attributes"');
     276        shouldBe('mutations[4].target', 'subDiv3');
     277        shouldBe('mutations[4].attributeName', '"f"');
     278
     279        shouldBe('mutations[5].type', '"attributes"');
     280        shouldBe('mutations[5].target', 'subDiv2');
     281        shouldBe('mutations[5].attributeName', '"g"');
     282
     283        observer.disconnect();
     284        debug('');
     285        runNextTest();
     286    }
     287    start();
     288}
     289
     290var tests = [testBasic, testMultipleObservers, testMultipleObservations, testTransientDetachedBasic, testTransientDetachedDetailed];
    139291var testIndex = 0;
    140292
  • trunk/Source/WebCore/ChangeLog

    r98657 r98659  
     12011-10-27  Rafael Weinstein  <rafaelw@chromium.org>
     2
     3        [MutationObservers] Implement subtree observation of transiently disconnected nodes
     4        https://bugs.webkit.org/show_bug.cgi?id=70788
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        This patch adds support for observing all descendant nodes reachable from a subtree
     9        observation until delivery of mutations -- even if they become detached. We do this by
     10        introducing a "transient registration" which can exist for a short time along side
     11        normal registrations on Node. Transient registrations have a reference to the node
     12        which "owns" the subtree observation registration (the "registrationNode"). Transient
     13        registrations are cleared immediately before mutations are delivered to an observer,
     14        or when the observer re-observes at the registrationNode, in-effect resetting the
     15        observation.
     16
     17        New tests added to fast/mutation/observe-subtree.html.
     18
     19        * dom/CharacterData.cpp:
     20        (WebCore::CharacterData::dispatchModifiedEvent):
     21        * dom/ChildListMutationScope.cpp:
     22        (WebCore::MutationAccumulationRouter::ChildListMutationAccumulator::ChildListMutationAccumulator):
     23        (WebCore::MutationAccumulationRouter::ChildListMutationAccumulator::enqueueMutationRecord):
     24        (WebCore::MutationAccumulationRouter::MutationAccumulationRouter::incrementScopingLevel):
     25        * dom/ContainerNode.cpp:
     26        (WebCore::dispatchChildRemovalEvents):
     27        * dom/Element.cpp:
     28        (WebCore::enqueueAttributesMutationRecord):
     29        * dom/Node.cpp:
     30        (WebCore::addMatchingObservers):
     31        (WebCore::Node::getRegisteredMutationObserversOfType):
     32        (WebCore::Node::registerMutationObserver):
     33        (WebCore::Node::unregisterMutationObserver):
     34        (WebCore::Node::notifySubtreeObserversOfDisconnection):
     35        * dom/Node.h:
     36        * dom/NodeRareData.h:
     37        (WebCore::MutationObserverEntry::MutationObserverEntry):
     38        (WebCore::MutationObserverEntry::operator==):
     39        * dom/WebKitMutationObserver.cpp:
     40        (WebCore::WebKitMutationObserver::observe):
     41        (WebCore::unregisterTransientEntries):
     42        (WebCore::WebKitMutationObserver::disconnect):
     43        (WebCore::WebKitMutationObserver::observedNodeDestructed):
     44        (WebCore::WebKitMutationObserver::observedSubtreeWillDisconnect):
     45        (WebCore::WebKitMutationObserver::clearTransientEntries):
     46        (WebCore::WebKitMutationObserver::deliver):
     47        * dom/WebKitMutationObserver.h:
     48
    1492011-10-27  Pratik Solanki  <psolanki@apple.com>
    250
  • trunk/Source/WebCore/dom/CharacterData.cpp

    r98262 r98659  
    194194{
    195195#if ENABLE(MUTATION_OBSERVERS)
    196     Vector<WebKitMutationObserver*> observers;
     196    HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
    197197    getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::CharacterData);
    198198    if (!observers.isEmpty()) {
    199199        RefPtr<MutationRecord> mutation = MutationRecord::createCharacterData(this);
    200         for (size_t i = 0; i < observers.size(); ++i)
    201             observers[i]->enqueueMutationRecord(mutation);
     200        for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
     201                iter->first->enqueueMutationRecord(mutation);
    202202    }
    203203#endif
  • trunk/Source/WebCore/dom/ChildListMutationScope.cpp

    r98262 r98659  
    5454    WTF_MAKE_NONCOPYABLE(ChildListMutationAccumulator);
    5555public:
    56     ChildListMutationAccumulator(PassRefPtr<Node>, Vector<WebKitMutationObserver*>&);
     56    ChildListMutationAccumulator(PassRefPtr<Node>, HashMap<WebKitMutationObserver*, MutationObserverOptions>&);
    5757    ~ChildListMutationAccumulator();
    5858
     
    7474    RefPtr<Node> m_lastAdded;
    7575
    76     Vector<WebKitMutationObserver*> m_observers;
     76    HashMap<WebKitMutationObserver*, MutationObserverOptions> m_observers;
    7777};
    7878
     
    101101};
    102102
    103 ChildListMutationAccumulator::ChildListMutationAccumulator(PassRefPtr<Node> target, Vector<WebKitMutationObserver*>& observers)
     103ChildListMutationAccumulator::ChildListMutationAccumulator(PassRefPtr<Node> target, HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers)
    104104    : m_target(target)
    105105{
     
    170170        m_target, StaticNodeList::adopt(m_addedNodes), StaticNodeList::adopt(m_removedNodes), m_previousSibling, m_nextSibling);
    171171
    172     for (size_t i = 0; i < m_observers.size(); ++i)
    173         m_observers[i]->enqueueMutationRecord(mutation);
     172    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = m_observers.begin(); iter != m_observers.end(); ++iter)
     173        iter->first->enqueueMutationRecord(mutation);
    174174
    175175    clear();
     
    245245    }
    246246
    247     Vector<WebKitMutationObserver*> observers;
     247    HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
    248248    target->getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::ChildList);
    249249    if (observers.isEmpty())
  • trunk/Source/WebCore/dom/ContainerNode.cpp

    r98121 r98659  
    11451145        ChildListMutationScope mutation(c->parentNode());
    11461146        mutation.willRemoveChild(c);
     1147        c->notifyMutationObserversNodeWillDetach();
    11471148    }
    11481149#endif
  • trunk/Source/WebCore/dom/Element.cpp

    r98492 r98659  
    620620static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name)
    621621{
    622     Vector<WebKitMutationObserver*> observers;
     622    HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
    623623    element->getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::Attributes);
    624624    if (observers.isEmpty())
     
    626626
    627627    RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name);
    628     for (size_t i = 0; i < observers.size(); ++i)
    629         observers[i]->enqueueMutationRecord(mutation);
     628    for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
     629        iter->first->enqueueMutationRecord(mutation);
    630630}
    631631#endif
  • trunk/Source/WebCore/dom/Node.cpp

    r98388 r98659  
    550550
    551551#if ENABLE(MUTATION_OBSERVERS)
    552     Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
    553     if (observerEntries) {
    554         for (size_t i = 0; i < observerEntries->size(); ++i)
    555             observerEntries->at(i).observer->observedNodeDestructed(this);
     552    if (Vector<MutationObserverRegistration>* registry = mutationObserverRegistry()) {
     553        for (size_t i = 0; i < registry->size(); ++i)
     554            registry->at(i).observer->observedNodeDestructed(this);
    556555    }
    557556#endif
     
    26992698
    27002699#if ENABLE(MUTATION_OBSERVERS)
    2701 Vector<MutationObserverEntry>* Node::mutationObserverEntries()
    2702 {
    2703     return hasRareData() ? rareData()->mutationObserverEntries() : 0;
    2704 }
    2705 
    2706 static void addMatchingObservers(HashSet<WebKitMutationObserver*>& observerSet, Vector<MutationObserverEntry>* observerEntries, MutationObserverOptions options)
    2707 {
    2708     if (!observerEntries)
    2709         return;
    2710 
    2711     const size_t size = observerEntries->size();
     2700Vector<MutationObserverRegistration>* Node::mutationObserverRegistry()
     2701{
     2702    return hasRareData() ? rareData()->mutationObserverRegistry() : 0;
     2703}
     2704
     2705static void addMatchingObservers(HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers, Vector<MutationObserverRegistration>* registry, MutationObserverOptions options)
     2706{
     2707    if (!registry)
     2708        return;
     2709
     2710    const size_t size = registry->size();
    27122711    for (size_t i = 0; i < size; ++i) {
    2713         MutationObserverEntry& entry = observerEntries->at(i);
    2714         if (entry.hasAllOptions(options))
    2715             observerSet.add(entry.observer.get());
    2716     }
    2717 }
    2718 
    2719 void Node::getRegisteredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type)
    2720 {
    2721     HashSet<WebKitMutationObserver*> observerSet;
    2722     addMatchingObservers(observerSet, mutationObserverEntries(), type);
     2712        MutationObserverRegistration& entry = registry->at(i);
     2713
     2714        if (!entry.hasAllOptions(options))
     2715            continue;
     2716
     2717        pair<HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator, bool> result = observers.add(entry.observer.get(), entry.options);
     2718        if (!result.second)
     2719            result.first->second |= entry.options;
     2720    }
     2721}
     2722
     2723void Node::getRegisteredMutationObserversOfType(HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers, WebKitMutationObserver::MutationType type)
     2724{
     2725    addMatchingObservers(observers, mutationObserverRegistry(), type);
    27232726    for (Node* node = parentNode(); node; node = node->parentNode())
    2724         addMatchingObservers(observerSet, node->mutationObserverEntries(), type | WebKitMutationObserver::Subtree);
    2725 
    2726     // FIXME: this method should output a HashSet instead of a Vector.
    2727     if (!observerSet.isEmpty())
    2728         copyToVector(observerSet, observers);
    2729 }
    2730 
    2731 Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options)
    2732 {
    2733     Vector<MutationObserverEntry>* observerEntries = ensureRareData()->ensureMutationObserverEntries();
    2734     MutationObserverEntry entry(observer, options);
    2735 
    2736     size_t index = observerEntries->find(entry);
     2727        addMatchingObservers(observers, node->mutationObserverRegistry(), type | WebKitMutationObserver::Subtree);
     2728}
     2729
     2730Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options, Node* registrationNode)
     2731{
     2732    Vector<MutationObserverRegistration>* registry = ensureRareData()->ensureMutationObserverRegistry();
     2733    MutationObserverRegistration registration(observer, options, registrationNode);
     2734
     2735    size_t index = registry->find(registration);
    27372736    if (index == notFound) {
    2738         observerEntries->append(entry);
     2737        registry->append(registration);
    27392738        return MutationObserverRegistered;
    27402739    }
    27412740
    2742     (*observerEntries)[index].options = entry.options;
     2741    registry->at(index).options = registration.options;
    27432742    return MutationRegistrationOptionsReset;
    27442743}
    27452744
    2746 void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer)
    2747 {
    2748     Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
    2749     ASSERT(observerEntries);
    2750     if (!observerEntries)
    2751         return;
    2752 
    2753     MutationObserverEntry entry(observer, 0);
    2754     size_t index = observerEntries->find(entry);
     2745void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer, Node* registrationNode)
     2746{
     2747    Vector<MutationObserverRegistration>* registry = mutationObserverRegistry();
     2748    ASSERT(registry);
     2749    if (!registry)
     2750        return;
     2751
     2752    MutationObserverRegistration registration(observer, 0, registrationNode);
     2753    size_t index = registry->find(registration);
    27552754    ASSERT(index != notFound);
    27562755    if (index == notFound)
    27572756        return;
    27582757
    2759     observerEntries->remove(index);
     2758    registry->remove(index);
     2759}
     2760
     2761void Node::notifyMutationObserversNodeWillDetach()
     2762{
     2763    for (Node* node = parentNode(); node; node = node->parentNode()) {
     2764        Vector<MutationObserverRegistration>* registry = node->mutationObserverRegistry();
     2765        if (!registry)
     2766            continue;
     2767
     2768        const size_t size = registry->size();
     2769        for (size_t i = 0; i < size; ++i) {
     2770            MutationObserverRegistration& registration = registry->at(i);
     2771            if (!registration.hasAllOptions(WebKitMutationObserver::Subtree))
     2772                continue;
     2773
     2774            Node* registrationNode = registration.registrationNode;
     2775            if (!registrationNode)
     2776                registrationNode = node;
     2777            registration.observer->willDetachNodeInObservedSubtree(registrationNode, registration.options, this);
     2778        }
     2779    }
    27602780}
    27612781#endif // ENABLE(MUTATION_OBSERVERS)
  • trunk/Source/WebCore/dom/Node.h

    r98402 r98659  
    8282class TreeScope;
    8383
    84 struct MutationObserverEntry;
     84struct MutationObserverRegistration;
    8585
    8686typedef int ExceptionCode;
     
    591591
    592592#if ENABLE(MUTATION_OBSERVERS)
    593     void getRegisteredMutationObserversOfType(Vector<WebKitMutationObserver*>&, WebKitMutationObserver::MutationType);
     593    void getRegisteredMutationObserversOfType(HashMap<WebKitMutationObserver*, MutationObserverOptions>&, WebKitMutationObserver::MutationType);
    594594
    595595    enum MutationRegistrationResult {
     
    597597        MutationRegistrationOptionsReset
    598598    };
    599     MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, MutationObserverOptions);
    600 
    601     void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>);
     599    MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, MutationObserverOptions, Node* registrationNode = 0);
     600
     601    void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>, Node* registrationNode = 0);
     602
     603    void notifyMutationObserversNodeWillDetach();
    602604#endif // ENABLE(MUTATION_OBSERVERS)
    603605
     
    726728
    727729#if ENABLE(MUTATION_OBSERVERS)
    728     Vector<MutationObserverEntry>* mutationObserverEntries();
     730    Vector<MutationObserverRegistration>* mutationObserverRegistry();
    729731#endif
    730732
  • trunk/Source/WebCore/dom/NodeRareData.h

    r98163 r98659  
    9090
    9191#if ENABLE(MUTATION_OBSERVERS)
    92 struct MutationObserverEntry {
    93     MutationObserverEntry(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options)
     92struct MutationObserverRegistration {
     93    MutationObserverRegistration(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options, Node* node)
    9494        : observer(observer)
    9595        , options(options)
    96     {
    97     }
    98 
    99     bool operator==(const MutationObserverEntry& other) const
    100     {
    101         return observer == other.observer;
     96        , registrationNode(node)
     97    {
     98    }
     99
     100    bool operator==(const MutationObserverRegistration& other) const
     101    {
     102        return observer == other.observer && registrationNode == other.registrationNode;
    102103    }
    103104
     
    109110    RefPtr<WebKitMutationObserver> observer;
    110111    MutationObserverOptions options;
     112
     113    // registrationNode will be 0 if the registration is non-transient. I.e. The registrationNode is the Node in whose
     114    // registry it exists.
     115    // Note that this doesn't need to be a RefPtr because the observer will be holding a RefPtr to the same node at
     116    // least for the lifetime of this Registration in its m_transientObservedNodes map.
     117    Node* registrationNode;
    111118};
    112119#endif // ENABLE(MUTATION_OBSERVERS)
     
    162169
    163170#if ENABLE(MUTATION_OBSERVERS)
    164     Vector<MutationObserverEntry>* mutationObserverEntries() { return m_mutationObservers.get(); }
    165     Vector<MutationObserverEntry>* ensureMutationObserverEntries()
    166     {
    167         if (!m_mutationObservers)
    168             m_mutationObservers = adoptPtr(new Vector<MutationObserverEntry>);
    169         return m_mutationObservers.get();
     171    Vector<MutationObserverRegistration>* mutationObserverRegistry() { return m_mutationObserverRegistry.get(); }
     172    Vector<MutationObserverRegistration>* ensureMutationObserverRegistry()
     173    {
     174        if (!m_mutationObserverRegistry)
     175            m_mutationObserverRegistry = adoptPtr(new Vector<MutationObserverRegistration>);
     176        return m_mutationObserverRegistry.get();
    170177    }
    171178#endif
     
    189196
    190197#if ENABLE(MUTATION_OBSERVERS)
    191     OwnPtr<Vector<MutationObserverEntry> > m_mutationObservers;
     198    OwnPtr<Vector<MutationObserverRegistration> > m_mutationObserverRegistry;
    192199#endif
    193200};
  • trunk/Source/WebCore/dom/WebKitMutationObserver.cpp

    r98121 r98659  
    5454WebKitMutationObserver::~WebKitMutationObserver()
    5555{
     56    clearAllTransientObservations();
     57}
     58
     59static inline void clearTransientObservationsForRegistration(WebKitMutationObserver* observer, Node* registrationNode, PassOwnPtr<NodeHashSet> transientNodes)
     60{
     61    for (NodeHashSet::iterator iter = transientNodes->begin(); iter != transientNodes->end(); ++iter)
     62        (*iter)->unregisterMutationObserver(observer, registrationNode);
    5663}
    5764
     
    6067    // FIXME: More options composition work needs to be done here, e.g., validation.
    6168
    62     if (node->registerMutationObserver(this, options) == Node::MutationObserverRegistered)
     69    if (node->registerMutationObserver(this, options) == Node::MutationObserverRegistered) {
    6370        m_observedNodes.append(node);
     71        return;
     72    }
     73
     74    HashMap<RefPtr<Node>, NodeHashSet*>::iterator iter = m_transientObservedNodes.find(node);
     75    if (iter == m_transientObservedNodes.end())
     76        return;
     77
     78    clearTransientObservationsForRegistration(this, node, adoptPtr(iter->second));
     79    m_transientObservedNodes.remove(iter);
    6480}
    6581
     
    7086
    7187    m_observedNodes.clear();
     88    clearAllTransientObservations();
    7289}
    7390
    7491void WebKitMutationObserver::observedNodeDestructed(Node* node)
    7592{
     93    ASSERT(!m_transientObservedNodes.contains(node));
     94
    7695    size_t index = m_observedNodes.find(node);
    7796    ASSERT(index != notFound);
     
    96115}
    97116
     117void WebKitMutationObserver::willDetachNodeInObservedSubtree(PassRefPtr<Node> prpRegistrationNode, MutationObserverOptions options, PassRefPtr<Node> prpDetachingNode)
     118{
     119    RefPtr<Node> registrationNode = prpRegistrationNode;
     120    RefPtr<Node> detachingNode = prpDetachingNode;
     121
     122    detachingNode->registerMutationObserver(this, options, registrationNode.get());
     123    HashMap<RefPtr<Node>, NodeHashSet*>::iterator iter = m_transientObservedNodes.find(registrationNode);
     124    if (iter != m_transientObservedNodes.end()) {
     125        iter->second->add(detachingNode);
     126        return;
     127    }
     128
     129    OwnPtr<NodeHashSet> set = adoptPtr(new NodeHashSet());
     130    set->add(detachingNode);
     131    m_transientObservedNodes.set(registrationNode, set.leakPtr());
     132}
     133
     134void WebKitMutationObserver::clearAllTransientObservations()
     135{
     136    for (HashMap<RefPtr<Node>, NodeHashSet*>::iterator iter = m_transientObservedNodes.begin(); iter != m_transientObservedNodes.end(); ++iter)
     137        clearTransientObservationsForRegistration(this, iter->first.get(), adoptPtr(iter->second));
     138
     139    m_transientObservedNodes.clear();
     140}
     141
    98142void WebKitMutationObserver::deliver()
    99143{
    100144    MutationRecordArray records;
    101145    records.swap(m_records);
     146    clearAllTransientObservations();
    102147    m_callback->handleEvent(&records, this);
    103148}
  • trunk/Source/WebCore/dom/WebKitMutationObserver.h

    r97714 r98659  
    3434#if ENABLE(MUTATION_OBSERVERS)
    3535
     36#include <wtf/HashMap.h>
     37#include <wtf/HashSet.h>
    3638#include <wtf/PassOwnPtr.h>
    3739#include <wtf/RefCounted.h>
     
    4648
    4749typedef unsigned char MutationObserverOptions;
     50typedef HashSet<RefPtr<Node> > NodeHashSet;
    4851
    4952class WebKitMutationObserver : public RefCounted<WebKitMutationObserver> {
     
    6972    void observe(Node*, MutationObserverOptions);
    7073    void disconnect();
     74    void willDetachNodeInObservedSubtree(PassRefPtr<Node> registrationNode, MutationObserverOptions, PassRefPtr<Node> detachingNode);
    7175    void observedNodeDestructed(Node*);
    7276    void enqueueMutationRecord(PassRefPtr<MutationRecord>);
     
    7579    WebKitMutationObserver(PassRefPtr<MutationCallback>);
    7680
     81    void clearAllTransientObservations();
    7782    void deliver();
    7883
     
    8085    Vector<RefPtr<MutationRecord> > m_records;
    8186    Vector<Node*> m_observedNodes; // NodeRareData has a RefPtr to this, so use a weak pointer to avoid a cycle.
     87
     88    // FIXME: Change this to be OwnPtr<NodeHashSet> when OwnPtr supports being contained as map values.
     89    HashMap<RefPtr<Node>, NodeHashSet*> m_transientObservedNodes;
    8290};
    8391
Note: See TracChangeset for help on using the changeset viewer.