diff options
Diffstat (limited to 'Source/WebCore/dom/Node.cpp')
-rw-r--r-- | Source/WebCore/dom/Node.cpp | 1645 |
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(®istration->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([®istration] (auto& current) { + return current.get() == ®istration; + }); } -void Node::registerTransientMutationObserver(MutationObserverRegistration* registration) +void Node::registerTransientMutationObserver(MutationObserverRegistration& registration) { - ensureRareData().ensureMutationObserverData().transientRegistry.add(registration); + ensureRareData().ensureMutationObserverData().transientRegistry.add(®istration); } -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(®istration)); + transientRegistry->remove(®istration); } 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) |