Diff
Modified: trunk/LayoutTests/ChangeLog (98658 => 98659)
--- trunk/LayoutTests/ChangeLog 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/LayoutTests/ChangeLog 2011-10-27 23:38:09 UTC (rev 98659)
@@ -1,3 +1,16 @@
+2011-10-27 Rafael Weinstein <rafa...@chromium.org>
+
+ [MutationObservers] Implement subtree observation of transiently disconnected nodes
+ https://bugs.webkit.org/show_bug.cgi?id=70788
+
+ Reviewed by Ryosuke Niwa.
+
+ 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:
+
2011-10-27 Filip Pizlo <fpi...@apple.com>
If the bytecode generator emits code after the return in the first basic block,
Modified: trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt (98658 => 98659)
--- trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/LayoutTests/fast/mutation/observe-subtree-expected.txt 2011-10-27 23:38:09 UTC (rev 98659)
@@ -33,6 +33,48 @@
PASS mutations[0].attributeName is "foo"
PASS mutations[0].attributeNamespace is null
+Testing that transiently detached nodes are still observed via subtree.
+...both changes should be received. Change detached subDiv again.
+PASS mutations.length is 2
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "foo"
+PASS mutations[1].type is "attributes"
+PASS mutations[1].target is subDiv
+PASS mutations[1].attributeName is "test"
+...transient subtree observation was stopped after delivery, so subDiv change should not be received. Reattach and change again.
+PASS mutations is null
+...reattached subtree should now be observable. Try detaching and re-observing.
+PASS mutations.length is 1
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "foo"
+...The change made before re-observing should be received, but not the one after.
+PASS mutations.length is 1
+PASS mutations[0].type is "characterData"
+PASS mutations[0].target is subDiv.firstChild
+
+Testing correct behavior of transient observation with complex movement .
+...All changes should be received except for setting the "d" attribute on subDiv3 before it was reachable from div.
+PASS mutations.length is 6
+PASS mutations[0].type is "attributes"
+PASS mutations[0].target is subDiv
+PASS mutations[0].attributeName is "a"
+PASS mutations[1].type is "attributes"
+PASS mutations[1].target is subDiv2
+PASS mutations[1].attributeName is "b"
+PASS mutations[2].type is "characterData"
+PASS mutations[2].target is text
+PASS mutations[3].type is "attributes"
+PASS mutations[3].target is subDiv2
+PASS mutations[3].attributeName is "e"
+PASS mutations[4].type is "attributes"
+PASS mutations[4].target is subDiv3
+PASS mutations[4].attributeName is "f"
+PASS mutations[5].type is "attributes"
+PASS mutations[5].target is subDiv2
+PASS mutations[5].attributeName is "g"
+
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/fast/mutation/observe-subtree.html (98658 => 98659)
--- trunk/LayoutTests/fast/mutation/observe-subtree.html 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/LayoutTests/fast/mutation/observe-subtree.html 2011-10-27 23:38:09 UTC (rev 98659)
@@ -15,7 +15,7 @@
var mutations;
var mutations2;
var div;
-var subDiv;
+var subDiv, subDiv2, subDiv3, text;
var calls;
function testBasic() {
@@ -135,7 +135,159 @@
start();
}
-var tests = [testBasic, testMultipleObservers, testMultipleObservations];
+
+function testTransientDetachedBasic() {
+ var observer;
+
+ function start() {
+ debug('Testing that transiently detached nodes are still observed via subtree.');
+
+ mutations = null;
+ div = document.createElement('div');
+ subDiv = div.appendChild(document.createElement('div'));
+ subDiv.innerHTML = 'hello, world';
+ observer = new WebKitMutationObserver(function(mutations) {
+ window.mutations = mutations;
+ });
+
+ observer.observe(div, {attributes: true, characterData: true, subtree: true});
+ subDiv.setAttribute('foo', 'bar');
+ div.removeChild(subDiv);
+ subDiv.setAttribute('test', 'test');
+ setTimeout(checkDeliveredAndChangeAgain, 0);
+ }
+
+ function checkDeliveredAndChangeAgain() {
+ debug('...both changes should be received. Change detached subDiv again.');
+
+ shouldBe('mutations.length', '2');
+ shouldBe('mutations[0].type', '"attributes"');
+ shouldBe('mutations[0].target', 'subDiv');
+ shouldBe('mutations[0].attributeName', '"foo"');
+ shouldBe('mutations[1].type', '"attributes"');
+ shouldBe('mutations[1].target', 'subDiv');
+ shouldBe('mutations[1].attributeName', '"test"');
+
+ mutations = null;
+ subDiv.setAttribute('foo', 'baz');
+
+ setTimeout(checkNotDeliveredAndReattach);
+ }
+
+ function checkNotDeliveredAndReattach() {
+ debug('...transient subtree observation was stopped after delivery, so subDiv change should not be received. Reattach and change again.');
+
+ shouldBe('mutations', 'null');
+
+ mutations = null
+ div.appendChild(subDiv);
+ subDiv.setAttribute('foo', 'bat');
+
+ setTimeout(checkDeliveredAndReobserve);
+ }
+
+ function checkDeliveredAndReobserve() {
+ debug('...reattached subtree should now be observable. Try detaching and re-observing.');
+
+ shouldBe('mutations.length', '1');
+ shouldBe('mutations[0].type', '"attributes"');
+ shouldBe('mutations[0].target', 'subDiv');
+ shouldBe('mutations[0].attributeName', '"foo"');
+
+ mutations = null;
+ div.removeChild(subDiv);
+ subDiv.firstChild.textContent = 'badbye';
+ observer.observe(div, {attributes: true, characterData: true, subtree: true});
+ subDiv.setAttribute('foo', 'boo');
+
+ setTimeout(finish);
+ }
+
+
+ function finish() {
+ debug('...The change made before re-observing should be received, but not the one after.');
+
+ shouldBe('mutations.length', '1');
+ shouldBe('mutations[0].type', '"characterData"');
+ shouldBe('mutations[0].target', 'subDiv.firstChild');
+
+ observer.disconnect();
+ debug('');
+ runNextTest();
+ }
+ start();
+}
+
+function testTransientDetachedDetailed() {
+ var observer;
+
+ function start() {
+ debug('Testing correct behavior of transient observation with complex movement .');
+
+ mutations = null;
+ div = document.createElement('div');
+ subDiv = div.appendChild(document.createElement('div'));
+ subDiv2 = subDiv.appendChild(document.createElement('div'));
+ subDiv2.innerHTML = 'hello, world';
+ subDiv3 = document.createElement('div');
+
+ observer = new WebKitMutationObserver(function(mutations) {
+ window.mutations = mutations;
+ });
+
+ observer.observe(div, {attributes: true, characterData: true, subtree: true});
+ div.removeChild(subDiv);
+ subDiv.removeChild(subDiv2);
+ text = subDiv2.removeChild(subDiv2.firstChild);
+
+ subDiv.setAttribute('a', 'a');
+ subDiv2.setAttribute('b', 'b');
+ text.textContent = 'c';
+ subDiv3.appendChild(subDiv2);
+ subDiv3.setAttribute('d', 'd');
+ subDiv2.setAttribute('e', 'e');
+ div.appendChild(subDiv3);
+ subDiv3.setAttribute('f', 'f');
+ subDiv2.setAttribute('g', 'g');
+
+ setTimeout(finish, 0);
+ }
+
+ function finish() {
+ debug('...All changes should be received except for setting the "d" attribute on subDiv3 before it was reachable from div.');
+
+ shouldBe('mutations.length', '6');
+ shouldBe('mutations[0].type', '"attributes"');
+ shouldBe('mutations[0].target', 'subDiv');
+ shouldBe('mutations[0].attributeName', '"a"');
+
+ shouldBe('mutations[1].type', '"attributes"');
+ shouldBe('mutations[1].target', 'subDiv2');
+ shouldBe('mutations[1].attributeName', '"b"');
+
+ shouldBe('mutations[2].type', '"characterData"');
+ shouldBe('mutations[2].target', 'text');
+
+ shouldBe('mutations[3].type', '"attributes"');
+ shouldBe('mutations[3].target', 'subDiv2');
+ shouldBe('mutations[3].attributeName', '"e"');
+
+ shouldBe('mutations[4].type', '"attributes"');
+ shouldBe('mutations[4].target', 'subDiv3');
+ shouldBe('mutations[4].attributeName', '"f"');
+
+ shouldBe('mutations[5].type', '"attributes"');
+ shouldBe('mutations[5].target', 'subDiv2');
+ shouldBe('mutations[5].attributeName', '"g"');
+
+ observer.disconnect();
+ debug('');
+ runNextTest();
+ }
+ start();
+}
+
+var tests = [testBasic, testMultipleObservers, testMultipleObservations, testTransientDetachedBasic, testTransientDetachedDetailed];
var testIndex = 0;
function runNextTest() {
Modified: trunk/Source/WebCore/ChangeLog (98658 => 98659)
--- trunk/Source/WebCore/ChangeLog 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/ChangeLog 2011-10-27 23:38:09 UTC (rev 98659)
@@ -1,3 +1,51 @@
+2011-10-27 Rafael Weinstein <rafa...@chromium.org>
+
+ [MutationObservers] Implement subtree observation of transiently disconnected nodes
+ https://bugs.webkit.org/show_bug.cgi?id=70788
+
+ Reviewed by Ryosuke Niwa.
+
+ 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:
+
2011-10-27 Pratik Solanki <psola...@apple.com>
Ask CG to not parse image metadata
Modified: trunk/Source/WebCore/dom/CharacterData.cpp (98658 => 98659)
--- trunk/Source/WebCore/dom/CharacterData.cpp 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/CharacterData.cpp 2011-10-27 23:38:09 UTC (rev 98659)
@@ -193,12 +193,12 @@
void CharacterData::dispatchModifiedEvent(StringImpl* oldData)
{
#if ENABLE(MUTATION_OBSERVERS)
- Vector<WebKitMutationObserver*> observers;
+ HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::CharacterData);
if (!observers.isEmpty()) {
RefPtr<MutationRecord> mutation = MutationRecord::createCharacterData(this);
- for (size_t i = 0; i < observers.size(); ++i)
- observers[i]->enqueueMutationRecord(mutation);
+ for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
+ iter->first->enqueueMutationRecord(mutation);
}
#endif
if (parentNode())
Modified: trunk/Source/WebCore/dom/ChildListMutationScope.cpp (98658 => 98659)
--- trunk/Source/WebCore/dom/ChildListMutationScope.cpp 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/ChildListMutationScope.cpp 2011-10-27 23:38:09 UTC (rev 98659)
@@ -53,7 +53,7 @@
class ChildListMutationAccumulator : public RefCounted<ChildListMutationAccumulator> {
WTF_MAKE_NONCOPYABLE(ChildListMutationAccumulator);
public:
- ChildListMutationAccumulator(PassRefPtr<Node>, Vector<WebKitMutationObserver*>&);
+ ChildListMutationAccumulator(PassRefPtr<Node>, HashMap<WebKitMutationObserver*, MutationObserverOptions>&);
~ChildListMutationAccumulator();
void childAdded(PassRefPtr<Node>);
@@ -73,7 +73,7 @@
RefPtr<Node> m_nextSibling;
RefPtr<Node> m_lastAdded;
- Vector<WebKitMutationObserver*> m_observers;
+ HashMap<WebKitMutationObserver*, MutationObserverOptions> m_observers;
};
class MutationAccumulationRouter {
@@ -100,7 +100,7 @@
static MutationAccumulationRouter* s_instance;
};
-ChildListMutationAccumulator::ChildListMutationAccumulator(PassRefPtr<Node> target, Vector<WebKitMutationObserver*>& observers)
+ChildListMutationAccumulator::ChildListMutationAccumulator(PassRefPtr<Node> target, HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers)
: m_target(target)
{
m_observers.swap(observers);
@@ -169,8 +169,8 @@
RefPtr<MutationRecord> mutation = MutationRecord::createChildList(
m_target, StaticNodeList::adopt(m_addedNodes), StaticNodeList::adopt(m_removedNodes), m_previousSibling, m_nextSibling);
- for (size_t i = 0; i < m_observers.size(); ++i)
- m_observers[i]->enqueueMutationRecord(mutation);
+ for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = m_observers.begin(); iter != m_observers.end(); ++iter)
+ iter->first->enqueueMutationRecord(mutation);
clear();
}
@@ -244,7 +244,7 @@
return;
}
- Vector<WebKitMutationObserver*> observers;
+ HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
target->getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::ChildList);
if (observers.isEmpty())
m_accumulations.set(target, 0);
Modified: trunk/Source/WebCore/dom/ContainerNode.cpp (98658 => 98659)
--- trunk/Source/WebCore/dom/ContainerNode.cpp 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/ContainerNode.cpp 2011-10-27 23:38:09 UTC (rev 98659)
@@ -1144,6 +1144,7 @@
if (c->parentNode()) {
ChildListMutationScope mutation(c->parentNode());
mutation.willRemoveChild(c);
+ c->notifyMutationObserversNodeWillDetach();
}
#endif
Modified: trunk/Source/WebCore/dom/Element.cpp (98658 => 98659)
--- trunk/Source/WebCore/dom/Element.cpp 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/Element.cpp 2011-10-27 23:38:09 UTC (rev 98659)
@@ -619,14 +619,14 @@
#if ENABLE(MUTATION_OBSERVERS)
static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name)
{
- Vector<WebKitMutationObserver*> observers;
+ HashMap<WebKitMutationObserver*, MutationObserverOptions> observers;
element->getRegisteredMutationObserversOfType(observers, WebKitMutationObserver::Attributes);
if (observers.isEmpty())
return;
RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name);
- for (size_t i = 0; i < observers.size(); ++i)
- observers[i]->enqueueMutationRecord(mutation);
+ for (HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
+ iter->first->enqueueMutationRecord(mutation);
}
#endif
Modified: trunk/Source/WebCore/dom/Node.cpp (98658 => 98659)
--- trunk/Source/WebCore/dom/Node.cpp 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/Node.cpp 2011-10-27 23:38:09 UTC (rev 98659)
@@ -549,10 +549,9 @@
treeScope()->removeNodeListCache();
#if ENABLE(MUTATION_OBSERVERS)
- Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
- if (observerEntries) {
- for (size_t i = 0; i < observerEntries->size(); ++i)
- observerEntries->at(i).observer->observedNodeDestructed(this);
+ if (Vector<MutationObserverRegistration>* registry = mutationObserverRegistry()) {
+ for (size_t i = 0; i < registry->size(); ++i)
+ registry->at(i).observer->observedNodeDestructed(this);
}
#endif
@@ -2698,66 +2697,87 @@
}
#if ENABLE(MUTATION_OBSERVERS)
-Vector<MutationObserverEntry>* Node::mutationObserverEntries()
+Vector<MutationObserverRegistration>* Node::mutationObserverRegistry()
{
- return hasRareData() ? rareData()->mutationObserverEntries() : 0;
+ return hasRareData() ? rareData()->mutationObserverRegistry() : 0;
}
-static void addMatchingObservers(HashSet<WebKitMutationObserver*>& observerSet, Vector<MutationObserverEntry>* observerEntries, MutationObserverOptions options)
+static void addMatchingObservers(HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers, Vector<MutationObserverRegistration>* registry, MutationObserverOptions options)
{
- if (!observerEntries)
+ if (!registry)
return;
- const size_t size = observerEntries->size();
+ const size_t size = registry->size();
for (size_t i = 0; i < size; ++i) {
- MutationObserverEntry& entry = observerEntries->at(i);
- if (entry.hasAllOptions(options))
- observerSet.add(entry.observer.get());
+ MutationObserverRegistration& entry = registry->at(i);
+
+ if (!entry.hasAllOptions(options))
+ continue;
+
+ pair<HashMap<WebKitMutationObserver*, MutationObserverOptions>::iterator, bool> result = observers.add(entry.observer.get(), entry.options);
+ if (!result.second)
+ result.first->second |= entry.options;
}
}
-void Node::getRegisteredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type)
+void Node::getRegisteredMutationObserversOfType(HashMap<WebKitMutationObserver*, MutationObserverOptions>& observers, WebKitMutationObserver::MutationType type)
{
- HashSet<WebKitMutationObserver*> observerSet;
- addMatchingObservers(observerSet, mutationObserverEntries(), type);
+ addMatchingObservers(observers, mutationObserverRegistry(), type);
for (Node* node = parentNode(); node; node = node->parentNode())
- addMatchingObservers(observerSet, node->mutationObserverEntries(), type | WebKitMutationObserver::Subtree);
-
- // FIXME: this method should output a HashSet instead of a Vector.
- if (!observerSet.isEmpty())
- copyToVector(observerSet, observers);
+ addMatchingObservers(observers, node->mutationObserverRegistry(), type | WebKitMutationObserver::Subtree);
}
-Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options)
+Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options, Node* registrationNode)
{
- Vector<MutationObserverEntry>* observerEntries = ensureRareData()->ensureMutationObserverEntries();
- MutationObserverEntry entry(observer, options);
+ Vector<MutationObserverRegistration>* registry = ensureRareData()->ensureMutationObserverRegistry();
+ MutationObserverRegistration registration(observer, options, registrationNode);
- size_t index = observerEntries->find(entry);
+ size_t index = registry->find(registration);
if (index == notFound) {
- observerEntries->append(entry);
+ registry->append(registration);
return MutationObserverRegistered;
}
- (*observerEntries)[index].options = entry.options;
+ registry->at(index).options = registration.options;
return MutationRegistrationOptionsReset;
}
-void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer)
+void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer, Node* registrationNode)
{
- Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries();
- ASSERT(observerEntries);
- if (!observerEntries)
+ Vector<MutationObserverRegistration>* registry = mutationObserverRegistry();
+ ASSERT(registry);
+ if (!registry)
return;
- MutationObserverEntry entry(observer, 0);
- size_t index = observerEntries->find(entry);
+ MutationObserverRegistration registration(observer, 0, registrationNode);
+ size_t index = registry->find(registration);
ASSERT(index != notFound);
if (index == notFound)
return;
- observerEntries->remove(index);
+ registry->remove(index);
}
+
+void Node::notifyMutationObserversNodeWillDetach()
+{
+ for (Node* node = parentNode(); node; node = node->parentNode()) {
+ Vector<MutationObserverRegistration>* registry = node->mutationObserverRegistry();
+ if (!registry)
+ continue;
+
+ const size_t size = registry->size();
+ for (size_t i = 0; i < size; ++i) {
+ MutationObserverRegistration& registration = registry->at(i);
+ if (!registration.hasAllOptions(WebKitMutationObserver::Subtree))
+ continue;
+
+ Node* registrationNode = registration.registrationNode;
+ if (!registrationNode)
+ registrationNode = node;
+ registration.observer->willDetachNodeInObservedSubtree(registrationNode, registration.options, this);
+ }
+ }
+}
#endif // ENABLE(MUTATION_OBSERVERS)
Modified: trunk/Source/WebCore/dom/Node.h (98658 => 98659)
--- trunk/Source/WebCore/dom/Node.h 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/Node.h 2011-10-27 23:38:09 UTC (rev 98659)
@@ -81,7 +81,7 @@
class TagNodeList;
class TreeScope;
-struct MutationObserverEntry;
+struct MutationObserverRegistration;
typedef int ExceptionCode;
@@ -590,15 +590,17 @@
#endif
#if ENABLE(MUTATION_OBSERVERS)
- void getRegisteredMutationObserversOfType(Vector<WebKitMutationObserver*>&, WebKitMutationObserver::MutationType);
+ void getRegisteredMutationObserversOfType(HashMap<WebKitMutationObserver*, MutationObserverOptions>&, WebKitMutationObserver::MutationType);
enum MutationRegistrationResult {
MutationObserverRegistered,
MutationRegistrationOptionsReset
};
- MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, MutationObserverOptions);
+ MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, MutationObserverOptions, Node* registrationNode = 0);
- void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>);
+ void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>, Node* registrationNode = 0);
+
+ void notifyMutationObserversNodeWillDetach();
#endif // ENABLE(MUTATION_OBSERVERS)
private:
@@ -725,7 +727,7 @@
void trackForDebugging();
#if ENABLE(MUTATION_OBSERVERS)
- Vector<MutationObserverEntry>* mutationObserverEntries();
+ Vector<MutationObserverRegistration>* mutationObserverRegistry();
#endif
mutable uint32_t m_nodeFlags;
Modified: trunk/Source/WebCore/dom/NodeRareData.h (98658 => 98659)
--- trunk/Source/WebCore/dom/NodeRareData.h 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/NodeRareData.h 2011-10-27 23:38:09 UTC (rev 98659)
@@ -89,16 +89,17 @@
};
#if ENABLE(MUTATION_OBSERVERS)
-struct MutationObserverEntry {
- MutationObserverEntry(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options)
+struct MutationObserverRegistration {
+ MutationObserverRegistration(PassRefPtr<WebKitMutationObserver> observer, MutationObserverOptions options, Node* node)
: observer(observer)
, options(options)
+ , registrationNode(node)
{
}
- bool operator==(const MutationObserverEntry& other) const
+ bool operator==(const MutationObserverRegistration& other) const
{
- return observer == other.observer;
+ return observer == other.observer && registrationNode == other.registrationNode;
}
bool hasAllOptions(MutationObserverOptions options) const
@@ -108,6 +109,12 @@
RefPtr<WebKitMutationObserver> observer;
MutationObserverOptions options;
+
+ // registrationNode will be 0 if the registration is non-transient. I.e. The registrationNode is the Node in whose
+ // registry it exists.
+ // Note that this doesn't need to be a RefPtr because the observer will be holding a RefPtr to the same node at
+ // least for the lifetime of this Registration in its m_transientObservedNodes map.
+ Node* registrationNode;
};
#endif // ENABLE(MUTATION_OBSERVERS)
@@ -161,12 +168,12 @@
}
#if ENABLE(MUTATION_OBSERVERS)
- Vector<MutationObserverEntry>* mutationObserverEntries() { return m_mutationObservers.get(); }
- Vector<MutationObserverEntry>* ensureMutationObserverEntries()
+ Vector<MutationObserverRegistration>* mutationObserverRegistry() { return m_mutationObserverRegistry.get(); }
+ Vector<MutationObserverRegistration>* ensureMutationObserverRegistry()
{
- if (!m_mutationObservers)
- m_mutationObservers = adoptPtr(new Vector<MutationObserverEntry>);
- return m_mutationObservers.get();
+ if (!m_mutationObserverRegistry)
+ m_mutationObserverRegistry = adoptPtr(new Vector<MutationObserverRegistration>);
+ return m_mutationObserverRegistry.get();
}
#endif
@@ -188,7 +195,7 @@
bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1;
#if ENABLE(MUTATION_OBSERVERS)
- OwnPtr<Vector<MutationObserverEntry> > m_mutationObservers;
+ OwnPtr<Vector<MutationObserverRegistration> > m_mutationObserverRegistry;
#endif
};
Modified: trunk/Source/WebCore/dom/WebKitMutationObserver.cpp (98658 => 98659)
--- trunk/Source/WebCore/dom/WebKitMutationObserver.cpp 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/WebKitMutationObserver.cpp 2011-10-27 23:38:09 UTC (rev 98659)
@@ -53,14 +53,30 @@
WebKitMutationObserver::~WebKitMutationObserver()
{
+ clearAllTransientObservations();
}
+static inline void clearTransientObservationsForRegistration(WebKitMutationObserver* observer, Node* registrationNode, PassOwnPtr<NodeHashSet> transientNodes)
+{
+ for (NodeHashSet::iterator iter = transientNodes->begin(); iter != transientNodes->end(); ++iter)
+ (*iter)->unregisterMutationObserver(observer, registrationNode);
+}
+
void WebKitMutationObserver::observe(Node* node, MutationObserverOptions options)
{
// FIXME: More options composition work needs to be done here, e.g., validation.
- if (node->registerMutationObserver(this, options) == Node::MutationObserverRegistered)
+ if (node->registerMutationObserver(this, options) == Node::MutationObserverRegistered) {
m_observedNodes.append(node);
+ return;
+ }
+
+ HashMap<RefPtr<Node>, NodeHashSet*>::iterator iter = m_transientObservedNodes.find(node);
+ if (iter == m_transientObservedNodes.end())
+ return;
+
+ clearTransientObservationsForRegistration(this, node, adoptPtr(iter->second));
+ m_transientObservedNodes.remove(iter);
}
void WebKitMutationObserver::disconnect()
@@ -69,10 +85,13 @@
m_observedNodes[i]->unregisterMutationObserver(this);
m_observedNodes.clear();
+ clearAllTransientObservations();
}
void WebKitMutationObserver::observedNodeDestructed(Node* node)
{
+ ASSERT(!m_transientObservedNodes.contains(node));
+
size_t index = m_observedNodes.find(node);
ASSERT(index != notFound);
if (index == notFound)
@@ -95,10 +114,36 @@
activeMutationObservers().add(this);
}
+void WebKitMutationObserver::willDetachNodeInObservedSubtree(PassRefPtr<Node> prpRegistrationNode, MutationObserverOptions options, PassRefPtr<Node> prpDetachingNode)
+{
+ RefPtr<Node> registrationNode = prpRegistrationNode;
+ RefPtr<Node> detachingNode = prpDetachingNode;
+
+ detachingNode->registerMutationObserver(this, options, registrationNode.get());
+ HashMap<RefPtr<Node>, NodeHashSet*>::iterator iter = m_transientObservedNodes.find(registrationNode);
+ if (iter != m_transientObservedNodes.end()) {
+ iter->second->add(detachingNode);
+ return;
+ }
+
+ OwnPtr<NodeHashSet> set = adoptPtr(new NodeHashSet());
+ set->add(detachingNode);
+ m_transientObservedNodes.set(registrationNode, set.leakPtr());
+}
+
+void WebKitMutationObserver::clearAllTransientObservations()
+{
+ for (HashMap<RefPtr<Node>, NodeHashSet*>::iterator iter = m_transientObservedNodes.begin(); iter != m_transientObservedNodes.end(); ++iter)
+ clearTransientObservationsForRegistration(this, iter->first.get(), adoptPtr(iter->second));
+
+ m_transientObservedNodes.clear();
+}
+
void WebKitMutationObserver::deliver()
{
MutationRecordArray records;
records.swap(m_records);
+ clearAllTransientObservations();
m_callback->handleEvent(&records, this);
}
Modified: trunk/Source/WebCore/dom/WebKitMutationObserver.h (98658 => 98659)
--- trunk/Source/WebCore/dom/WebKitMutationObserver.h 2011-10-27 23:35:43 UTC (rev 98658)
+++ trunk/Source/WebCore/dom/WebKitMutationObserver.h 2011-10-27 23:38:09 UTC (rev 98659)
@@ -33,6 +33,8 @@
#if ENABLE(MUTATION_OBSERVERS)
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
@@ -45,6 +47,7 @@
class Node;
typedef unsigned char MutationObserverOptions;
+typedef HashSet<RefPtr<Node> > NodeHashSet;
class WebKitMutationObserver : public RefCounted<WebKitMutationObserver> {
public:
@@ -68,17 +71,22 @@
void observe(Node*, MutationObserverOptions);
void disconnect();
+ void willDetachNodeInObservedSubtree(PassRefPtr<Node> registrationNode, MutationObserverOptions, PassRefPtr<Node> detachingNode);
void observedNodeDestructed(Node*);
void enqueueMutationRecord(PassRefPtr<MutationRecord>);
private:
WebKitMutationObserver(PassRefPtr<MutationCallback>);
+ void clearAllTransientObservations();
void deliver();
RefPtr<MutationCallback> m_callback;
Vector<RefPtr<MutationRecord> > m_records;
Vector<Node*> m_observedNodes; // NodeRareData has a RefPtr to this, so use a weak pointer to avoid a cycle.
+
+ // FIXME: Change this to be OwnPtr<NodeHashSet> when OwnPtr supports being contained as map values.
+ HashMap<RefPtr<Node>, NodeHashSet*> m_transientObservedNodes;
};
}