summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/Node.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom/Node.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/dom/Node.cpp')
-rw-r--r--Source/WebCore/dom/Node.cpp1645
1 files changed, 885 insertions, 760 deletions
diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp
index 0db540bbf..0258126d3 100644
--- a/Source/WebCore/dom/Node.cpp
+++ b/Source/WebCore/dom/Node.cpp
@@ -2,7 +2,7 @@
* 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, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
*
@@ -29,76 +29,59 @@
#include "Attr.h"
#include "BeforeLoadEvent.h"
#include "ChildListMutationScope.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
-#include "CSSParser.h"
-#include "CSSRule.h"
-#include "CSSSelector.h"
-#include "CSSSelectorList.h"
-#include "CSSStyleRule.h"
-#include "CSSStyleSheet.h"
+#include "ComposedTreeAncestorIterator.h"
#include "ContainerNodeAlgorithms.h"
#include "ContextMenuController.h"
-#include "DOMImplementation.h"
+#include "DataTransfer.h"
#include "DocumentType.h"
#include "ElementIterator.h"
#include "ElementRareData.h"
+#include "ElementTraversal.h"
#include "EventDispatcher.h"
-#include "EventException.h"
#include "EventHandler.h"
-#include "FlowThreadController.h"
+#include "ExceptionCode.h"
#include "FrameView.h"
+#include "HTMLBodyElement.h"
#include "HTMLCollection.h"
#include "HTMLElement.h"
#include "HTMLImageElement.h"
+#include "HTMLSlotElement.h"
#include "HTMLStyleElement.h"
-#include "InsertionPoint.h"
+#include "InputEvent.h"
+#include "InspectorController.h"
#include "KeyboardEvent.h"
#include "Logging.h"
#include "MutationEvent.h"
+#include "NoEventDispatchAssertion.h"
#include "NodeRenderStyle.h"
-#include "PlatformMouseEvent.h"
-#include "PlatformWheelEvent.h"
#include "ProcessingInstruction.h"
#include "ProgressEvent.h"
+#include "Range.h"
#include "RenderBlock.h"
#include "RenderBox.h"
#include "RenderTextControl.h"
#include "RenderView.h"
#include "ScopedEventQueue.h"
-#include "Settings.h"
#include "StorageEvent.h"
#include "StyleResolver.h"
+#include "StyleSheetContents.h"
#include "TemplateContentDocumentFragment.h"
#include "TextEvent.h"
#include "TouchEvent.h"
#include "TreeScopeAdopter.h"
#include "WheelEvent.h"
+#include "XMLNSNames.h"
#include "XMLNames.h"
-#include "htmlediting.h"
-#include <runtime/Operations.h>
-#include <runtime/VM.h>
#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/SHA1.h>
+#include <wtf/Variant.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
-#if ENABLE(INDIE_UI)
-#include "UIRequestEvent.h"
-#endif
-
-#if ENABLE(INSPECTOR)
-#include "InspectorController.h"
-#endif
-
namespace WebCore {
using namespace HTMLNames;
-bool Node::isSupported(const String& feature, const String& version)
-{
- return DOMImplementation::hasFeature(feature, version);
-}
-
#if DUMP_NODE_STATISTICS
static HashSet<Node*> liveNodeSet;
#endif
@@ -113,14 +96,11 @@ void Node::dumpStatistics()
size_t textNodes = 0;
size_t cdataNodes = 0;
size_t commentNodes = 0;
- size_t entityReferenceNodes = 0;
size_t entityNodes = 0;
size_t piNodes = 0;
size_t documentNodes = 0;
size_t docTypeNodes = 0;
size_t fragmentNodes = 0;
- size_t notationNodes = 0;
- size_t xpathNSNodes = 0;
size_t shadowRootNodes = 0;
HashMap<String, size_t> perTagCount;
@@ -131,14 +111,12 @@ void Node::dumpStatistics()
size_t elementsWithRareData = 0;
size_t elementsWithNamedNodeMap = 0;
- for (HashSet<Node*>::iterator it = liveNodeSet.begin(); it != liveNodeSet.end(); ++it) {
- Node* node = *it;
-
+ for (auto* node : liveNodeSet) {
if (node->hasRareData()) {
++nodesWithRareData;
- if (node->isElementNode()) {
+ if (is<Element>(*node)) {
++elementsWithRareData;
- if (toElement(node)->hasNamedNodeMap())
+ if (downcast<Element>(*node).hasNamedNodeMap())
++elementsWithNamedNodeMap;
}
}
@@ -148,18 +126,18 @@ void Node::dumpStatistics()
++elementNodes;
// Tag stats
- Element* element = toElement(node);
- HashMap<String, size_t>::AddResult result = perTagCount.add(element->tagName(), 1);
+ Element& element = downcast<Element>(*node);
+ HashMap<String, size_t>::AddResult result = perTagCount.add(element.tagName(), 1);
if (!result.isNewEntry)
result.iterator->value++;
- if (ElementData* elementData = element->elementData()) {
+ if (const ElementData* elementData = element.elementData()) {
unsigned length = elementData->length();
attributes += length;
++elementsWithAttributeStorage;
for (unsigned i = 0; i < length; ++i) {
- Attribute& attr = elementData->attributeAt(i);
- if (attr.attr())
+ const Attribute& attr = elementData->attributeAt(i);
+ if (!attr.isEmpty())
++attributesWithAttr;
}
}
@@ -181,10 +159,6 @@ void Node::dumpStatistics()
++commentNodes;
break;
}
- case ENTITY_REFERENCE_NODE: {
- ++entityReferenceNodes;
- break;
- }
case ENTITY_NODE: {
++entityNodes;
break;
@@ -208,14 +182,6 @@ void Node::dumpStatistics()
++fragmentNodes;
break;
}
- case NOTATION_NODE: {
- ++notationNodes;
- break;
- }
- case XPATH_NAMESPACE_NODE: {
- ++xpathNSNodes;
- break;
- }
}
}
@@ -228,19 +194,16 @@ void Node::dumpStatistics()
printf(" Number of Text nodes: %zu\n", textNodes);
printf(" Number of CDATASection nodes: %zu\n", cdataNodes);
printf(" Number of Comment nodes: %zu\n", commentNodes);
- printf(" Number of EntityReference nodes: %zu\n", entityReferenceNodes);
printf(" Number of Entity nodes: %zu\n", entityNodes);
printf(" Number of ProcessingInstruction nodes: %zu\n", piNodes);
printf(" Number of Document nodes: %zu\n", documentNodes);
printf(" Number of DocumentType nodes: %zu\n", docTypeNodes);
printf(" Number of DocumentFragment nodes: %zu\n", fragmentNodes);
- printf(" Number of Notation nodes: %zu\n", notationNodes);
- printf(" Number of XPathNS nodes: %zu\n", xpathNSNodes);
printf(" Number of ShadowRoot nodes: %zu\n", shadowRootNodes);
printf("Element tag name distibution:\n");
- for (HashMap<String, size_t>::iterator it = perTagCount.begin(); it != perTagCount.end(); ++it)
- printf(" Number of <%s> tags: %zu\n", it->key.utf8().data(), it->value);
+ for (auto& stringSizePair : perTagCount)
+ printf(" Number of <%s> tags: %zu\n", stringSizePair.key.utf8().data(), stringSizePair.value);
printf("Attributes:\n");
printf(" Number of Attributes (non-Node and Node): %zu [%zu]\n", attributes, sizeof(Attribute));
@@ -252,10 +215,17 @@ void Node::dumpStatistics()
}
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, nodeCounter, ("WebCoreNode"));
-DEFINE_DEBUG_ONLY_GLOBAL(HashSet<Node*>, ignoreSet, );
#ifndef NDEBUG
static bool shouldIgnoreLeaks = false;
+
+static HashSet<Node*>& ignoreSet()
+{
+ static NeverDestroyed<HashSet<Node*>> ignore;
+
+ return ignore;
+}
+
#endif
void Node::startIgnoringLeaks()
@@ -276,7 +246,7 @@ void Node::trackForDebugging()
{
#ifndef NDEBUG
if (shouldIgnoreLeaks)
- ignoreSet.add(this);
+ ignoreSet().add(this);
else
nodeCounter.increment();
#endif
@@ -286,10 +256,29 @@ void Node::trackForDebugging()
#endif
}
+Node::Node(Document& document, ConstructionType type)
+ : m_refCount(1)
+ , m_nodeFlags(type)
+ , m_treeScope(&document)
+{
+ ASSERT(isMainThread());
+
+ document.incrementReferencingNodeCount();
+
+#if !defined(NDEBUG) || (defined(DUMP_NODE_STATISTICS) && DUMP_NODE_STATISTICS)
+ trackForDebugging();
+#endif
+}
+
Node::~Node()
{
+ ASSERT(isMainThread());
+ ASSERT(!m_refCount);
+ ASSERT(m_deletionHasBegun);
+ ASSERT(!m_adoptionIsRequired);
+
#ifndef NDEBUG
- if (!ignoreSet.remove(this))
+ if (!ignoreSet().remove(this))
nodeCounter.decrement();
#endif
@@ -297,7 +286,7 @@ Node::~Node()
liveNodeSet.remove(this);
#endif
- ASSERT(!renderer());
+ ASSERT_WITH_SECURITY_IMPLICATION(!renderer());
ASSERT(!parentNode());
ASSERT(!m_previous);
ASSERT(!m_next);
@@ -305,53 +294,42 @@ Node::~Node()
if (hasRareData())
clearRareData();
- if (!isContainerNode()) {
- if (Document* document = documentInternal())
- willBeDeletedFrom(document);
- }
+ if (!isContainerNode())
+ willBeDeletedFrom(document());
- m_treeScope->selfOnlyDeref();
+ if (hasEventTargetData())
+ clearEventTargetData();
- InspectorCounters::decrementCounter(InspectorCounters::NodeCounter);
+ document().decrementReferencingNodeCount();
}
-void Node::willBeDeletedFrom(Document* document)
+void Node::willBeDeletedFrom(Document& document)
{
if (hasEventTargetData()) {
+ document.didRemoveWheelEventHandler(*this, EventHandlerRemoval::All);
#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS)
- if (document)
- document->removeTouchEventListener(this, true);
+ document.removeTouchEventListener(this, true);
+ document.removeTouchEventHandler(this, true);
+#else
+ // FIXME: This should call didRemoveTouchEventHandler().
#endif
- clearEventTargetData();
- }
-
- if (document) {
- if (AXObjectCache* cache = document->existingAXObjectCache())
- cache->remove(this);
}
-}
-NodeRareData* Node::rareData() const
-{
- ASSERT_WITH_SECURITY_IMPLICATION(hasRareData());
- return static_cast<NodeRareData*>(m_data.m_rareData);
+ if (AXObjectCache* cache = document.existingAXObjectCache())
+ cache->remove(this);
}
-NodeRareData& Node::ensureRareData()
+void Node::materializeRareData()
{
- if (hasRareData())
- return *rareData();
-
NodeRareData* data;
- if (isElementNode())
- data = ElementRareData::create(toRenderElement(m_data.m_renderer)).leakPtr();
+ if (is<Element>(*this))
+ data = std::make_unique<ElementRareData>(downcast<RenderElement>(m_data.m_renderer)).release();
else
- data = NodeRareData::create(m_data.m_renderer).leakPtr();
+ data = std::make_unique<NodeRareData>(m_data.m_renderer).release();
ASSERT(data);
m_data.m_rareData = data;
setFlag(HasRareDataFlag);
- return *data;
}
void Node::clearRareData()
@@ -373,35 +351,21 @@ Node* Node::toNode()
return this;
}
-HTMLInputElement* Node::toInputElement()
-{
- // If one of the below ASSERTs trigger, you are calling this function
- // directly or indirectly from a constructor or destructor of this object.
- // Don't do this!
- ASSERT(!(isHTMLElement() && hasTagName(inputTag)));
- return 0;
-}
-
String Node::nodeValue() const
{
return String();
}
-void Node::setNodeValue(const String& /*nodeValue*/, ExceptionCode& ec)
+ExceptionOr<void> Node::setNodeValue(const String&)
{
- // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
- if (isReadOnlyNode()) {
- ec = NO_MODIFICATION_ALLOWED_ERR;
- return;
- }
-
// By default, setting nodeValue has no effect.
+ return { };
}
-PassRefPtr<NodeList> Node::childNodes()
+RefPtr<NodeList> Node::childNodes()
{
- if (isContainerNode())
- return ensureRareData().ensureNodeLists().ensureChildNodeList(toContainerNode(*this));
+ if (is<ContainerNode>(*this))
+ return ensureRareData().ensureNodeLists().ensureChildNodeList(downcast<ContainerNode>(*this));
return ensureRareData().ensureNodeLists().ensureEmptyChildNodeList(*this);
}
@@ -421,46 +385,173 @@ Node* Node::firstDescendant() const
return n;
}
-bool Node::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
+Element* Node::previousElementSibling() const
{
- if (!isContainerNode()) {
- ec = HIERARCHY_REQUEST_ERR;
- return false;
+ return ElementTraversal::previousSibling(*this);
+}
+
+Element* Node::nextElementSibling() const
+{
+ return ElementTraversal::nextSibling(*this);
+}
+
+ExceptionOr<void> Node::insertBefore(Node& newChild, Node* refChild)
+{
+ if (!is<ContainerNode>(*this))
+ return Exception { HIERARCHY_REQUEST_ERR };
+ return downcast<ContainerNode>(*this).insertBefore(newChild, refChild);
+}
+
+ExceptionOr<void> Node::replaceChild(Node& newChild, Node& oldChild)
+{
+ if (!is<ContainerNode>(*this))
+ return Exception { HIERARCHY_REQUEST_ERR };
+ return downcast<ContainerNode>(*this).replaceChild(newChild, oldChild);
+}
+
+ExceptionOr<void> Node::removeChild(Node& oldChild)
+{
+ if (!is<ContainerNode>(*this))
+ return Exception { NOT_FOUND_ERR };
+ return downcast<ContainerNode>(*this).removeChild(oldChild);
+}
+
+ExceptionOr<void> Node::appendChild(Node& newChild)
+{
+ if (!is<ContainerNode>(*this))
+ return Exception { HIERARCHY_REQUEST_ERR };
+ return downcast<ContainerNode>(*this).appendChild(newChild);
+}
+
+static HashSet<RefPtr<Node>> nodeSetPreTransformedFromNodeOrStringVector(const Vector<NodeOrString>& vector)
+{
+ HashSet<RefPtr<Node>> nodeSet;
+ for (const auto& variant : vector) {
+ WTF::switchOn(variant,
+ [&] (const RefPtr<Node>& node) { nodeSet.add(const_cast<Node*>(node.get())); },
+ [] (const String&) { }
+ );
}
- return toContainerNode(this)->insertBefore(newChild, refChild, ec);
+ return nodeSet;
}
-bool Node::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec)
+static RefPtr<Node> firstPrecedingSiblingNotInNodeSet(Node& context, const HashSet<RefPtr<Node>>& nodeSet)
{
- if (!isContainerNode()) {
- ec = HIERARCHY_REQUEST_ERR;
- return false;
+ for (auto* sibling = context.previousSibling(); sibling; sibling = sibling->previousSibling()) {
+ if (!nodeSet.contains(sibling))
+ return sibling;
}
- return toContainerNode(this)->replaceChild(newChild, oldChild, ec);
+ return nullptr;
}
-bool Node::removeChild(Node* oldChild, ExceptionCode& ec)
+static RefPtr<Node> firstFollowingSiblingNotInNodeSet(Node& context, const HashSet<RefPtr<Node>>& nodeSet)
{
- if (!isContainerNode()) {
- ec = NOT_FOUND_ERR;
- return false;
+ for (auto* sibling = context.nextSibling(); sibling; sibling = sibling->nextSibling()) {
+ if (!nodeSet.contains(sibling))
+ return sibling;
}
- return toContainerNode(this)->removeChild(oldChild, ec);
+ return nullptr;
}
-bool Node::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
+ExceptionOr<RefPtr<Node>> Node::convertNodesOrStringsIntoNode(Vector<NodeOrString>&& nodeOrStringVector)
{
- if (!isContainerNode()) {
- ec = HIERARCHY_REQUEST_ERR;
- return false;
+ if (nodeOrStringVector.isEmpty())
+ return nullptr;
+
+ Vector<Ref<Node>> nodes;
+ nodes.reserveInitialCapacity(nodeOrStringVector.size());
+ for (auto& variant : nodeOrStringVector) {
+ WTF::switchOn(variant,
+ [&](RefPtr<Node>& node) { nodes.uncheckedAppend(*node.get()); },
+ [&](String& string) { nodes.uncheckedAppend(Text::create(document(), string)); }
+ );
+ }
+
+ if (nodes.size() == 1)
+ return RefPtr<Node> { WTFMove(nodes.first()) };
+
+ auto nodeToReturn = DocumentFragment::create(document());
+ for (auto& node : nodes) {
+ auto appendResult = nodeToReturn->appendChild(node);
+ if (appendResult.hasException())
+ return appendResult.releaseException();
}
- return toContainerNode(this)->appendChild(newChild, ec);
+ return RefPtr<Node> { WTFMove(nodeToReturn) };
}
-void Node::remove(ExceptionCode& ec)
+ExceptionOr<void> Node::before(Vector<NodeOrString>&& nodeOrStringVector)
{
- if (ContainerNode* parent = parentNode())
- parent->removeChild(this, ec);
+ RefPtr<ContainerNode> parent = parentNode();
+ if (!parent)
+ return { };
+
+ auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector);
+ auto viablePreviousSibling = firstPrecedingSiblingNotInNodeSet(*this, nodeSet);
+
+ auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector));
+ if (result.hasException())
+ return result.releaseException();
+ auto node = result.releaseReturnValue();
+ if (!node)
+ return { };
+
+ if (viablePreviousSibling)
+ viablePreviousSibling = viablePreviousSibling->nextSibling();
+ else
+ viablePreviousSibling = parent->firstChild();
+
+ return parent->insertBefore(*node, viablePreviousSibling.get());
+}
+
+ExceptionOr<void> Node::after(Vector<NodeOrString>&& nodeOrStringVector)
+{
+ RefPtr<ContainerNode> parent = parentNode();
+ if (!parent)
+ return { };
+
+ auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector);
+ auto viableNextSibling = firstFollowingSiblingNotInNodeSet(*this, nodeSet);
+
+ auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector));
+ if (result.hasException())
+ return result.releaseException();
+ auto node = result.releaseReturnValue();
+ if (!node)
+ return { };
+
+ return parent->insertBefore(*node, viableNextSibling.get());
+}
+
+ExceptionOr<void> Node::replaceWith(Vector<NodeOrString>&& nodeOrStringVector)
+{
+ RefPtr<ContainerNode> parent = parentNode();
+ if (!parent)
+ return { };
+
+ auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector);
+ auto viableNextSibling = firstFollowingSiblingNotInNodeSet(*this, nodeSet);
+
+ auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector));
+ if (result.hasException())
+ return result.releaseException();
+
+ if (parentNode() == parent) {
+ if (auto node = result.releaseReturnValue())
+ return parent->replaceChild(*node, *this);
+ return parent->removeChild(*this);
+ }
+
+ if (auto node = result.releaseReturnValue())
+ return parent->insertBefore(*node, viableNextSibling.get());
+ return { };
+}
+
+ExceptionOr<void> Node::remove()
+{
+ auto* parent = parentNode();
+ if (!parent)
+ return { };
+ return parent->removeChild(*this);
}
void Node::normalize()
@@ -474,23 +565,23 @@ void Node::normalize()
while (node) {
NodeType type = node->nodeType();
if (type == ELEMENT_NODE)
- toElement(node.get())->normalizeAttributes();
+ downcast<Element>(*node).normalizeAttributes();
if (node == this)
break;
if (type != TEXT_NODE) {
- node = NodeTraversal::nextPostOrder(node.get());
+ node = NodeTraversal::nextPostOrder(*node);
continue;
}
- RefPtr<Text> text = toText(node.get());
+ RefPtr<Text> text = downcast<Text>(node.get());
// Remove empty text nodes.
if (!text->length()) {
// Care must be taken to get the next node before removing the current node.
- node = NodeTraversal::nextPostOrder(node.get());
- text->remove(IGNORE_EXCEPTION);
+ node = NodeTraversal::nextPostOrder(*node);
+ text->remove();
continue;
}
@@ -498,37 +589,44 @@ void Node::normalize()
while (Node* nextSibling = node->nextSibling()) {
if (nextSibling->nodeType() != TEXT_NODE)
break;
- RefPtr<Text> nextText = toText(nextSibling);
+ RefPtr<Text> nextText = downcast<Text>(nextSibling);
// Remove empty text nodes.
if (!nextText->length()) {
- nextText->remove(IGNORE_EXCEPTION);
+ nextText->remove();
continue;
}
// Both non-empty text nodes. Merge them.
unsigned offset = text->length();
- text->appendData(nextText->data(), IGNORE_EXCEPTION);
+ text->appendData(nextText->data());
document().textNodesMerged(nextText.get(), offset);
- nextText->remove(IGNORE_EXCEPTION);
+ nextText->remove();
}
- node = NodeTraversal::nextPostOrder(node.get());
+ node = NodeTraversal::nextPostOrder(*node);
}
}
+ExceptionOr<Ref<Node>> Node::cloneNodeForBindings(bool deep)
+{
+ if (UNLIKELY(isShadowRoot()))
+ return Exception { NOT_SUPPORTED_ERR };
+ return cloneNode(deep);
+}
+
const AtomicString& Node::prefix() const
{
// For nodes other than elements and attributes, the prefix is always null
return nullAtom;
}
-void Node::setPrefix(const AtomicString& /*prefix*/, ExceptionCode& ec)
+ExceptionOr<void> Node::setPrefix(const AtomicString&)
{
// The spec says that for nodes other than elements and attributes, prefix is always null.
// It does not say what to do when the user tries to set the prefix on another type of
// node, however Mozilla throws a NAMESPACE_ERR exception.
- ec = NAMESPACE_ERR;
+ return Exception { NAMESPACE_ERR };
}
const AtomicString& Node::localName() const
@@ -541,42 +639,30 @@ const AtomicString& Node::namespaceURI() const
return nullAtom;
}
-bool Node::isContentEditable(UserSelectAllTreatment treatment)
+bool Node::isContentEditable()
{
- document().updateStyleIfNeeded();
- return hasEditableStyle(Editable, treatment);
+ return computeEditability(UserSelectAllDoesNotAffectEditability, ShouldUpdateStyle::Update) != Editability::ReadOnly;
}
bool Node::isContentRichlyEditable()
{
- document().updateStyleIfNeeded();
- return hasEditableStyle(RichlyEditable, UserSelectAllIsAlwaysNonEditable);
+ return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) == Editability::CanEditRichly;
}
void Node::inspect()
{
-#if ENABLE(INSPECTOR)
if (document().page())
document().page()->inspectorController().inspect(this);
-#endif
}
-bool Node::hasEditableStyle(EditableLevel editableLevel, UserSelectAllTreatment treatment) const
+static Node::Editability computeEditabilityFromComputedStyle(const Node& startNode, Node::UserSelectAllTreatment treatment)
{
- if (!document().hasLivingRenderTree())
- return false;
- if (document().frame() && document().frame()->page() && document().frame()->page()->isEditable() && !containingShadowRoot())
- return true;
-
- if (isPseudoElement())
- return false;
-
// Ideally we'd call ASSERT(!needsStyleRecalc()) here, but
- // ContainerNode::setFocus() calls setNeedsStyleRecalc(), so the assertion
+ // ContainerNode::setFocus() calls invalidateStyleForSubtree(), so the assertion
// would fire in the middle of Document::setFocusedElement().
- for (const Node* node = this; node; node = node->parentNode()) {
- RenderStyle* style = node->isDocumentNode() ? node->renderStyle() : const_cast<Node*>(node)->computedStyle();
+ for (const Node* node = &startNode; node; node = node->parentNode()) {
+ auto* style = node->isDocumentNode() ? node->renderStyle() : const_cast<Node*>(node)->computedStyle();
if (!style)
continue;
if (style->display() == NONE)
@@ -584,60 +670,54 @@ bool Node::hasEditableStyle(EditableLevel editableLevel, UserSelectAllTreatment
#if ENABLE(USERSELECT_ALL)
// Elements with user-select: all style are considered atomic
// therefore non editable.
- if (treatment == UserSelectAllIsAlwaysNonEditable && style->userSelect() == SELECT_ALL)
- return false;
+ if (treatment == Node::UserSelectAllIsAlwaysNonEditable && style->userSelect() == SELECT_ALL)
+ return Node::Editability::ReadOnly;
#else
UNUSED_PARAM(treatment);
#endif
switch (style->userModify()) {
case READ_ONLY:
- return false;
+ return Node::Editability::ReadOnly;
case READ_WRITE:
- return true;
+ return Node::Editability::CanEditRichly;
case READ_WRITE_PLAINTEXT_ONLY:
- return editableLevel != RichlyEditable;
+ return Node::Editability::CanEditPlainText;
}
ASSERT_NOT_REACHED();
- return false;
+ return Node::Editability::ReadOnly;
}
- return false;
+ return Node::Editability::ReadOnly;
}
-bool Node::isEditableToAccessibility(EditableLevel editableLevel) const
+Node::Editability Node::computeEditability(UserSelectAllTreatment treatment, ShouldUpdateStyle shouldUpdateStyle) const
{
- if (hasEditableStyle(editableLevel))
- return true;
+ if (!document().hasLivingRenderTree() || isPseudoElement())
+ return Editability::ReadOnly;
- // FIXME: Respect editableLevel for ARIA editable elements.
- if (editableLevel == RichlyEditable)
- return false;
-
- ASSERT(AXObjectCache::accessibilityEnabled());
- ASSERT(document().existingAXObjectCache());
+ if (isInShadowTree())
+ return HTMLElement::editabilityFromContentEditableAttr(*this);
- if (AXObjectCache* cache = document().existingAXObjectCache())
- return cache->rootAXEditableElement(this);
+ if (document().frame() && document().frame()->page() && document().frame()->page()->isEditable())
+ return Editability::CanEditRichly;
- return false;
+ if (shouldUpdateStyle == ShouldUpdateStyle::Update && document().needsStyleRecalc()) {
+ if (!document().usesStyleBasedEditability())
+ return HTMLElement::editabilityFromContentEditableAttr(*this);
+ document().updateStyleIfNeeded();
+ }
+ return computeEditabilityFromComputedStyle(*this, treatment);
}
RenderBox* Node::renderBox() const
{
RenderObject* renderer = this->renderer();
- return renderer && renderer->isBox() ? toRenderBox(renderer) : 0;
+ return is<RenderBox>(renderer) ? downcast<RenderBox>(renderer) : nullptr;
}
RenderBoxModelObject* Node::renderBoxModelObject() const
{
RenderObject* renderer = this->renderer();
- return renderer && renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0;
-}
-
-LayoutRect Node::boundingBox() const
-{
- if (renderer())
- return renderer()->absoluteBoundingBoxRect();
- return LayoutRect();
+ return is<RenderBoxModelObject>(renderer) ? downcast<RenderBoxModelObject>(renderer) : nullptr;
}
LayoutRect Node::renderRect(bool* isReplaced)
@@ -645,7 +725,7 @@ LayoutRect Node::renderRect(bool* isReplaced)
RenderObject* hitRenderer = this->renderer();
ASSERT(hitRenderer);
RenderObject* renderer = hitRenderer;
- while (renderer && !renderer->isBody() && !renderer->isRoot()) {
+ while (renderer && !renderer->isBody() && !renderer->isDocumentElementRenderer()) {
if (renderer->isRenderBlock() || renderer->isInlineBlockOrInlineTable() || renderer->isReplaced()) {
*isReplaced = renderer->isReplaced();
return renderer->absoluteBoundingBoxRect();
@@ -655,16 +735,6 @@ LayoutRect Node::renderRect(bool* isReplaced)
return LayoutRect();
}
-void Node::markAncestorsWithChildNeedsStyleRecalc()
-{
- ContainerNode* ancestor = isPseudoElement() ? toPseudoElement(this)->hostElement() : parentOrShadowHostNode();
- for (; ancestor && !ancestor->childNeedsStyleRecalc(); ancestor = ancestor->parentOrShadowHostNode())
- ancestor->setChildNeedsStyleRecalc();
-
- if (document().childNeedsStyleRecalc())
- document().scheduleStyleRecalc();
-}
-
void Node::refEventTarget()
{
ref();
@@ -675,26 +745,70 @@ void Node::derefEventTarget()
deref();
}
-void Node::setNeedsStyleRecalc(StyleChangeType changeType)
+void Node::adjustStyleValidity(Style::Validity validity, Style::InvalidationMode mode)
+{
+ if (validity > styleValidity()) {
+ m_nodeFlags &= ~StyleValidityMask;
+ m_nodeFlags |= static_cast<unsigned>(validity) << StyleValidityShift;
+ }
+ if (mode == Style::InvalidationMode::RecompositeLayer)
+ setFlag(StyleResolutionShouldRecompositeLayerFlag);
+}
+
+inline void Node::updateAncestorsForStyleRecalc()
+{
+ auto composedAncestors = composedTreeAncestors(*this);
+ auto it = composedAncestors.begin();
+ auto end = composedAncestors.end();
+ if (it != end) {
+ it->setDirectChildNeedsStyleRecalc();
+
+ if (it->childrenAffectedByPropertyBasedBackwardPositionalRules())
+ it->adjustStyleValidity(Style::Validity::SubtreeInvalid, Style::InvalidationMode::Normal);
+
+ for (; it != end; ++it) {
+ // Iterator skips over shadow roots.
+ if (auto* shadowRoot = it->shadowRoot())
+ shadowRoot->setChildNeedsStyleRecalc();
+ if (it->childNeedsStyleRecalc())
+ break;
+ it->setChildNeedsStyleRecalc();
+ }
+ }
+
+ auto* documentElement = document().documentElement();
+ if (!documentElement)
+ return;
+ if (!documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
+ return;
+ document().setChildNeedsStyleRecalc();
+ document().scheduleStyleRecalc();
+}
+
+void Node::invalidateStyle(Style::Validity validity, Style::InvalidationMode mode)
{
- ASSERT(changeType != NoStyleChange);
+ ASSERT(validity != Style::Validity::Valid);
if (!inRenderedDocument())
return;
- StyleChangeType existingChangeType = styleChangeType();
- if (changeType > existingChangeType)
- setStyleChange(changeType);
+ // FIXME: This should eventually be an ASSERT.
+ if (document().inRenderTreeUpdate())
+ return;
+
+ // FIXME: Why the second condition?
+ bool markAncestors = styleValidity() == Style::Validity::Valid || validity == Style::Validity::SubtreeAndRenderersInvalid;
+
+ adjustStyleValidity(validity, mode);
- if (existingChangeType == NoStyleChange || changeType == ReconstructRenderTree)
- markAncestorsWithChildNeedsStyleRecalc();
+ if (markAncestors)
+ updateAncestorsForStyleRecalc();
}
-unsigned Node::nodeIndex() const
+unsigned Node::computeNodeIndex() const
{
- Node *_tempNode = previousSibling();
- unsigned count=0;
- for ( count=0; _tempNode; count++ )
- _tempNode = _tempNode->previousSibling();
+ unsigned count = 0;
+ for (Node* sibling = previousSibling(); sibling; sibling = sibling->previousSibling())
+ ++count;
return count;
}
@@ -727,10 +841,15 @@ bool Document::shouldInvalidateNodeListAndCollectionCaches(const QualifiedName*
void Document::invalidateNodeListAndCollectionCaches(const QualifiedName* attrName)
{
- for (HashSet<LiveNodeList*>::iterator it = m_listsInvalidatedAtDocument.begin(), end = m_listsInvalidatedAtDocument.end(); it != end; ++it)
- (*it)->invalidateCache(attrName);
- for (HashSet<HTMLCollection*>::iterator it = m_collectionsInvalidatedAtDocument.begin(), end = m_collectionsInvalidatedAtDocument.end(); it != end; ++it)
- (*it)->invalidateCache(attrName);
+ Vector<LiveNodeList*, 8> lists;
+ copyToVector(m_listsInvalidatedAtDocument, lists);
+ for (auto* list : lists)
+ list->invalidateCacheForAttribute(attrName);
+
+ Vector<HTMLCollection*, 8> collections;
+ copyToVector(m_collectionsInvalidatedAtDocument, collections);
+ for (auto* collection : collections)
+ collection->invalidateCacheForAttribute(attrName);
}
void Node::invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
@@ -760,7 +879,7 @@ void Node::invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName*
NodeListsNodeData* Node::nodeLists()
{
- return hasRareData() ? rareData()->nodeLists() : 0;
+ return hasRareData() ? rareData()->nodeLists() : nullptr;
}
void Node::clearNodeLists()
@@ -768,41 +887,36 @@ void Node::clearNodeLists()
rareData()->clearNodeLists();
}
-void Node::checkSetPrefix(const AtomicString& prefix, ExceptionCode& ec)
+ExceptionOr<void> Node::checkSetPrefix(const AtomicString& prefix)
{
// Perform error checking as required by spec for setting Node.prefix. Used by
// Element::setPrefix() and Attr::setPrefix()
- if (!prefix.isEmpty() && !Document::isValidName(prefix)) {
- ec = INVALID_CHARACTER_ERR;
- return;
- }
-
- if (isReadOnlyNode()) {
- ec = NO_MODIFICATION_ALLOWED_ERR;
- return;
- }
+ if (!prefix.isEmpty() && !Document::isValidName(prefix))
+ return Exception { INVALID_CHARACTER_ERR };
// FIXME: Raise NAMESPACE_ERR if prefix is malformed per the Namespaces in XML specification.
- const AtomicString& nodeNamespaceURI = namespaceURI();
- if ((nodeNamespaceURI.isEmpty() && !prefix.isEmpty())
- || (prefix == xmlAtom && nodeNamespaceURI != XMLNames::xmlNamespaceURI)) {
- ec = NAMESPACE_ERR;
- return;
- }
+ auto& namespaceURI = this->namespaceURI();
+ if (namespaceURI.isEmpty() && !prefix.isEmpty())
+ return Exception { NAMESPACE_ERR };
+ if (prefix == xmlAtom && namespaceURI != XMLNames::xmlNamespaceURI)
+ return Exception { NAMESPACE_ERR };
+
// Attribute-specific checks are in Attr::setPrefix().
+
+ return { };
}
-bool Node::isDescendantOf(const Node* other) const
+bool Node::isDescendantOf(const Node& other) const
{
// Return true if other is an ancestor of this, otherwise false
- if (!other || !other->hasChildNodes() || inDocument() != other->inDocument())
+ if (!other.hasChildNodes() || isConnected() != other.isConnected())
return false;
- if (other->isDocumentNode())
- return &document() == other && !isDocumentNode() && inDocument();
- for (const ContainerNode* n = parentNode(); n; n = n->parentNode()) {
- if (n == other)
+ if (other.isDocumentNode())
+ return &document() == &other && !isDocumentNode() && isConnected();
+ for (const auto* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
+ if (ancestor == &other)
return true;
}
return false;
@@ -812,19 +926,19 @@ bool Node::isDescendantOrShadowDescendantOf(const Node* other) const
{
if (!other)
return false;
- if (isDescendantOf(other))
+ if (isDescendantOf(*other))
return true;
const Node* shadowAncestorNode = deprecatedShadowAncestorNode();
if (!shadowAncestorNode)
return false;
- return shadowAncestorNode == other || shadowAncestorNode->isDescendantOf(other);
+ return shadowAncestorNode == other || shadowAncestorNode->isDescendantOf(*other);
}
bool Node::contains(const Node* node) const
{
if (!node)
return false;
- return this == node || node->isDescendantOf(this);
+ return this == node || node->isDescendantOf(*this);
}
bool Node::containsIncludingShadowDOM(const Node* node) const
@@ -838,7 +952,6 @@ bool Node::containsIncludingShadowDOM(const Node* node) const
bool Node::containsIncludingHostElements(const Node* node) const
{
-#if ENABLE(TEMPLATE_ELEMENT)
while (node) {
if (node == this)
return true;
@@ -848,14 +961,11 @@ bool Node::containsIncludingHostElements(const Node* node) const
node = node->parentOrShadowHostNode();
}
return false;
-#else
- return containsIncludingShadowDOM(node);
-#endif
}
Node* Node::pseudoAwarePreviousSibling() const
{
- Element* parentOrHost = isPseudoElement() ? toPseudoElement(this)->hostElement() : parentElement();
+ Element* parentOrHost = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentElement();
if (parentOrHost && !previousSibling()) {
if (isAfterPseudoElement() && parentOrHost->lastChild())
return parentOrHost->lastChild();
@@ -867,7 +977,7 @@ Node* Node::pseudoAwarePreviousSibling() const
Node* Node::pseudoAwareNextSibling() const
{
- Element* parentOrHost = isPseudoElement() ? toPseudoElement(this)->hostElement() : parentElement();
+ Element* parentOrHost = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentElement();
if (parentOrHost && !nextSibling()) {
if (isBeforePseudoElement() && parentOrHost->firstChild())
return parentOrHost->firstChild();
@@ -879,14 +989,14 @@ Node* Node::pseudoAwareNextSibling() const
Node* Node::pseudoAwareFirstChild() const
{
- if (isElementNode()) {
- const Element* currentElement = toElement(this);
- Node* first = currentElement->beforePseudoElement();
+ if (is<Element>(*this)) {
+ const Element& currentElement = downcast<Element>(*this);
+ Node* first = currentElement.beforePseudoElement();
if (first)
return first;
- first = currentElement->firstChild();
+ first = currentElement.firstChild();
if (!first)
- first = currentElement->afterPseudoElement();
+ first = currentElement.afterPseudoElement();
return first;
}
return firstChild();
@@ -894,26 +1004,25 @@ Node* Node::pseudoAwareFirstChild() const
Node* Node::pseudoAwareLastChild() const
{
- if (isElementNode()) {
- const Element* currentElement = toElement(this);
- Node* last = currentElement->afterPseudoElement();
+ if (is<Element>(*this)) {
+ const Element& currentElement = downcast<Element>(*this);
+ Node* last = currentElement.afterPseudoElement();
if (last)
return last;
- last = currentElement->lastChild();
+ last = currentElement.lastChild();
if (!last)
- last = currentElement->beforePseudoElement();
+ last = currentElement.beforePseudoElement();
return last;
}
return lastChild();
}
-RenderStyle* Node::computedStyle(PseudoId pseudoElementSpecifier)
+const RenderStyle* Node::computedStyle(PseudoId pseudoElementSpecifier)
{
- for (Node* node = this; node; node = node->parentOrShadowHostNode()) {
- if (node->isElementNode())
- return toElement(node)->computedStyle(pseudoElementSpecifier);
- }
- return nullptr;
+ auto* composedParent = composedTreeAncestors(*this).first();
+ if (!composedParent)
+ return nullptr;
+ return composedParent->computedStyle(pseudoElementSpecifier);
}
int Node::maxCharacterOffset() const
@@ -942,22 +1051,108 @@ bool Node::canStartSelection() const
Element* Node::shadowHost() const
{
if (ShadowRoot* root = containingShadowRoot())
- return root->hostElement();
- return 0;
+ return root->host();
+ return nullptr;
}
Node* Node::deprecatedShadowAncestorNode() const
{
if (ShadowRoot* root = containingShadowRoot())
- return root->hostElement();
+ return root->host();
return const_cast<Node*>(this);
}
ShadowRoot* Node::containingShadowRoot() const
{
- ContainerNode* root = treeScope().rootNode();
- return root && root->isShadowRoot() ? toShadowRoot(root) : 0;
+ ContainerNode& root = treeScope().rootNode();
+ return is<ShadowRoot>(root) ? downcast<ShadowRoot>(&root) : nullptr;
+}
+
+#if !ASSERT_DISABLED
+// https://dom.spec.whatwg.org/#concept-closed-shadow-hidden
+static bool isClosedShadowHiddenUsingSpecDefinition(const Node& A, const Node& B)
+{
+ return A.isInShadowTree()
+ && !A.rootNode().containsIncludingShadowDOM(&B)
+ && (A.containingShadowRoot()->mode() != ShadowRootMode::Open || isClosedShadowHiddenUsingSpecDefinition(*A.shadowHost(), B));
+}
+#endif
+
+// http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node
+bool Node::isClosedShadowHidden(const Node& otherNode) const
+{
+ // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small.
+ Vector<TreeScope*, 8> ancestorScopesOfThisNode;
+
+ for (auto* scope = &treeScope(); scope; scope = scope->parentTreeScope())
+ ancestorScopesOfThisNode.append(scope);
+
+ for (auto* treeScopeThatCanAccessOtherNode = &otherNode.treeScope(); treeScopeThatCanAccessOtherNode; treeScopeThatCanAccessOtherNode = treeScopeThatCanAccessOtherNode->parentTreeScope()) {
+ for (auto* scope : ancestorScopesOfThisNode) {
+ if (scope == treeScopeThatCanAccessOtherNode) {
+ ASSERT(!isClosedShadowHiddenUsingSpecDefinition(otherNode, *this));
+ return false; // treeScopeThatCanAccessOtherNode is a shadow-including inclusive ancestor of this node.
+ }
+ }
+ auto& root = treeScopeThatCanAccessOtherNode->rootNode();
+ if (is<ShadowRoot>(root) && downcast<ShadowRoot>(root).mode() != ShadowRootMode::Open)
+ break;
+ }
+
+ ASSERT(isClosedShadowHiddenUsingSpecDefinition(otherNode, *this));
+ return true;
+}
+
+static inline ShadowRoot* parentShadowRoot(const Node& node)
+{
+ if (auto* parent = node.parentElement())
+ return parent->shadowRoot();
+ return nullptr;
+}
+
+HTMLSlotElement* Node::assignedSlot() const
+{
+ if (auto* shadowRoot = parentShadowRoot(*this))
+ return shadowRoot->findAssignedSlot(*this);
+ return nullptr;
+}
+
+HTMLSlotElement* Node::assignedSlotForBindings() const
+{
+ auto* shadowRoot = parentShadowRoot(*this);
+ if (shadowRoot && shadowRoot->mode() == ShadowRootMode::Open)
+ return shadowRoot->findAssignedSlot(*this);
+ return nullptr;
+}
+
+ContainerNode* Node::parentInComposedTree() const
+{
+ ASSERT(isMainThreadOrGCThread());
+ if (auto* slot = assignedSlot())
+ return slot;
+ if (is<ShadowRoot>(*this))
+ return downcast<ShadowRoot>(*this).host();
+ return parentNode();
+}
+
+Element* Node::parentElementInComposedTree() const
+{
+ if (auto* slot = assignedSlot())
+ return slot;
+ if (auto* parent = parentNode()) {
+ if (is<ShadowRoot>(*parent))
+ return downcast<ShadowRoot>(*parent).host();
+ if (is<Element>(*parent))
+ return downcast<Element>(parent);
+ }
+ return nullptr;
+}
+
+bool Node::isInUserAgentShadowTree() const
+{
+ auto* shadowRoot = containingShadowRoot();
+ return shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent;
}
Node* Node::nonBoundaryShadowTreeRootNode()
@@ -978,45 +1173,70 @@ Node* Node::nonBoundaryShadowTreeRootNode()
ContainerNode* Node::nonShadowBoundaryParentNode() const
{
ContainerNode* parent = parentNode();
- return parent && !parent->isShadowRoot() ? parent : 0;
+ return parent && !parent->isShadowRoot() ? parent : nullptr;
}
Element* Node::parentOrShadowHostElement() const
{
ContainerNode* parent = parentOrShadowHostNode();
if (!parent)
- return 0;
+ return nullptr;
+
+ if (is<ShadowRoot>(*parent))
+ return downcast<ShadowRoot>(*parent).host();
+
+ if (!is<Element>(*parent))
+ return nullptr;
+
+ return downcast<Element>(parent);
+}
- if (parent->isShadowRoot())
- return toShadowRoot(parent)->hostElement();
+Node& Node::rootNode() const
+{
+ if (isInTreeScope())
+ return treeScope().rootNode();
- if (!parent->isElementNode())
- return 0;
+ Node* node = const_cast<Node*>(this);
+ Node* highest = node;
+ for (; node; node = node->parentNode())
+ highest = node;
+ return *highest;
+}
- return toElement(parent);
+// https://dom.spec.whatwg.org/#concept-shadow-including-root
+Node& Node::shadowIncludingRoot() const
+{
+ auto& root = rootNode();
+ if (!is<ShadowRoot>(root))
+ return root;
+ auto* host = downcast<ShadowRoot>(root).host();
+ return host ? host->shadowIncludingRoot() : root;
}
-Node* Node::insertionParentForBinding() const
+Node& Node::getRootNode(const GetRootNodeOptions& options) const
{
- return findInsertionPointOf(this);
+ return options.composed ? shadowIncludingRoot() : rootNode();
}
Node::InsertionNotificationRequest Node::insertedInto(ContainerNode& insertionPoint)
{
- ASSERT(insertionPoint.inDocument() || isContainerNode());
- if (insertionPoint.inDocument())
- setFlag(InDocumentFlag);
+ ASSERT(insertionPoint.isConnected() || isContainerNode());
+ if (insertionPoint.isConnected())
+ setFlag(IsConnectedFlag);
if (parentOrShadowHostNode()->isInShadowTree())
setFlag(IsInShadowTreeFlag);
+
+ invalidateStyle(Style::Validity::SubtreeAndRenderersInvalid);
+
return InsertionDone;
}
void Node::removedFrom(ContainerNode& insertionPoint)
{
- ASSERT(insertionPoint.inDocument() || isContainerNode());
- if (insertionPoint.inDocument())
- clearFlag(InDocumentFlag);
- if (isInShadowTree() && !treeScope().rootNode()->isShadowRoot())
+ ASSERT(insertionPoint.isConnected() || isContainerNode());
+ if (insertionPoint.isConnected())
+ clearFlag(IsConnectedFlag);
+ if (isInShadowTree() && !treeScope().rootNode().isShadowRoot())
clearFlag(IsInShadowTreeFlag);
}
@@ -1026,23 +1246,13 @@ bool Node::isRootEditableElement() const
|| !parentNode()->isElementNode() || hasTagName(bodyTag));
}
-Element* Node::rootEditableElement(EditableType editableType) const
-{
- if (editableType == HasEditableAXRole) {
- if (AXObjectCache* cache = document().existingAXObjectCache())
- return const_cast<Element*>(cache->rootAXEditableElement(this));
- }
-
- return rootEditableElement();
-}
-
Element* Node::rootEditableElement() const
{
- Element* result = 0;
- for (Node* n = const_cast<Node*>(this); n && n->hasEditableStyle(); n = n->parentNode()) {
- if (n->isElementNode())
- result = toElement(n);
- if (n->hasTagName(bodyTag))
+ Element* result = nullptr;
+ for (Node* node = const_cast<Node*>(this); node && node->hasEditableStyle(); node = node->parentNode()) {
+ if (is<Element>(*node))
+ result = downcast<Element>(node);
+ if (is<HTMLBodyElement>(*node))
break;
}
return result;
@@ -1056,9 +1266,10 @@ Document* Node::ownerDocument() const
return document == this ? nullptr : document;
}
-URL Node::baseURI() const
+const URL& Node::baseURI() const
{
- return parentNode() ? parentNode()->baseURI() : URL();
+ auto& url = document().baseURL();
+ return url.isNull() ? blankURL() : url;
}
bool Node::isEqualNode(Node* other) const
@@ -1070,23 +1281,58 @@ bool Node::isEqualNode(Node* other) const
if (nodeType != other->nodeType())
return false;
- if (nodeName() != other->nodeName())
- return false;
-
- if (localName() != other->localName())
- return false;
-
- if (namespaceURI() != other->namespaceURI())
- return false;
-
- if (prefix() != other->prefix())
- return false;
-
- if (nodeValue() != other->nodeValue())
- return false;
-
- if (isElementNode() && !toElement(this)->hasEquivalentAttributes(toElement(other)))
- return false;
+ switch (nodeType) {
+ case Node::DOCUMENT_TYPE_NODE: {
+ auto& thisDocType = downcast<DocumentType>(*this);
+ auto& otherDocType = downcast<DocumentType>(*other);
+ if (thisDocType.name() != otherDocType.name())
+ return false;
+ if (thisDocType.publicId() != otherDocType.publicId())
+ return false;
+ if (thisDocType.systemId() != otherDocType.systemId())
+ return false;
+ break;
+ }
+ case Node::ELEMENT_NODE: {
+ auto& thisElement = downcast<Element>(*this);
+ auto& otherElement = downcast<Element>(*other);
+ if (thisElement.tagQName() != otherElement.tagQName())
+ return false;
+ if (!thisElement.hasEquivalentAttributes(&otherElement))
+ return false;
+ break;
+ }
+ case Node::PROCESSING_INSTRUCTION_NODE: {
+ auto& thisProcessingInstruction = downcast<ProcessingInstruction>(*this);
+ auto& otherProcessingInstruction = downcast<ProcessingInstruction>(*other);
+ if (thisProcessingInstruction.target() != otherProcessingInstruction.target())
+ return false;
+ if (thisProcessingInstruction.data() != otherProcessingInstruction.data())
+ return false;
+ break;
+ }
+ case Node::CDATA_SECTION_NODE:
+ case Node::TEXT_NODE:
+ case Node::COMMENT_NODE: {
+ auto& thisCharacterData = downcast<CharacterData>(*this);
+ auto& otherCharacterData = downcast<CharacterData>(*other);
+ if (thisCharacterData.data() != otherCharacterData.data())
+ return false;
+ break;
+ }
+ case Node::ATTRIBUTE_NODE: {
+ auto& thisAttribute = downcast<Attr>(*this);
+ auto& otherAttribute = downcast<Attr>(*other);
+ if (thisAttribute.qualifiedName() != otherAttribute.qualifiedName())
+ return false;
+ if (thisAttribute.value() != otherAttribute.value())
+ return false;
+ break;
+ }
+ case Node::DOCUMENT_NODE:
+ case Node::DOCUMENT_FRAGMENT_NODE:
+ break;
+ }
Node* child = firstChild();
Node* otherChild = other->firstChild();
@@ -1102,184 +1348,106 @@ bool Node::isEqualNode(Node* other) const
if (otherChild)
return false;
- if (nodeType == DOCUMENT_TYPE_NODE) {
- const DocumentType* documentTypeThis = static_cast<const DocumentType*>(this);
- const DocumentType* documentTypeOther = static_cast<const DocumentType*>(other);
-
- if (documentTypeThis->publicId() != documentTypeOther->publicId())
- return false;
-
- if (documentTypeThis->systemId() != documentTypeOther->systemId())
- return false;
-
- if (documentTypeThis->internalSubset() != documentTypeOther->internalSubset())
- return false;
-
- // FIXME: We don't compare entities or notations because currently both are always empty.
- }
-
return true;
}
-bool Node::isDefaultNamespace(const AtomicString& namespaceURIMaybeEmpty) const
+// https://dom.spec.whatwg.org/#locate-a-namespace
+static const AtomicString& locateDefaultNamespace(const Node& node, const AtomicString& prefix)
{
- const AtomicString& namespaceURI = namespaceURIMaybeEmpty.isEmpty() ? nullAtom : namespaceURIMaybeEmpty;
+ switch (node.nodeType()) {
+ case Node::ELEMENT_NODE: {
+ auto& element = downcast<Element>(node);
+ auto& namespaceURI = element.namespaceURI();
+ if (!namespaceURI.isNull() && element.prefix() == prefix)
+ return namespaceURI;
- switch (nodeType()) {
- case ELEMENT_NODE: {
- const Element* elem = toElement(this);
-
- if (elem->prefix().isNull())
- return elem->namespaceURI() == namespaceURI;
+ if (element.hasAttributes()) {
+ for (auto& attribute : element.attributesIterator()) {
+ if (attribute.namespaceURI() != XMLNSNames::xmlnsNamespaceURI)
+ continue;
- if (elem->hasAttributes()) {
- for (const Attribute& attribute : elem->attributesIterator()) {
- if (attribute.localName() == xmlnsAtom)
- return attribute.value() == namespaceURI;
+ if ((prefix.isNull() && attribute.prefix().isNull() && attribute.localName() == xmlnsAtom) || (attribute.prefix() == xmlnsAtom && attribute.localName() == prefix)) {
+ auto& result = attribute.value();
+ return result.isEmpty() ? nullAtom : result;
}
}
-
- if (Element* ancestor = ancestorElement())
- return ancestor->isDefaultNamespace(namespaceURI);
-
- return false;
}
- case DOCUMENT_NODE:
- if (Element* de = toDocument(this)->documentElement())
- return de->isDefaultNamespace(namespaceURI);
- return false;
- case ENTITY_NODE:
- case NOTATION_NODE:
- case DOCUMENT_TYPE_NODE:
- case DOCUMENT_FRAGMENT_NODE:
- return false;
- case ATTRIBUTE_NODE: {
- const Attr* attr = static_cast<const Attr*>(this);
- if (attr->ownerElement())
- return attr->ownerElement()->isDefaultNamespace(namespaceURI);
- return false;
- }
- default:
- if (Element* ancestor = ancestorElement())
- return ancestor->isDefaultNamespace(namespaceURI);
- return false;
+ auto* parent = node.parentElement();
+ return parent ? locateDefaultNamespace(*parent, prefix) : nullAtom;
+ }
+ case Node::DOCUMENT_NODE:
+ if (auto* documentElement = downcast<Document>(node).documentElement())
+ return locateDefaultNamespace(*documentElement, prefix);
+ return nullAtom;
+ case Node::DOCUMENT_TYPE_NODE:
+ case Node::DOCUMENT_FRAGMENT_NODE:
+ return nullAtom;
+ case Node::ATTRIBUTE_NODE:
+ if (auto* ownerElement = downcast<Attr>(node).ownerElement())
+ return locateDefaultNamespace(*ownerElement, prefix);
+ return nullAtom;
+ default:
+ if (auto* parent = node.parentElement())
+ return locateDefaultNamespace(*parent, prefix);
+ return nullAtom;
}
}
-String Node::lookupPrefix(const AtomicString &namespaceURI) const
+// https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
+bool Node::isDefaultNamespace(const AtomicString& potentiallyEmptyNamespace) const
{
- // Implemented according to
- // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespacePrefixAlgo
-
- if (namespaceURI.isEmpty())
- return String();
-
- switch (nodeType()) {
- case ELEMENT_NODE:
- return lookupNamespacePrefix(namespaceURI, static_cast<const Element *>(this));
- case DOCUMENT_NODE:
- if (Element* de = toDocument(this)->documentElement())
- return de->lookupPrefix(namespaceURI);
- return String();
- case ENTITY_NODE:
- case NOTATION_NODE:
- case DOCUMENT_FRAGMENT_NODE:
- case DOCUMENT_TYPE_NODE:
- return String();
- case ATTRIBUTE_NODE: {
- const Attr *attr = static_cast<const Attr *>(this);
- if (attr->ownerElement())
- return attr->ownerElement()->lookupPrefix(namespaceURI);
- return String();
- }
- default:
- if (Element* ancestor = ancestorElement())
- return ancestor->lookupPrefix(namespaceURI);
- return String();
- }
+ const AtomicString& namespaceURI = potentiallyEmptyNamespace.isEmpty() ? nullAtom : potentiallyEmptyNamespace;
+ return locateDefaultNamespace(*this, nullAtom) == namespaceURI;
}
-String Node::lookupNamespaceURI(const String &prefix) const
+// https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
+const AtomicString& Node::lookupNamespaceURI(const AtomicString& potentiallyEmptyPrefix) const
{
- // Implemented according to
- // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespaceURIAlgo
-
- if (!prefix.isNull() && prefix.isEmpty())
- return String();
-
- switch (nodeType()) {
- case ELEMENT_NODE: {
- const Element *elem = static_cast<const Element *>(this);
-
- if (!elem->namespaceURI().isNull() && elem->prefix() == prefix)
- return elem->namespaceURI();
-
- if (elem->hasAttributes()) {
- for (const Attribute& attribute : elem->attributesIterator()) {
-
- if (attribute.prefix() == xmlnsAtom && attribute.localName() == prefix) {
- if (!attribute.value().isEmpty())
- return attribute.value();
-
- return String();
- }
- if (attribute.localName() == xmlnsAtom && prefix.isNull()) {
- if (!attribute.value().isEmpty())
- return attribute.value();
-
- return String();
- }
- }
- }
- if (Element* ancestor = ancestorElement())
- return ancestor->lookupNamespaceURI(prefix);
- return String();
- }
- case DOCUMENT_NODE:
- if (Element* de = toDocument(this)->documentElement())
- return de->lookupNamespaceURI(prefix);
- return String();
- case ENTITY_NODE:
- case NOTATION_NODE:
- case DOCUMENT_TYPE_NODE:
- case DOCUMENT_FRAGMENT_NODE:
- return String();
- case ATTRIBUTE_NODE: {
- const Attr *attr = static_cast<const Attr *>(this);
-
- if (attr->ownerElement())
- return attr->ownerElement()->lookupNamespaceURI(prefix);
- else
- return String();
- }
- default:
- if (Element* ancestor = ancestorElement())
- return ancestor->lookupNamespaceURI(prefix);
- return String();
- }
+ const AtomicString& prefix = potentiallyEmptyPrefix.isEmpty() ? nullAtom : potentiallyEmptyPrefix;
+ return locateDefaultNamespace(*this, prefix);
}
-String Node::lookupNamespacePrefix(const AtomicString &_namespaceURI, const Element *originalElement) const
+// https://dom.spec.whatwg.org/#locate-a-namespace-prefix
+static const AtomicString& locateNamespacePrefix(const Element& element, const AtomicString& namespaceURI)
{
- if (_namespaceURI.isNull())
- return String();
-
- if (originalElement->lookupNamespaceURI(prefix()) == _namespaceURI)
- return prefix();
-
- ASSERT(isElementNode());
- const Element* thisElement = toElement(this);
- if (thisElement->hasAttributes()) {
- for (const Attribute& attribute : thisElement->attributesIterator()) {
- if (attribute.prefix() == xmlnsAtom && attribute.value() == _namespaceURI
- && originalElement->lookupNamespaceURI(attribute.localName()) == _namespaceURI)
+ if (element.namespaceURI() == namespaceURI)
+ return element.prefix();
+
+ if (element.hasAttributes()) {
+ for (auto& attribute : element.attributesIterator()) {
+ if (attribute.prefix() == xmlnsAtom && attribute.value() == namespaceURI)
return attribute.localName();
}
}
+ auto* parent = element.parentElement();
+ return parent ? locateNamespacePrefix(*parent, namespaceURI) : nullAtom;
+}
+
+// https://dom.spec.whatwg.org/#dom-node-lookupprefix
+const AtomicString& Node::lookupPrefix(const AtomicString& namespaceURI) const
+{
+ if (namespaceURI.isEmpty())
+ return nullAtom;
- if (Element* ancestor = ancestorElement())
- return ancestor->lookupNamespacePrefix(_namespaceURI, originalElement);
- return String();
+ switch (nodeType()) {
+ case ELEMENT_NODE:
+ return locateNamespacePrefix(downcast<Element>(*this), namespaceURI);
+ case DOCUMENT_NODE:
+ if (auto* documentElement = downcast<Document>(*this).documentElement())
+ return locateNamespacePrefix(*documentElement, namespaceURI);
+ return nullAtom;
+ case DOCUMENT_FRAGMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ return nullAtom;
+ case ATTRIBUTE_NODE:
+ if (auto* ownerElement = downcast<Attr>(*this).ownerElement())
+ return locateNamespacePrefix(*ownerElement, namespaceURI);
+ return nullAtom;
+ default:
+ if (auto* parent = parentElement())
+ return locateNamespacePrefix(*parent, namespaceURI);
+ return nullAtom;
+ }
}
static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool& isNullString, StringBuilder& content)
@@ -1305,8 +1473,6 @@ static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool&
}
FALLTHROUGH;
case Node::ATTRIBUTE_NODE:
- case Node::ENTITY_NODE:
- case Node::ENTITY_REFERENCE_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
isNullString = false;
for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
@@ -1318,8 +1484,6 @@ static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool&
case Node::DOCUMENT_NODE:
case Node::DOCUMENT_TYPE_NODE:
- case Node::NOTATION_NODE:
- case Node::XPATH_NAMESPACE_NODE:
break;
}
}
@@ -1332,83 +1496,78 @@ String Node::textContent(bool convertBRsToNewlines) const
return isNullString ? String() : content.toString();
}
-void Node::setTextContent(const String& text, ExceptionCode& ec)
+ExceptionOr<void> Node::setTextContent(const String& text)
{
switch (nodeType()) {
- case TEXT_NODE:
- case CDATA_SECTION_NODE:
- case COMMENT_NODE:
- case PROCESSING_INSTRUCTION_NODE:
- setNodeValue(text, ec);
- return;
- case ELEMENT_NODE:
- case ATTRIBUTE_NODE:
- case ENTITY_NODE:
- case ENTITY_REFERENCE_NODE:
- case DOCUMENT_FRAGMENT_NODE: {
- Ref<ContainerNode> container(*toContainerNode(this));
- ChildListMutationScope mutation(container.get());
- container->removeChildren();
- if (!text.isEmpty())
- container->appendChild(document().createTextNode(text), ec);
- return;
- }
- case DOCUMENT_NODE:
- case DOCUMENT_TYPE_NODE:
- case NOTATION_NODE:
- case XPATH_NAMESPACE_NODE:
- // Do nothing.
- return;
+ case ATTRIBUTE_NODE:
+ case TEXT_NODE:
+ case CDATA_SECTION_NODE:
+ case COMMENT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
+ return setNodeValue(text);
+ case ELEMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE: {
+ auto& container = downcast<ContainerNode>(*this);
+ if (text.isEmpty())
+ container.replaceAllChildren(nullptr);
+ else
+ container.replaceAllChildren(document().createTextNode(text));
+ return { };
+ }
+ case DOCUMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ // Do nothing.
+ return { };
}
ASSERT_NOT_REACHED();
+ return { };
}
-Element* Node::ancestorElement() const
+bool Node::offsetInCharacters() const
{
- // In theory, there can be EntityReference nodes between elements, but this is currently not supported.
- for (ContainerNode* n = parentNode(); n; n = n->parentNode()) {
- if (n->isElementNode())
- return toElement(n);
- }
- return 0;
+ return false;
}
-bool Node::offsetInCharacters() const
+static SHA1::Digest hashPointer(void* pointer)
{
- return false;
+ SHA1 sha1;
+ sha1.addBytes(reinterpret_cast<const uint8_t*>(&pointer), sizeof(pointer));
+ SHA1::Digest digest;
+ sha1.computeHash(digest);
+ return digest;
}
-static inline unsigned short compareDetachedElementsPosition(Node* firstNode, Node* secondNode)
+static inline unsigned short compareDetachedElementsPosition(Node& firstNode, Node& secondNode)
{
// If the 2 nodes are not in the same tree, return the result of adding DOCUMENT_POSITION_DISCONNECTED,
// DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, and either DOCUMENT_POSITION_PRECEDING or
// DOCUMENT_POSITION_FOLLOWING, with the constraint that this is to be consistent. Whether to return
- // DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING is implemented here via pointer
- // comparison.
- // See step 3 in http://www.w3.org/TR/2012/WD-dom-20121206/#dom-node-comparedocumentposition
- unsigned short direction = (firstNode > secondNode) ? Node::DOCUMENT_POSITION_PRECEDING : Node::DOCUMENT_POSITION_FOLLOWING;
+ // DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING is implemented by comparing cryptographic
+ // hashes of Node pointers.
+ // See step 3 in https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
+ SHA1::Digest firstHash = hashPointer(&firstNode);
+ SHA1::Digest secondHash = hashPointer(&secondNode);
+
+ unsigned short direction = memcmp(firstHash.data(), secondHash.data(), SHA1::hashSize) > 0 ? Node::DOCUMENT_POSITION_PRECEDING : Node::DOCUMENT_POSITION_FOLLOWING;
+
return Node::DOCUMENT_POSITION_DISCONNECTED | Node::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | direction;
}
-unsigned short Node::compareDocumentPosition(Node* otherNode)
+unsigned short Node::compareDocumentPosition(Node& otherNode)
{
- // It is not clear what should be done if |otherNode| is 0.
- if (!otherNode)
- return DOCUMENT_POSITION_DISCONNECTED;
-
- if (otherNode == this)
+ if (&otherNode == this)
return DOCUMENT_POSITION_EQUIVALENT;
- Attr* attr1 = isAttributeNode() ? toAttr(this) : nullptr;
- Attr* attr2 = otherNode->isAttributeNode() ? toAttr(otherNode) : nullptr;
+ Attr* attr1 = is<Attr>(*this) ? downcast<Attr>(this) : nullptr;
+ Attr* attr2 = is<Attr>(otherNode) ? &downcast<Attr>(otherNode) : nullptr;
Node* start1 = attr1 ? attr1->ownerElement() : this;
- Node* start2 = attr2 ? attr2->ownerElement() : otherNode;
+ Node* start2 = attr2 ? attr2->ownerElement() : &otherNode;
// If either of start1 or start2 is null, then we are disconnected, since one of the nodes is
// an orphaned attribute node.
if (!start1 || !start2)
- return compareDetachedElementsPosition(this, otherNode);
+ return compareDetachedElementsPosition(*this, otherNode);
Vector<Node*, 16> chain1;
Vector<Node*, 16> chain2;
@@ -1439,10 +1598,9 @@ unsigned short Node::compareDocumentPosition(Node* otherNode)
// If one node is in the document and the other is not, we must be disconnected.
// If the nodes have different owning documents, they must be disconnected. Note that we avoid
- // comparing Attr nodes here, since they return false from inDocument() all the time (which seems like a bug).
- if (start1->inDocument() != start2->inDocument() ||
- &start1->treeScope() != &start2->treeScope())
- return compareDetachedElementsPosition(this, otherNode);
+ // comparing Attr nodes here, since they return false from isConnected() all the time (which seems like a bug).
+ if (start1->isConnected() != start2->isConnected() || &start1->treeScope() != &start2->treeScope())
+ return compareDetachedElementsPosition(*this, otherNode);
// We need to find a common ancestor container, and then compare the indices of the two immediate children.
Node* current;
@@ -1456,7 +1614,7 @@ unsigned short Node::compareDocumentPosition(Node* otherNode)
// If the two elements don't have a common root, they're not in the same tree.
if (chain1[index1 - 1] != chain2[index2 - 1])
- return compareDetachedElementsPosition(this, otherNode);
+ return compareDetachedElementsPosition(*this, otherNode);
// Walk the two chains backwards and look for the first difference.
for (unsigned i = std::min(index1, index2); i; --i) {
@@ -1497,8 +1655,7 @@ FloatPoint Node::convertToPage(const FloatPoint& p) const
return renderer()->localToAbsolute(p, UseTransforms);
// Otherwise go up the tree looking for a renderer
- Element *parent = ancestorElement();
- if (parent)
+ if (auto* parent = parentElement())
return parent->convertToPage(p);
// No parent - no conversion needed
@@ -1512,22 +1669,21 @@ FloatPoint Node::convertFromPage(const FloatPoint& p) const
return renderer()->absoluteToLocal(p, UseTransforms);
// Otherwise go up the tree looking for a renderer
- Element *parent = ancestorElement();
- if (parent)
+ if (auto* parent = parentElement())
return parent->convertFromPage(p);
// No parent - no conversion needed
return p;
}
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
static void appendAttributeDesc(const Node* node, StringBuilder& stringBuilder, const QualifiedName& name, const char* attrDesc)
{
- if (!node->isElementNode())
+ if (!is<Element>(*node))
return;
- String attr = toElement(node)->getAttribute(name);
+ const AtomicString& attr = downcast<Element>(*node).getAttribute(name);
if (attr.isEmpty())
return;
@@ -1548,7 +1704,7 @@ void Node::showNode(const char* prefix) const
StringBuilder attrs;
appendAttributeDesc(this, attrs, classAttr, " CLASS=");
appendAttributeDesc(this, attrs, styleAttr, " STYLE=");
- fprintf(stderr, "%s%s\t%p%s\n", prefix, nodeName().utf8().data(), this, attrs.toString().utf8().data());
+ fprintf(stderr, "%s%s\t%p (renderer %p) %s%s%s\n", prefix, nodeName().utf8().data(), this, renderer(), attrs.toString().utf8().data(), needsStyleRecalc() ? " (needs style recalc)" : "", childNeedsStyleRecalc() ? " (child needs style recalc)" : "");
}
}
@@ -1567,9 +1723,9 @@ void Node::showNodePathForThis() const
}
for (unsigned index = chain.size(); index > 0; --index) {
const Node* node = chain[index - 1];
- if (node->isShadowRoot()) {
+ if (is<ShadowRoot>(*node)) {
int count = 0;
- for (const ShadowRoot* shadowRoot = toShadowRoot(node); shadowRoot && shadowRoot != node; shadowRoot = shadowRoot->shadowRoot())
+ for (const ShadowRoot* shadowRoot = downcast<ShadowRoot>(node); shadowRoot && shadowRoot != node; shadowRoot = shadowRoot->shadowRoot())
++count;
fprintf(stderr, "/#shadow-root[%d]", count);
continue;
@@ -1579,8 +1735,8 @@ void Node::showNodePathForThis() const
case ELEMENT_NODE: {
fprintf(stderr, "/%s", node->nodeName().utf8().data());
- const Element* element = toElement(node);
- const AtomicString& idattr = element->getIdAttribute();
+ const Element& element = downcast<Element>(*node);
+ const AtomicString& idattr = element.getIdAttribute();
bool hasIdAttr = !idattr.isNull() && !idattr.isEmpty();
if (node->previousSibling() || node->nextSibling()) {
int count = 0;
@@ -1610,7 +1766,7 @@ void Node::showNodePathForThis() const
static void traverseTreeAndMark(const String& baseIndent, const Node* rootNode, const Node* markedNode1, const char* markedLabel1, const Node* markedNode2, const char* markedLabel2)
{
- for (const Node* node = rootNode; node; node = NodeTraversal::next(node)) {
+ for (const Node* node = rootNode; node; node = NodeTraversal::next(*node)) {
if (node == markedNode1)
fprintf(stderr, "%s", markedLabel1);
if (node == markedNode2)
@@ -1688,26 +1844,23 @@ void Node::showTreeForThisAcrossFrame() const
showSubTreeAcrossFrame(rootNode, this, "");
}
-#endif
+#endif // ENABLE(TREE_DEBUGGING)
// --------
void NodeListsNodeData::invalidateCaches(const QualifiedName* attrName)
{
- for (auto it = m_atomicNameCaches.begin(), end = m_atomicNameCaches.end(); it != end; ++it)
- it->value->invalidateCache(attrName);
-
- for (auto it = m_nameCaches.begin(), end = m_nameCaches.end(); it != end; ++it)
- it->value->invalidateCache(attrName);
+ for (auto& atomicName : m_atomicNameCaches)
+ atomicName.value->invalidateCacheForAttribute(attrName);
- for (auto it = m_cachedCollections.begin(), end = m_cachedCollections.end(); it != end; ++it)
- it->value->invalidateCache(attrName);
+ for (auto& collection : m_cachedCollections)
+ collection.value->invalidateCacheForAttribute(attrName);
if (attrName)
return;
- for (auto it = m_tagNodeListCacheNS.begin(), end = m_tagNodeListCacheNS.end(); it != end; ++it)
- it->value->invalidateCache();
+ for (auto& tagCollection : m_tagCollectionNSCache)
+ tagCollection.value->invalidateCacheForAttribute(nullptr);
}
void Node::getSubresourceURLs(ListHashSet<URL>& urls) const
@@ -1717,15 +1870,15 @@ void Node::getSubresourceURLs(ListHashSet<URL>& urls) const
Element* Node::enclosingLinkEventParentOrSelf()
{
- for (Node* node = this; node; node = node->parentOrShadowHostNode()) {
+ for (Node* node = this; node; node = node->parentInComposedTree()) {
// For imagemaps, the enclosing link element is the associated area element not the image itself.
// So we don't let images be the enclosing link element, even though isLink sometimes returns
// true for them.
- if (node->isLink() && !isHTMLImageElement(node))
- return toElement(node);
+ if (node->isLink() && !is<HTMLImageElement>(*node))
+ return downcast<Element>(node);
}
- return 0;
+ return nullptr;
}
EventTargetInterface Node::eventTargetInterface() const
@@ -1733,69 +1886,58 @@ EventTargetInterface Node::eventTargetInterface() const
return NodeEventTargetInterfaceType;
}
-void Node::didMoveToNewDocument(Document* oldDocument)
+void Node::didMoveToNewDocument(Document& oldDocument)
{
TreeScopeAdopter::ensureDidMoveToNewDocumentWasCalled(oldDocument);
- if (const EventTargetData* eventTargetData = this->eventTargetData()) {
- const EventListenerMap& listenerMap = eventTargetData->eventListenerMap;
- if (!listenerMap.isEmpty()) {
- Vector<AtomicString> types = listenerMap.eventTypes();
- for (unsigned i = 0; i < types.size(); ++i)
- document().addListenerTypeIfNeeded(types[i]);
+ if (auto* eventTargetData = this->eventTargetData()) {
+ if (!eventTargetData->eventListenerMap.isEmpty()) {
+ for (auto& type : eventTargetData->eventListenerMap.eventTypes())
+ document().addListenerTypeIfNeeded(type);
}
}
- if (AXObjectCache::accessibilityEnabled() && oldDocument)
- if (AXObjectCache* cache = oldDocument->existingAXObjectCache())
+ if (AXObjectCache::accessibilityEnabled()) {
+ if (auto* cache = oldDocument.existingAXObjectCache())
cache->remove(this);
-
- const EventListenerVector& mousewheelListeners = getEventListeners(eventNames().mousewheelEvent);
- for (size_t i = 0; i < mousewheelListeners.size(); ++i) {
- oldDocument->didRemoveWheelEventHandler();
- document().didAddWheelEventHandler();
}
- const EventListenerVector& wheelListeners = getEventListeners(eventNames().wheelEvent);
- for (size_t i = 0; i < wheelListeners.size(); ++i) {
- oldDocument->didRemoveWheelEventHandler();
- document().didAddWheelEventHandler();
+ unsigned numWheelEventHandlers = eventListeners(eventNames().mousewheelEvent).size() + eventListeners(eventNames().wheelEvent).size();
+ for (unsigned i = 0; i < numWheelEventHandlers; ++i) {
+ oldDocument.didRemoveWheelEventHandler(*this);
+ document().didAddWheelEventHandler(*this);
}
- Vector<AtomicString> touchEventNames = eventNames().touchEventNames();
- for (size_t i = 0; i < touchEventNames.size(); ++i) {
- const EventListenerVector& listeners = getEventListeners(touchEventNames[i]);
- for (size_t j = 0; j < listeners.size(); ++j) {
- oldDocument->didRemoveTouchEventHandler(this);
- document().didAddTouchEventHandler(this);
- }
+ unsigned numTouchEventHandlers = 0;
+ for (auto& name : eventNames().touchEventNames())
+ numTouchEventHandlers += eventListeners(name).size();
+
+ for (unsigned i = 0; i < numTouchEventHandlers; ++i) {
+ oldDocument.didRemoveTouchEventHandler(*this);
+ document().didAddTouchEventHandler(*this);
}
- if (Vector<OwnPtr<MutationObserverRegistration>>* registry = mutationObserverRegistry()) {
- for (size_t i = 0; i < registry->size(); ++i) {
- document().addMutationObserverTypes(registry->at(i)->mutationTypes());
- }
+ if (auto* registry = mutationObserverRegistry()) {
+ for (auto& registration : *registry)
+ document().addMutationObserverTypes(registration->mutationTypes());
}
- if (HashSet<MutationObserverRegistration*>* transientRegistry = transientMutationObserverRegistry()) {
- for (HashSet<MutationObserverRegistration*>::iterator iter = transientRegistry->begin(); iter != transientRegistry->end(); ++iter) {
- document().addMutationObserverTypes((*iter)->mutationTypes());
- }
+ if (auto* transientRegistry = transientMutationObserverRegistry()) {
+ for (auto& registration : *transientRegistry)
+ document().addMutationObserverTypes(registration->mutationTypes());
}
}
-static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, PassRefPtr<EventListener> prpListener, bool useCapture)
+static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, Ref<EventListener>&& listener, const EventTarget::AddEventListenerOptions& options)
{
- RefPtr<EventListener> listener = prpListener;
-
- if (!targetNode->EventTarget::addEventListener(eventType, listener, useCapture))
+ if (!targetNode->EventTarget::addEventListener(eventType, listener.copyRef(), options))
return false;
targetNode->document().addListenerTypeIfNeeded(eventType);
- if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent)
- targetNode->document().didAddWheelEventHandler();
+ if (eventNames().isWheelEventType(eventType))
+ targetNode->document().didAddWheelEventHandler(*targetNode);
else if (eventNames().isTouchEventType(eventType))
- targetNode->document().didAddTouchEventHandler(targetNode);
+ targetNode->document().didAddTouchEventHandler(*targetNode);
#if PLATFORM(IOS)
if (targetNode == &targetNode->document() && eventType == eventNames().scrollEvent)
@@ -1806,7 +1948,7 @@ static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eve
// This code was added to address <rdar://problem/5846492> Onorientationchange event not working for document.body.
// Forward this call to addEventListener() to the window since these are window-only events.
if (eventType == eventNames().orientationchangeEvent || eventType == eventNames().resizeEvent)
- targetNode->document().domWindow()->addEventListener(eventType, listener, useCapture);
+ targetNode->document().domWindow()->addEventListener(eventType, WTFMove(listener), options);
#if ENABLE(TOUCH_EVENTS)
if (eventNames().isTouchEventType(eventType))
@@ -1816,28 +1958,28 @@ static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eve
#if ENABLE(IOS_GESTURE_EVENTS) && ENABLE(TOUCH_EVENTS)
if (eventType == eventNames().gesturestartEvent || eventType == eventNames().gesturechangeEvent || eventType == eventNames().gestureendEvent)
- targetNode->document().addTouchEventListener(targetNode);
+ targetNode->document().addTouchEventHandler(targetNode);
#endif
return true;
}
-bool Node::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
+bool Node::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
- return tryAddEventListener(this, eventType, listener, useCapture);
+ return tryAddEventListener(this, eventType, WTFMove(listener), options);
}
-static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener* listener, bool useCapture)
+static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener& listener, const EventTarget::ListenerOptions& options)
{
- if (!targetNode->EventTarget::removeEventListener(eventType, listener, useCapture))
+ if (!targetNode->EventTarget::removeEventListener(eventType, listener, options))
return false;
// FIXME: Notify Document that the listener has vanished. We need to keep track of a number of
// listeners for each type, not just a bool - see https://bugs.webkit.org/show_bug.cgi?id=33861
- if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent)
- targetNode->document().didRemoveWheelEventHandler();
+ if (eventNames().isWheelEventType(eventType))
+ targetNode->document().didRemoveWheelEventHandler(*targetNode);
else if (eventNames().isTouchEventType(eventType))
- targetNode->document().didRemoveTouchEventHandler(targetNode);
+ targetNode->document().didRemoveTouchEventHandler(*targetNode);
#if PLATFORM(IOS)
if (targetNode == &targetNode->document() && eventType == eventNames().scrollEvent)
@@ -1847,7 +1989,7 @@ static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString&
// This code was added to address <rdar://problem/5846492> Onorientationchange event not working for document.body.
// Forward this call to removeEventListener() to the window since these are window-only events.
if (eventType == eventNames().orientationchangeEvent || eventType == eventNames().resizeEvent)
- targetNode->document().domWindow()->removeEventListener(eventType, listener, useCapture);
+ targetNode->document().domWindow()->removeEventListener(eventType, listener, options);
#if ENABLE(TOUCH_EVENTS)
if (eventNames().isTouchEventType(eventType))
@@ -1857,140 +1999,149 @@ static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString&
#if ENABLE(IOS_GESTURE_EVENTS) && ENABLE(TOUCH_EVENTS)
if (eventType == eventNames().gesturestartEvent || eventType == eventNames().gesturechangeEvent || eventType == eventNames().gestureendEvent)
- targetNode->document().removeTouchEventListener(targetNode);
+ targetNode->document().removeTouchEventHandler(targetNode);
#endif
return true;
}
-bool Node::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
+bool Node::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
{
- return tryRemoveEventListener(this, eventType, listener, useCapture);
+ return tryRemoveEventListener(this, eventType, listener, options);
}
-typedef HashMap<Node*, OwnPtr<EventTargetData>> EventTargetDataMap;
+typedef HashMap<Node*, std::unique_ptr<EventTargetData>> EventTargetDataMap;
static EventTargetDataMap& eventTargetDataMap()
{
- DEFINE_STATIC_LOCAL(EventTargetDataMap, map, ());
+ static NeverDestroyed<EventTargetDataMap> map;
+
return map;
}
+static StaticLock s_eventTargetDataMapLock;
+
EventTargetData* Node::eventTargetData()
{
- return hasEventTargetData() ? eventTargetDataMap().get(this) : 0;
+ return hasEventTargetData() ? eventTargetDataMap().get(this) : nullptr;
+}
+
+EventTargetData* Node::eventTargetDataConcurrently()
+{
+ auto locker = holdLock(s_eventTargetDataMapLock);
+ return hasEventTargetData() ? eventTargetDataMap().get(this) : nullptr;
}
EventTargetData& Node::ensureEventTargetData()
{
if (hasEventTargetData())
return *eventTargetDataMap().get(this);
+
+ auto locker = holdLock(s_eventTargetDataMapLock);
setHasEventTargetData(true);
- EventTargetData* data = new EventTargetData;
- eventTargetDataMap().set(this, adoptPtr(data));
- return *data;
+ return *eventTargetDataMap().add(this, std::make_unique<EventTargetData>()).iterator->value;
}
void Node::clearEventTargetData()
{
+ auto locker = holdLock(s_eventTargetDataMapLock);
eventTargetDataMap().remove(this);
}
-Vector<OwnPtr<MutationObserverRegistration>>* Node::mutationObserverRegistry()
+Vector<std::unique_ptr<MutationObserverRegistration>>* Node::mutationObserverRegistry()
{
if (!hasRareData())
- return 0;
- NodeMutationObserverData* data = rareData()->mutationObserverData();
+ return nullptr;
+ auto* data = rareData()->mutationObserverData();
if (!data)
- return 0;
+ return nullptr;
return &data->registry;
}
HashSet<MutationObserverRegistration*>* Node::transientMutationObserverRegistry()
{
if (!hasRareData())
- return 0;
- NodeMutationObserverData* data = rareData()->mutationObserverData();
+ return nullptr;
+ auto* data = rareData()->mutationObserverData();
if (!data)
- return 0;
+ return nullptr;
return &data->transientRegistry;
}
-template<typename Registry>
-static inline void collectMatchingObserversForMutation(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, Registry* registry, Node* target, MutationObserver::MutationType type, const QualifiedName* attributeName)
+template<typename Registry> static inline void collectMatchingObserversForMutation(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, Registry* registry, Node& target, MutationObserver::MutationType type, const QualifiedName* attributeName)
{
if (!registry)
return;
- for (typename Registry::iterator iter = registry->begin(); iter != registry->end(); ++iter) {
- const MutationObserverRegistration& registration = **iter;
- if (registration.shouldReceiveMutationFrom(target, type, attributeName)) {
- MutationRecordDeliveryOptions deliveryOptions = registration.deliveryOptions();
- HashMap<MutationObserver*, MutationRecordDeliveryOptions>::AddResult result = observers.add(registration.observer(), deliveryOptions);
+
+ for (auto& registration : *registry) {
+ if (registration->shouldReceiveMutationFrom(target, type, attributeName)) {
+ auto deliveryOptions = registration->deliveryOptions();
+ auto result = observers.add(&registration->observer(), deliveryOptions);
if (!result.isNewEntry)
result.iterator->value |= deliveryOptions;
}
}
}
-void Node::getRegisteredMutationObserversOfType(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, MutationObserver::MutationType type, const QualifiedName* attributeName)
+HashMap<MutationObserver*, MutationRecordDeliveryOptions> Node::registeredMutationObservers(MutationObserver::MutationType type, const QualifiedName* attributeName)
{
+ HashMap<MutationObserver*, MutationRecordDeliveryOptions> result;
ASSERT((type == MutationObserver::Attributes && attributeName) || !attributeName);
- collectMatchingObserversForMutation(observers, mutationObserverRegistry(), this, type, attributeName);
- collectMatchingObserversForMutation(observers, transientMutationObserverRegistry(), this, type, attributeName);
+ collectMatchingObserversForMutation(result, mutationObserverRegistry(), *this, type, attributeName);
+ collectMatchingObserversForMutation(result, transientMutationObserverRegistry(), *this, type, attributeName);
for (Node* node = parentNode(); node; node = node->parentNode()) {
- collectMatchingObserversForMutation(observers, node->mutationObserverRegistry(), this, type, attributeName);
- collectMatchingObserversForMutation(observers, node->transientMutationObserverRegistry(), this, type, attributeName);
+ collectMatchingObserversForMutation(result, node->mutationObserverRegistry(), *this, type, attributeName);
+ collectMatchingObserversForMutation(result, node->transientMutationObserverRegistry(), *this, type, attributeName);
}
+ return result;
}
-void Node::registerMutationObserver(MutationObserver* observer, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter)
+void Node::registerMutationObserver(MutationObserver& observer, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter)
{
- MutationObserverRegistration* registration = 0;
- Vector<OwnPtr<MutationObserverRegistration>>& registry = ensureRareData().ensureMutationObserverData().registry;
- for (size_t i = 0; i < registry.size(); ++i) {
- if (registry[i]->observer() == observer) {
- registration = registry[i].get();
+ MutationObserverRegistration* registration = nullptr;
+ auto& registry = ensureRareData().ensureMutationObserverData().registry;
+
+ for (auto& candidateRegistration : registry) {
+ if (&candidateRegistration->observer() == &observer) {
+ registration = candidateRegistration.get();
registration->resetObservation(options, attributeFilter);
}
}
if (!registration) {
- registry.append(MutationObserverRegistration::create(observer, this, options, attributeFilter));
+ registry.append(std::make_unique<MutationObserverRegistration>(observer, *this, options, attributeFilter));
registration = registry.last().get();
}
document().addMutationObserverTypes(registration->mutationTypes());
}
-void Node::unregisterMutationObserver(MutationObserverRegistration* registration)
+void Node::unregisterMutationObserver(MutationObserverRegistration& registration)
{
- Vector<OwnPtr<MutationObserverRegistration>>* registry = mutationObserverRegistry();
+ auto* registry = mutationObserverRegistry();
ASSERT(registry);
if (!registry)
return;
- size_t index = registry->find(registration);
- ASSERT(index != notFound);
- if (index == notFound)
- return;
-
- registry->remove(index);
+ registry->removeFirstMatching([&registration] (auto& current) {
+ return current.get() == &registration;
+ });
}
-void Node::registerTransientMutationObserver(MutationObserverRegistration* registration)
+void Node::registerTransientMutationObserver(MutationObserverRegistration& registration)
{
- ensureRareData().ensureMutationObserverData().transientRegistry.add(registration);
+ ensureRareData().ensureMutationObserverData().transientRegistry.add(&registration);
}
-void Node::unregisterTransientMutationObserver(MutationObserverRegistration* registration)
+void Node::unregisterTransientMutationObserver(MutationObserverRegistration& registration)
{
- HashSet<MutationObserverRegistration*>* transientRegistry = transientMutationObserverRegistry();
+ auto* transientRegistry = transientMutationObserverRegistry();
ASSERT(transientRegistry);
if (!transientRegistry)
return;
- ASSERT(transientRegistry->contains(registration));
- transientRegistry->remove(registration);
+ ASSERT(transientRegistry->contains(&registration));
+ transientRegistry->remove(&registration);
}
void Node::notifyMutationObserversNodeWillDetach()
@@ -1999,15 +2150,13 @@ void Node::notifyMutationObserversNodeWillDetach()
return;
for (Node* node = parentNode(); node; node = node->parentNode()) {
- if (Vector<OwnPtr<MutationObserverRegistration>>* registry = node->mutationObserverRegistry()) {
- const size_t size = registry->size();
- for (size_t i = 0; i < size; ++i)
- registry->at(i)->observedSubtreeNodeWillDetach(this);
+ if (auto* registry = node->mutationObserverRegistry()) {
+ for (auto& registration : *registry)
+ registration->observedSubtreeNodeWillDetach(*this);
}
-
- if (HashSet<MutationObserverRegistration*>* transientRegistry = node->transientMutationObserverRegistry()) {
- for (HashSet<MutationObserverRegistration*>::iterator iter = transientRegistry->begin(); iter != transientRegistry->end(); ++iter)
- (*iter)->observedSubtreeNodeWillDetach(this);
+ if (auto* transientRegistry = node->transientMutationObserverRegistry()) {
+ for (auto* registration : *transientRegistry)
+ registration->observedSubtreeNodeWillDetach(*this);
}
}
}
@@ -2017,24 +2166,25 @@ void Node::handleLocalEvents(Event& event)
if (!hasEventTargetData())
return;
- if (isElementNode() && toElement(*this).isDisabledFormControl() && event.isMouseEvent())
+ // FIXME: Should we deliver wheel events to disabled form controls or not?
+ if (is<Element>(*this) && downcast<Element>(*this).isDisabledFormControl() && event.isMouseEvent() && !event.isWheelEvent())
return;
- fireEventListeners(&event);
+ fireEventListeners(event);
}
-void Node::dispatchScopedEvent(PassRefPtr<Event> event)
+void Node::dispatchScopedEvent(Event& event)
{
EventDispatcher::dispatchScopedEvent(*this, event);
}
-bool Node::dispatchEvent(PassRefPtr<Event> event)
+bool Node::dispatchEvent(Event& event)
{
#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)
- if (event->isTouchEvent())
- return dispatchTouchEvent(adoptRef(toTouchEvent(event.leakRef())));
+ if (is<TouchEvent>(event))
+ return dispatchTouchEvent(downcast<TouchEvent>(event));
#endif
- return EventDispatcher::dispatchEvent(this, event);
+ return EventDispatcher::dispatchEvent(*this, event);
}
void Node::dispatchSubtreeModifiedEvent()
@@ -2042,7 +2192,7 @@ void Node::dispatchSubtreeModifiedEvent()
if (isInShadowTree())
return;
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(*this));
if (!document().hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER))
return;
@@ -2053,38 +2203,30 @@ void Node::dispatchSubtreeModifiedEvent()
dispatchScopedEvent(MutationEvent::create(subtreeModifiedEventName, true));
}
-bool Node::dispatchDOMActivateEvent(int detail, PassRefPtr<Event> underlyingEvent)
+bool Node::dispatchDOMActivateEvent(int detail, Event& underlyingEvent)
{
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
- RefPtr<UIEvent> event = UIEvent::create(eventNames().DOMActivateEvent, true, true, document().defaultView(), detail);
- event->setUnderlyingEvent(underlyingEvent);
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
+ Ref<UIEvent> event = UIEvent::create(eventNames().DOMActivateEvent, true, true, document().defaultView(), detail);
+ event->setUnderlyingEvent(&underlyingEvent);
dispatchScopedEvent(event);
return event->defaultHandled();
}
#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)
-bool Node::dispatchTouchEvent(PassRefPtr<TouchEvent> event)
+bool Node::dispatchTouchEvent(TouchEvent& event)
{
- return EventDispatcher::dispatchEvent(this, event);
+ return EventDispatcher::dispatchEvent(*this, event);
}
#endif
-#if ENABLE(INDIE_UI)
-bool Node::dispatchUIRequestEvent(PassRefPtr<UIRequestEvent> event)
-{
- EventDispatcher::dispatchEvent(this, event);
- return event->defaultHandled() || event->defaultPrevented();
-}
-#endif
-
bool Node::dispatchBeforeLoadEvent(const String& sourceURL)
{
if (!document().hasListenerType(Document::BEFORELOAD_LISTENER))
return true;
- Ref<Node> protect(*this);
- RefPtr<BeforeLoadEvent> beforeLoadEvent = BeforeLoadEvent::create(sourceURL);
- dispatchEvent(beforeLoadEvent.get());
+ Ref<Node> protectedThis(*this);
+ Ref<BeforeLoadEvent> beforeLoadEvent = BeforeLoadEvent::create(sourceURL);
+ dispatchEvent(beforeLoadEvent);
return !beforeLoadEvent->defaultPrevented();
}
@@ -2093,19 +2235,20 @@ void Node::dispatchInputEvent()
dispatchScopedEvent(Event::create(eventNames().inputEvent, true, false));
}
-void Node::defaultEventHandler(Event* event)
+void Node::defaultEventHandler(Event& event)
{
- if (event->target() != this)
+ if (event.target() != this)
return;
- const AtomicString& eventType = event->type();
+ const AtomicString& eventType = event.type();
if (eventType == eventNames().keydownEvent || eventType == eventNames().keypressEvent) {
- if (event->isKeyboardEvent())
+ if (is<KeyboardEvent>(event)) {
if (Frame* frame = document().frame())
- frame->eventHandler().defaultKeyboardEventHandler(static_cast<KeyboardEvent*>(event));
+ frame->eventHandler().defaultKeyboardEventHandler(downcast<KeyboardEvent>(event));
+ }
} else if (eventType == eventNames().clickEvent) {
- int detail = event->isUIEvent() ? static_cast<UIEvent*>(event)->detail() : 0;
+ int detail = is<UIEvent>(event) ? downcast<UIEvent>(event).detail() : 0;
if (dispatchDOMActivateEvent(detail, event))
- event->setDefaultHandled();
+ event.setDefaultHandled();
#if ENABLE(CONTEXT_MENUS)
} else if (eventType == eventNames().contextmenuEvent) {
if (Frame* frame = document().frame())
@@ -2113,29 +2256,27 @@ void Node::defaultEventHandler(Event* event)
page->contextMenuController().handleContextMenuEvent(event);
#endif
} else if (eventType == eventNames().textInputEvent) {
- if (event->eventInterface() == TextEventInterfaceType)
+ if (is<TextEvent>(event)) {
if (Frame* frame = document().frame())
- frame->eventHandler().defaultTextInputEventHandler(static_cast<TextEvent*>(event));
+ frame->eventHandler().defaultTextInputEventHandler(downcast<TextEvent>(event));
+ }
#if ENABLE(PAN_SCROLLING)
- } else if (eventType == eventNames().mousedownEvent && event->isMouseEvent()) {
- MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
- if (mouseEvent->button() == MiddleButton) {
+ } else if (eventType == eventNames().mousedownEvent && is<MouseEvent>(event)) {
+ if (downcast<MouseEvent>(event).button() == MiddleButton) {
if (enclosingLinkEventParentOrSelf())
return;
RenderObject* renderer = this->renderer();
- while (renderer && (!renderer->isBox() || !toRenderBox(renderer)->canBeScrolledAndHasScrollableArea()))
+ while (renderer && (!is<RenderBox>(*renderer) || !downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea()))
renderer = renderer->parent();
if (renderer) {
if (Frame* frame = document().frame())
- frame->eventHandler().startPanScrolling(toRenderBox(renderer));
+ frame->eventHandler().startPanScrolling(downcast<RenderBox>(*renderer));
}
}
#endif
- } else if ((eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) && event->eventInterface() == WheelEventInterfaceType) {
- WheelEvent* wheelEvent = static_cast<WheelEvent*>(event);
-
+ } else if (eventNames().isWheelEventType(eventType) && is<WheelEvent>(event)) {
// If we don't have a renderer, send the wheel event to the first node we find with a renderer.
// This is needed for <option> and <optgroup> elements so that <select>s get a wheel scroll.
Node* startNode = this;
@@ -2144,22 +2285,18 @@ void Node::defaultEventHandler(Event* event)
if (startNode && startNode->renderer())
if (Frame* frame = document().frame())
- frame->eventHandler().defaultWheelEventHandler(startNode, wheelEvent);
+ frame->eventHandler().defaultWheelEventHandler(startNode, downcast<WheelEvent>(event));
#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS)
- } else if (event->eventInterface() == TouchEventInterfaceType && eventNames().isTouchEventType(eventType)) {
- TouchEvent* touchEvent = static_cast<TouchEvent*>(event);
-
+ } else if (is<TouchEvent>(event) && eventNames().isTouchEventType(eventType)) {
RenderObject* renderer = this->renderer();
- while (renderer && (!renderer->isBox() || !toRenderBox(renderer)->canBeScrolledAndHasScrollableArea()))
+ while (renderer && (!is<RenderBox>(*renderer) || !downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea()))
renderer = renderer->parent();
if (renderer && renderer->node()) {
if (Frame* frame = document().frame())
- frame->eventHandler().defaultTouchEventHandler(renderer->node(), touchEvent);
+ frame->eventHandler().defaultTouchEventHandler(*renderer->node(), downcast<TouchEvent>(event));
}
#endif
- } else if (event->type() == eventNames().webkitEditableContentChangedEvent) {
- dispatchInputEvent();
}
}
@@ -2167,9 +2304,9 @@ bool Node::willRespondToMouseMoveEvents()
{
// FIXME: Why is the iOS code path different from the non-iOS code path?
#if !PLATFORM(IOS)
- if (!isElementNode())
+ if (!is<Element>(*this))
return false;
- if (toElement(this)->isDisabledFormControl())
+ if (downcast<Element>(*this).isDisabledFormControl())
return false;
#endif
return hasEventListeners(eventNames().mousemoveEvent) || hasEventListeners(eventNames().mouseoverEvent) || hasEventListeners(eventNames().mouseoutEvent);
@@ -2181,11 +2318,12 @@ bool Node::willRespondToMouseClickEvents()
#if PLATFORM(IOS)
return isContentEditable() || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent);
#else
- if (!isElementNode())
+ if (!is<Element>(*this))
return false;
- if (toElement(this)->isDisabledFormControl())
+ if (downcast<Element>(*this).isDisabledFormControl())
return false;
- return isContentEditable(UserSelectAllIsAlwaysNonEditable) || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent) || hasEventListeners(eventNames().DOMActivateEvent);
+ return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) != Editability::ReadOnly
+ || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent) || hasEventListeners(eventNames().DOMActivateEvent);
#endif
}
@@ -2194,31 +2332,6 @@ bool Node::willRespondToMouseWheelEvents()
return hasEventListeners(eventNames().mousewheelEvent);
}
-// This is here so it can be inlined into Node::removedLastRef.
-// FIXME: Really? Seems like this could be inlined into Node::removedLastRef if it was in TreeScope.h.
-// FIXME: It also not seem important to inline this. Is this really hot?
-inline void TreeScope::removedLastRefToScope()
-{
- ASSERT(!deletionHasBegun());
- if (m_selfOnlyRefCount) {
- // If removing a child removes the last self-only ref, we don't want the scope to be destroyed
- // until after removeDetachedChildren returns, so we protect ourselves with an extra self-only ref.
- selfOnlyRef();
- dropChildren();
-#ifndef NDEBUG
- // We need to do this right now since selfOnlyDeref() can delete this.
- rootNode()->m_inRemovedLastRefFunction = false;
-#endif
- selfOnlyDeref();
- } else {
-#ifndef NDEBUG
- rootNode()->m_inRemovedLastRefFunction = false;
- beginDeletion();
-#endif
- delete this;
- }
-}
-
// It's important not to inline removedLastRef, because we don't want to inline the code to
// delete a Node at each deref call site.
void Node::removedLastRef()
@@ -2226,8 +2339,8 @@ void Node::removedLastRef()
// An explicit check for Document here is better than a virtual function since it is
// faster for non-Document nodes, and because the call to removedLastRef that is inlined
// at all deref call sites is smaller if it's a non-virtual function.
- if (isTreeScope()) {
- treeScope().removedLastRefToScope();
+ if (is<Document>(*this)) {
+ downcast<Document>(*this).removedLastRef();
return;
}
@@ -2239,9 +2352,9 @@ void Node::removedLastRef()
void Node::textRects(Vector<IntRect>& rects) const
{
- RefPtr<Range> range = Range::create(document());
- range->selectNodeContents(const_cast<Node*>(this), IGNORE_EXCEPTION);
- range->textRects(rects);
+ auto range = Range::create(document());
+ range->selectNodeContents(const_cast<Node&>(*this));
+ range->absoluteTextRects(rects);
}
unsigned Node::connectedSubframeCount() const
@@ -2284,12 +2397,24 @@ void Node::updateAncestorConnectedSubframeCountForInsertion() const
bool Node::inRenderedDocument() const
{
- return inDocument() && document().hasLivingRenderTree();
+ return isConnected() && document().hasLivingRenderTree();
+}
+
+void* Node::opaqueRootSlow() const
+{
+ const Node* node = this;
+ for (;;) {
+ const Node* nextNode = node->parentOrShadowHostNode();
+ if (!nextNode)
+ break;
+ node = nextNode;
+ }
+ return const_cast<void*>(static_cast<const void*>(node));
}
} // namespace WebCore
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showTree(const WebCore::Node* node)
{
@@ -2303,4 +2428,4 @@ void showNodePath(const WebCore::Node* node)
node->showNodePathForThis();
}
-#endif
+#endif // ENABLE(TREE_DEBUGGING)