/*
 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#pragma once

#include <WebCore/CharacterData.h>
#include <WebCore/Document.h>
#include <WebCore/Element.h>
#include <WebCore/EventLoop.h>
#include <WebCore/EventTargetInlines.h>
#include <WebCore/GCReachableRef.h>
#include <WebCore/InspectorInstrumentationPublic.h>
#include <WebCore/LayoutRect.h>
#include <WebCore/Node.h>
#include <WebCore/NodeDocument.h>
#include <WebCore/NodeInlinesLight.h>
#include <WebCore/PseudoElement.h>
#include <WebCore/RenderBox.h>
#include <WebCore/ShadowRoot.h>
#include <WebCore/TreeScopeInlines.h>

namespace WebCore {

inline bool Node::isUserAgentShadowRoot() const
{
    auto* shadowRoot = dynamicDowncast<ShadowRoot>(*this);
    return shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent;
}

inline ContainerNode* Node::parentOrShadowHostNode() const
{
    ASSERT(isMainThreadOrGCThread());
    if (auto* shadowRoot = dynamicDowncast<ShadowRoot>(*this))
        return shadowRoot->host();
    return parentNode();
}

inline Document* Node::ownerDocument() const
{
    auto* document = &this->document();
    return document == this ? nullptr : document;
}

inline RenderBox* Node::renderBox() const
{
    return dynamicDowncast<RenderBox>(renderer());
}

inline RenderBoxModelObject* Node::renderBoxModelObject() const
{
    return dynamicDowncast<RenderBoxModelObject>(renderer());
}

inline bool Node::hasAttributes() const
{
    auto* element = dynamicDowncast<Element>(*this);
    return element && element->hasAttributes();
}

inline NamedNodeMap* Node::attributesMap() const
{
    if (auto* element = dynamicDowncast<Element>(*this))
        return &element->attributesMap();
    return nullptr;
}

inline void Node::setRenderer(RenderObject* renderer)
{
    m_renderer = renderer;

    if (InspectorInstrumentationPublic::hasFrontends()) [[unlikely]]
        notifyInspectorOfRendererChange();
}

inline Element* Node::parentElement() const
{
    return dynamicDowncast<Element>(parentNode());
}

bool Node::isBeforePseudoElement() const
{
    auto* pseudoElement = dynamicDowncast<PseudoElement>(*this);
    return pseudoElement && pseudoElement->pseudoElementType() == PseudoElementType::Before;
}

bool Node::isAfterPseudoElement() const
{
    auto* pseudoElement = dynamicDowncast<PseudoElement>(*this);
    return pseudoElement && pseudoElement->pseudoElementType() == PseudoElementType::After;
}

std::optional<PseudoElementType> Node::pseudoElementType() const
{
    if (auto* pseudoElement = dynamicDowncast<PseudoElement>(*this))
        return pseudoElement->pseudoElementType();
    return { };
}

std::optional<Style::PseudoElementIdentifier> Node::pseudoElementIdentifier() const
{
    if (auto type = pseudoElementType())
        return Style::PseudoElementIdentifier { *type };
    return { };
}

inline void Node::setTabIndexState(TabIndexState state)
{
    auto bitfields = rareDataBitfields();
    bitfields.tabIndexState = std::to_underlying(state);
    setRareDataBitfields(bitfields);
}

inline unsigned Node::length() const
{
    if (auto characterData = dynamicDowncast<CharacterData>(*this))
        return characterData->length();
    return countChildNodes();
}

inline unsigned Node::countChildNodes() const
{
    auto* containerNode = dynamicDowncast<ContainerNode>(*this);
    return containerNode ? containerNode->countChildNodes() : 0;
}

inline Node* Node::traverseToChildAt(unsigned index) const
{
    auto* containerNode = dynamicDowncast<ContainerNode>(*this);
    return containerNode ? containerNode->traverseToChildAt(index) : nullptr;
}

inline Node* Node::firstChild() const
{
    auto* containerNode = dynamicDowncast<ContainerNode>(*this);
    return containerNode ? containerNode->firstChild() : nullptr;
}

inline Node* Node::lastChild() const
{
    auto* containerNode = dynamicDowncast<ContainerNode>(*this);
    return containerNode ? containerNode->lastChild() : nullptr;
}

inline bool Node::hasChildNodes() const
{
    return firstChild();
}

inline Node& Node::rootNode() const
{
    if (isInTreeScope())
        return treeScope().rootNode();
    return *m_shadowIncludingRoot;
}

inline void Node::setParentNode(ContainerNode* parent)
{
    ASSERT(isMainThread());
    m_parentNode = parent;
    m_refCountAndParentBit = (m_refCountAndParentBit & s_refCountMask) | !!parent;
}

ALWAYS_INLINE bool Node::hasOneRef() const
{
    ASSERT(!deletionHasBegun());
    ASSERT(!m_inRemovedLastRefFunction);
    return refCount() == 1;
}

ALWAYS_INLINE void Node::clearStyleFlags(OptionSet<NodeStyleFlag> flags)
{
    auto bitfields = styleBitfields();
    bitfields.clearFlags(flags);
    setStyleBitfields(bitfields);
}

inline void Node::clearChildNeedsStyleRecalc()
{
    clearStateFlag(StateFlag::DescendantNeedsStyleResolution);
    clearStateFlag(StateFlag::DirectChildNeedsStyleResolution);
}

inline void Node::setHasValidStyle()
{
    auto bitfields = styleBitfields();
    bitfields.setStyleValidity(Style::Validity::Valid);
    setStyleBitfields(bitfields);
    clearStateFlag(StateFlag::IsComputedStyleInvalidFlag);
    clearStateFlag(StateFlag::HasInvalidRenderer);
    clearStateFlag(StateFlag::StyleResolutionShouldRecompositeLayer);
}

inline void Node::setTreeScopeRecursively(TreeScope& newTreeScope)
{
    ASSERT(!isDocumentNode());
    ASSERT(!deletionHasBegun());
    if (m_treeScope != &newTreeScope) {
        Ref oldTreeScope = *m_treeScope;
        moveTreeToNewScope(*this, oldTreeScope, newTreeScope);
    }
}

inline ContainerNode* Node::parentNodeGuaranteedHostFree() const
{
    ASSERT(!isShadowRoot());
    return parentNode();
}

inline void Node::setTreeScope(TreeScope& scope)
{
    Locker locker { TreeScope::treeScopeMutationLock() };
    m_treeScope = &scope;
}

template<typename NodeClass>
inline NodeClass& Node::traverseToRootNodeInternal(const NodeClass& node)
{
    auto* current = const_cast<NodeClass*>(&node);
    while (current->parentNode())
        current = current->parentNode();
    return *current;
}

inline void Node::relaxAdoptionRequirement()
{
#if ASSERT_ENABLED
    ASSERT_WITH_SECURITY_IMPLICATION(!deletionHasBegun());
    ASSERT(m_adoptionIsRequired);
    m_adoptionIsRequired = false;
#endif
}

inline IntRect Node::pixelSnappedAbsoluteBoundingRect(bool* isReplaced)
{
    return snappedIntRect(absoluteBoundingRect(isReplaced));
}

// Used in Node::addSubresourceAttributeURLs() and in addSubresourceStyleURLs()
inline void addSubresourceURL(OrderedHashSet<URL>& urls, const URL& url)
{
    if (!url.isNull())
        urls.add(url);
}

inline void collectChildNodes(Node& node, NodeVector& children)
{
    for (SUPPRESS_UNCOUNTED_LOCAL Node* child = node.firstChild(); child; child = child->nextSibling())
        children.append(*child);
}

template<typename T, typename Task>
void Node::queueTaskKeepingNodeAlive(T& node, TaskSource source, Task&& task)
{
    static_assert(!std::is_base_of_v<ActiveDOMObject, T>, "Use ActiveDOMObject::queueTaskKeepingObjectAlive instead");
    protect(protect(node.document())->eventLoop())->queueTask(source, [protectedThis = GCReachableRef(node), task = std::forward<Task>(task)]() mutable {
        // Static analyzer does not know about GCReachableRef.
        SUPPRESS_UNCOUNTED_ARG task(protectedThis.get());
    });
}

} // namespace WebCore
