Changeset 236781 in webkit
- Timestamp:
- Oct 2, 2018, 5:29:46 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r236779 r236781 1 2018-10-01 Ryosuke Niwa <rniwa@webkit.org> 2 3 GC can collect JS wrappers of nodes in the mutation records waiting to be delivered 4 https://bugs.webkit.org/show_bug.cgi?id=190115 5 6 Reviewed by Geoffrey Garen. 7 8 Added a regression test. 9 10 * fast/dom/MutationObserver/mutation-observer-retains-js-wrappers-of-targets-alive-expected.txt: Added. 11 * fast/dom/MutationObserver/mutation-observer-retains-js-wrappers-of-targets-alive.html: Added. 12 1 13 2018-10-02 Chris Dumez <cdumez@apple.com> 2 14 -
trunk/Source/WebCore/ChangeLog
r236779 r236781 1 2018-10-01 Ryosuke Niwa <rniwa@webkit.org> 2 3 GC can collect JS wrappers of nodes in the mutation records waiting to be delivered 4 https://bugs.webkit.org/show_bug.cgi?id=190115 5 6 Reviewed by Geoffrey Garen. 7 8 Fixed the bug by retaining JS wrappers of elements in mutation records using GCReachableRef. 9 10 This patch deploys GCReachableRef in two places: MutationObserver where each mutation record's 11 target is kept alive and MutationObserverRegistration where each node which had been removed 12 from an observed tree is kept alive for a subtree observation. 13 14 Test: fast/dom/MutationObserver/mutation-observer-retains-js-wrappers-of-targets-alive.html 15 16 * dom/GCReachableRef.h: 17 (WebCore::GCReachableRef): Made it work with hash table. 18 (WebCore::GCReachableRef::operator T& const): 19 (WebCore::GCReachableRef::GCReachableRef): 20 (WebCore::GCReachableRef::isHashTableDeletedValue const): 21 (WebCore::GCReachableRef::isHashTableEmptyValue const): 22 (WebCore::GCReachableRef::ptrAllowingHashTableEmptyValue const): 23 (WebCore::GCReachableRef::ptrAllowingHashTableEmptyValue): 24 (WebCore::GCReachableRef::assignToHashTableEmptyValue): 25 (WTF::HashTraits<WebCore::GCReachableRef<P>>::emptyValue): 26 (WTF::HashTraits<WebCore::GCReachableRef<P>>::constructEmptyValue): 27 (WTF::HashTraits<WebCore::GCReachableRef<P>>::isEmptyValue): 28 (WTF::HashTraits<WebCore::GCReachableRef<P>>::assignToEmpty): 29 (WTF::HashTraits<WebCore::GCReachableRef<P>>::peek): 30 (WTF::HashTraits<WebCore::GCReachableRef<P>>::take): 31 * dom/MutationObserver.cpp: 32 (WebCore::MutationObserver::takeRecords): Don't clear m_pendingTargets because that would allow wrappers 33 to be collected before elements in mutation records are accessed. We delay until the end of the current 34 microtask at which point deliver() function is called. 35 (WebCore::MutationObserver::disconnect): 36 (WebCore::MutationObserver::enqueueMutationRecord): Add the target to the list of elements to keep alive. 37 This is needed for a newly inserted node, a node with attribute change, etc... 38 (WebCore::MutationObserver::deliver): Keep the set of transient registration targets alive until mutation 39 records are delivered to each observer. These are nodes which had been removed from a tree and whose 40 subtree had still been obsreved up until this point. 41 * dom/MutationObserver.h: 42 * dom/MutationObserverRegistration.cpp: 43 (WebCore::MutationObserverRegistration::observedSubtreeNodeWillDetach): 44 (WebCore::MutationObserverRegistration::takeTransientRegistrations): Return the hash set of elemenets 45 that need to be kept alive so that MutationObserver::deliver can keep them alive until the deliver 46 function had been called. 47 (WebCore::MutationObserverRegistration::addRegistrationNodesToSet const): 48 * dom/MutationObserverRegistration.h: 49 1 50 2018-10-02 Chris Dumez <cdumez@apple.com> 2 51 -
trunk/Source/WebCore/dom/GCReachableRef.h
r236693 r236781 28 28 #include <wtf/DumbPtrTraits.h> 29 29 #include <wtf/HashCountedSet.h> 30 #include <wtf/Ref .h>30 #include <wtf/RefPtr.h> 31 31 32 32 namespace WebCore { … … 70 70 T* ptr() const RETURNS_NONNULL { return &get(); } 71 71 T& get() const { ASSERT(m_ptr); return *m_ptr; } 72 operator T&() const { ASSERT(m_ptr); return *m_ptr; }72 operator T&() const { return get(); } 73 73 bool operator!() const { return !get(); } 74 74 75 // Hash table deleted values, which are only constructed and never copied or destroyed. 76 GCReachableRef(WTF::HashTableDeletedValueType) 77 : m_ptr(RefPtr<T>::hashTableDeletedValue()) 78 { } 79 bool isHashTableDeletedValue() const { return m_ptr.isHashTableDeletedValue(); } 80 81 GCReachableRef(WTF::HashTableEmptyValueType) 82 : m_ptr(nullptr) 83 { } 84 bool isHashTableEmptyValue() const { return !m_ptr; } 85 86 const T* ptrAllowingHashTableEmptyValue() const { ASSERT(m_ptr || isHashTableEmptyValue()); return m_ptr.get(); } 87 T* ptrAllowingHashTableEmptyValue() { ASSERT(m_ptr || isHashTableEmptyValue()); return m_ptr.get(); } 88 89 void assignToHashTableEmptyValue(GCReachableRef&& reference) 90 { 91 ASSERT(!m_ptr); 92 m_ptr = WTFMove(reference.m_ptr); 93 ASSERT(m_ptr); 94 } 95 75 96 private: 76 77 97 RefPtr<T> m_ptr; 78 98 }; 79 99 80 } 100 } // namespace WebCore 101 102 namespace WTF { 103 104 template<typename P> struct HashTraits<WebCore::GCReachableRef<P>> : SimpleClassHashTraits<WebCore::GCReachableRef<P>> { 105 static const bool emptyValueIsZero = true; 106 static WebCore::GCReachableRef<P> emptyValue() { return HashTableEmptyValue; } 107 108 template <typename> 109 static void constructEmptyValue(WebCore::GCReachableRef<P>& slot) 110 { 111 new (NotNull, std::addressof(slot)) WebCore::GCReachableRef<P>(HashTableEmptyValue); 112 } 113 114 static const bool hasIsEmptyValueFunction = true; 115 static bool isEmptyValue(const WebCore::GCReachableRef<P>& value) { return value.isHashTableEmptyValue(); } 116 117 static void assignToEmpty(WebCore::GCReachableRef<P>& emptyValue, WebCore::GCReachableRef<P>&& newValue) 118 { 119 ASSERT(isEmptyValue(emptyValue)); 120 emptyValue.assignToHashTableEmptyValue(WTFMove(newValue)); 121 } 122 123 typedef P* PeekType; 124 static PeekType peek(const Ref<P>& value) { return const_cast<PeekType>(value.ptrAllowingHashTableEmptyValue()); } 125 static PeekType peek(P* value) { return value; } 126 127 typedef std::optional<Ref<P>> TakeType; 128 static TakeType take(Ref<P>&& value) { return isEmptyValue(value) ? std::nullopt : std::optional<Ref<P>>(WTFMove(value)); } 129 }; 130 131 template <typename T, typename U> 132 struct GetPtrHelper<WebCore::GCReachableRef<T, U>> { 133 typedef T* PtrType; 134 static T* getPtr(const WebCore::GCReachableRef<T, U>& reference) { return const_cast<T*>(reference.ptr()); } 135 }; 136 137 template <typename T, typename U> 138 struct IsSmartPtr<WebCore::GCReachableRef<T, U>> { 139 static const bool value = true; 140 }; 141 142 template<typename P> struct PtrHash<WebCore::GCReachableRef<P>> : PtrHashBase<WebCore::GCReachableRef<P>, IsSmartPtr<WebCore::GCReachableRef<P>>::value> { 143 static const bool safeToCompareToEmptyOrDeleted = false; 144 }; 145 146 template<typename P> struct DefaultHash<WebCore::GCReachableRef<P>> { 147 typedef PtrHash<WebCore::GCReachableRef<P>> Hash; 148 }; 149 150 } // namespace WTF 151 -
trunk/Source/WebCore/dom/MutationObserver.cpp
r236440 r236781 115 115 Vector<Ref<MutationRecord>> records; 116 116 records.swap(m_records); 117 // Don't clear m_pendingTargets here because we can collect JS wrappers 118 // between the time takeRecords is called and nodes in records are accesssed. 117 119 return records; 118 120 } … … 120 122 void MutationObserver::disconnect() 121 123 { 124 m_pendingTargets.clear(); 122 125 m_records.clear(); 123 126 HashSet<MutationObserverRegistration*> registrations(m_registrations); … … 182 185 { 183 186 ASSERT(isMainThread()); 187 ASSERT(mutation->target()); 188 m_pendingTargets.add(*mutation->target()); 184 189 m_records.append(WTFMove(mutation)); 185 190 activeMutationObservers().add(this); … … 222 227 ASSERT(canDeliver()); 223 228 224 // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary229 // Calling takeTransientRegistrations() can modify m_registrations, so it's necessary 225 230 // to make a copy of the transient registrations before operating on them. 226 231 Vector<MutationObserverRegistration*, 1> transientRegistrations; 232 Vector<std::unique_ptr<HashSet<GCReachableRef<Node>>>, 1> nodesToKeepAlive; 233 HashSet<GCReachableRef<Node>> pendingTargets; 234 pendingTargets.swap(m_pendingTargets); 227 235 for (auto* registration : m_registrations) { 228 236 if (registration->hasTransientRegistrations()) … … 230 238 } 231 239 for (auto& registration : transientRegistrations) 232 registration->clearTransientRegistrations();240 nodesToKeepAlive.append(registration->takeTransientRegistrations()); 233 241 234 242 if (m_records.isEmpty()) -
trunk/Source/WebCore/dom/MutationObserver.h
r230695 r236781 33 33 34 34 #include "ExceptionOr.h" 35 #include "GCReachableRef.h" 35 36 #include <wtf/Forward.h> 36 37 #include <wtf/HashSet.h> … … 110 111 Ref<MutationCallback> m_callback; 111 112 Vector<Ref<MutationRecord>> m_records; 113 HashSet<GCReachableRef<Node>> m_pendingTargets; 112 114 HashSet<MutationObserverRegistration*> m_registrations; 113 115 unsigned m_priority; -
trunk/Source/WebCore/dom/MutationObserverRegistration.cpp
r210216 r236781 49 49 MutationObserverRegistration::~MutationObserverRegistration() 50 50 { 51 clearTransientRegistrations();51 takeTransientRegistrations(); 52 52 m_observer->observationEnded(*this); 53 53 } … … 55 55 void MutationObserverRegistration::resetObservation(MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) 56 56 { 57 clearTransientRegistrations();57 takeTransientRegistrations(); 58 58 m_options = options; 59 59 m_attributeFilter = attributeFilter; … … 69 69 70 70 if (!m_transientRegistrationNodes) { 71 m_transientRegistrationNodes = std::make_unique<HashSet< RefPtr<Node>>>();71 m_transientRegistrationNodes = std::make_unique<HashSet<GCReachableRef<Node>>>(); 72 72 73 73 ASSERT(!m_nodeKeptAlive); 74 m_nodeKeptAlive = &m_node; // Balanced in clearTransientRegistrations.74 m_nodeKeptAlive = &m_node; // Balanced in takeTransientRegistrations. 75 75 } 76 m_transientRegistrationNodes->add( &node);76 m_transientRegistrationNodes->add(node); 77 77 } 78 78 79 void MutationObserverRegistration::clearTransientRegistrations()79 std::unique_ptr<HashSet<GCReachableRef<Node>>> MutationObserverRegistration::takeTransientRegistrations() 80 80 { 81 81 if (!m_transientRegistrationNodes) { 82 82 ASSERT(!m_nodeKeptAlive); 83 return ;83 return nullptr; 84 84 } 85 85 … … 87 87 node->unregisterTransientMutationObserver(*this); 88 88 89 m_transientRegistrationNodes = nullptr;89 auto returnValue = WTFMove(m_transientRegistrationNodes); 90 90 91 91 ASSERT(m_nodeKeptAlive); 92 92 m_nodeKeptAlive = nullptr; // Balanced in observeSubtreeNodeWillDetach. 93 94 return returnValue; 93 95 } 94 96 … … 117 119 return; 118 120 for (auto& node : *m_transientRegistrationNodes) 119 nodes.add(node. get());121 nodes.add(node.ptr()); 120 122 } 121 123 -
trunk/Source/WebCore/dom/MutationObserverRegistration.h
r210216 r236781 31 31 #pragma once 32 32 33 #include "GCReachableRef.h" 33 34 #include "MutationObserver.h" 34 35 #include <wtf/HashSet.h> … … 48 49 void resetObservation(MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); 49 50 void observedSubtreeNodeWillDetach(Node&); 50 void clearTransientRegistrations();51 std::unique_ptr<HashSet<GCReachableRef<Node>>> takeTransientRegistrations(); 51 52 bool hasTransientRegistrations() const { return m_transientRegistrationNodes && !m_transientRegistrationNodes->isEmpty(); } 52 53 … … 65 66 Node& m_node; 66 67 RefPtr<Node> m_nodeKeptAlive; 67 std::unique_ptr<HashSet< RefPtr<Node>>> m_transientRegistrationNodes;68 std::unique_ptr<HashSet<GCReachableRef<Node>>> m_transientRegistrationNodes; 68 69 MutationObserverOptions m_options; 69 70 HashSet<AtomicString> m_attributeFilter;
Note:
See TracChangeset
for help on using the changeset viewer.