| /* |
| * 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 "core/dom/ChildListMutationScope.h" |
| |
| #include "core/dom/MutationObserverInterestGroup.h" |
| #include "core/dom/MutationRecord.h" |
| #include "core/dom/StaticNodeList.h" |
| #include "wtf/HashMap.h" |
| #include "wtf/StdLibExtras.h" |
| |
| namespace blink { |
| |
| // The accumulator map is used to make sure that there is only one mutation |
| // accumulator for a given node even if there are multiple ChildListMutationScopes |
| // on the stack. The map is always empty when there are no ChildListMutationScopes |
| // on the stack. |
| typedef HeapHashMap<Member<Node>, Member<ChildListMutationAccumulator>> AccumulatorMap; |
| |
| static AccumulatorMap& accumulatorMap() |
| { |
| DEFINE_STATIC_LOCAL(AccumulatorMap, map, (new AccumulatorMap)); |
| return map; |
| } |
| |
| ChildListMutationAccumulator::ChildListMutationAccumulator(Node* target, MutationObserverInterestGroup* observers) |
| : m_target(target) |
| , m_lastAdded(nullptr) |
| , m_observers(observers) |
| , m_mutationScopes(0) |
| { |
| } |
| |
| void ChildListMutationAccumulator::leaveMutationScope() |
| { |
| DCHECK_GT(m_mutationScopes, 0u); |
| if (!--m_mutationScopes) { |
| if (!isEmpty()) |
| enqueueMutationRecord(); |
| accumulatorMap().remove(m_target.get()); |
| } |
| } |
| |
| ChildListMutationAccumulator* ChildListMutationAccumulator::getOrCreate(Node& target) |
| { |
| AccumulatorMap::AddResult result = accumulatorMap().add(&target, nullptr); |
| ChildListMutationAccumulator* accumulator; |
| if (!result.isNewEntry) { |
| accumulator = result.storedValue->value; |
| } else { |
| accumulator = new ChildListMutationAccumulator(&target, MutationObserverInterestGroup::createForChildListMutation(target)); |
| result.storedValue->value = accumulator; |
| } |
| return accumulator; |
| } |
| |
| inline bool ChildListMutationAccumulator::isAddedNodeInOrder(Node* child) |
| { |
| return isEmpty() || (m_lastAdded == child->previousSibling() && m_nextSibling == child->nextSibling()); |
| } |
| |
| void ChildListMutationAccumulator::childAdded(Node* child) |
| { |
| DCHECK(hasObservers()); |
| |
| if (!isAddedNodeInOrder(child)) |
| enqueueMutationRecord(); |
| |
| if (isEmpty()) { |
| m_previousSibling = child->previousSibling(); |
| m_nextSibling = child->nextSibling(); |
| } |
| |
| m_lastAdded = child; |
| m_addedNodes.append(child); |
| } |
| |
| inline bool ChildListMutationAccumulator::isRemovedNodeInOrder(Node* child) |
| { |
| return isEmpty() || m_nextSibling == child; |
| } |
| |
| void ChildListMutationAccumulator::willRemoveChild(Node* child) |
| { |
| DCHECK(hasObservers()); |
| |
| if (!m_addedNodes.isEmpty() || !isRemovedNodeInOrder(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 ChildListMutationAccumulator::enqueueMutationRecord() |
| { |
| DCHECK(hasObservers()); |
| DCHECK(!isEmpty()); |
| |
| StaticNodeList* addedNodes = StaticNodeList::adopt(m_addedNodes); |
| StaticNodeList* removedNodes = StaticNodeList::adopt(m_removedNodes); |
| MutationRecord* record = MutationRecord::createChildList(m_target, addedNodes, removedNodes, m_previousSibling.release(), m_nextSibling.release()); |
| m_observers->enqueueMutationRecord(record); |
| m_lastAdded = nullptr; |
| DCHECK(isEmpty()); |
| } |
| |
| bool ChildListMutationAccumulator::isEmpty() |
| { |
| bool result = m_removedNodes.isEmpty() && m_addedNodes.isEmpty(); |
| #if DCHECK_IS_ON() |
| if (result) { |
| DCHECK(!m_previousSibling); |
| DCHECK(!m_nextSibling); |
| DCHECK(!m_lastAdded); |
| } |
| #endif |
| return result; |
| } |
| |
| DEFINE_TRACE(ChildListMutationAccumulator) |
| { |
| visitor->trace(m_target); |
| visitor->trace(m_removedNodes); |
| visitor->trace(m_addedNodes); |
| visitor->trace(m_previousSibling); |
| visitor->trace(m_nextSibling); |
| visitor->trace(m_lastAdded); |
| visitor->trace(m_observers); |
| } |
| |
| } // namespace blink |