blob: 8b29e698f9015c0c6a17b5e310c1de7a0edbde80 [file] [log] [blame]
// Copyright 2019 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CORE_FXCRT_RETAINED_TREE_NODE_H_
#define CORE_FXCRT_RETAINED_TREE_NODE_H_
#include "core/fxcrt/retain_ptr.h"
#include "third_party/base/logging.h"
namespace fxcrt {
// For DOM/XML-ish trees, where references outside the tree are RetainPtr<T>,
// and the parent node also "retains" its children but doesn't always have
// a direct pointer to them.
template <typename T>
class RetainedTreeNode {
public:
template <typename U, typename... Args>
friend RetainPtr<U> pdfium::MakeRetain(Args&&... args);
T* GetParent() const { return m_pParent; }
T* GetFirstChild() const { return m_pFirstChild; }
T* GetLastChild() const { return m_pLastChild; }
T* GetNextSibling() const { return m_pNextSibling; }
T* GetPrevSibling() const { return m_pPrevSibling; }
void AppendFirstChild(const RetainPtr<T>& child) {
BecomeParent(child);
if (m_pFirstChild) {
m_pFirstChild->m_pPrevSibling = child.Get();
child->m_pNextSibling = m_pFirstChild;
m_pFirstChild = child.Get();
} else {
m_pFirstChild = child.Get();
m_pLastChild = child.Get();
}
}
void AppendLastChild(const RetainPtr<T>& child) {
BecomeParent(child);
if (m_pLastChild) {
m_pLastChild->m_pNextSibling = child.Get();
child->m_pPrevSibling = m_pLastChild;
m_pLastChild = child.Get();
} else {
m_pFirstChild = child.Get();
m_pLastChild = child.Get();
}
}
void InsertBefore(const RetainPtr<T>& child, T* other) {
if (!other) {
AppendLastChild(child);
return;
}
CHECK(other->m_pParent == this);
BecomeParent(child);
child->m_pNextSibling = other;
child->m_pPrevSibling = other->m_pPrevSibling;
if (other->m_pPrevSibling)
other->m_pPrevSibling->m_pNextSibling = child.Get();
else
m_pFirstChild = child.Get();
other->m_pPrevSibling = child.Get();
}
void InsertAfter(const RetainPtr<T>& child, T* other) {
if (!other) {
AppendFirstChild(child);
return;
}
CHECK(other->m_pParent == this);
BecomeParent(child);
child->m_pNextSibling = other->m_pNextSibling;
child->m_pPrevSibling = other;
if (other->m_pNextSibling)
other->m_pNextSibling->m_pPrevSibling = child.Get();
else
m_pLastChild = child.Get();
other->m_pNextSibling = child.Get();
}
void RemoveChild(const RetainPtr<T>& child) {
CHECK(child->m_pParent == this);
if (child->m_pNextSibling)
child->m_pNextSibling->m_pPrevSibling = child->m_pPrevSibling;
else
m_pLastChild = child->m_pPrevSibling;
if (child->m_pPrevSibling)
child->m_pPrevSibling->m_pNextSibling = child->m_pNextSibling;
else
m_pFirstChild = child->m_pNextSibling;
child->m_pParent = nullptr;
child->m_pPrevSibling = nullptr;
child->m_pNextSibling = nullptr;
}
protected:
RetainedTreeNode() = default;
virtual ~RetainedTreeNode() {
while (m_pFirstChild)
RemoveChild(pdfium::WrapRetain(m_pFirstChild));
}
private:
template <typename U>
friend struct ReleaseDeleter;
template <typename U>
friend class RetainPtr;
RetainedTreeNode(const RetainedTreeNode& that) = delete;
RetainedTreeNode& operator=(const RetainedTreeNode& that) = delete;
void Retain() { ++m_nRefCount; }
void Release() {
ASSERT(m_nRefCount > 0);
if (--m_nRefCount == 0 && !m_pParent)
delete this;
}
// Child left in state where sibling members need subsequent adjustment.
void BecomeParent(const RetainPtr<T>& child) {
if (child->m_pParent)
child->m_pParent->RemoveChild(child);
child->m_pParent = static_cast<T*>(this);
ASSERT(!child->m_pNextSibling);
ASSERT(!child->m_pPrevSibling);
}
intptr_t m_nRefCount = 0;
T* m_pParent = nullptr; // Raw, intra-tree pointer.
T* m_pFirstChild = nullptr; // Raw, intra-tree pointer.
T* m_pLastChild = nullptr; // Raw, intra-tree pointer.
T* m_pNextSibling = nullptr; // Raw, intra-tree pointer
T* m_pPrevSibling = nullptr; // Raw, intra-tree pointer
};
} // namespace fxcrt
using fxcrt::RetainedTreeNode;
#endif // CORE_FXCRT_RETAINED_TREE_NODE_H_