1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/dom/ChildListMutationScope.h" 33 34 #include "core/dom/MutationObserverInterestGroup.h" 35 #include "core/dom/MutationRecord.h" 36 #include "core/dom/StaticNodeList.h" 37 #include "wtf/HashMap.h" 38 #include "wtf/StdLibExtras.h" 39 40 namespace WebCore { 41 42 // The accumulator map is used to make sure that there is only one mutation 43 // accumulator for a given node even if there are multiple ChildListMutationScopes 44 // on the stack. The map is always empty when there are no ChildListMutationScopes 45 // on the stack. 46 typedef WillBeHeapHashMap<RawPtrWillBeMember<Node>, RawPtrWillBeMember<ChildListMutationAccumulator> > AccumulatorMap; 47 48 static AccumulatorMap& accumulatorMap() 49 { 50 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<AccumulatorMap>, map, (adoptPtrWillBeNoop(new AccumulatorMap()))); 51 return *map; 52 } 53 54 ChildListMutationAccumulator::ChildListMutationAccumulator(PassRefPtrWillBeRawPtr<Node> target, PassOwnPtrWillBeRawPtr<MutationObserverInterestGroup> observers) 55 : m_target(target) 56 , m_lastAdded(nullptr) 57 , m_observers(observers) 58 , m_mutationScopes(0) 59 { 60 } 61 62 void ChildListMutationAccumulator::leaveMutationScope() 63 { 64 ASSERT(m_mutationScopes > 0); 65 if (!--m_mutationScopes) { 66 if (!isEmpty()) 67 enqueueMutationRecord(); 68 accumulatorMap().remove(m_target.get()); 69 } 70 } 71 72 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(ChildListMutationAccumulator); 73 74 PassRefPtrWillBeRawPtr<ChildListMutationAccumulator> ChildListMutationAccumulator::getOrCreate(Node& target) 75 { 76 AccumulatorMap::AddResult result = accumulatorMap().add(&target, nullptr); 77 RefPtrWillBeRawPtr<ChildListMutationAccumulator> accumulator; 78 if (!result.isNewEntry) 79 accumulator = result.storedValue->value; 80 else { 81 accumulator = adoptRefWillBeNoop(new ChildListMutationAccumulator(PassRefPtrWillBeRawPtr<Node>(target), MutationObserverInterestGroup::createForChildListMutation(target))); 82 result.storedValue->value = accumulator.get(); 83 } 84 return accumulator.release(); 85 } 86 87 inline bool ChildListMutationAccumulator::isAddedNodeInOrder(Node* child) 88 { 89 return isEmpty() || (m_lastAdded == child->previousSibling() && m_nextSibling == child->nextSibling()); 90 } 91 92 void ChildListMutationAccumulator::childAdded(PassRefPtrWillBeRawPtr<Node> prpChild) 93 { 94 ASSERT(hasObservers()); 95 96 RefPtrWillBeRawPtr<Node> child = prpChild; 97 98 if (!isAddedNodeInOrder(child.get())) 99 enqueueMutationRecord(); 100 101 if (isEmpty()) { 102 m_previousSibling = child->previousSibling(); 103 m_nextSibling = child->nextSibling(); 104 } 105 106 m_lastAdded = child.get(); 107 m_addedNodes.append(child.release()); 108 } 109 110 inline bool ChildListMutationAccumulator::isRemovedNodeInOrder(Node* child) 111 { 112 return isEmpty() || m_nextSibling == child; 113 } 114 115 void ChildListMutationAccumulator::willRemoveChild(PassRefPtrWillBeRawPtr<Node> prpChild) 116 { 117 ASSERT(hasObservers()); 118 119 RefPtrWillBeRawPtr<Node> child = prpChild; 120 121 if (!m_addedNodes.isEmpty() || !isRemovedNodeInOrder(child.get())) 122 enqueueMutationRecord(); 123 124 if (isEmpty()) { 125 m_previousSibling = child->previousSibling(); 126 m_nextSibling = child->nextSibling(); 127 m_lastAdded = child->previousSibling(); 128 } else 129 m_nextSibling = child->nextSibling(); 130 131 m_removedNodes.append(child.release()); 132 } 133 134 void ChildListMutationAccumulator::enqueueMutationRecord() 135 { 136 ASSERT(hasObservers()); 137 ASSERT(!isEmpty()); 138 139 RefPtrWillBeRawPtr<StaticNodeList> addedNodes = StaticNodeList::adopt(m_addedNodes); 140 RefPtrWillBeRawPtr<StaticNodeList> removedNodes = StaticNodeList::adopt(m_removedNodes); 141 RefPtrWillBeRawPtr<MutationRecord> record = MutationRecord::createChildList(m_target, addedNodes.release(), removedNodes.release(), m_previousSibling.release(), m_nextSibling.release()); 142 m_observers->enqueueMutationRecord(record.release()); 143 m_lastAdded = nullptr; 144 ASSERT(isEmpty()); 145 } 146 147 bool ChildListMutationAccumulator::isEmpty() 148 { 149 bool result = m_removedNodes.isEmpty() && m_addedNodes.isEmpty(); 150 #ifndef NDEBUG 151 if (result) { 152 ASSERT(!m_previousSibling); 153 ASSERT(!m_nextSibling); 154 ASSERT(!m_lastAdded); 155 } 156 #endif 157 return result; 158 } 159 160 void ChildListMutationAccumulator::trace(Visitor* visitor) 161 { 162 visitor->trace(m_target); 163 visitor->trace(m_removedNodes); 164 visitor->trace(m_addedNodes); 165 visitor->trace(m_previousSibling); 166 visitor->trace(m_nextSibling); 167 visitor->trace(m_lastAdded); 168 visitor->trace(m_observers); 169 } 170 171 } // namespace WebCore 172