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-20111020160458.patch (text/plain), 50.43 KB, created by
Rafael Weinstein
on 2011-10-20 16:04:59 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Rafael Weinstein
Created:
2011-10-20 16:04:59 PDT
Size:
50.43 KB
patch
obsolete
>Subversion Revision: 98030 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index cbabe25cccb9a55820f0fc32ff3093c549268e77..4380a129864dfaa4267274dd3ded70e51b091bfb 100755 >--- 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/ChildListMutationScope.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/ChildListMutationScope.h: Added. >+ * dom/ContainerNode.cpp: >+ (WebCore::ContainerNode::insertBefore): >+ (WebCore::ContainerNode::replaceChild): >+ (WebCore::willRemoveChildren): >+ (WebCore::dispatchChildInsertionEvents): >+ (WebCore::dispatchChildRemovalEvents): >+ * dom/Element.cpp: >+ (WebCore::enqueueAttributesMutationRecord): >+ * dom/Node.cpp: >+ (WebCore::Node::setTextContent): >+ (WebCore::Node::registeredMutationObserversOfType): >+ * dom/WebKitMutationObserver.cpp: >+ (WebCore::WebKitMutationObserver::disconnect): >+ * html/HTMLElement.cpp: >+ (WebCore::replaceChildrenWithFragment): >+ (WebCore::replaceChildrenWithText): >+ > 2011-10-20 Raymond Toy <rtoy@google.com> > > Implement SSE denormal disabler for windows. >diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt >index a2fc5bb0e003b21b785c363b03fcf76361c2c89c..9075380a9df83089a52575c154464cab385af5ad 100644 >--- a/Source/WebCore/CMakeLists.txt >+++ b/Source/WebCore/CMakeLists.txt >@@ -513,6 +513,7 @@ SET(WebCore_SOURCES > dom/CDATASection.cpp > dom/CharacterData.cpp > dom/CheckedRadioButtons.cpp >+ dom/ChildListMutationScope.cpp > dom/ChildNodeList.cpp > dom/ClassNodeList.cpp > dom/ClientRect.cpp >diff --git a/Source/WebCore/GNUmakefile.list.am b/Source/WebCore/GNUmakefile.list.am >index f1bfa26e5577fbe00f5d30caf4fdf534dd913dcd..677d18714194418771337beeec354458cc4b7012 100644 >--- a/Source/WebCore/GNUmakefile.list.am >+++ b/Source/WebCore/GNUmakefile.list.am >@@ -1120,6 +1120,8 @@ webcore_sources += \ > Source/WebCore/dom/CharacterData.h \ > Source/WebCore/dom/CheckedRadioButtons.cpp \ > Source/WebCore/dom/CheckedRadioButtons.h \ >+ Source/WebCore/dom/ChildListMutationScope.cpp \ >+ Source/WebCore/dom/ChildListMutationScope.h \ > Source/WebCore/dom/ChildNodeList.cpp \ > Source/WebCore/dom/ChildNodeList.h \ > Source/WebCore/dom/ClassNodeList.cpp \ >diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi >index 77943d87771c12b776a22af2e4547baa50f3df68..7c449b9baaa8957dafd3de76b3ca9bfc104379ce 100644 >--- a/Source/WebCore/WebCore.gypi >+++ b/Source/WebCore/WebCore.gypi >@@ -5125,6 +5125,8 @@ > 'dom/CSSMappedAttributeDeclaration.cpp', > 'dom/CharacterData.cpp', > 'dom/CheckedRadioButtons.cpp', >+ 'dom/ChildListMutationScope.cpp', >+ 'dom/ChildListMutationScope.h', > 'dom/ChildNodeList.cpp', > 'dom/ChildNodeList.h', > 'dom/ClassNodeList.cpp', >diff --git a/Source/WebCore/WebCore.pro b/Source/WebCore/WebCore.pro >index 65c234ce6d444f52ca5acfc1e7465e86645f63e5..0b2cf1b5d0594fb65be5feca8dde706d60902c0a 100644 >--- a/Source/WebCore/WebCore.pro >+++ b/Source/WebCore/WebCore.pro >@@ -465,6 +465,7 @@ SOURCES += \ > dom/CDATASection.cpp \ > dom/CharacterData.cpp \ > dom/CheckedRadioButtons.cpp \ >+ dom/ChildListMutationScope.cpp \ > dom/ChildNodeList.cpp \ > dom/ClassNodeList.cpp \ > dom/ClientRect.cpp \ >diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj >index f3d05986e772be0e214dc325957182cd43434aae..0f297111d5c1dc947827900353d84955152a43bb 100755 >--- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj >+++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj >@@ -43865,6 +43865,62 @@ > RelativePath="..\dom\CheckedRadioButtons.h" > > > </File> >+ <File >+ RelativePath="..\dom\ChildListMutationScope.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\ChildListMutationScope.h" >+ > >+ </File> > <File > RelativePath="..\dom\ChildNodeList.cpp" > > >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index f54bc0a72240188c6111266990be0e0509ee7ee8..01a39126b6d37f53877ad852950db1e54e875970 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -5805,6 +5805,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 /* ChildListMutationScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D619A305144E00BE004BC302 /* ChildListMutationScope.cpp */; }; >+ D619A308144E00BE004BC302 /* ChildListMutationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D619A306144E00BE004BC302 /* ChildListMutationScope.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, ); }; }; >@@ -13135,6 +13137,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 /* ChildListMutationScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ChildListMutationScope.cpp; sourceTree = "<group>"; }; >+ D619A306144E00BE004BC302 /* ChildListMutationScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChildListMutationScope.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>"; }; >@@ -21009,6 +21013,8 @@ > 93EEC1E609C2877700C515D1 /* CharacterData.idl */, > 93F925420F7EF5B8007E37C9 /* CheckedRadioButtons.cpp */, > 93F925410F7EF5B8007E37C9 /* CheckedRadioButtons.h */, >+ D619A305144E00BE004BC302 /* ChildListMutationScope.cpp */, >+ D619A306144E00BE004BC302 /* ChildListMutationScope.h */, > A818721A0977D3C0005826D9 /* ChildNodeList.cpp */, > A81872150977D3C0005826D9 /* ChildNodeList.h */, > BC904B720D10998F00680D32 /* ClassNodeList.cpp */, >@@ -21824,6 +21830,7 @@ > F55B3DB21251F12D003EF269 /* CheckboxInputType.h in Headers */, > A00B721A11DE6428008AB9FF /* CheckedInt.h in Headers */, > 93F925430F7EF5B8007E37C9 /* CheckedRadioButtons.h in Headers */, >+ D619A308144E00BE004BC302 /* ChildListMutationScope.h in Headers */, > A81872200977D3C0005826D9 /* ChildNodeList.h in Headers */, > 14D823520AF92A790004F057 /* Chrome.h in Headers */, > 14D824080AF93AEB0004F057 /* ChromeClient.h in Headers */, >@@ -25172,6 +25179,7 @@ > 9326DC0C09DAD5D600AFC847 /* CharsetData.cpp in Sources */, > F55B3DB11251F12D003EF269 /* CheckboxInputType.cpp in Sources */, > 93F925440F7EF5B8007E37C9 /* CheckedRadioButtons.cpp in Sources */, >+ D619A307144E00BE004BC302 /* ChildListMutationScope.cpp in Sources */, > A81872250977D3C0005826D9 /* ChildNodeList.cpp in Sources */, > 14D8238B0AF92DF60004F057 /* Chrome.cpp in Sources */, > ABAF22080C03B1C700B0BCF0 /* ChromeMac.mm in Sources */, >diff --git a/Source/WebCore/dom/ChildListMutationScope.cpp b/Source/WebCore/dom/ChildListMutationScope.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..c79a9039bca30c38bb562a24570ace23d2baa0e5 >--- /dev/null >+++ b/Source/WebCore/dom/ChildListMutationScope.cpp >@@ -0,0 +1,296 @@ >+/* >+ * 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 "ChildListMutationScope.h" >+ >+#include "DocumentFragment.h" >+#include "Element.h" >+#include "MutationRecord.h" >+#include "Node.h" >+#include "StaticNodeList.h" >+#include <wtf/HashMap.h> >+ >+namespace WebCore { >+ >+namespace { >+ >+// ChildListMutationAccumulator 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 ChildListMutationAccumulator { >+ WTF_MAKE_NONCOPYABLE(ChildListMutationAccumulator); >+public: >+ ChildListMutationAccumulator(PassRefPtr<Node>, Vector<WebKitMutationObserver*>&); >+ ~ChildListMutationAccumulator(); >+ >+ void childAdded(PassRefPtr<Node>); >+ void willRemoveChild(PassRefPtr<Node>); >+ void enqueueMutationRecord(); >+ >+private: >+ void clear(); >+ bool isEmpty(); >+ bool isAddedNodeInOrder(Node*); >+ bool isRemovedNodeInOrder(Node*); >+ 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 MutationAccumulationRouter { >+ WTF_MAKE_NONCOPYABLE(MutationAccumulationRouter); >+public: >+ ~MutationAccumulationRouter(); >+ >+ static MutationAccumulationRouter* instance(); >+ >+ void incrementScopingLevel(Node*); >+ void decrementScopingLevel(Node*); >+ >+ void childAdded(Node*, PassRefPtr<Node>); >+ void willRemoveChild(Node*, PassRefPtr<Node>); >+ >+private: >+ MutationAccumulationRouter(); >+ static void initialize(); >+ >+ typedef HashMap<Node*, unsigned> ScopingLevelMap; >+ ScopingLevelMap m_scopingLevels; >+ HashMap<Node*, ChildListMutationAccumulator*> m_accumulations; >+ >+ static MutationAccumulationRouter* s_instance; >+}; >+ >+ChildListMutationAccumulator::ChildListMutationAccumulator(PassRefPtr<Node> target, Vector<WebKitMutationObserver*>& observers) >+ : m_target(target) >+{ >+ m_observers.swap(observers); >+ clear(); >+} >+ >+ChildListMutationAccumulator::~ChildListMutationAccumulator() >+{ >+ ASSERT(isEmpty()); >+} >+ >+bool ChildListMutationAccumulator::isAddedNodeInOrder(Node* child) >+{ >+ return isEmpty() || (m_lastAdded == child->previousSibling() && m_nextSibling == child->nextSibling()); >+} >+ >+void ChildListMutationAccumulator::childAdded(PassRefPtr<Node> prpChild) >+{ >+ RefPtr<Node> child = prpChild; >+ >+ if (!isAddedNodeInOrder(child.get())) >+ enqueueMutationRecord(); >+ >+ if (isEmpty()) { >+ m_previousSibling = child->previousSibling(); >+ m_nextSibling = child->nextSibling(); >+ } >+ >+ m_addedNodes.append(child); >+ m_lastAdded = child; >+} >+ >+bool ChildListMutationAccumulator::isRemovedNodeInOrder(Node* child) >+{ >+ return isEmpty() || m_nextSibling.get() == child; >+} >+ >+void ChildListMutationAccumulator::willRemoveChild(PassRefPtr<Node> prpChild) >+{ >+ RefPtr<Node> child = prpChild; >+ >+ ASSERT(m_addedNodes.isEmpty() && isRemovedNodeInOrder(child.get())); >+ >+ if (!m_addedNodes.isEmpty() || !isRemovedNodeInOrder(child.get())) >+ 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 ChildListMutationAccumulator::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 ChildListMutationAccumulator::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 ChildListMutationAccumulator::isEmpty() >+{ >+ return m_removedNodes.isEmpty() && m_addedNodes.isEmpty(); >+} >+ >+MutationAccumulationRouter* MutationAccumulationRouter::s_instance = 0; >+ >+MutationAccumulationRouter::MutationAccumulationRouter() >+{ >+} >+ >+MutationAccumulationRouter::~MutationAccumulationRouter() >+{ >+ ASSERT(m_scopingLevels.isEmpty()); >+ ASSERT(m_accumulations.isEmpty()); >+} >+ >+void MutationAccumulationRouter::initialize() >+{ >+ ASSERT(!s_instance); >+ s_instance = adoptPtr(new MutationAccumulationRouter).leakPtr(); >+} >+ >+ >+MutationAccumulationRouter* MutationAccumulationRouter::instance() >+{ >+ if (!s_instance) >+ initialize(); >+ >+ return s_instance; >+} >+ >+void MutationAccumulationRouter::childAdded(Node* target, PassRefPtr<Node> child) >+{ >+ HashMap<Node*, ChildListMutationAccumulator*>::iterator iter = m_accumulations.find(target); >+ ASSERT(iter != m_accumulations.end()); >+ >+ ChildListMutationAccumulator* record = iter->second; >+ if (record) >+ record->childAdded(child); >+} >+ >+void MutationAccumulationRouter::willRemoveChild(Node* target, PassRefPtr<Node> child) >+{ >+ HashMap<Node*, ChildListMutationAccumulator*>::iterator iter = m_accumulations.find(target); >+ ASSERT(iter != m_accumulations.end()); >+ >+ ChildListMutationAccumulator* record = iter->second; >+ if (record) >+ record->willRemoveChild(child); >+} >+ >+void MutationAccumulationRouter::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 ChildListMutationAccumulator(target, observers)); >+} >+ >+void MutationAccumulationRouter::decrementScopingLevel(Node* target) >+{ >+ ScopingLevelMap::iterator iter = m_scopingLevels.find(target); >+ ASSERT(iter != m_scopingLevels.end()); >+ >+ --(iter->second); >+ if (iter->second > 0) >+ return; >+ >+ OwnPtr<ChildListMutationAccumulator> record = adoptPtr(m_accumulations.take(target)); >+ m_scopingLevels.remove(iter); >+ >+ if (record) >+ record->enqueueMutationRecord(); >+} >+ >+} // namespace >+ >+ChildListMutationScope::ChildListMutationScope(PassRefPtr<Node> target) >+ : m_target(target) >+{ >+ MutationAccumulationRouter::instance()->incrementScopingLevel(m_target.get()); >+} >+ >+ChildListMutationScope::~ChildListMutationScope() >+{ >+ MutationAccumulationRouter::instance()->decrementScopingLevel(m_target.get()); >+} >+ >+void ChildListMutationScope::childAdded(PassRefPtr<Node> child) >+{ >+ MutationAccumulationRouter::instance()->childAdded(m_target.get(), child); >+} >+ >+void ChildListMutationScope::willRemoveChild(PassRefPtr<Node> child) >+{ >+ MutationAccumulationRouter::instance()->willRemoveChild(m_target.get(), child); >+} >+ >+} // namespace WebCore >+ >+#endif // ENABLE(MUTATION_OBSERVERS) >diff --git a/Source/WebCore/dom/ChildListMutationScope.h b/Source/WebCore/dom/ChildListMutationScope.h >new file mode 100644 >index 0000000000000000000000000000000000000000..1f2f8c491cd6a8da1c28500da94e7fd59c01ea37 >--- /dev/null >+++ b/Source/WebCore/dom/ChildListMutationScope.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 ChildListMutationScope_h >+#define ChildListMutationScope_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 // ChildListMutationScope_h >diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp >index 3ea49936935749b13192b76341d509b1538e4c0c..c6854c3f93a7e07421de0a042e331113cc651090 100644 >--- a/Source/WebCore/dom/ContainerNode.cpp >+++ b/Source/WebCore/dom/ContainerNode.cpp >@@ -25,6 +25,7 @@ > > #include "BeforeLoadEvent.h" > #include "MemoryCache.h" >+#include "ChildListMutationScope.h" > #include "ContainerNodeAlgorithms.h" > #include "DeleteButtonController.h" > #include "EventNames.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/Node.cpp b/Source/WebCore/dom/Node.cpp >index 23ff7fe21a83f231eb68dc7cc2f4a4169dd6cc45..915a5dc1dc09c64533d95aa259310f279aba28c3 100644 >--- a/Source/WebCore/dom/Node.cpp >+++ b/Source/WebCore/dom/Node.cpp >@@ -28,6 +28,7 @@ > #include "AXObjectCache.h" > #include "Attr.h" > #include "Attribute.h" >+#include "ChildListMutationScope.h" > #include "Chrome.h" > #include "ChromeClient.h" > #include "CSSParser.h" >@@ -2130,6 +2131,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); >@@ -2700,9 +2704,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..2c9b7a5a3596f16c37dc2f599075bd340a358261 100644 >--- a/Source/WebCore/html/HTMLElement.cpp >+++ b/Source/WebCore/html/HTMLElement.cpp >@@ -27,6 +27,7 @@ > #include "Attribute.h" > #include "CSSPropertyNames.h" > #include "CSSValueKeywords.h" >+#include "ChildListMutationScope.h" > #include "DocumentFragment.h" > #include "Event.h" > #include "EventListener.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 c9b51827405dc1488a1c36760dbca96c5dabe880..24f8332ff920c17909a72ae168f82843d8dbad3c 100755 >--- 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-20 Tony Chang <tony@chromium.org> > > fix repaint bugs in new flexbox >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 3cb27f39afb93344adb13f7b8c5f8a4eae18d652..a07d4be5e1f4633b1850bd40c5f926591d44f926 100644 >--- a/LayoutTests/fast/mutation/observe-attributes.html >+++ b/LayoutTests/fast/mutation/observe-attributes.html >@@ -10,8 +10,7 @@ > <script> > > window.jsTestIsAsync = true; >-var mutations; >-var mutations2; >+var mutations, mutations2; > var calls; > > function testBasic() { >@@ -107,7 +106,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