summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing/htmlediting.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/editing/htmlediting.cpp')
-rw-r--r--Source/WebCore/editing/htmlediting.cpp1116
1 files changed, 570 insertions, 546 deletions
diff --git a/Source/WebCore/editing/htmlediting.cpp b/Source/WebCore/editing/htmlediting.cpp
index 9e721c6da..f40341c9e 100644
--- a/Source/WebCore/editing/htmlediting.cpp
+++ b/Source/WebCore/editing/htmlediting.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2007, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,39 +29,49 @@
#include "AXObjectCache.h"
#include "Document.h"
#include "Editor.h"
-#include "ExceptionCodePlaceholder.h"
#include "Frame.h"
-#include "HTMLBRElement.h"
+#include "HTMLBodyElement.h"
+#include "HTMLDListElement.h"
#include "HTMLDivElement.h"
#include "HTMLElementFactory.h"
#include "HTMLInterchange.h"
#include "HTMLLIElement.h"
#include "HTMLNames.h"
#include "HTMLOListElement.h"
-#include "HTMLObjectElement.h"
#include "HTMLParagraphElement.h"
+#include "HTMLSpanElement.h"
#include "HTMLTableElement.h"
#include "HTMLTextFormControlElement.h"
#include "HTMLUListElement.h"
#include "NodeTraversal.h"
#include "PositionIterator.h"
+#include "RenderBlock.h"
#include "RenderElement.h"
+#include "RenderTableCell.h"
#include "ShadowRoot.h"
#include "Text.h"
+#include "TextIterator.h"
#include "VisibleUnits.h"
#include <wtf/Assertions.h>
#include <wtf/StdLibExtras.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
using namespace HTMLNames;
-// Atomic means that the node has no children, or has children which are ignored for the
-// purposes of editing.
-bool isAtomicNode(const Node *node)
+static bool isVisiblyAdjacent(const Position&, const Position&);
+
+bool canHaveChildrenForEditing(const Node& node)
{
- return node && (!node->hasChildNodes() || editingIgnoresContent(node));
+ return !is<Text>(node) && node.canContainRangeEndPoint();
+}
+
+// Atomic means that the node has no children, or has children which are ignored for the purposes of editing.
+bool isAtomicNode(const Node* node)
+{
+ return node && (!node->hasChildNodes() || editingIgnoresContent(*node));
}
// Compare two positions, taking into account the possibility that one or both
@@ -70,16 +80,15 @@ int comparePositions(const Position& a, const Position& b)
{
TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
- ASSERT(commonScope);
if (!commonScope)
return 0;
- Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
+ Node* nodeA = commonScope->ancestorNodeInThisScope(a.containerNode());
ASSERT(nodeA);
bool hasDescendentA = nodeA != a.containerNode();
int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
- Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
+ Node* nodeB = commonScope->ancestorNodeInThisScope(b.containerNode());
ASSERT(nodeB);
bool hasDescendentB = nodeB != b.containerNode();
int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
@@ -92,7 +101,10 @@ int comparePositions(const Position& a, const Position& b)
bias = 1;
}
- int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
+ auto comparisonResult = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
+ if (comparisonResult.hasException())
+ return bias;
+ auto result = comparisonResult.releaseReturnValue();
return result ? result : bias;
}
@@ -101,202 +113,230 @@ int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
return comparePositions(a.deepEquivalent(), b.deepEquivalent());
}
-Node* highestEditableRoot(const Position& position, EditableType editableType)
+ContainerNode* highestEditableRoot(const Position& position, EditableType editableType)
{
- Node* node = position.deprecatedNode();
- if (!node)
- return 0;
-
- Node* highestEditableRoot = editableRootForPosition(position, editableType);
+ ContainerNode* highestEditableRoot = editableRootForPosition(position, editableType);
if (!highestEditableRoot)
- return 0;
+ return nullptr;
- node = highestEditableRoot;
- while (!node->hasTagName(bodyTag)) {
+ for (ContainerNode* node = highestEditableRoot; !is<HTMLBodyElement>(*node); ) {
node = node->parentNode();
if (!node)
break;
- if (node->hasEditableStyle(editableType))
+ // FIXME: Can this ever be a Document or DocumentFragment? If not, this should return Element* instead.
+ if (hasEditableStyle(*node, editableType))
highestEditableRoot = node;
}
return highestEditableRoot;
}
-Node* lowestEditableAncestor(Node* node)
+Element* lowestEditableAncestor(Node* node)
{
- if (!node)
- return 0;
-
- while (node) {
+ for (; node; node = node->parentNode()) {
if (node->hasEditableStyle())
return node->rootEditableElement();
- if (node->hasTagName(bodyTag))
+ if (is<HTMLBodyElement>(*node))
break;
- node = node->parentNode();
}
-
- return 0;
+ return nullptr;
}
-bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle)
+static bool isEditableToAccessibility(const Node& node)
{
- Node* node = p.deprecatedNode();
- if (!node)
+ ASSERT(AXObjectCache::accessibilityEnabled());
+ ASSERT(node.document().existingAXObjectCache());
+
+ if (auto* cache = node.document().existingAXObjectCache())
+ return cache->rootAXEditableElement(&node);
+
+ return false;
+}
+
+static bool computeEditability(const Node& node, EditableType editableType, Node::ShouldUpdateStyle shouldUpdateStyle)
+{
+ if (node.computeEditability(Node::UserSelectAllIsAlwaysNonEditable, shouldUpdateStyle) != Node::Editability::ReadOnly)
+ return true;
+
+ switch (editableType) {
+ case ContentIsEditable:
return false;
- if (updateStyle == UpdateStyle)
- node->document().updateLayoutIgnorePendingStylesheets();
- else
- ASSERT(updateStyle == DoNotUpdateStyle);
+ case HasEditableAXRole:
+ return isEditableToAccessibility(node);
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
- if (node->renderer() && node->renderer()->isTable())
- node = node->parentNode();
-
- return node->hasEditableStyle(editableType);
+bool hasEditableStyle(const Node& node, EditableType editableType)
+{
+ return computeEditability(node, editableType, Node::ShouldUpdateStyle::DoNotUpdate);
}
-bool isAtUnsplittableElement(const Position& pos)
+bool isEditableNode(const Node& node)
{
- Node* node = pos.deprecatedNode();
- return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell));
+ return computeEditability(node, ContentIsEditable, Node::ShouldUpdateStyle::Update);
}
-
-
-bool isRichlyEditablePosition(const Position& p, EditableType editableType)
+
+bool isEditablePosition(const Position& position, EditableType editableType)
{
- Node* node = p.deprecatedNode();
- if (!node)
- return false;
-
- if (node->renderer() && node->renderer()->isTable())
- node = node->parentNode();
-
- return node->hasRichlyEditableStyle(editableType);
+ Node* node = position.containerNode();
+ return node && computeEditability(*node, editableType, Node::ShouldUpdateStyle::Update);
}
-Element* editableRootForPosition(const Position& p, EditableType editableType)
+bool isAtUnsplittableElement(const Position& position)
{
- Node* node = p.containerNode();
+ Node* node = position.containerNode();
+ return node == editableRootForPosition(position) || node == enclosingNodeOfType(position, isTableCell);
+}
+
+bool isRichlyEditablePosition(const Position& position)
+{
+ auto* node = position.containerNode();
+ return node && node->hasRichlyEditableStyle();
+}
+
+Element* editableRootForPosition(const Position& position, EditableType editableType)
+{
+ Node* node = position.containerNode();
if (!node)
- return 0;
-
- if (node->renderer() && node->renderer()->isTable())
- node = node->parentNode();
-
- return node->rootEditableElement(editableType);
+ return nullptr;
+
+ switch (editableType) {
+ case HasEditableAXRole:
+ if (auto* cache = node->document().existingAXObjectCache())
+ return const_cast<Element*>(cache->rootAXEditableElement(node));
+ FALLTHROUGH;
+ case ContentIsEditable:
+ return node->rootEditableElement();
+ }
+ return nullptr;
}
// Finds the enclosing element until which the tree can be split.
// When a user hits ENTER, he/she won't expect this element to be split into two.
// You may pass it as the second argument of splitTreeToNode.
-Element* unsplittableElementForPosition(const Position& p)
+Element* unsplittableElementForPosition(const Position& position)
{
// Since enclosingNodeOfType won't search beyond the highest root editable node,
// this code works even if the closest table cell was outside of the root editable node.
- Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell));
- if (enclosingCell)
+ if (auto* enclosingCell = downcast<Element>(enclosingNodeOfType(position, &isTableCell)))
return enclosingCell;
-
- return editableRootForPosition(p);
+ return editableRootForPosition(position);
}
Position nextCandidate(const Position& position)
{
- PositionIterator p = position;
- while (!p.atEnd()) {
- p.increment();
- if (p.isCandidate())
- return p;
+ for (PositionIterator nextPosition = position; !nextPosition.atEnd(); ) {
+ nextPosition.increment();
+ if (nextPosition.isCandidate())
+ return nextPosition;
}
- return Position();
+ return { };
}
Position nextVisuallyDistinctCandidate(const Position& position)
{
- Position p = position;
- Position downstreamStart = p.downstream();
- while (!p.atEndOfTree()) {
- p = p.next(Character);
- if (p.isCandidate() && p.downstream() != downstreamStart)
- return p;
+ // FIXME: Use PositionIterator instead.
+ Position nextPosition = position;
+ Position downstreamStart = nextPosition.downstream();
+ while (!nextPosition.atEndOfTree()) {
+ nextPosition = nextPosition.next(Character);
+ if (nextPosition.isCandidate() && nextPosition.downstream() != downstreamStart)
+ return nextPosition;
+ if (auto* node = nextPosition.containerNode()) {
+ if (!node->renderer())
+ nextPosition = lastPositionInOrAfterNode(node);
+ }
}
- return Position();
+ return { };
}
Position previousCandidate(const Position& position)
{
- PositionIterator p = position;
- while (!p.atStart()) {
- p.decrement();
- if (p.isCandidate())
- return p;
+ PositionIterator previousPosition = position;
+ while (!previousPosition.atStart()) {
+ previousPosition.decrement();
+ if (previousPosition.isCandidate())
+ return previousPosition;
}
- return Position();
+ return { };
}
Position previousVisuallyDistinctCandidate(const Position& position)
{
- Position p = position;
- Position downstreamStart = p.downstream();
- while (!p.atStartOfTree()) {
- p = p.previous(Character);
- if (p.isCandidate() && p.downstream() != downstreamStart)
- return p;
+ // FIXME: Use PositionIterator instead.
+ Position previousPosition = position;
+ Position downstreamStart = previousPosition.downstream();
+ while (!previousPosition.atStartOfTree()) {
+ previousPosition = previousPosition.previous(Character);
+ if (previousPosition.isCandidate() && previousPosition.downstream() != downstreamStart)
+ return previousPosition;
+ if (auto* node = previousPosition.containerNode()) {
+ if (!node->renderer())
+ previousPosition = firstPositionInOrBeforeNode(node);
+ }
}
- return Position();
+ return { };
}
-VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
+Position firstEditablePositionAfterPositionInRoot(const Position& position, ContainerNode* highestRoot)
{
+ if (!highestRoot)
+ return { };
+
// position falls before highestRoot.
if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->hasEditableStyle())
return firstPositionInNode(highestRoot);
- Position p = position;
+ Position candidate = position;
if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
- Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
+ auto* shadowAncestor = highestRoot->treeScope().ancestorNodeInThisScope(position.deprecatedNode());
if (!shadowAncestor)
- return VisiblePosition();
+ return { };
- p = positionAfterNode(shadowAncestor);
+ candidate = positionAfterNode(shadowAncestor);
}
- while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
- p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(p.deprecatedNode()) : nextVisuallyDistinctCandidate(p);
-
- if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
- return VisiblePosition();
-
- return VisiblePosition(p);
+ while (candidate.deprecatedNode() && !isEditablePosition(candidate) && candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ candidate = isAtomicNode(candidate.deprecatedNode()) ? positionInParentAfterNode(candidate.deprecatedNode()) : nextVisuallyDistinctCandidate(candidate);
+
+ if (candidate.deprecatedNode() && candidate.deprecatedNode() != highestRoot && !candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ return { };
+
+ return candidate;
}
-VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
+Position lastEditablePositionBeforePositionInRoot(const Position& position, ContainerNode* highestRoot)
{
+ if (!highestRoot)
+ return { };
+
// When position falls after highestRoot, the result is easy to compute.
if (comparePositions(position, lastPositionInNode(highestRoot)) == 1)
return lastPositionInNode(highestRoot);
- Position p = position;
+ Position candidate = position;
if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
- Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
+ auto* shadowAncestor = highestRoot->treeScope().ancestorNodeInThisScope(position.deprecatedNode());
if (!shadowAncestor)
- return VisiblePosition();
+ return { };
- p = firstPositionInOrBeforeNode(shadowAncestor);
+ candidate = firstPositionInOrBeforeNode(shadowAncestor);
}
+
+ while (candidate.deprecatedNode() && !isEditablePosition(candidate) && candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ candidate = isAtomicNode(candidate.deprecatedNode()) ? positionInParentBeforeNode(candidate.deprecatedNode()) : previousVisuallyDistinctCandidate(candidate);
- while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
- p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(p.deprecatedNode()) : previousVisuallyDistinctCandidate(p);
-
- if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
- return VisiblePosition();
+ if (candidate.deprecatedNode() && candidate.deprecatedNode() != highestRoot && !candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ return { };
- return VisiblePosition(p);
+ return candidate;
}
-// FIXME: The method name, comment, and code say three different things here!
+// FIXME: The function name, comment, and code say three different things here!
// Whether or not content before and after this node will collapse onto the same line as it.
bool isBlock(const Node* node)
{
@@ -315,7 +355,7 @@ bool isInline(const Node* node)
Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule)
{
Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule);
- return enclosingNode && enclosingNode->isElementNode() ? toElement(enclosingNode) : 0;
+ return is<Element>(enclosingNode) ? downcast<Element>(enclosingNode) : nullptr;
}
TextDirection directionOfEnclosingBlock(const Position& position)
@@ -333,77 +373,89 @@ TextDirection directionOfEnclosingBlock(const Position& position)
// in a node. It returns 1 for some elements even though they do not have children, which
// creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent
// on a Position before using it to create a DOM Range, or an exception will be thrown.
-int lastOffsetForEditing(const Node* node)
+int lastOffsetForEditing(const Node& node)
{
- ASSERT(node);
- if (!node)
- return 0;
- if (node->offsetInCharacters())
- return node->maxCharacterOffset();
+ if (node.offsetInCharacters())
+ return node.maxCharacterOffset();
- if (node->hasChildNodes())
- return node->childNodeCount();
+ if (node.hasChildNodes())
+ return node.countChildNodes();
- // NOTE: This should preempt the childNodeCount for, e.g., select nodes
+ // NOTE: This should preempt the countChildNodes() for, e.g., select nodes.
+ // FIXME: What does the comment above mean?
if (editingIgnoresContent(node))
return 1;
return 0;
}
+bool isAmbiguousBoundaryCharacter(UChar character)
+{
+ // These are characters that can behave as word boundaries, but can appear within words.
+ // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
+ // FIXME: this is required until <rdar://problem/6853027> is fixed and text checking can do this for us.
+ return character == '\'' || character == '@' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
+}
+
String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
{
- Vector<UChar> rebalancedString;
- append(rebalancedString, string);
+ StringBuilder rebalancedString;
bool previousCharacterWasSpace = false;
- for (size_t i = 0; i < rebalancedString.size(); i++) {
- if (!isWhitespace(rebalancedString[i])) {
+ unsigned length = string.length();
+ for (unsigned i = 0; i < length; ++i) {
+ auto character = string[i];
+ if (!deprecatedIsEditingWhitespace(character)) {
previousCharacterWasSpace = false;
continue;
}
-
- if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == rebalancedString.size() && endIsEndOfParagraph)) {
- rebalancedString[i] = noBreakSpace;
+ LChar selectedWhitespaceCharacter;
+ if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i == length - 1 && endIsEndOfParagraph)) {
+ selectedWhitespaceCharacter = noBreakSpace;
previousCharacterWasSpace = false;
} else {
- rebalancedString[i] = ' ';
+ selectedWhitespaceCharacter = ' ';
previousCharacterWasSpace = true;
}
-
+ if (character == selectedWhitespaceCharacter)
+ continue;
+ rebalancedString.reserveCapacity(length);
+ rebalancedString.append(string, rebalancedString.length(), i - rebalancedString.length());
+ rebalancedString.append(selectedWhitespaceCharacter);
}
- return String::adopt(rebalancedString);
+ if (rebalancedString.isEmpty())
+ return string;
+
+ rebalancedString.reserveCapacity(length);
+ rebalancedString.append(string, rebalancedString.length(), length - rebalancedString.length());
+ return rebalancedString.toString();
}
-bool isTableStructureNode(const Node *node)
+bool isTableStructureNode(const Node* node)
{
- RenderObject* renderer = node->renderer();
- return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
+ auto* renderer = node->renderer();
+ return renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol());
}
const String& nonBreakingSpaceString()
{
- DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
+ static NeverDestroyed<String> nonBreakingSpaceString(&noBreakSpace, 1);
return nonBreakingSpaceString;
}
-// FIXME: need to dump this
-bool isSpecialElement(const Node *n)
+static bool isSpecialHTMLElement(const Node* node)
{
- if (!n)
- return false;
-
- if (!n->isHTMLElement())
+ if (!is<HTMLElement>(node))
return false;
- if (n->isLink())
+ if (downcast<HTMLElement>(*node).isLink())
return true;
- RenderObject* renderer = n->renderer();
+ auto* renderer = downcast<HTMLElement>(*node).renderer();
if (!renderer)
return false;
-
+
if (renderer->style().display() == TABLE || renderer->style().display() == INLINE_TABLE)
return true;
@@ -412,162 +464,140 @@ bool isSpecialElement(const Node *n)
if (renderer->style().position() != StaticPosition)
return true;
-
- return false;
-}
-static Node* firstInSpecialElement(const Position& pos)
-{
- Node* rootEditableElement = pos.containerNode()->rootEditableElement();
- for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
- if (isSpecialElement(n)) {
- VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
- VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM);
- if (isTableElement(n) && vPos == firstInElement.next())
- return n;
- if (vPos == firstInElement)
- return n;
- }
- return 0;
+ return false;
}
-static Node* lastInSpecialElement(const Position& pos)
+static HTMLElement* firstInSpecialElement(const Position& position)
{
- Node* rootEditableElement = pos.containerNode()->rootEditableElement();
- for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
- if (isSpecialElement(n)) {
- VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
- VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM);
- if (isTableElement(n) && vPos == lastInElement.previous())
- return n;
- if (vPos == lastInElement)
- return n;
- }
- return 0;
+ auto* rootEditableElement = position.containerNode()->rootEditableElement();
+ for (Node* node = position.deprecatedNode(); node && node->rootEditableElement() == rootEditableElement; node = node->parentNode()) {
+ if (!isSpecialHTMLElement(node))
+ continue;
+ VisiblePosition vPos(position, DOWNSTREAM);
+ VisiblePosition firstInElement(firstPositionInOrBeforeNode(node), DOWNSTREAM);
+ if ((isRenderedTable(node) && vPos == firstInElement.next()) || vPos == firstInElement)
+ return &downcast<HTMLElement>(*node);
+ }
+ return nullptr;
}
-bool isFirstVisiblePositionInSpecialElement(const Position& pos)
+static HTMLElement* lastInSpecialElement(const Position& position)
{
- return firstInSpecialElement(pos);
+ auto* rootEditableElement = position.containerNode()->rootEditableElement();
+ for (Node* node = position.deprecatedNode(); node && node->rootEditableElement() == rootEditableElement; node = node->parentNode()) {
+ if (!isSpecialHTMLElement(node))
+ continue;
+ VisiblePosition vPos(position, DOWNSTREAM);
+ VisiblePosition lastInElement(lastPositionInOrAfterNode(node), DOWNSTREAM);
+ if ((isRenderedTable(node) && vPos == lastInElement.previous()) || vPos == lastInElement)
+ return &downcast<HTMLElement>(*node);
+ }
+ return nullptr;
}
-Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
+Position positionBeforeContainingSpecialElement(const Position& position, HTMLElement** containingSpecialElement)
{
- Node* n = firstInSpecialElement(pos);
- if (!n)
- return pos;
- Position result = positionInParentBeforeNode(n);
- if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
- return pos;
+ auto* element = firstInSpecialElement(position);
+ if (!element)
+ return position;
+ Position result = positionInParentBeforeNode(element);
+ if (result.isNull() || result.deprecatedNode()->rootEditableElement() != position.deprecatedNode()->rootEditableElement())
+ return position;
if (containingSpecialElement)
- *containingSpecialElement = n;
+ *containingSpecialElement = element;
return result;
}
-bool isLastVisiblePositionInSpecialElement(const Position& pos)
-{
- return lastInSpecialElement(pos);
-}
-
-Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
+Position positionAfterContainingSpecialElement(const Position& position, HTMLElement** containingSpecialElement)
{
- Node* n = lastInSpecialElement(pos);
- if (!n)
- return pos;
- Position result = positionInParentAfterNode(n);
- if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
- return pos;
+ auto* element = lastInSpecialElement(position);
+ if (!element)
+ return position;
+ Position result = positionInParentAfterNode(element);
+ if (result.isNull() || result.deprecatedNode()->rootEditableElement() != position.deprecatedNode()->rootEditableElement())
+ return position;
if (containingSpecialElement)
- *containingSpecialElement = n;
+ *containingSpecialElement = element;
return result;
}
-Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
-{
- if (isFirstVisiblePositionInSpecialElement(pos))
- return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
- if (isLastVisiblePositionInSpecialElement(pos))
- return positionAfterContainingSpecialElement(pos, containingSpecialElement);
- return pos;
-}
-
-Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
+Element* isFirstPositionAfterTable(const VisiblePosition& position)
{
- Position upstream(visiblePosition.deepEquivalent().upstream());
- if (upstream.deprecatedNode() && upstream.deprecatedNode()->renderer() && upstream.deprecatedNode()->renderer()->isTable() && upstream.atLastEditingPositionForNode())
- return upstream.deprecatedNode();
-
- return 0;
+ Position upstream(position.deepEquivalent().upstream());
+ auto* node = upstream.deprecatedNode();
+ if (!node)
+ return nullptr;
+ auto* renderer = node->renderer();
+ if (!renderer || !renderer->isTable() || !upstream.atLastEditingPositionForNode())
+ return nullptr;
+ return &downcast<Element>(*node);
}
-Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
+Element* isLastPositionBeforeTable(const VisiblePosition& position)
{
- Position downstream(visiblePosition.deepEquivalent().downstream());
- if (downstream.deprecatedNode() && downstream.deprecatedNode()->renderer() && downstream.deprecatedNode()->renderer()->isTable() && downstream.atFirstEditingPositionForNode())
- return downstream.deprecatedNode();
-
- return 0;
+ Position downstream(position.deepEquivalent().downstream());
+ auto* node = downstream.deprecatedNode();
+ if (!node)
+ return nullptr;
+ auto* renderer = node->renderer();
+ if (!renderer || !renderer->isTable() || !downstream.atFirstEditingPositionForNode())
+ return nullptr;
+ return &downcast<Element>(*node);
}
// Returns the visible position at the beginning of a node
-VisiblePosition visiblePositionBeforeNode(Node* node)
+VisiblePosition visiblePositionBeforeNode(Node& node)
{
- ASSERT(node);
- if (node->childNodeCount())
- return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
- ASSERT(node->parentNode());
- ASSERT(!node->parentNode()->isShadowRoot());
- return positionInParentBeforeNode(node);
+ if (node.hasChildNodes())
+ return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM);
+ ASSERT(node.parentNode());
+ ASSERT(!node.parentNode()->isShadowRoot());
+ return positionInParentBeforeNode(&node);
}
// Returns the visible position at the ending of a node
-VisiblePosition visiblePositionAfterNode(Node* node)
+VisiblePosition visiblePositionAfterNode(Node& node)
{
- ASSERT(node);
- if (node->childNodeCount())
- return VisiblePosition(lastPositionInOrAfterNode(node), DOWNSTREAM);
- ASSERT(node->parentNode());
- ASSERT(!node->parentNode()->isShadowRoot());
- return positionInParentAfterNode(node);
+ if (node.hasChildNodes())
+ return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM);
+ ASSERT(node.parentNode());
+ ASSERT(!node.parentNode()->isShadowRoot());
+ return positionInParentAfterNode(&node);
}
-bool isListElement(Node *n)
+bool isListHTMLElement(Node* node)
{
- return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
+ return node && (is<HTMLUListElement>(*node) || is<HTMLOListElement>(*node) || is<HTMLDListElement>(*node));
}
-bool isListItem(const Node *n)
+bool isListItem(const Node* node)
{
- return n && (isListElement(n->parentNode()) || (n->renderer() && n->renderer()->isListItem()));
+ return node && (isListHTMLElement(node->parentNode()) || (node->renderer() && node->renderer()->isListItem()));
}
-Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
+Element* enclosingElementWithTag(const Position& position, const QualifiedName& tagName)
{
- if (p.isNull())
- return 0;
-
- Node* root = highestEditableRoot(p);
- for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
- if (root && !n->hasEditableStyle())
+ auto* root = highestEditableRoot(position);
+ for (Node* node = position.deprecatedNode(); node; node = node->parentNode()) {
+ if (root && !node->hasEditableStyle())
continue;
- if (n->hasTagName(tagName))
- return n;
- if (n == root)
- return 0;
+ if (!is<Element>(*node))
+ continue;
+ if (downcast<Element>(*node).hasTagName(tagName))
+ return &downcast<Element>(*node);
+ if (node == root)
+ return nullptr;
}
-
- return 0;
+ return nullptr;
}
-Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
+Node* enclosingNodeOfType(const Position& position, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
{
// FIXME: support CanSkipCrossEditingBoundary
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
- if (p.isNull())
- return 0;
-
- Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
- for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
+ auto* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(position) : nullptr;
+ for (Node* n = position.deprecatedNode(); n; n = n->parentNode()) {
// Don't return a non-editable node if the input position was editable, since
// the callers from editing will no doubt want to perform editing inside the returned node.
if (root && !n->hasEditableStyle())
@@ -575,17 +605,16 @@ Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*),
if (nodeIsOfType(n))
return n;
if (n == root)
- return 0;
+ return nullptr;
}
-
- return 0;
+ return nullptr;
}
-Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
+Node* highestEnclosingNodeOfType(const Position& position, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
{
- Node* highest = 0;
- Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
- for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
+ Node* highest = nullptr;
+ auto* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(position) : nullptr;
+ for (Node* n = position.containerNode(); n && n != stayWithin; n = n->parentNode()) {
if (root && !n->hasEditableStyle())
continue;
if (nodeIsOfType(n))
@@ -593,7 +622,6 @@ Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const N
if (n == root)
break;
}
-
return highest;
}
@@ -601,43 +629,40 @@ static bool hasARenderedDescendant(Node* node, Node* excludedNode)
{
for (Node* n = node->firstChild(); n;) {
if (n == excludedNode) {
- n = NodeTraversal::nextSkippingChildren(n, node);
+ n = NodeTraversal::nextSkippingChildren(*n, node);
continue;
}
if (n->renderer())
return true;
- n = NodeTraversal::next(n, node);
+ n = NodeTraversal::next(*n, node);
}
return false;
}
Node* highestNodeToRemoveInPruning(Node* node)
{
- Node* previousNode = 0;
- Node* rootEditableElement = node ? node->rootEditableElement() : 0;
+ Node* previousNode = nullptr;
+ auto* rootEditableElement = node ? node->rootEditableElement() : nullptr;
for (; node; node = node->parentNode()) {
- if (RenderObject* renderer = node->renderer()) {
+ if (auto* renderer = node->renderer()) {
if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node)
return previousNode;
}
previousNode = node;
}
- return 0;
+ return nullptr;
}
-Node* enclosingTableCell(const Position& p)
+Element* enclosingTableCell(const Position& position)
{
- return toElement(enclosingNodeOfType(p, isTableCell));
+ return downcast<Element>(enclosingNodeOfType(position, isTableCell));
}
Element* enclosingAnchorElement(const Position& p)
{
- if (p.isNull())
- return nullptr;
-
for (Node* node = p.deprecatedNode(); node; node = node->parentNode()) {
- if (node->isElementNode() && node->isLink())
- return toElement(node);
+ if (is<Element>(*node) && node->isLink())
+ return downcast<Element>(node);
}
return nullptr;
}
@@ -645,90 +670,90 @@ Element* enclosingAnchorElement(const Position& p)
HTMLElement* enclosingList(Node* node)
{
if (!node)
- return 0;
+ return nullptr;
- Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
+ auto* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
- for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
- if (n->hasTagName(ulTag) || n->hasTagName(olTag))
- return toHTMLElement(n);
- if (n == root)
- return 0;
+ for (ContainerNode* ancestor = node->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
+ if (is<HTMLUListElement>(*ancestor) || is<HTMLOListElement>(*ancestor))
+ return downcast<HTMLElement>(ancestor);
+ if (ancestor == root)
+ return nullptr;
}
- return 0;
+ return nullptr;
}
Node* enclosingListChild(Node *node)
{
if (!node)
- return 0;
+ return nullptr;
+
// Check for a list item element, or for a node whose parent is a list element. Such a node
// will appear visually as a list item (but without a list marker)
- Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
+ auto* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
- // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
+ // FIXME: This function is inappropriately named since it starts with node instead of node->parentNode()
for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
- if (n->hasTagName(liTag) || (isListElement(n->parentNode()) && n != root))
+ if (is<HTMLLIElement>(*n) || (isListHTMLElement(n->parentNode()) && n != root))
return n;
if (n == root || isTableCell(n))
- return 0;
+ return nullptr;
}
-
- return 0;
+
+ return nullptr;
}
static HTMLElement* embeddedSublist(Node* listItem)
{
// Check the DOM so that we'll find collapsed sublists without renderers.
for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
- if (isListElement(n))
- return toHTMLElement(n);
+ if (isListHTMLElement(n))
+ return downcast<HTMLElement>(n);
}
-
- return 0;
+ return nullptr;
}
static Node* appendedSublist(Node* listItem)
{
// Check the DOM so that we'll find collapsed sublists without renderers.
for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
- if (isListElement(n))
- return toHTMLElement(n);
+ if (isListHTMLElement(n))
+ return downcast<HTMLElement>(n);
if (isListItem(listItem))
- return 0;
+ return nullptr;
}
- return 0;
+ return nullptr;
}
-// FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
-Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
+// FIXME: This function should not need to call isStartOfParagraph/isEndOfParagraph.
+Node* enclosingEmptyListItem(const VisiblePosition& position)
{
// Check that position is on a line by itself inside a list item
- Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
- if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
- return 0;
+ auto* listChildNode = enclosingListChild(position.deepEquivalent().deprecatedNode());
+ if (!listChildNode || !isStartOfParagraph(position) || !isEndOfParagraph(position))
+ return nullptr;
VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
- if (firstInListChild != visiblePos || lastInListChild != visiblePos)
- return 0;
-
+ if (firstInListChild != position || lastInListChild != position)
+ return nullptr;
+
if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
- return 0;
-
+ return nullptr;
+
return listChildNode;
}
HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
{
- HTMLElement* list = enclosingList(node);
+ auto* list = enclosingList(node);
if (!list)
- return 0;
+ return nullptr;
- while (HTMLElement* nextList = enclosingList(list)) {
+ while (auto* nextList = enclosingList(list)) {
if (nextList == rootList)
break;
list = nextList;
@@ -739,23 +764,17 @@ HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
bool canMergeLists(Element* firstList, Element* secondList)
{
- if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
+ if (!is<HTMLElement>(firstList) || !is<HTMLElement>(secondList))
return false;
- return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul)
- && firstList->hasEditableStyle() && secondList->hasEditableStyle() // both lists are editable
- && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries
- && isVisiblyAdjacent(positionInParentAfterNode(firstList), positionInParentBeforeNode(secondList));
- // Make sure there is no visible content between this li and the previous list
-}
+ auto& first = downcast<HTMLElement>(*firstList);
+ auto& second = downcast<HTMLElement>(*secondList);
-Node* highestAncestor(Node* node)
-{
- ASSERT(node);
- Node* parent = node;
- while ((node = node->parentNode()))
- parent = node;
- return parent;
+ return first.localName() == second.localName() // make sure the list types match (ol vs. ul)
+ && first.hasEditableStyle() && second.hasEditableStyle() // both lists are editable
+ && first.rootEditableElement() == second.rootEditableElement() // don't cross editing boundaries
+ // Make sure there is no visible content between this li and the previous list.
+ && isVisiblyAdjacent(positionInParentAfterNode(&first), positionInParentBeforeNode(&second));
}
static Node* previousNodeConsideringAtomicNodes(const Node* node)
@@ -768,7 +787,7 @@ static Node* previousNodeConsideringAtomicNodes(const Node* node)
}
if (node->parentNode())
return node->parentNode();
- return 0;
+ return nullptr;
}
static Node* nextNodeConsideringAtomicNodes(const Node* node)
@@ -782,48 +801,42 @@ static Node* nextNodeConsideringAtomicNodes(const Node* node)
n = n->parentNode();
if (n)
return n->nextSibling();
- return 0;
+ return nullptr;
}
Node* previousLeafNode(const Node* node)
{
- Node* n = previousNodeConsideringAtomicNodes(node);
- while (n) {
- if (isAtomicNode(n))
- return n;
- n = previousNodeConsideringAtomicNodes(n);
+ while ((node = previousNodeConsideringAtomicNodes(node))) {
+ if (isAtomicNode(node))
+ return const_cast<Node*>(node);
}
- return 0;
+ return nullptr;
}
Node* nextLeafNode(const Node* node)
{
- Node* n = nextNodeConsideringAtomicNodes(node);
- while (n) {
- if (isAtomicNode(n))
- return n;
- n = nextNodeConsideringAtomicNodes(n);
+ while ((node = nextNodeConsideringAtomicNodes(node))) {
+ if (isAtomicNode(node))
+ return const_cast<Node*>(node);
}
- return 0;
+ return nullptr;
}
-// FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
-bool isTableElement(Node* n)
+// FIXME: Do not require renderer, so that this can be used within fragments.
+bool isRenderedTable(const Node* node)
{
- if (!n || !n->isElementNode())
+ if (!is<Element>(node))
return false;
-
- RenderObject* renderer = n->renderer();
- return (renderer && (renderer->style().display() == TABLE || renderer->style().display() == INLINE_TABLE));
+ auto* renderer = downcast<Element>(*node).renderer();
+ return renderer && renderer->isTable();
}
bool isTableCell(const Node* node)
{
- RenderObject* r = node->renderer();
- if (!r)
+ auto* renderer = node->renderer();
+ if (!renderer)
return node->hasTagName(tdTag) || node->hasTagName(thTag);
-
- return r->isTableCell();
+ return renderer->isTableCell();
}
bool isEmptyTableCell(const Node* node)
@@ -841,17 +854,17 @@ bool isEmptyTableCell(const Node* node)
// Make sure the rendered node is a table cell or <br>.
// If it's a <br>, then the parent node has to be a table cell.
- RenderObject* renderer = node->renderer();
+ auto* renderer = node->renderer();
if (renderer->isBR()) {
renderer = renderer->parent();
if (!renderer)
return false;
}
- if (!renderer->isTableCell())
+ if (!is<RenderTableCell>(*renderer))
return false;
// Check that the table cell contains no child renderers except for perhaps a single <br>.
- RenderObject* childRenderer = toRenderElement(renderer)->firstChild();
+ auto* childRenderer = downcast<RenderTableCell>(*renderer).firstChild();
if (!childRenderer)
return true;
if (!childRenderer->isBR())
@@ -859,206 +872,159 @@ bool isEmptyTableCell(const Node* node)
return !childRenderer->nextSibling();
}
-PassRefPtr<HTMLElement> createDefaultParagraphElement(Document& document)
+Ref<HTMLElement> createDefaultParagraphElement(Document& document)
{
switch (document.frame()->editor().defaultParagraphSeparator()) {
case EditorParagraphSeparatorIsDiv:
return HTMLDivElement::create(document);
case EditorParagraphSeparatorIsP:
- return HTMLParagraphElement::create(document);
+ break;
}
-
- ASSERT_NOT_REACHED();
- return 0;
+ return HTMLParagraphElement::create(document);
}
-PassRefPtr<HTMLElement> createBreakElement(Document& document)
-{
- return HTMLBRElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createOrderedListElement(Document& document)
-{
- return HTMLOListElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createUnorderedListElement(Document& document)
-{
- return HTMLUListElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createListItemElement(Document& document)
-{
- return HTMLLIElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
+Ref<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
{
return HTMLElementFactory::createElement(name, document);
}
-PassRefPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
+Ref<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
{
return createHTMLElement(document, QualifiedName(nullAtom, tagName, xhtmlNamespaceURI));
}
-bool isTabSpanNode(const Node *node)
+bool isTabSpanNode(const Node* node)
{
- return node && node->hasTagName(spanTag) && node->isElementNode() && static_cast<const Element *>(node)->getAttribute(classAttr) == AppleTabSpanClass;
+ return is<HTMLSpanElement>(node) && downcast<HTMLSpanElement>(*node).attributeWithoutSynchronization(classAttr) == AppleTabSpanClass;
}
-bool isTabSpanTextNode(const Node *node)
+bool isTabSpanTextNode(const Node* node)
{
- return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
+ return is<Text>(node) && isTabSpanNode(node->parentNode());
}
-Node* tabSpanNode(const Node *node)
-{
- return isTabSpanTextNode(node) ? node->parentNode() : 0;
-}
-
-Position positionOutsideTabSpan(const Position& pos)
+HTMLSpanElement* tabSpanNode(const Node* node)
{
- Node* node = pos.containerNode();
- if (isTabSpanTextNode(node))
- node = tabSpanNode(node);
- else if (!isTabSpanNode(node))
- return pos;
-
- if (node && VisiblePosition(pos) == lastPositionInNode(node))
- return positionInParentAfterNode(node);
-
- return positionInParentBeforeNode(node);
+ return isTabSpanTextNode(node) ? downcast<HTMLSpanElement>(node->parentNode()) : nullptr;
}
-PassRefPtr<Element> createTabSpanElement(Document& document, PassRefPtr<Node> prpTabTextNode)
+static Ref<Element> createTabSpanElement(Document& document, Text& tabTextNode)
{
- RefPtr<Node> tabTextNode = prpTabTextNode;
+ auto spanElement = HTMLSpanElement::create(document);
- // Make the span to hold the tab.
- RefPtr<Element> spanElement = document.createElement(spanTag, false);
- spanElement->setAttribute(classAttr, AppleTabSpanClass);
+ spanElement->setAttributeWithoutSynchronization(classAttr, AppleTabSpanClass);
spanElement->setAttribute(styleAttr, "white-space:pre");
- // Add tab text to that span.
- if (!tabTextNode)
- tabTextNode = document.createEditingTextNode("\t");
+ spanElement->appendChild(tabTextNode);
- spanElement->appendChild(tabTextNode.release(), ASSERT_NO_EXCEPTION);
-
- return spanElement.release();
+ return WTFMove(spanElement);
}
-PassRefPtr<Element> createTabSpanElement(Document& document, const String& tabText)
+Ref<Element> createTabSpanElement(Document& document, const String& tabText)
{
return createTabSpanElement(document, document.createTextNode(tabText));
}
-PassRefPtr<Element> createTabSpanElement(Document& document)
+Ref<Element> createTabSpanElement(Document& document)
{
- return createTabSpanElement(document, PassRefPtr<Node>());
+ return createTabSpanElement(document, document.createEditingTextNode(ASCIILiteral("\t")));
}
-bool isNodeRendered(const Node* node)
+bool isNodeRendered(const Node& node)
{
- if (!node)
- return false;
-
- RenderObject* renderer = node->renderer();
- if (!renderer)
- return false;
-
- return renderer->style().visibility() == VISIBLE;
+ auto* renderer = node.renderer();
+ return renderer && renderer->style().visibility() == VISIBLE;
}
-unsigned numEnclosingMailBlockquotes(const Position& p)
+unsigned numEnclosingMailBlockquotes(const Position& position)
{
- unsigned num = 0;
- for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
- if (isMailBlockquote(n))
- num++;
-
- return num;
+ unsigned count = 0;
+ for (Node* node = position.deprecatedNode(); node; node = node->parentNode()) {
+ if (isMailBlockquote(node))
+ ++count;
+ }
+ return count;
}
-void updatePositionForNodeRemoval(Position& position, Node* node)
+void updatePositionForNodeRemoval(Position& position, Node& node)
{
if (position.isNull())
return;
switch (position.anchorType()) {
case Position::PositionIsBeforeChildren:
- if (position.containerNode() == node)
- position = positionInParentBeforeNode(node);
+ if (node.containsIncludingShadowDOM(position.containerNode()))
+ position = positionInParentBeforeNode(&node);
break;
case Position::PositionIsAfterChildren:
- if (position.containerNode() == node)
- position = positionInParentAfterNode(node);
+ if (node.containsIncludingShadowDOM(position.containerNode()))
+ position = positionInParentBeforeNode(&node);
break;
case Position::PositionIsOffsetInAnchor:
- if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
+ if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.computeNodeIndex())
position.moveToOffset(position.offsetInContainerNode() - 1);
- else if (node->containsIncludingShadowDOM(position.containerNode()))
- position = positionInParentBeforeNode(node);
+ else if (node.containsIncludingShadowDOM(position.containerNode()))
+ position = positionInParentBeforeNode(&node);
break;
case Position::PositionIsAfterAnchor:
- if (node->containsIncludingShadowDOM(position.anchorNode()))
- position = positionInParentAfterNode(node);
+ if (node.containsIncludingShadowDOM(position.anchorNode()))
+ position = positionInParentAfterNode(&node);
break;
case Position::PositionIsBeforeAnchor:
- if (node->containsIncludingShadowDOM(position.anchorNode()))
- position = positionInParentBeforeNode(node);
+ if (node.containsIncludingShadowDOM(position.anchorNode()))
+ position = positionInParentBeforeNode(&node);
break;
}
}
-bool isMailBlockquote(const Node *node)
+bool isMailBlockquote(const Node* node)
{
- if (!node || !node->hasTagName(blockquoteTag))
+ ASSERT(node);
+ if (!node->hasTagName(blockquoteTag))
return false;
-
- return static_cast<const Element *>(node)->getAttribute("type") == "cite";
+ return downcast<HTMLElement>(*node).attributeWithoutSynchronization(typeAttr) == "cite";
}
-int caretMinOffset(const Node* n)
+int caretMinOffset(const Node& node)
{
- RenderObject* r = n->renderer();
- ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
- return r ? r->caretMinOffset() : 0;
+ auto* renderer = node.renderer();
+ ASSERT(!node.isCharacterDataNode() || !renderer || renderer->isText());
+ return renderer ? renderer->caretMinOffset() : 0;
}
// If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
// return the number of children for container nodes and the length for unrendered text nodes.
-int caretMaxOffset(const Node* n)
+int caretMaxOffset(const Node& node)
{
// For rendered text nodes, return the last position that a caret could occupy.
- if (n->isTextNode() && n->renderer())
- return n->renderer()->caretMaxOffset();
- // For containers return the number of children. For others do the same as above.
- return lastOffsetForEditing(n);
+ if (is<Text>(node)) {
+ if (auto* renderer = downcast<Text>(node).renderer())
+ return renderer->caretMaxOffset();
+ }
+ return lastOffsetForEditing(node);
}
-bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
+bool lineBreakExistsAtVisiblePosition(const VisiblePosition& position)
{
- return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
+ return lineBreakExistsAtPosition(position.deepEquivalent().downstream());
}
bool lineBreakExistsAtPosition(const Position& position)
{
if (position.isNull())
return false;
-
+
if (position.anchorNode()->hasTagName(brTag) && position.atFirstEditingPositionForNode())
return true;
-
+
if (!position.anchorNode()->renderer())
return false;
-
- if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style().preserveNewline())
+
+ if (!is<Text>(*position.anchorNode()) || !position.anchorNode()->renderer()->style().preserveNewline())
return false;
- Text* textNode = toText(position.anchorNode());
+ Text& textNode = downcast<Text>(*position.anchorNode());
unsigned offset = position.offsetInContainerNode();
- return offset < textNode->length() && textNode->data()[offset] == '\n';
+ return offset < textNode.length() && textNode.data()[offset] == '\n';
}
// Modifies selections that have an end point at the edge of a table
@@ -1074,17 +1040,19 @@ VisibleSelection selectionForParagraphIteration(const VisibleSelection& original
// if the start of the selection is inside that table, then the last paragraph
// that we'll want modify is the last one inside the table, not the table itself
// (a table is itself a paragraph).
- if (Node* table = isFirstPositionAfterTable(endOfSelection))
- if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
+ if (auto* table = isFirstPositionAfterTable(endOfSelection)) {
+ if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(*table))
newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
+ }
// If the start of the selection to modify is just before a table,
// and if the end of the selection is inside that table, then the first paragraph
// we'll want to modify is the first one inside the table, not the paragraph
// containing the table itself.
- if (Node* table = isLastPositionBeforeTable(startOfSelection))
- if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
+ if (auto* table = isLastPositionBeforeTable(startOfSelection)) {
+ if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(*table))
newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
+ }
return newSelection;
}
@@ -1100,74 +1068,74 @@ int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtr<Conta
if (visiblePosition.isNull())
return 0;
- Position p(visiblePosition.deepEquivalent());
- Document& document = p.anchorNode()->document();
- ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
-
- if (shadowRoot)
- scope = shadowRoot;
- else
- scope = document.documentElement();
+ auto position = visiblePosition.deepEquivalent();
+ auto& document = *position.document();
+
+ auto* editableRoot = highestEditableRoot(position, AXObjectCache::accessibilityEnabled() ? HasEditableAXRole : ContentIsEditable);
+ if (editableRoot && !document.inDesignMode())
+ scope = editableRoot;
+ else {
+ if (position.containerNode()->isInShadowTree())
+ scope = position.containerNode()->containingShadowRoot();
+ else
+ scope = &document;
+ }
- RefPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
- return TextIterator::rangeLength(range.get(), true);
+ auto range = Range::create(document, firstPositionInNode(scope.get()), position.parentAnchoredEquivalent());
+ return TextIterator::rangeLength(range.ptr(), true);
}
-// FIXME: Merge these two functions.
-int indexForVisiblePosition(Node* node, const VisiblePosition& visiblePosition, bool forSelectionPreservation)
+// FIXME: Merge this function with the one above.
+int indexForVisiblePosition(Node& node, const VisiblePosition& visiblePosition, bool forSelectionPreservation)
{
- ASSERT(node);
- RefPtr<Range> range = Range::create(node->document(), firstPositionInNode(node), visiblePosition.deepEquivalent().parentAnchoredEquivalent());
- return TextIterator::rangeLength(range.get(), forSelectionPreservation);
+ auto range = Range::create(node.document(), firstPositionInNode(&node), visiblePosition.deepEquivalent().parentAnchoredEquivalent());
+ return TextIterator::rangeLength(range.ptr(), forSelectionPreservation);
}
VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
{
- RefPtr<Range> range = TextIterator::rangeFromLocationAndLength(scope, index, 0, true);
+ auto range = TextIterator::rangeFromLocationAndLength(scope, index, 0, true);
// Check for an invalid index. Certain editing operations invalidate indices because
// of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions.
if (!range)
- return VisiblePosition();
- return VisiblePosition(range->startPosition());
+ return { };
+ return { range->startPosition() };
}
-VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node* node, int index)
+VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node& node, int index)
{
- ASSERT(node);
if (index <= 0)
- return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
+ return { firstPositionInOrBeforeNode(&node), DOWNSTREAM };
- RefPtr<Range> range = Range::create(node->document());
- range->selectNodeContents(node, IGNORE_EXCEPTION);
- CharacterIterator it(range.get());
+ RefPtr<Range> range = Range::create(node.document());
+ range->selectNodeContents(node);
+ CharacterIterator it(*range);
it.advance(index - 1);
-
- return VisiblePosition(Position(it.range()->endContainer(), it.range()->endOffset(), Position::PositionIsOffsetInAnchor), UPSTREAM);
+ return { it.atEnd() ? range->endPosition() : it.range()->endPosition(), UPSTREAM };
}
// Determines whether two positions are visibly next to each other (first then second)
// while ignoring whitespaces and unrendered nodes
-bool isVisiblyAdjacent(const Position& first, const Position& second)
+static bool isVisiblyAdjacent(const Position& first, const Position& second)
{
return VisiblePosition(first) == VisiblePosition(second.upstream());
}
// Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
// Call this function to determine whether a node is visibly fit inside selectedRange
-bool isNodeVisiblyContainedWithin(Node* node, const Range* selectedRange)
+bool isNodeVisiblyContainedWithin(Node& node, const Range& range)
{
- ASSERT(node);
- ASSERT(selectedRange);
- // If the node is inside the range, then it surely is contained within
- if (selectedRange->compareNode(node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
+ // If the node is inside the range, then it surely is contained within.
+ auto comparisonResult = range.compareNode(node);
+ if (!comparisonResult.hasException() && comparisonResult.releaseReturnValue() == Range::NODE_INSIDE)
return true;
- bool startIsVisuallySame = visiblePositionBeforeNode(node) == selectedRange->startPosition();
- if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange->endPosition()) < 0)
+ bool startIsVisuallySame = visiblePositionBeforeNode(node) == range.startPosition();
+ if (startIsVisuallySame && comparePositions(positionInParentAfterNode(&node), range.endPosition()) < 0)
return true;
- bool endIsVisuallySame = visiblePositionAfterNode(node) == selectedRange->endPosition();
- if (endIsVisuallySame && comparePositions(selectedRange->startPosition(), positionInParentBeforeNode(node)) < 0)
+ bool endIsVisuallySame = visiblePositionAfterNode(node) == range.endPosition();
+ if (endIsVisuallySame && comparePositions(range.startPosition(), positionInParentBeforeNode(&node)) < 0)
return true;
return startIsVisuallySame && endIsVisuallySame;
@@ -1181,36 +1149,28 @@ bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
}
-bool areIdenticalElements(const Node* first, const Node* second)
+bool areIdenticalElements(const Node& first, const Node& second)
{
- if (!first->isElementNode() || !second->isElementNode())
- return false;
-
- const Element* firstElement = toElement(first);
- const Element* secondElement = toElement(second);
- if (!firstElement->hasTagName(secondElement->tagQName()))
+ if (!is<Element>(first) || !is<Element>(second))
return false;
-
- return firstElement->hasEquivalentAttributes(secondElement);
+ auto& firstElement = downcast<Element>(first);
+ auto& secondElement = downcast<Element>(second);
+ return firstElement.hasTagName(secondElement.tagQName()) && firstElement.hasEquivalentAttributes(&secondElement);
}
bool isNonTableCellHTMLBlockElement(const Node* node)
{
- if (!node->isElementNode())
- return false;
-
- const Element* element = toElement(node);
- return element->hasTagName(listingTag)
- || element->hasTagName(olTag)
- || element->hasTagName(preTag)
- || isHTMLTableElement(element)
- || element->hasTagName(ulTag)
- || element->hasTagName(xmpTag)
- || element->hasTagName(h1Tag)
- || element->hasTagName(h2Tag)
- || element->hasTagName(h3Tag)
- || element->hasTagName(h4Tag)
- || element->hasTagName(h5Tag);
+ return node->hasTagName(listingTag)
+ || node->hasTagName(olTag)
+ || node->hasTagName(preTag)
+ || is<HTMLTableElement>(*node)
+ || node->hasTagName(ulTag)
+ || node->hasTagName(xmpTag)
+ || node->hasTagName(h1Tag)
+ || node->hasTagName(h2Tag)
+ || node->hasTagName(h3Tag)
+ || node->hasTagName(h4Tag)
+ || node->hasTagName(h5Tag);
}
Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
@@ -1220,9 +1180,9 @@ Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selec
// It is important to skip certain irrelevant content at the start of the selection, so we do not wind up
// with a spurious "mixed" style.
- VisiblePosition visiblePosition = selection.start();
+ auto visiblePosition = selection.visibleStart();
if (visiblePosition.isNull())
- return Position();
+ return { };
// if the selection is a caret, just return the position, since the style
// behind us is relevant
@@ -1239,25 +1199,89 @@ Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selec
}
// FIXME: Should this be deprecated like deprecatedEnclosingBlockFlowElement is?
-bool isBlockFlowElement(const Node* node)
+bool isBlockFlowElement(const Node& node)
{
- if (!node->isElementNode())
+ if (!node.isElementNode())
return false;
- RenderObject* renderer = node->renderer();
+ auto* renderer = downcast<Element>(node).renderer();
return renderer && renderer->isRenderBlockFlow();
}
Element* deprecatedEnclosingBlockFlowElement(Node* node)
{
if (!node)
- return 0;
- if (isBlockFlowElement(node))
- return toElement(node);
+ return nullptr;
+ if (isBlockFlowElement(*node))
+ return downcast<Element>(node);
while ((node = node->parentNode())) {
- if (isBlockFlowElement(node) || node->hasTagName(bodyTag))
- return toElement(node);
+ if (isBlockFlowElement(*node) || is<HTMLBodyElement>(*node))
+ return downcast<Element>(node);
}
- return 0;
+ return nullptr;
+}
+
+static inline bool caretRendersInsideNode(Node& node)
+{
+ return !isRenderedTable(&node) && !editingIgnoresContent(node);
+}
+
+RenderBlock* rendererForCaretPainting(Node* node)
+{
+ if (!node)
+ return nullptr;
+
+ auto* renderer = node->renderer();
+ if (!renderer)
+ return nullptr;
+
+ // If caretNode is a block and caret is inside it, then caret should be painted by that block.
+ bool paintedByBlock = is<RenderBlockFlow>(*renderer) && caretRendersInsideNode(*node);
+ return paintedByBlock ? downcast<RenderBlock>(renderer) : renderer->containingBlock();
+}
+
+LayoutRect localCaretRectInRendererForCaretPainting(const VisiblePosition& caretPosition, RenderBlock*& caretPainter)
+{
+ if (caretPosition.isNull())
+ return LayoutRect();
+
+ ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
+
+ // First compute a rect local to the renderer at the selection start.
+ RenderObject* renderer;
+ LayoutRect localRect = caretPosition.localCaretRect(renderer);
+
+ return localCaretRectInRendererForRect(localRect, caretPosition.deepEquivalent().deprecatedNode(), renderer, caretPainter);
+}
+
+LayoutRect localCaretRectInRendererForRect(LayoutRect& localRect, Node* node, RenderObject* renderer, RenderBlock*& caretPainter)
+{
+ // Get the renderer that will be responsible for painting the caret
+ // (which is either the renderer we just found, or one of its containers).
+ caretPainter = rendererForCaretPainting(node);
+
+ // Compute an offset between the renderer and the caretPainter.
+ while (renderer != caretPainter) {
+ RenderElement* containerObject = renderer->container();
+ if (!containerObject)
+ return LayoutRect();
+ localRect.move(renderer->offsetFromContainer(*containerObject, localRect.location()));
+ renderer = containerObject;
+ }
+
+ return localRect;
+}
+
+IntRect absoluteBoundsForLocalCaretRect(RenderBlock* rendererForCaretPainting, const LayoutRect& rect, bool* insideFixed)
+{
+ if (insideFixed)
+ *insideFixed = false;
+
+ if (!rendererForCaretPainting || rect.isEmpty())
+ return IntRect();
+
+ LayoutRect localRect(rect);
+ rendererForCaretPainting->flipForWritingMode(localRect);
+ return rendererForCaretPainting->localToAbsoluteQuad(FloatRect(localRect), UseTransforms, insideFixed).enclosingBoundingBox();
}
} // namespace WebCore