| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * Copyright (C) 2021 Apple 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" |
| #include "MutationRecord.h" |
| |
| #include "CharacterData.h" |
| #include "JSNode.h" |
| #include "StaticNodeList.h" |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/StdLibExtras.h> |
| |
| namespace WebCore { |
| |
| namespace { |
| |
| static void visitNodeList(JSC::AbstractSlotVisitor& visitor, NodeList& nodeList) |
| { |
| ASSERT(!nodeList.isLiveNodeList()); |
| unsigned length = nodeList.length(); |
| for (unsigned i = 0; i < length; ++i) |
| addWebCoreOpaqueRoot(visitor, nodeList.item(i)); |
| } |
| |
| class ChildListRecord final : public MutationRecord { |
| public: |
| ChildListRecord(ContainerNode& target, Ref<NodeList>&& added, Ref<NodeList>&& removed, RefPtr<Node>&& previousSibling, RefPtr<Node>&& nextSibling) |
| : m_target(target) |
| , m_addedNodes(WTFMove(added)) |
| , m_removedNodes(WTFMove(removed)) |
| , m_previousSibling(WTFMove(previousSibling)) |
| , m_nextSibling(WTFMove(nextSibling)) |
| { |
| } |
| |
| private: |
| const AtomString& type() override; |
| Node* target() override { return m_target.ptr(); } |
| NodeList* addedNodes() override { return m_addedNodes.get(); } |
| NodeList* removedNodes() override { return m_removedNodes.get(); } |
| Node* previousSibling() override { return m_previousSibling.get(); } |
| Node* nextSibling() override { return m_nextSibling.get(); } |
| |
| void visitNodesConcurrently(JSC::AbstractSlotVisitor& visitor) const final |
| { |
| addWebCoreOpaqueRoot(visitor, m_target.get()); |
| if (m_addedNodes) |
| visitNodeList(visitor, *m_addedNodes); |
| if (m_removedNodes) |
| visitNodeList(visitor, *m_removedNodes); |
| } |
| |
| Ref<ContainerNode> m_target; |
| RefPtr<NodeList> m_addedNodes; |
| RefPtr<NodeList> m_removedNodes; |
| RefPtr<Node> m_previousSibling; |
| RefPtr<Node> m_nextSibling; |
| }; |
| |
| class RecordWithEmptyNodeLists : public MutationRecord { |
| public: |
| RecordWithEmptyNodeLists(Node& target, const String& oldValue) |
| : m_target(target) |
| , m_oldValue(oldValue) |
| { |
| } |
| |
| private: |
| Node* target() override { return m_target.ptr(); } |
| String oldValue() override { return m_oldValue; } |
| NodeList* addedNodes() override { return lazilyInitializeEmptyNodeList(m_addedNodes); } |
| NodeList* removedNodes() override { return lazilyInitializeEmptyNodeList(m_removedNodes); } |
| |
| static NodeList* lazilyInitializeEmptyNodeList(RefPtr<NodeList>& nodeList) |
| { |
| if (!nodeList) |
| nodeList = StaticNodeList::create(); |
| return nodeList.get(); |
| } |
| |
| void visitNodesConcurrently(JSC::AbstractSlotVisitor& visitor) const final |
| { |
| addWebCoreOpaqueRoot(visitor, m_target.get()); |
| } |
| |
| Ref<Node> m_target; |
| String m_oldValue; |
| RefPtr<NodeList> m_addedNodes; |
| RefPtr<NodeList> m_removedNodes; |
| }; |
| |
| class AttributesRecord final : public RecordWithEmptyNodeLists { |
| public: |
| AttributesRecord(Element& target, const QualifiedName& name, const AtomString& oldValue) |
| : RecordWithEmptyNodeLists(target, oldValue) |
| , m_attributeName(name.localName()) |
| , m_attributeNamespace(name.namespaceURI()) |
| { |
| } |
| |
| private: |
| const AtomString& type() override; |
| const AtomString& attributeName() override { return m_attributeName; } |
| const AtomString& attributeNamespace() override { return m_attributeNamespace; } |
| |
| AtomString m_attributeName; |
| AtomString m_attributeNamespace; |
| }; |
| |
| class CharacterDataRecord : public RecordWithEmptyNodeLists { |
| public: |
| CharacterDataRecord(CharacterData& target, const String& oldValue) |
| : RecordWithEmptyNodeLists(target, oldValue) |
| { |
| } |
| |
| private: |
| const AtomString& type() override; |
| }; |
| |
| class MutationRecordWithNullOldValue final : public MutationRecord { |
| public: |
| MutationRecordWithNullOldValue(MutationRecord& record) |
| : m_record(record) |
| { |
| } |
| |
| private: |
| const AtomString& type() override { return m_record->type(); } |
| Node* target() override { return m_record->target(); } |
| NodeList* addedNodes() override { return m_record->addedNodes(); } |
| NodeList* removedNodes() override { return m_record->removedNodes(); } |
| Node* previousSibling() override { return m_record->previousSibling(); } |
| Node* nextSibling() override { return m_record->nextSibling(); } |
| const AtomString& attributeName() override { return m_record->attributeName(); } |
| const AtomString& attributeNamespace() override { return m_record->attributeNamespace(); } |
| |
| String oldValue() override { return String(); } |
| |
| void visitNodesConcurrently(JSC::AbstractSlotVisitor& visitor) const final |
| { |
| m_record->visitNodesConcurrently(visitor); |
| } |
| |
| Ref<MutationRecord> m_record; |
| }; |
| |
| const AtomString& ChildListRecord::type() |
| { |
| static MainThreadNeverDestroyed<const AtomString> childList("childList"_s); |
| return childList; |
| } |
| |
| const AtomString& AttributesRecord::type() |
| { |
| static MainThreadNeverDestroyed<const AtomString> attributes("attributes"_s); |
| return attributes; |
| } |
| |
| const AtomString& CharacterDataRecord::type() |
| { |
| static MainThreadNeverDestroyed<const AtomString> characterData("characterData"_s); |
| return characterData; |
| } |
| |
| } // namespace |
| |
| Ref<MutationRecord> MutationRecord::createChildList(ContainerNode& target, Ref<NodeList>&& added, Ref<NodeList>&& removed, RefPtr<Node>&& previousSibling, RefPtr<Node>&& nextSibling) |
| { |
| return adoptRef(static_cast<MutationRecord&>(*new ChildListRecord(target, WTFMove(added), WTFMove(removed), WTFMove(previousSibling), WTFMove(nextSibling)))); |
| } |
| |
| Ref<MutationRecord> MutationRecord::createAttributes(Element& target, const QualifiedName& name, const AtomString& oldValue) |
| { |
| return adoptRef(static_cast<MutationRecord&>(*new AttributesRecord(target, name, oldValue))); |
| } |
| |
| Ref<MutationRecord> MutationRecord::createCharacterData(CharacterData& target, const String& oldValue) |
| { |
| return adoptRef(static_cast<MutationRecord&>(*new CharacterDataRecord(target, oldValue))); |
| } |
| |
| Ref<MutationRecord> MutationRecord::createWithNullOldValue(MutationRecord& record) |
| { |
| return adoptRef(static_cast<MutationRecord&>(*new MutationRecordWithNullOldValue(record))); |
| } |
| |
| MutationRecord::~MutationRecord() = default; |
| |
| } // namespace WebCore |