WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-68955-20111019121741.patch (text/plain), 48.83 KB, created by
Rafael Weinstein
on 2011-10-19 12:17:41 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Rafael Weinstein
Created:
2011-10-19 12:17:41 PDT
Size:
48.83 KB
patch
obsolete
>Subversion Revision: 97864 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 8f3a7810f943a9545025b4249c1646de37fa15bd..72e1788d15ddb13c07d3a82622b5d743ce5a0da5 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,65 @@ >+2011-10-18 Rafael Weinstein <rafaelw@chromium.org> >+ >+ [MutationObservers] Implement WebKitMutationObserver.observe for childList changes >+ https://bugs.webkit.org/show_bug.cgi?id=68955 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch adds a ChildListMutationScope object which manages the coalescing of >+ multiple child removals and additions within DOM operations into single childList >+ mutation records. >+ >+ Note that this patch doesn't cover all the cases which can be coalesced (it only >+ covers ContainerNode.*, and Node.innerHTML/innerText). A separate bug, >+ https://bugs.webkit.org/show_bug.cgi?id=70385, has been opened to track the >+ remaining cases. >+ >+ Test: fast/mutation/observe-childList.html >+ >+ * CMakeLists.txt: >+ * GNUmakefile.list.am: >+ * WebCore.gypi: >+ * WebCore.pro: >+ * WebCore.vcproj/WebCore.vcproj: >+ * WebCore.xcodeproj/project.pbxproj: >+ * dom/ContainerNode.cpp: >+ (WebCore::ContainerNode::insertBefore): >+ (WebCore::ContainerNode::replaceChild): >+ (WebCore::willRemoveChildren): >+ (WebCore::dispatchChildInsertionEvents): >+ (WebCore::dispatchChildRemovalEvents): >+ * dom/Element.cpp: >+ (WebCore::enqueueAttributesMutationRecord): >+ * dom/MutationScope.cpp: Added. >+ (WebCore::ChildListAccumulation::ChildListAccumulation::ChildListAccumulation): >+ (WebCore::ChildListAccumulation::ChildListAccumulation::~ChildListAccumulation): >+ (WebCore::ChildListAccumulation::ChildListAccumulation::childAdded): >+ (WebCore::ChildListAccumulation::ChildListAccumulation::willRemoveChild): >+ (WebCore::ChildListAccumulation::ChildListAccumulation::dispatch): >+ (WebCore::ChildListAccumulation::ChildListAccumulation::clear): >+ (WebCore::ChildListAccumulation::ChildListAccumulation::isEmpty): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::ChildListAccumulator): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::~ChildListAccumulator): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::initialize): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::instance): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::childAdded): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::willRemoveChild): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::incrementScopingLevel): >+ (WebCore::ChildListAccumulation::ChildListAccumulator::decrementScopingLevel): >+ (WebCore::ChildListMutationScope::ChildListMutationScope): >+ (WebCore::ChildListMutationScope::~ChildListMutationScope): >+ (WebCore::ChildListMutationScope::childAdded): >+ (WebCore::ChildListMutationScope::willRemoveChild): >+ * dom/MutationScope.h: Added. >+ * dom/Node.cpp: >+ (WebCore::Node::setTextContent): >+ (WebCore::Node::registeredMutationObserversOfType): >+ * dom/WebKitMutationObserver.cpp: >+ (WebCore::WebKitMutationObserver::disconnect): >+ * html/HTMLElement.cpp: >+ (WebCore::replaceChildrenWithFragment): >+ (WebCore::replaceChildrenWithText): >+ > 2011-10-19 Renata Hodovan <reni@webkit.org> > > Add new renderer for SVGRectElement. >diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt >index db50ff9f69a6e9d339f24c51a8c99e71fc43f62c..b1b5896ddd3a1fa3da2f3fc821a98f8620ca58a4 100644 >--- a/Source/WebCore/CMakeLists.txt >+++ b/Source/WebCore/CMakeLists.txt >@@ -563,6 +563,7 @@ SET(WebCore_SOURCES > dom/MouseRelatedEvent.cpp > dom/MutationEvent.cpp > dom/MutationRecord.cpp >+ dom/MutationScope.cpp > dom/NamedNodeMap.cpp > dom/NameNodeList.cpp > dom/Node.cpp >diff --git a/Source/WebCore/GNUmakefile.list.am b/Source/WebCore/GNUmakefile.list.am >index 4806f993e1c75e24779052d55d5787bb5ebee9e9..97847cf8d44df28eaf4540389dbb634c8b3dbdbf 100644 >--- a/Source/WebCore/GNUmakefile.list.am >+++ b/Source/WebCore/GNUmakefile.list.am >@@ -1253,6 +1253,8 @@ webcore_sources += \ > Source/WebCore/dom/MutationEvent.h \ > Source/WebCore/dom/MutationRecord.cpp \ > Source/WebCore/dom/MutationRecord.h \ >+ Source/WebCore/dom/MutationScope.cpp \ >+ Source/WebCore/dom/MutationScope.h \ > Source/WebCore/dom/NamedNodeMap.cpp \ > Source/WebCore/dom/NamedNodeMap.h \ > Source/WebCore/dom/NameNodeList.cpp \ >diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi >index 9277596207efee60314219f7f07acf4ca63f46ce..efedcf32505d55f2a482e591238a216fd073aab4 100644 >--- a/Source/WebCore/WebCore.gypi >+++ b/Source/WebCore/WebCore.gypi >@@ -5231,6 +5231,8 @@ > 'dom/MutationEvent.h', > 'dom/MutationRecord.cpp', > 'dom/MutationRecord.h', >+ 'dom/MutationScope.cpp', >+ 'dom/MutationScope.h', > 'dom/NameNodeList.cpp', > 'dom/NameNodeList.h', > 'dom/NamedNodeMap.cpp', >diff --git a/Source/WebCore/WebCore.pro b/Source/WebCore/WebCore.pro >index d943e58a0fb4c69ce4e52f89e3254e18f5a6ba7b..4161e46718255124c0c347b846a6b562dc18dc3b 100644 >--- a/Source/WebCore/WebCore.pro >+++ b/Source/WebCore/WebCore.pro >@@ -521,6 +521,7 @@ SOURCES += \ > dom/MouseRelatedEvent.cpp \ > dom/MutationEvent.cpp \ > dom/MutationRecord.cpp \ >+ dom/MutationScope.cpp \ > dom/NamedNodeMap.cpp \ > dom/NameNodeList.cpp \ > dom/Node.cpp \ >diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj >index 25d6acdea586f8285cff7d9dba426b264de5bb4b..6249f320227e223908e1ef7838b5a747732e1393 100755 >--- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj >+++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj >@@ -47610,6 +47610,62 @@ > > > </File> > <File >+ RelativePath="..\dom\MutationScope.cpp" >+ > >+ <FileConfiguration >+ Name="Debug|Win32" >+ ExcludedFromBuild="true" >+ > >+ <Tool >+ Name="VCCLCompilerTool" >+ /> >+ </FileConfiguration> >+ <FileConfiguration >+ Name="Release|Win32" >+ ExcludedFromBuild="true" >+ > >+ <Tool >+ Name="VCCLCompilerTool" >+ /> >+ </FileConfiguration> >+ <FileConfiguration >+ Name="Debug_Cairo_CFLite|Win32" >+ ExcludedFromBuild="true" >+ > >+ <Tool >+ Name="VCCLCompilerTool" >+ /> >+ </FileConfiguration> >+ <FileConfiguration >+ Name="Release_Cairo_CFLite|Win32" >+ ExcludedFromBuild="true" >+ > >+ <Tool >+ Name="VCCLCompilerTool" >+ /> >+ </FileConfiguration> >+ <FileConfiguration >+ Name="Debug_All|Win32" >+ ExcludedFromBuild="true" >+ > >+ <Tool >+ Name="VCCLCompilerTool" >+ /> >+ </FileConfiguration> >+ <FileConfiguration >+ Name="Production|Win32" >+ ExcludedFromBuild="true" >+ > >+ <Tool >+ Name="VCCLCompilerTool" >+ /> >+ </FileConfiguration> >+ </File> >+ <File >+ RelativePath="..\dom\MutationScope.h" >+ > >+ </File> >+ <File > RelativePath="..\dom\NamedNodeMap.cpp" > > > </File> >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index b9768af361fe3b6dcf45040763ce75b1f29fbd91..3bd29e9018d96b75a017878e3388475eb85d303f 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -5786,6 +5786,8 @@ > D3AA10F4123A98AA0092152B /* MediaQueryMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AA10F2123A98AA0092152B /* MediaQueryMatcher.h */; }; > D3D4E972130C7CFE007BA540 /* HTMLSummaryElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D3D4E970130C7CFE007BA540 /* HTMLSummaryElement.cpp */; }; > D3D4E973130C7CFE007BA540 /* HTMLSummaryElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D3D4E971130C7CFE007BA540 /* HTMLSummaryElement.h */; }; >+ D619A307144E00BE004BC302 /* MutationScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D619A305144E00BE004BC302 /* MutationScope.cpp */; }; >+ D619A308144E00BE004BC302 /* MutationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D619A306144E00BE004BC302 /* MutationScope.h */; }; > D70AD65713E1342B005B50B4 /* RenderRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D70AD65513E1342B005B50B4 /* RenderRegion.cpp */; }; > D70AD65813E1342B005B50B4 /* RenderRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = D70AD65613E1342B005B50B4 /* RenderRegion.h */; }; > D8B6152F1032495100C8554A /* Cookie.h in Headers */ = {isa = PBXBuildFile; fileRef = D8B6152E1032495100C8554A /* Cookie.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -13090,6 +13092,8 @@ > D3AA10F2123A98AA0092152B /* MediaQueryMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaQueryMatcher.h; sourceTree = "<group>"; }; > D3D4E970130C7CFE007BA540 /* HTMLSummaryElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLSummaryElement.cpp; sourceTree = "<group>"; }; > D3D4E971130C7CFE007BA540 /* HTMLSummaryElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLSummaryElement.h; sourceTree = "<group>"; }; >+ D619A305144E00BE004BC302 /* MutationScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MutationScope.cpp; sourceTree = "<group>"; }; >+ D619A306144E00BE004BC302 /* MutationScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MutationScope.h; sourceTree = "<group>"; }; > D70AD65513E1342B005B50B4 /* RenderRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderRegion.cpp; sourceTree = "<group>"; }; > D70AD65613E1342B005B50B4 /* RenderRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderRegion.h; sourceTree = "<group>"; }; > D8B6152E1032495100C8554A /* Cookie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cookie.h; sourceTree = "<group>"; }; >@@ -21122,6 +21126,8 @@ > C6F08FB91430FE8F00685849 /* MutationRecord.cpp */, > C6F08FBA1430FE8F00685849 /* MutationRecord.h */, > C6F08FBB1430FE8F00685849 /* MutationRecord.idl */, >+ D619A305144E00BE004BC302 /* MutationScope.cpp */, >+ D619A306144E00BE004BC302 /* MutationScope.h */, > BCF34A3611A231CD00C71804 /* NamedNodeMap.cpp */, > A81872180977D3C0005826D9 /* NamedNodeMap.h */, > 85ACA9F90A9B631000671E90 /* NamedNodeMap.idl */, >@@ -23622,6 +23628,7 @@ > C6F0900A14327B6100685849 /* MutationCallback.h in Headers */, > 85031B4A0A44EFC700F992E0 /* MutationEvent.h in Headers */, > C6F08FBD1430FE8F00685849 /* MutationRecord.h in Headers */, >+ D619A308144E00BE004BC302 /* MutationScope.h in Headers */, > A81872230977D3C0005826D9 /* NamedNodeMap.h in Headers */, > A818721E0977D3C0005826D9 /* NameNodeList.h in Headers */, > E10B9B6C0B747599003ED890 /* NativeXPathNSResolver.h in Headers */, >@@ -26686,6 +26693,7 @@ > B1AD4E6213A12A0B00846B27 /* MutableTextTrack.cpp in Sources */, > 85031B490A44EFC700F992E0 /* MutationEvent.cpp in Sources */, > C6F08FBC1430FE8F00685849 /* MutationRecord.cpp in Sources */, >+ D619A307144E00BE004BC302 /* MutationScope.cpp in Sources */, > BCF34A3711A231CD00C71804 /* NamedNodeMap.cpp in Sources */, > A81872240977D3C0005826D9 /* NameNodeList.cpp in Sources */, > E10B9B6D0B747599003ED890 /* NativeXPathNSResolver.cpp in Sources */, >diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp >index 3ea49936935749b13192b76341d509b1538e4c0c..415aeea5a63526397f8fe70ee7383cf6351c7a9e 100644 >--- a/Source/WebCore/dom/ContainerNode.cpp >+++ b/Source/WebCore/dom/ContainerNode.cpp >@@ -35,6 +35,7 @@ > #include "InlineTextBox.h" > #include "InspectorInstrumentation.h" > #include "MutationEvent.h" >+#include "MutationScope.h" > #include "ResourceLoadScheduler.h" > #include "Page.h" > #include "RenderBox.h" >@@ -141,6 +142,10 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce > if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do > return true; > >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(this); >+#endif >+ > RefPtr<Node> next = refChild; > RefPtr<Node> refChildPreviousSibling = refChild->previousSibling(); > for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { >@@ -272,6 +277,10 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce > return false; > } > >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(this); >+#endif >+ > RefPtr<Node> prev = oldChild->previousSibling(); > RefPtr<Node> next = oldChild->nextSibling(); > >@@ -400,6 +409,10 @@ static void willRemoveChildren(ContainerNode* container) > NodeVector children; > collectNodes(container, children); > >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(container); >+#endif >+ > for (NodeVector::const_iterator it = children.begin(); it != children.end(); it++) { > Node* child = it->get(); > // fire removed from document mutation events. >@@ -611,6 +624,10 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bo > if (targets.isEmpty()) > return true; > >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(this); >+#endif >+ > // Now actually add the child(ren) > RefPtr<Node> prev = lastChild(); > for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { >@@ -1095,6 +1112,13 @@ static void dispatchChildInsertionEvents(Node* child) > RefPtr<Node> c = child; > RefPtr<Document> document = child->document(); > >+#if ENABLE(MUTATION_OBSERVERS) >+ if (c->parentNode()) { >+ ChildListMutationScope mutation(c->parentNode()); >+ mutation.childAdded(c); >+ } >+#endif >+ > if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER)) > c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode())); > >@@ -1116,6 +1140,13 @@ static void dispatchChildRemovalEvents(Node* child) > RefPtr<Node> c = child; > RefPtr<Document> document = child->document(); > >+#if ENABLE(MUTATION_OBSERVERS) >+ if (c->parentNode()) { >+ ChildListMutationScope mutation(c->parentNode()); >+ mutation.willRemoveChild(c); >+ } >+#endif >+ > // dispatch pre-removal mutation events > if (c->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) > c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, c->parentNode())); >diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp >index d699b19116e831d73fd5523a84eaa2eb586fef57..33f365549cd6056a1dee312877addea8fdbf4987 100644 >--- a/Source/WebCore/dom/Element.cpp >+++ b/Source/WebCore/dom/Element.cpp >@@ -625,8 +625,8 @@ static void enqueueAttributesMutationRecord(Element* element, const QualifiedNam > return; > > RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name); >- for (Vector<WebKitMutationObserver*>::iterator iter = observers.begin(); iter != observers.end(); ++iter) >- (*iter)->enqueueMutationRecord(mutation); >+ for (size_t i = 0; i < observers.size(); ++i) >+ observers[i]->enqueueMutationRecord(mutation); > } > #endif > >diff --git a/Source/WebCore/dom/MutationScope.cpp b/Source/WebCore/dom/MutationScope.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..ea11de1c318dfce2af23570be4de718b56b2674b >--- /dev/null >+++ b/Source/WebCore/dom/MutationScope.cpp >@@ -0,0 +1,289 @@ >+/* >+ * Copyright (C) 2011 Google Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions are >+ * met: >+ * >+ * * Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * * Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following disclaimer >+ * in the documentation and/or other materials provided with the >+ * distribution. >+ * * Neither the name of Google Inc. nor the names of its >+ * contributors may be used to endorse or promote products derived from >+ * this software without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+ >+#if ENABLE(MUTATION_OBSERVERS) >+ >+#include "MutationScope.h" >+ >+#include "DocumentFragment.h" >+#include "Element.h" >+#include "MutationRecord.h" >+#include "Node.h" >+#include "StaticNodeList.h" >+#include <wtf/HashMap.h> >+ >+namespace WebCore { >+ >+namespace { >+ >+// RecordAccumulator expects that all removals from a parent take place in order and precede >+// any additions. If this is violated (i.e. because of code changes elsewhere in WebCore) >+// it will likely result in both (a) ASSERTions failing, and (b) mutation records being >+// enqueued for delivery before the outer-most scope closes. >+class RecordAccumulator { >+ WTF_MAKE_NONCOPYABLE(RecordAccumulator); >+public: >+ RecordAccumulator(PassRefPtr<Node>, Vector<WebKitMutationObserver*>&); >+ ~RecordAccumulator(); >+ >+ void childAdded(PassRefPtr<Node>); >+ void willRemoveChild(PassRefPtr<Node>); >+ void enqueueMutationRecord(); >+ >+private: >+ void clear(); >+ bool isEmpty(); >+ >+ RefPtr<Node> m_target; >+ >+ Vector<RefPtr<Node> > m_removedNodes; >+ Vector<RefPtr<Node> > m_addedNodes; >+ RefPtr<Node> m_previousSibling; >+ RefPtr<Node> m_nextSibling; >+ RefPtr<Node> m_lastAdded; >+ >+ Vector<WebKitMutationObserver*> m_observers; >+}; >+ >+class AccumulationManager { >+ WTF_MAKE_NONCOPYABLE(AccumulationManager); >+public: >+ ~AccumulationManager(); >+ >+ static AccumulationManager* instance(); >+ >+ void incrementScopingLevel(Node*); >+ void decrementScopingLevel(Node*); >+ >+ void childAdded(Node*, PassRefPtr<Node>); >+ void willRemoveChild(Node*, PassRefPtr<Node>); >+ >+private: >+ AccumulationManager(); >+ static void initialize(); >+ >+ typedef HashMap<Node*, unsigned> ScopingLevelMap; >+ ScopingLevelMap m_scopingLevels; >+ HashMap<Node*, RecordAccumulator*> m_accumulations; >+ >+ static AccumulationManager* s_instance; >+}; >+ >+RecordAccumulator::RecordAccumulator(PassRefPtr<Node> target, Vector<WebKitMutationObserver*>& observers) >+ : m_target(target) >+{ >+ m_observers.swap(observers); >+ clear(); >+} >+ >+RecordAccumulator::~RecordAccumulator() >+{ >+ ASSERT(isEmpty()); >+} >+ >+void RecordAccumulator::childAdded(PassRefPtr<Node> prpChild) >+{ >+ RefPtr<Node> child = prpChild; >+ >+ // Additions must be in-order. >+ if (!isEmpty() && (m_lastAdded.get() != child->previousSibling() || m_nextSibling != child->nextSibling())) >+ enqueueMutationRecord(); >+ >+ if (isEmpty()) { >+ m_previousSibling = child->previousSibling(); >+ m_nextSibling = child->nextSibling(); >+ } >+ >+ m_addedNodes.append(child); >+ m_lastAdded = child; >+} >+ >+void RecordAccumulator::willRemoveChild(PassRefPtr<Node> prpChild) >+{ >+ RefPtr<Node> child = prpChild; >+ ASSERT(m_addedNodes.isEmpty()); // Removals must precede additions. See comment on the class. >+ >+ if (!m_addedNodes.isEmpty()) >+ enqueueMutationRecord(); >+ >+ ASSERT(isEmpty() || m_nextSibling.get() == child); // Removals must be continuous and in-order. >+ if (!isEmpty() && m_nextSibling.get() != child) >+ enqueueMutationRecord(); >+ >+ if (isEmpty()) { >+ m_previousSibling = child->previousSibling(); >+ m_nextSibling = child->nextSibling(); >+ m_lastAdded = child->previousSibling(); >+ } else >+ m_nextSibling = child->nextSibling(); >+ >+ m_removedNodes.append(child); >+} >+ >+void RecordAccumulator::enqueueMutationRecord() >+{ >+ ASSERT(!isEmpty()); >+ if (isEmpty()) { >+ clear(); >+ return; >+ } >+ >+ 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); >+ >+ clear(); >+} >+ >+void RecordAccumulator::clear() >+{ >+ if (!m_removedNodes.isEmpty()) >+ m_removedNodes.clear(); >+ if (!m_addedNodes.isEmpty()) >+ m_addedNodes.clear(); >+ m_previousSibling = 0; >+ m_nextSibling = 0; >+ m_lastAdded = 0; >+} >+ >+bool RecordAccumulator::isEmpty() >+{ >+ return m_removedNodes.isEmpty() && m_addedNodes.isEmpty(); >+} >+ >+AccumulationManager* AccumulationManager::s_instance = 0; >+ >+AccumulationManager::AccumulationManager() >+{ >+} >+ >+AccumulationManager::~AccumulationManager() >+{ >+ ASSERT(m_scopingLevels.isEmpty()); >+ ASSERT(m_accumulations.isEmpty()); >+} >+ >+void AccumulationManager::initialize() >+{ >+ ASSERT(!s_instance); >+ s_instance = adoptPtr(new AccumulationManager).leakPtr(); >+} >+ >+ >+AccumulationManager* AccumulationManager::instance() >+{ >+ if (!s_instance) >+ initialize(); >+ >+ return s_instance; >+} >+ >+void AccumulationManager::childAdded(Node* target, PassRefPtr<Node> child) >+{ >+ HashMap<Node*, RecordAccumulator*>::iterator iter = m_accumulations.find(target); >+ ASSERT(iter != m_accumulations.end()); >+ >+ RecordAccumulator* record = iter->second; >+ if (record) >+ record->childAdded(child); >+} >+ >+void AccumulationManager::willRemoveChild(Node* target, PassRefPtr<Node> child) >+{ >+ HashMap<Node*, RecordAccumulator*>::iterator iter = m_accumulations.find(target); >+ ASSERT(iter != m_accumulations.end()); >+ >+ RecordAccumulator* record = iter->second; >+ if (record) >+ record->willRemoveChild(child); >+} >+ >+void AccumulationManager::incrementScopingLevel(Node* target) >+{ >+ pair<ScopingLevelMap::iterator, bool> result = m_scopingLevels.add(target, 1); >+ if (!result.second) { >+ ++(result.first->second); >+ return; >+ } >+ >+ Vector<WebKitMutationObserver*> observers; >+ target->registeredMutationObserversOfType(observers, WebKitMutationObserver::ChildList); >+ if (observers.isEmpty()) >+ m_accumulations.set(target, 0); >+ else >+ m_accumulations.set(target, new RecordAccumulator(target, observers)); >+} >+ >+void AccumulationManager::decrementScopingLevel(Node* target) >+{ >+ ScopingLevelMap::iterator iter = m_scopingLevels.find(target); >+ ASSERT(iter != m_scopingLevels.end()); >+ >+ --(iter->second); >+ if (iter->second > 0) >+ return; >+ >+ OwnPtr<RecordAccumulator> record = adoptPtr(m_accumulations.take(target)); >+ m_scopingLevels.remove(iter); >+ >+ if (record) >+ record->enqueueMutationRecord(); >+} >+ >+} // namespace >+ >+ChildListMutationScope::ChildListMutationScope(PassRefPtr<Node> target) >+ : m_target(target) >+{ >+ AccumulationManager::instance()->incrementScopingLevel(m_target.get()); >+} >+ >+ChildListMutationScope::~ChildListMutationScope() >+{ >+ AccumulationManager::instance()->decrementScopingLevel(m_target.get()); >+} >+ >+void ChildListMutationScope::childAdded(PassRefPtr<Node> child) >+{ >+ AccumulationManager::instance()->childAdded(m_target.get(), child); >+} >+ >+void ChildListMutationScope::willRemoveChild(PassRefPtr<Node> child) >+{ >+ AccumulationManager::instance()->willRemoveChild(m_target.get(), child); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(MUTATION_OBSERVERS) >diff --git a/Source/WebCore/dom/MutationScope.h b/Source/WebCore/dom/MutationScope.h >new file mode 100644 >index 0000000000000000000000000000000000000000..5c60856c83b6dba2638cd307e75d77b24ab64c3b >--- /dev/null >+++ b/Source/WebCore/dom/MutationScope.h >@@ -0,0 +1,61 @@ >+/* >+ * Copyright (C) 2011 Google Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions are >+ * met: >+ * >+ * * Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * * Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following disclaimer >+ * in the documentation and/or other materials provided with the >+ * distribution. >+ * * Neither the name of Google Inc. nor the names of its >+ * contributors may be used to endorse or promote products derived from >+ * this software without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef MutationScope_h >+#define MutationScope_h >+ >+#if ENABLE(MUTATION_OBSERVERS) >+ >+#include <wtf/Noncopyable.h> >+#include <wtf/PassRefPtr.h> >+#include <wtf/RefPtr.h> >+ >+namespace WebCore { >+ >+class Node; >+ >+class ChildListMutationScope { >+ WTF_MAKE_NONCOPYABLE(ChildListMutationScope); >+public: >+ ChildListMutationScope(PassRefPtr<Node> target); >+ ~ChildListMutationScope(); >+ >+ void childAdded(PassRefPtr<Node>); >+ void willRemoveChild(PassRefPtr<Node>); >+ >+private: >+ RefPtr<Node> m_target; >+}; >+ >+} // namespace WebCore >+ >+#endif // ENABLE(MUTATION_OBSERVERS) >+ >+#endif // MutationScope_h >diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp >index 1f85fd912eadfd5a73159d6dede930e64849c940..c66ddcc784d22ea27b35cbe88a48c8381455a736 100644 >--- a/Source/WebCore/dom/Node.cpp >+++ b/Source/WebCore/dom/Node.cpp >@@ -66,6 +66,7 @@ > #include "Logging.h" > #include "MouseEvent.h" > #include "MutationEvent.h" >+#include "MutationScope.h" > #include "NameNodeList.h" > #include "NamedNodeMap.h" > #include "NodeRareData.h" >@@ -2124,6 +2125,9 @@ void Node::setTextContent(const String& text, ExceptionCode& ec) > case DOCUMENT_FRAGMENT_NODE: > case SHADOW_ROOT_NODE: { > ContainerNode* container = toContainerNode(this); >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(this); >+#endif > container->removeChildren(); > if (!text.isEmpty()) > container->appendChild(document()->createTextNode(text), ec); >@@ -2694,9 +2698,9 @@ void Node::registeredMutationObserversOfType(Vector<WebKitMutationObserver*>& ob > if (!observerEntries || observerEntries->isEmpty()) > return; > >- for (Vector<MutationObserverEntry>::iterator iter = observerEntries->begin(); iter != observerEntries->end(); ++iter) { >- if (iter->matches(type)) >- observers.append(iter->observer.get()); >+ for (size_t i = 0; i < observerEntries->size(); ++i) { >+ if ((*observerEntries)[i].matches(type)) >+ observers.append((*observerEntries)[i].observer.get()); > } > } > >diff --git a/Source/WebCore/dom/WebKitMutationObserver.cpp b/Source/WebCore/dom/WebKitMutationObserver.cpp >index 7d736ae7bac64c4588fcba2fc3d3b775297bde14..83f15990ea2beca339bb0a73cc2969ba4df29fa5 100644 >--- a/Source/WebCore/dom/WebKitMutationObserver.cpp >+++ b/Source/WebCore/dom/WebKitMutationObserver.cpp >@@ -65,8 +65,8 @@ void WebKitMutationObserver::observe(Node* node, MutationObserverOptions options > > void WebKitMutationObserver::disconnect() > { >- for (Vector<Node*>::iterator iter = m_observedNodes.begin(); iter != m_observedNodes.end(); ++iter) >- (*iter)->unregisterMutationObserver(this); >+ for (size_t i = 0; i < m_observedNodes.size(); ++i) >+ m_observedNodes[i]->unregisterMutationObserver(this); > > m_observedNodes.clear(); > } >diff --git a/Source/WebCore/html/HTMLElement.cpp b/Source/WebCore/html/HTMLElement.cpp >index 5912da07d76a492c6d73288a1434dd131e319804..5d40cd57ce56b787da35f299c99720c3f93936dc 100644 >--- a/Source/WebCore/html/HTMLElement.cpp >+++ b/Source/WebCore/html/HTMLElement.cpp >@@ -41,6 +41,7 @@ > #include "HTMLNames.h" > #include "HTMLParserIdioms.h" > #include "HTMLTextFormControlElement.h" >+#include "MutationScope.h" > #include "RenderWordBreak.h" > #include "ScriptEventListener.h" > #include "Settings.h" >@@ -317,6 +318,10 @@ static inline bool hasOneTextChild(ContainerNode* node) > > static void replaceChildrenWithFragment(HTMLElement* element, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec) > { >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(element); >+#endif >+ > if (!fragment->firstChild()) { > element->removeChildren(); > return; >@@ -338,6 +343,10 @@ static void replaceChildrenWithFragment(HTMLElement* element, PassRefPtr<Documen > > static void replaceChildrenWithText(HTMLElement* element, const String& text, ExceptionCode& ec) > { >+#if ENABLE(MUTATION_OBSERVERS) >+ ChildListMutationScope mutation(element); >+#endif >+ > if (hasOneTextChild(element)) { > static_cast<Text*>(element->firstChild())->setData(text, ec); > return; >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 2c8398e31a7f69c2610e22aac572f6d04e756348..425e7c75d1b87893acef6a3f7dd47ff8e35a66ff 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,15 @@ >+2011-10-18 Rafael Weinstein <rafaelw@chromium.org> >+ >+ [MutationObservers] Implement WebKitMutationObserver.observe for childList changes >+ https://bugs.webkit.org/show_bug.cgi?id=68955 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * fast/mutation/observe-attributes-expected.txt: >+ * fast/mutation/observe-attributes.html: >+ * fast/mutation/observe-childList-expected.txt: Added. >+ * fast/mutation/observe-childList.html: Added. >+ > 2011-10-19 Renata Hodovan <reni@webkit.org> > > Add new renderer for SVGRectElement. >diff --git a/LayoutTests/fast/mutation/observe-attributes-expected.txt b/LayoutTests/fast/mutation/observe-attributes-expected.txt >index 9869fe2ee5b02c0e7768d81a332aafd3866ecc81..2f8598dff7a020ee3c727e9e37f986a6a2818695 100644 >--- a/LayoutTests/fast/mutation/observe-attributes-expected.txt >+++ b/LayoutTests/fast/mutation/observe-attributes-expected.txt >@@ -23,7 +23,7 @@ PASS mutations[1].attributeNamespace is null > Testing that observing without specifying "attributes" does not result in hearing about attribute changes. > PASS mutations is null > >-Testing that re-observing the same node with the same observing has the effect of resetting the options. >+Testing that re-observing the same node with the same observer has the effect of resetting the options. > PASS calls is 1 > PASS mutations.length is 1 > PASS mutations[0].type is "attributes" >diff --git a/LayoutTests/fast/mutation/observe-attributes.html b/LayoutTests/fast/mutation/observe-attributes.html >index 646e3931d1450c1dcd4ace4a7ebe5103d88eb3be..85ec6774d6f5c7fd1ba5b9dcba7ba3bff8350066 100644 >--- a/LayoutTests/fast/mutation/observe-attributes.html >+++ b/LayoutTests/fast/mutation/observe-attributes.html >@@ -12,8 +12,7 @@ > <script> > > window.jsTestIsAsync = true; >-var mutations; >-var mutations2; >+var mutations, mutations2; > var calls; > > function testBasic() { >@@ -109,7 +108,7 @@ function testMultipleRegistration() { > var observer; > > function start() { >- debug('Testing that re-observing the same node with the same observing has the effect of resetting the options.'); >+ debug('Testing that re-observing the same node with the same observer has the effect of resetting the options.'); > > calls = 0; > mutations = null; >diff --git a/LayoutTests/fast/mutation/observe-childList-expected.txt b/LayoutTests/fast/mutation/observe-childList-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..40ad737f0712e63a6c3b42c1b2023aee019e0f81 >--- /dev/null >+++ b/LayoutTests/fast/mutation/observe-childList-expected.txt >@@ -0,0 +1,93 @@ >+Test WebKitMutationObserver.observe on attributes. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+Testing basic aspects of childList observation. >+...can childList changes be observed at all >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 1 >+PASS mutations[0].addedNodes[0] is removedDiv1 >+...observer.disconnect() should prevent further delivery of mutations. >+PASS mutations is null >+...re-observing after disconnect works with the same observer. >+PASS mutations.length is 2 >+PASS mutations[0].type is "childList" >+PASS mutations[0].removedNodes.length is 1 >+PASS mutations[0].removedNodes[0] is removedDiv1 >+PASS mutations[1].type is "childList" >+PASS mutations[1].removedNodes.length is 1 >+PASS mutations[1].removedNodes[0] is removedDiv2 >+ >+Testing that observing without specifying "childList" does not result in hearing about childList changes. >+PASS mutations is null >+ >+Testing that re-observing the same node with the same observer has the effect of resetting the options. >+PASS calls is 1 >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations is null >+ >+Testing that multiple observers can be registered to a given node and both receive mutations. >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations2.length is 1 >+PASS mutations2[0].type is "childList" >+ >+Testing that innerText and innerHTML always result in a single childList mutation. >+...innerHTML >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 3 >+PASS mutations[0].addedNodes[0] is addedDiv1 >+PASS mutations[0].addedNodes[1] is addedDiv2 >+PASS mutations[0].addedNodes[2] is addedDiv3 >+PASS mutations[0].removedNodes.length is 2 >+PASS mutations[0].removedNodes[0] is removedDiv1 >+PASS mutations[0].removedNodes[1] is removedDiv2 >+...innerText >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 1 >+PASS mutations[0].removedNodes.length is 3 >+ >+Testing that replaceChild results in minimal childList mutations. >+...innerHTML >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 1 >+PASS mutations[0].addedNodes[0] is addedDiv1 >+PASS mutations[0].removedNodes.length is 1 >+PASS mutations[0].removedNodes[0] is removedDiv1 >+...innerText >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 2 >+PASS mutations[0].addedNodes[0] is addedDiv1 >+PASS mutations[0].addedNodes[1] is addedDiv2 >+PASS mutations[0].removedNodes.length is 1 >+PASS mutations[0].removedNodes[0] is removedDiv1 >+ >+Testing that insertBefore results in minimal childList mutations. >+...innerText >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 2 >+PASS mutations[0].addedNodes[0] is addedDiv1 >+PASS mutations[0].addedNodes[1] is addedDiv2 >+PASS mutations[0].removedNodes.length is 0 >+ >+Testing that appendChild results in minimal childList mutations. >+...innerText >+PASS mutations.length is 1 >+PASS mutations[0].type is "childList" >+PASS mutations[0].addedNodes.length is 2 >+PASS mutations[0].addedNodes[0] is addedDiv1 >+PASS mutations[0].addedNodes[1] is addedDiv2 >+PASS mutations[0].removedNodes.length is 0 >+ >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/fast/mutation/observe-childList.html b/LayoutTests/fast/mutation/observe-childList.html >new file mode 100644 >index 0000000000000000000000000000000000000000..d16ecbd8e30dbaf9a7960bb20c2c2946b2eb04fb >--- /dev/null >+++ b/LayoutTests/fast/mutation/observe-childList.html >@@ -0,0 +1,407 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<meta charset="utf-8"> >+<link rel="stylesheet" href="../js/resources/js-test-style.css"> >+<script src="../js/resources/js-test-pre.js"></script> >+<title></title> >+</head> >+<body> >+<p id=description></p> >+<div id="console"></div> >+<script> >+ >+window.jsTestIsAsync = true; >+var mutations, mutations2; >+var calls; >+var div, removedDiv1, removedDiv2, addedDiv1, addedDiv2, addedDiv3; >+ >+function testBasic() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing basic aspects of childList observation.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { childList: true }); >+ removedDiv1 = div.appendChild(document.createElement('div')); >+ setTimeout(checkDisconnectAndMutate, 0); >+ } >+ >+ function checkDisconnectAndMutate() { >+ debug('...can childList changes be observed at all'); >+ >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '1'); >+ shouldBe('mutations[0].addedNodes[0]', 'removedDiv1'); >+ >+ mutations = null; >+ observer.disconnect(); >+ removedDiv1 = div.appendChild(document.createElement('div')); >+ setTimeout(checkNotDeliveredAndMutateMultiple, 0); >+ } >+ >+ function checkNotDeliveredAndMutateMultiple() { >+ debug('...observer.disconnect() should prevent further delivery of mutations.'); >+ >+ shouldBe('mutations', 'null'); >+ observer.observe(div, { childList: true }); >+ removedDiv1 = div.removeChild(div.firstChild); >+ removedDiv2 = div.removeChild(div.firstChild); >+ setTimeout(finish); >+ } >+ >+ function finish() { >+ debug('...re-observing after disconnect works with the same observer.'); >+ >+ shouldBe('mutations.length', '2'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].removedNodes.length', '1'); >+ shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); >+ shouldBe('mutations[1].type', '"childList"'); >+ shouldBe('mutations[1].removedNodes.length', '1'); >+ shouldBe('mutations[1].removedNodes[0]', 'removedDiv2'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testWrongType() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing that observing without specifying "childList" does not result in hearing about childList changes.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { attributes: true, characterData: true }); >+ div.appendChild(document.createElement('div')); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations', 'null'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testMultipleRegistration() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing that re-observing the same node with the same observer has the effect of resetting the options.'); >+ >+ calls = 0; >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ calls++; >+ }); >+ >+ observer.observe(div, { childList: true, characterData: true }); >+ observer.observe(div, { childList: true }); >+ div.appendChild(document.createElement('div')); >+ setTimeout(checkDisconnectAndMutate, 0); >+ } >+ >+ function checkDisconnectAndMutate() { >+ shouldBe('calls', '1'); >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ mutations = null; >+ observer.observe(div, { childList: true, characterData: true }); >+ observer.observe(div, { attributes: true }); >+ div.appendChild(document.createElement('div')); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations', 'null'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testMultipleObservers() { >+ var div; >+ var observer; >+ var observer2; >+ >+ function start() { >+ debug('Testing that multiple observers can be registered to a given node and both receive mutations.'); >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ observer2 = new WebKitMutationObserver(function(m) { >+ mutations2 = m; >+ }); >+ observer.observe(div, { childList: true }); >+ observer2.observe(div, { childList: true }); >+ div.appendChild(document.createElement('div')); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations2.length', '1'); >+ shouldBe('mutations2[0].type', '"childList"'); >+ observer.disconnect(); >+ observer2.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testInnerHTMLAndInnerText() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing that innerText and innerHTML always result in a single childList mutation.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ div.innerHTML = '<span>Foo</span><div>Bar</div>'; >+ removedDiv1 = div.firstChild; >+ removedDiv2 = removedDiv1.nextSibling; >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { childList: true }); >+ div.innerHTML = 'foo<img src="bar.png"><p>Baz</p>'; >+ addedDiv1 = div.childNodes[0]; >+ addedDiv2 = div.childNodes[1]; >+ addedDiv3 = div.childNodes[2]; >+ setTimeout(checkInnerHTML, 0); >+ } >+ >+ function checkInnerHTML() { >+ debug('...innerHTML'); >+ >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '3'); >+ shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); >+ shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); >+ shouldBe('mutations[0].addedNodes[2]', 'addedDiv3'); >+ shouldBe('mutations[0].removedNodes.length', '2'); >+ shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); >+ shouldBe('mutations[0].removedNodes[1]', 'removedDiv2'); >+ >+ mutations = null; >+ div.innerText = 'foo'; >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ debug('...innerText'); >+ >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '1'); >+ shouldBe('mutations[0].removedNodes.length', '3'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testReplaceChild() { >+ var div; >+ var observer; >+ var fragment; >+ >+ function start() { >+ debug('Testing that replaceChild results in minimal childList mutations.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ div.innerHTML = '<span>Foo</span><div>Bar</div>'; >+ removedDiv1 = div.firstChild; >+ >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { childList: true }); >+ addedDiv1 = document.createElement('div'); >+ div.replaceChild(addedDiv1, div.firstChild); >+ setTimeout(checkReplaceWithNode, 0); >+ } >+ >+ function checkReplaceWithNode() { >+ debug('...simple replace child'); >+ >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '1'); >+ shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); >+ shouldBe('mutations[0].removedNodes.length', '1'); >+ shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); >+ >+ mutations = null; >+ fragment = document.createDocumentFragment(); >+ addedDiv1 = fragment.appendChild(document.createElement('div')); >+ addedDiv2 = fragment.appendChild(document.createElement('div')); >+ removedDiv1 = div.firstChild; >+ >+ div.replaceChild(fragment, removedDiv1); >+ >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ debug('...replace with DocumentFragment'); >+ >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '2'); >+ shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); >+ shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); >+ shouldBe('mutations[0].removedNodes.length', '1'); >+ shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); >+ >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testInsertBefore() { >+ var div; >+ var observer; >+ var fragment; >+ >+ function start() { >+ debug('Testing that insertBefore results in minimal childList mutations.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ div.innerHTML = '<span>Foo</span>'; >+ fragment = document.createDocumentFragment(); >+ addedDiv1 = fragment.appendChild(document.createElement('div')); >+ addedDiv2 = fragment.appendChild(document.createElement('div')); >+ >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { childList: true }); >+ div.insertBefore(fragment, div.firstChild); >+ setTimeout(finish, 0); >+ } >+ >+ >+ function finish() { >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '2'); >+ shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); >+ shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); >+ shouldBe('mutations[0].removedNodes.length', '0'); >+ >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testAppendChild() { >+ var div; >+ var observer; >+ var fragment; >+ >+ function start() { >+ debug('Testing that appendChild results in minimal childList mutations.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ div.innerHTML = '<span>Foo</span>'; >+ fragment = document.createDocumentFragment(); >+ addedDiv1 = fragment.appendChild(document.createElement('div')); >+ addedDiv2 = fragment.appendChild(document.createElement('div')); >+ >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { childList: true }); >+ div.appendChild(fragment); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"childList"'); >+ shouldBe('mutations[0].addedNodes.length', '2'); >+ shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); >+ shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); >+ shouldBe('mutations[0].removedNodes.length', '0'); >+ >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+var tests = [testBasic, testWrongType, testMultipleRegistration, testMultipleObservers, testInnerHTMLAndInnerText, testReplaceChild, testInsertBefore, testAppendChild]; >+var testIndex = 0; >+ >+function runNextTest() { >+ if (testIndex < tests.length) >+ tests[testIndex++](); >+ else >+ finishJSTest(); >+} >+ >+description('Test WebKitMutationObserver.observe on attributes.'); >+ >+if (!window.WebKitMutationObserver) >+ testFailed('This test requires ENABLE(MUTATION_OBSERVERS)'); >+else >+ runNextTest(); >+ >+var successfullyParsed = true; >+ >+</script> >+<script src="../js/resources/js-test-post.js"></script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 68955
:
111531
|
111535
|
111559
|
111625
|
111650
|
111656
|
111864
|
111869
|
111991
|
111995
|
112000