blob: 284e8234951ca2768f56af054ea6c9c0f0c88a6e [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2004-2011, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
*
* 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.
*
*/
#ifndef SKY_ENGINE_CORE_DOM_NODE_H_
#define SKY_ENGINE_CORE_DOM_NODE_H_
#include "sky/engine/bindings/exception_state_placeholder.h"
#include "sky/engine/core/dom/MutationObserver.h"
#include "sky/engine/core/dom/TreeScope.h"
#include "sky/engine/core/dom/TreeShared.h"
#include "sky/engine/core/editing/EditingBoundary.h"
#include "sky/engine/core/inspector/InspectorCounters.h"
#include "sky/engine/core/rendering/style/RenderStyleConstants.h"
#include "sky/engine/platform/geometry/LayoutRect.h"
#include "sky/engine/platform/heap/Handle.h"
#include "sky/engine/platform/weborigin/KURLHash.h"
#include "sky/engine/wtf/Forward.h"
// This needs to be here because Document.h also depends on it.
#define DUMP_NODE_STATISTICS 0
namespace blink {
class Attribute;
class ContainerNode;
class Document;
class Element;
class Event;
class EventDispatchMediator;
class EventListener;
class ExceptionState;
class FloatPoint;
class LocalFrame;
class IntRect;
class KeyboardEvent;
class NSResolver;
class NodeList;
class NodeRareData;
class QualifiedName;
class RegisteredEventListener;
class RenderBox;
class RenderBoxModelObject;
class RenderObject;
class RenderStyle;
template <typename NodeType> class StaticNodeTypeList;
typedef StaticNodeTypeList<Node> StaticNodeList;
class Text;
class WeakNodeMap;
const int nodeStyleChangeShift = 19;
enum StyleChangeType {
NoStyleChange = 0,
LocalStyleChange = 1 << nodeStyleChangeShift,
SubtreeStyleChange = 2 << nodeStyleChangeShift,
NeedsReattachStyleChange = 3 << nodeStyleChangeShift,
};
class NodeRareDataBase {
public:
RenderObject* renderer() const { return m_renderer; }
void setRenderer(RenderObject* renderer) { m_renderer = renderer; }
protected:
NodeRareDataBase(RenderObject* renderer)
: m_renderer(renderer)
{ }
private:
RenderObject* m_renderer;
};
// TreeShared should be the last to pack TreeShared::m_refCount and
// Node::m_nodeFlags on 64bit platforms.
class Node : public DartWrappable, public TreeShared<Node> {
DEFINE_WRAPPERTYPEINFO();
friend class Document;
friend class TreeScope;
friend class TreeScopeAdopter;
public:
enum NodeType {
ELEMENT_NODE = 1,
TEXT_NODE = 3,
DOCUMENT_NODE = 9,
DOCUMENT_FRAGMENT_NODE = 11,
};
enum DocumentPosition {
DOCUMENT_POSITION_EQUIVALENT = 0x00,
DOCUMENT_POSITION_DISCONNECTED = 0x01,
DOCUMENT_POSITION_PRECEDING = 0x02,
DOCUMENT_POSITION_FOLLOWING = 0x04,
DOCUMENT_POSITION_CONTAINS = 0x08,
DOCUMENT_POSITION_CONTAINED_BY = 0x10,
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20,
};
#if !ENABLE(OILPAN)
// All Nodes are placed in their own heap partition for security.
// See http://crbug.com/246860 for detail.
void* operator new(size_t);
void operator delete(void*);
#endif
static void dumpStatistics();
virtual ~Node();
// DOM methods & attributes for Node
virtual String nodeName() const = 0;
virtual NodeType nodeType() const = 0;
ContainerNode* parentNode() const;
void setParentNode(ContainerNode*);
Element* parentElement() const;
Node* previousSibling() const { return m_previous; }
Node* nextSibling() const { return m_next; }
Node* firstChild() const;
Node* lastChild() const;
Element* previousElementSibling();
Element* nextElementSibling();
// These functions release the nodes from |nodes|.
void newInsertBefore(Vector<RefPtr<Node>>& nodes, ExceptionState&);
void newInsertAfter(Vector<RefPtr<Node>>& nodes, ExceptionState&);
void replaceWith(Vector<RefPtr<Node>>& nodes, ExceptionState&);
void remove(ExceptionState&);
// These should all actually return a node, but this is only important for language bindings,
// which will already know and hold a ref on the right node to return.
PassRefPtr<Node> insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionState& = ASSERT_NO_EXCEPTION);
PassRefPtr<Node> replaceChild(PassRefPtr<Node> newChild, PassRefPtr<Node> oldChild, ExceptionState& = ASSERT_NO_EXCEPTION);
PassRefPtr<Node> removeChild(PassRefPtr<Node> child, ExceptionState& = ASSERT_NO_EXCEPTION);
PassRefPtr<Node> appendChild(PassRefPtr<Node> newChild, ExceptionState& = ASSERT_NO_EXCEPTION);
bool hasChildren() const { return firstChild(); }
virtual PassRefPtr<Node> cloneNode(bool deep = false) = 0;
virtual const AtomicString& localName() const;
String textContent() const;
void setTextContent(const String&);
// Other methods (not part of DOM)
bool isElementNode() const { return getFlag(IsElementFlag); }
bool isContainerNode() const { return getFlag(IsContainerFlag); }
bool isTextNode() const { return getFlag(IsTextFlag); }
bool isHTMLElement() const { return getFlag(IsHTMLFlag); }
// StyledElements allow inline style (style="border: 1px"), presentational attributes (ex. color),
// class names (ex. class="foo bar") and other non-basic styling features. They and also control
// if this element can participate in style sharing.
//
// FIXME: The only things that ever go through StyleResolver that aren't StyledElements are
// PseudoElements and VTTElements. It's possible we can just eliminate all the checks
// since those elements will never have class names, inline style, or other things that
// this apparently guards against.
bool isStyledElement() const { return isElementNode(); }
bool isDocumentNode() const;
bool isTreeScope() const;
bool isDocumentFragment() const { return getFlag(IsDocumentFragmentFlag); }
// Returns the enclosing event parent Element (or self) that, when clicked, would trigger a navigation.
Element* enclosingLinkEventParentOrSelf();
// These low-level calls give the caller responsibility for maintaining the integrity of the tree.
void setPreviousSibling(Node* previous) { m_previous = previous; }
void setNextSibling(Node* next) { m_next = next; }
virtual bool canContainRangeEndPoint() const { return false; }
// FIXME: These two functions belong in editing -- "atomic node" is an editing concept.
Node* previousNodeConsideringAtomicNodes() const;
Node* nextNodeConsideringAtomicNodes() const;
// Returns the next leaf node or 0 if there are no more.
// Delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes.
// Uses an editing-specific concept of what a leaf node is, and should probably be moved
// out of the Node class into an editing-specific source file.
Node* nextLeafNode() const;
// Returns the previous leaf node or 0 if there are no more.
// Delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes.
// Uses an editing-specific concept of what a leaf node is, and should probably be moved
// out of the Node class into an editing-specific source file.
Node* previousLeafNode() const;
bool isRootEditableElement() const;
Element* rootEditableElement() const;
Element* rootEditableElement(EditableType) const;
bool isUserActionElement() const { return getFlag(IsUserActionElementFlag); }
void setUserActionElement(bool flag) { setFlag(flag, IsUserActionElementFlag); }
bool active() const { return isUserActionElement() && isUserActionElementActive(); }
bool inActiveChain() const { return isUserActionElement() && isUserActionElementInActiveChain(); }
bool hovered() const { return isUserActionElement() && isUserActionElementHovered(); }
bool needsAttach() const { return styleChangeType() == NeedsReattachStyleChange; }
bool needsStyleRecalc() const { return styleChangeType() != NoStyleChange; }
StyleChangeType styleChangeType() const { return static_cast<StyleChangeType>(m_nodeFlags & StyleChangeMask); }
bool childNeedsStyleRecalc() const { return getFlag(ChildNeedsStyleRecalcFlag); }
bool isLink() const { return getFlag(IsLinkFlag); }
bool isEditingText() const { return isTextNode() && getFlag(IsEditingTextFlag); }
void setChildNeedsStyleRecalc() { setFlag(ChildNeedsStyleRecalcFlag); }
void clearChildNeedsStyleRecalc() { clearFlag(ChildNeedsStyleRecalcFlag); }
void setNeedsStyleRecalc(StyleChangeType);
void clearNeedsStyleRecalc();
void setIsLink(bool f);
bool isV8CollectableDuringMinorGC() const { return getFlag(V8CollectableDuringMinorGCFlag); }
void markV8CollectableDuringMinorGC() { setFlag(true, V8CollectableDuringMinorGCFlag); }
void clearV8CollectableDuringMinorGC() { setFlag(false, V8CollectableDuringMinorGCFlag); }
virtual void setActive(bool flag = true);
virtual void setHovered(bool flag = true);
enum UserSelectAllTreatment {
UserSelectAllDoesNotAffectEditability,
UserSelectAllIsAlwaysNonEditable
};
bool isContentEditable(UserSelectAllTreatment = UserSelectAllDoesNotAffectEditability);
bool isContentRichlyEditable();
bool hasEditableStyle(EditableType editableType = ContentIsEditable, UserSelectAllTreatment treatment = UserSelectAllIsAlwaysNonEditable) const
{
switch (editableType) {
case ContentIsEditable:
return hasEditableStyle(Editable, treatment);
case HasEditableAXRole:
return isEditableToAccessibility(Editable);
}
ASSERT_NOT_REACHED();
return false;
}
bool rendererIsRichlyEditable(EditableType editableType = ContentIsEditable) const
{
switch (editableType) {
case ContentIsEditable:
return hasEditableStyle(RichlyEditable, UserSelectAllIsAlwaysNonEditable);
case HasEditableAXRole:
return isEditableToAccessibility(RichlyEditable);
}
ASSERT_NOT_REACHED();
return false;
}
virtual LayoutRect boundingBox() const;
IntRect pixelSnappedBoundingBox() const { return pixelSnappedIntRect(boundingBox()); }
unsigned nodeIndex() const;
// Returns the DOM ownerDocument attribute. This method never returns NULL, except in the case
// of a Document node.
Document* ownerDocument() const;
// Returns the document associated with this node. A Document node returns itself.
Document& document() const
{
return treeScope().document();
}
TreeScope& treeScope() const
{
ASSERT(m_treeScope);
return *m_treeScope;
}
ContainerNode* owner() const;
bool inActiveDocument() const;
// Returns true if this node is associated with a document and is in its associated document's
// node tree, false otherwise.
bool inDocument() const
{
return getFlag(InDocumentFlag);
}
bool isInTreeScope() const { return getFlag(static_cast<NodeFlags>(InDocumentFlag)); }
unsigned countChildren() const;
bool isDescendantOf(const Node*) const;
bool contains(const Node*) const;
// Used to determine whether range offsets use characters or node indices.
virtual bool offsetInCharacters() const;
// Number of DOM 16-bit units contained in node. Note that rendered text length can be different - e.g. because of
// css-transform:capitalize breaking up precomposed characters and ligatures.
virtual int maxCharacterOffset() const;
// Whether or not a selection can be started in this object
virtual bool canStartSelection() const;
// -----------------------------------------------------------------------------
// Integration with rendering tree
// As renderer() includes a branch you should avoid calling it repeatedly in hot code paths.
// Note that if a Node has a renderer, it's parentNode is guaranteed to have one as well.
RenderObject* renderer() const { return hasRareData() ? m_data.m_rareData->renderer() : m_data.m_renderer; };
void setRenderer(RenderObject* renderer)
{
if (hasRareData())
m_data.m_rareData->setRenderer(renderer);
else
m_data.m_renderer = renderer;
}
// Use these two methods with caution.
RenderBox* renderBox() const;
RenderBoxModelObject* renderBoxModelObject() const;
struct AttachContext {
RenderStyle* resolvedStyle;
bool performingReattach;
AttachContext() : resolvedStyle(0), performingReattach(false) { }
};
// Attaches this node to the rendering tree. This calculates the style to be applied to the node and creates an
// appropriate RenderObject which will be inserted into the tree (except when the style has display: none). This
// makes the node visible in the FrameView.
virtual void attach(const AttachContext& = AttachContext());
// Detaches the node from the rendering tree, making it invisible in the rendered view. This method will remove
// the node's rendering object from the rendering tree and delete it.
virtual void detach(const AttachContext& = AttachContext());
#if ENABLE(ASSERT)
bool inDetach() const;
#endif
void reattach(const AttachContext& = AttachContext());
void lazyReattachIfAttached();
// Returns true if recalcStyle should be called on the object, if there is such a method (on Document and Element).
bool shouldCallRecalcStyle(StyleRecalcChange);
// Wrapper for nodes that don't have a renderer, but still cache the style (like HTMLOptionElement).
RenderStyle* renderStyle() const;
RenderStyle* parentRenderStyle() const;
RenderStyle* computedStyle() { return virtualComputedStyle(); }
virtual void insertedInto(ContainerNode* insertionPoint);
virtual void removedFrom(ContainerNode* insertionPoint);
String debugName() const;
#ifndef NDEBUG
virtual void formatForDebugger(char* buffer, unsigned length) const;
void showNode(const char* prefix = "") const;
void showTreeForThis() const;
void showNodePathForThis() const;
void showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2 = 0, const char* markedLabel2 = 0) const;
#endif
unsigned short compareDocumentPosition(const Node*) const;
void AcceptDartGCVisitor(DartGCVisitor& visitor) const override;
void getRegisteredMutationObserversOfType(HashMap<RawPtr<MutationObserver>, MutationRecordDeliveryOptions>&, MutationObserver::MutationType, const QualifiedName* attributeName);
void registerMutationObserver(MutationObserver&, MutationObserverOptions, const HashSet<AtomicString>& attributeFilter);
void unregisterMutationObserver(MutationObserverRegistration*);
void registerTransientMutationObserver(MutationObserverRegistration*);
void unregisterTransientMutationObserver(MutationObserverRegistration*);
void notifyMutationObserversNodeWillDetach();
Vector<RefPtr<Node>> getDestinationInsertionPoints();
void setAlreadySpellChecked(bool flag) { setFlag(flag, AlreadySpellCheckedFlag); }
bool isAlreadySpellChecked() { return getFlag(AlreadySpellCheckedFlag); }
unsigned lengthOfContents() const;
private:
enum NodeFlags {
HasRareDataFlag = 1,
// Node type flags. These never change once created.
IsTextFlag = 1 << 1,
IsContainerFlag = 1 << 2,
IsElementFlag = 1 << 3,
IsHTMLFlag = 1 << 4,
IsSVGFlag = 1 << 5,
IsDocumentFragmentFlag = 1 << 6,
IsInsertionPointFlag = 1 << 7,
// Changes based on if the element should be treated like a link,
// ex. When setting the href attribute on an <a>.
IsLinkFlag = 1 << 8,
// Changes based on :hover and :active state.
IsUserActionElementFlag = 1 << 9,
// Tree state flags. These change when the element is added/removed
// from a DOM tree.
InDocumentFlag = 1 << 10,
// Flags related to recalcStyle.
// FIXME(sky): Flags 11-17 are free.
ChildNeedsStyleRecalcFlag = 1 << 18,
StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1),
IsEditingTextFlag = 1 << 21,
HasWeakReferencesFlag = 1 << 22,
V8CollectableDuringMinorGCFlag = 1 << 23,
AlreadySpellCheckedFlag = 1 << 24,
DefaultNodeFlags = ChildNeedsStyleRecalcFlag | NeedsReattachStyleChange
};
// 9 bits remaining.
bool getFlag(NodeFlags mask) const { return m_nodeFlags & mask; }
void setFlag(bool f, NodeFlags mask) { m_nodeFlags = (m_nodeFlags & ~mask) | (-(int32_t)f & mask); }
void setFlag(NodeFlags mask) { m_nodeFlags |= mask; }
void clearFlag(NodeFlags mask) { m_nodeFlags &= ~mask; }
protected:
enum ConstructionType {
CreateOther = DefaultNodeFlags,
CreateText = DefaultNodeFlags | IsTextFlag,
CreateContainer = DefaultNodeFlags | IsContainerFlag,
CreateElement = CreateContainer | IsElementFlag,
CreateDocumentFragment = CreateContainer | IsDocumentFragmentFlag,
CreateHTMLElement = CreateElement | IsHTMLFlag,
CreateDocument = CreateContainer | InDocumentFlag,
CreateInsertionPoint = CreateHTMLElement | IsInsertionPointFlag,
CreateEditingText = CreateText | IsEditingTextFlag,
};
Node(TreeScope*, ConstructionType);
virtual void didMoveToNewDocument(Document& oldDocument);
void willBeDeletedFromDocument();
bool hasRareData() const { return getFlag(HasRareDataFlag); }
NodeRareData* rareData() const;
NodeRareData& ensureRareData();
void clearRareData();
void setTreeScope(TreeScope* scope) { m_treeScope = scope; }
// isTreeScopeInitialized() can be false
// - in the Node constructor called by these two classes where m_treeScope is set by TreeScope ctor.
bool isTreeScopeInitialized() const { return m_treeScope; }
void markAncestorsWithChildNeedsStyleRecalc();
private:
friend class TreeShared<Node>;
friend class WeakNodeMap;
unsigned styledSubtreeSize() const;
#if !ENABLE(OILPAN)
void removedLastRef();
#endif
bool hasTreeSharedParent() const { return !!parentNode(); }
enum EditableLevel { Editable, RichlyEditable };
bool hasEditableStyle(EditableLevel, UserSelectAllTreatment = UserSelectAllIsAlwaysNonEditable) const;
bool isEditableToAccessibility(EditableLevel) const;
bool isUserActionElementActive() const;
bool isUserActionElementInActiveChain() const;
bool isUserActionElementHovered() const;
void traceStyleChange(StyleChangeType);
void traceStyleChangeIfNeeded(StyleChangeType);
void setStyleChange(StyleChangeType);
virtual RenderStyle* virtualComputedStyle();
void trackForDebugging();
Vector<OwnPtr<MutationObserverRegistration> >* mutationObserverRegistry();
HashSet<RawPtr<MutationObserverRegistration> >* transientMutationObserverRegistry();
uint32_t m_nodeFlags;
ContainerNode* m_parentNode;
TreeScope* m_treeScope;
Node* m_previous;
Node* m_next;
// When a node has rare data we move the renderer into the rare data.
union DataUnion {
DataUnion() : m_renderer(0) { }
RenderObject* m_renderer;
NodeRareDataBase* m_rareData;
} m_data;
};
inline void Node::setParentNode(ContainerNode* parent)
{
ASSERT(isMainThread());
m_parentNode = parent;
}
inline ContainerNode* Node::parentNode() const
{
ASSERT(isMainThread());
return m_parentNode;
}
inline void Node::lazyReattachIfAttached()
{
if (styleChangeType() == NeedsReattachStyleChange)
return;
if (!inActiveDocument())
return;
AttachContext context;
context.performingReattach = true;
detach(context);
markAncestorsWithChildNeedsStyleRecalc();
}
inline bool Node::shouldCallRecalcStyle(StyleRecalcChange change)
{
return change >= Inherit || needsStyleRecalc() || childNeedsStyleRecalc();
}
// Allow equality comparisons of Nodes by reference or pointer, interchangeably.
DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES_REFCOUNTED(Node)
#define DEFINE_NODE_TYPE_CASTS(thisType, predicate) \
template<typename T> inline thisType* to##thisType(const RefPtr<T>& node) { return to##thisType(node.get()); } \
DEFINE_TYPE_CASTS(thisType, Node, node, node->predicate, node.predicate)
// This requires isClassName(const Node&).
#define DEFINE_NODE_TYPE_CASTS_WITH_FUNCTION(thisType) \
template<typename T> inline thisType* to##thisType(const RefPtr<T>& node) { return to##thisType(node.get()); } \
DEFINE_TYPE_CASTS(thisType, Node, node, is##thisType(*node), is##thisType(node))
#define DECLARE_NODE_FACTORY(T) \
static PassRefPtr<T> create(Document&)
#define DEFINE_NODE_FACTORY(T) \
PassRefPtr<T> T::create(Document& document) \
{ \
return adoptRef(new T(document)); \
}
} // namespace blink
#ifndef NDEBUG
// Outside the WebCore namespace for ease of invocation from gdb.
void showNode(const blink::Node*);
void showTree(const blink::Node*);
void showNodePath(const blink::Node*);
#endif
#endif // SKY_ENGINE_CORE_DOM_NODE_H_