summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/Position.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom/Position.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/dom/Position.cpp')
-rw-r--r--Source/WebCore/dom/Position.cpp784
1 files changed, 489 insertions, 295 deletions
diff --git a/Source/WebCore/dom/Position.cpp b/Source/WebCore/dom/Position.cpp
index 287b37b2b..10c4b540d 100644
--- a/Source/WebCore/dom/Position.cpp
+++ b/Source/WebCore/dom/Position.cpp
@@ -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
@@ -27,6 +27,9 @@
#include "Position.h"
#include "CSSComputedStyleDeclaration.h"
+#include "HTMLBRElement.h"
+#include "HTMLBodyElement.h"
+#include "HTMLHtmlElement.h"
#include "HTMLNames.h"
#include "HTMLTableElement.h"
#include "InlineElementBox.h"
@@ -35,30 +38,38 @@
#include "Logging.h"
#include "PositionIterator.h"
#include "RenderBlock.h"
+#include "RenderFlexibleBox.h"
+#include "RenderGrid.h"
#include "RenderInline.h"
+#include "RenderIterator.h"
#include "RenderLineBreak.h"
#include "RenderText.h"
#include "RuntimeEnabledFeatures.h"
#include "Text.h"
#include "TextIterator.h"
+#include "TextStream.h"
#include "VisiblePosition.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
#include <stdio.h>
#include <wtf/text/CString.h>
#include <wtf/unicode/CharacterNames.h>
-
+
+#if ENABLE(TREE_DEBUGGING)
+#include <wtf/text/StringBuilder.h>
+#endif
+
namespace WebCore {
using namespace HTMLNames;
static bool hasInlineBoxWrapper(RenderObject& renderer)
{
- if (renderer.isBox() && toRenderBox(renderer).inlineBoxWrapper())
+ if (is<RenderBox>(renderer) && downcast<RenderBox>(renderer).inlineBoxWrapper())
return true;
- if (renderer.isText() && toRenderText(renderer).firstTextBox())
+ if (is<RenderText>(renderer) && downcast<RenderText>(renderer).firstTextBox())
return true;
- if (renderer.isLineBreak() && toRenderLineBreak(renderer).inlineBoxWrapper())
+ if (is<RenderLineBreak>(renderer) && downcast<RenderLineBreak>(renderer).inlineBoxWrapper())
return true;
return false;
}
@@ -72,7 +83,7 @@ static Node* nextRenderedEditable(Node* node)
if (hasInlineBoxWrapper(*renderer))
return node;
}
- return 0;
+ return nullptr;
}
static Node* previousRenderedEditable(Node* node)
@@ -84,72 +95,55 @@ static Node* previousRenderedEditable(Node* node)
if (hasInlineBoxWrapper(*renderer))
return node;
}
- return 0;
+ return nullptr;
}
-Position::Position(PassRefPtr<Node> anchorNode, LegacyEditingOffset offset)
+Position::Position(Node* anchorNode, unsigned offset, LegacyEditingPositionFlag)
: m_anchorNode(anchorNode)
- , m_offset(offset.value())
+ , m_offset(offset)
, m_anchorType(anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset))
, m_isLegacyEditingPosition(true)
{
-#if ENABLE(SHADOW_DOM)
- ASSERT((m_anchorNode && RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) || !m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode());
-#else
ASSERT(!m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode());
-#endif
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
}
-Position::Position(PassRefPtr<Node> anchorNode, AnchorType anchorType)
+Position::Position(Node* anchorNode, AnchorType anchorType)
: m_anchorNode(anchorNode)
, m_offset(0)
, m_anchorType(anchorType)
, m_isLegacyEditingPosition(false)
{
-#if ENABLE(SHADOW_DOM)
- ASSERT((m_anchorNode && RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) || !m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode());
-#else
ASSERT(!m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode());
-#endif
-
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
-
ASSERT(anchorType != PositionIsOffsetInAnchor);
ASSERT(!((anchorType == PositionIsBeforeChildren || anchorType == PositionIsAfterChildren)
- && (m_anchorNode->isTextNode() || editingIgnoresContent(m_anchorNode.get()))));
+ && (is<Text>(*m_anchorNode) || editingIgnoresContent(*m_anchorNode))));
}
-Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorType)
+Position::Position(Node* anchorNode, int offset, AnchorType anchorType)
: m_anchorNode(anchorNode)
, m_offset(offset)
, m_anchorType(anchorType)
, m_isLegacyEditingPosition(false)
{
-#if ENABLE(SHADOW_DOM)
- ASSERT((m_anchorNode && RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled())
- || !m_anchorNode || !editingIgnoresContent(m_anchorNode.get()) || !m_anchorNode->isShadowRoot());
-#else
- ASSERT(!m_anchorNode || !editingIgnoresContent(m_anchorNode.get()) || !m_anchorNode->isShadowRoot());
-#endif
-
+ ASSERT(!m_anchorNode || !editingIgnoresContent(*m_anchorNode));
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
-
ASSERT(anchorType == PositionIsOffsetInAnchor);
}
-Position::Position(PassRefPtr<Text> textNode, unsigned offset)
+Position::Position(Text* textNode, unsigned offset)
: m_anchorNode(textNode)
- , m_offset(static_cast<int>(offset))
+ , m_offset(offset)
, m_anchorType(PositionIsOffsetInAnchor)
, m_isLegacyEditingPosition(false)
{
ASSERT(m_anchorNode);
}
-void Position::moveToPosition(PassRefPtr<Node> node, int offset)
+void Position::moveToPosition(Node* node, int offset)
{
- ASSERT(!editingIgnoresContent(node.get()));
+ ASSERT(!editingIgnoresContent(*node));
ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition);
m_anchorNode = node;
m_offset = offset;
@@ -167,7 +161,7 @@ void Position::moveToOffset(int offset)
Node* Position::containerNode() const
{
if (!m_anchorNode)
- return 0;
+ return nullptr;
switch (anchorType()) {
case PositionIsBeforeChildren:
@@ -176,27 +170,27 @@ Node* Position::containerNode() const
return m_anchorNode.get();
case PositionIsBeforeAnchor:
case PositionIsAfterAnchor:
- return findParent(m_anchorNode.get());
+ return m_anchorNode->parentNode();
}
ASSERT_NOT_REACHED();
- return 0;
+ return nullptr;
}
Text* Position::containerText() const
{
switch (anchorType()) {
case PositionIsOffsetInAnchor:
- return m_anchorNode && m_anchorNode->isTextNode() ? toText(m_anchorNode.get()) : 0;
+ return m_anchorNode && is<Text>(*m_anchorNode) ? downcast<Text>(m_anchorNode.get()) : nullptr;
case PositionIsBeforeAnchor:
case PositionIsAfterAnchor:
- return 0;
+ return nullptr;
case PositionIsBeforeChildren:
case PositionIsAfterChildren:
- ASSERT(!m_anchorNode || !m_anchorNode->isTextNode());
- return 0;
+ ASSERT(!m_anchorNode || !is<Text>(*m_anchorNode));
+ return nullptr;
}
ASSERT_NOT_REACHED();
- return 0;
+ return nullptr;
}
int Position::computeOffsetInContainerNode() const
@@ -212,9 +206,9 @@ int Position::computeOffsetInContainerNode() const
case PositionIsOffsetInAnchor:
return minOffsetForNode(m_anchorNode.get(), m_offset);
case PositionIsBeforeAnchor:
- return m_anchorNode->nodeIndex();
+ return m_anchorNode->computeNodeIndex();
case PositionIsAfterAnchor:
- return m_anchorNode->nodeIndex() + 1;
+ return m_anchorNode->computeNodeIndex() + 1;
}
ASSERT_NOT_REACHED();
return 0;
@@ -224,7 +218,8 @@ int Position::offsetForPositionAfterAnchor() const
{
ASSERT(m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren);
ASSERT(!m_isLegacyEditingPosition);
- return lastOffsetForEditing(m_anchorNode.get());
+ ASSERT(m_anchorNode);
+ return m_anchorNode ? lastOffsetForEditing(*m_anchorNode) : 0;
}
// Neighbor-anchored positions are invalid DOM positions, so they need to be
@@ -232,69 +227,70 @@ int Position::offsetForPositionAfterAnchor() const
Position Position::parentAnchoredEquivalent() const
{
if (!m_anchorNode)
- return Position();
+ return { };
// FIXME: This should only be necessary for legacy positions, but is also needed for positions before and after Tables
if (m_offset <= 0 && (m_anchorType != PositionIsAfterAnchor && m_anchorType != PositionIsAfterChildren)) {
- if (findParent(m_anchorNode.get()) && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get())))
+ if (m_anchorNode->parentNode() && (editingIgnoresContent(*m_anchorNode) || isRenderedTable(m_anchorNode.get())))
return positionInParentBeforeNode(m_anchorNode.get());
return Position(m_anchorNode.get(), 0, PositionIsOffsetInAnchor);
}
+
if (!m_anchorNode->offsetInCharacters()
- && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount())
- && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get()))
+ && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->countChildNodes())
+ && (editingIgnoresContent(*m_anchorNode) || isRenderedTable(m_anchorNode.get()))
&& containerNode()) {
return positionInParentAfterNode(m_anchorNode.get());
}
- return Position(containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor);
+ return { containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor };
}
Node* Position::computeNodeBeforePosition() const
{
if (!m_anchorNode)
- return 0;
+ return nullptr;
switch (anchorType()) {
case PositionIsBeforeChildren:
- return 0;
+ return nullptr;
case PositionIsAfterChildren:
return m_anchorNode->lastChild();
case PositionIsOffsetInAnchor:
- return m_anchorNode->childNode(m_offset - 1); // -1 converts to childNode((unsigned)-1) and returns null.
+ return m_offset ? m_anchorNode->traverseToChildAt(m_offset - 1) : nullptr;
case PositionIsBeforeAnchor:
return m_anchorNode->previousSibling();
case PositionIsAfterAnchor:
return m_anchorNode.get();
}
ASSERT_NOT_REACHED();
- return 0;
+ return nullptr;
}
Node* Position::computeNodeAfterPosition() const
{
if (!m_anchorNode)
- return 0;
+ return nullptr;
switch (anchorType()) {
case PositionIsBeforeChildren:
return m_anchorNode->firstChild();
case PositionIsAfterChildren:
- return 0;
+ return nullptr;
case PositionIsOffsetInAnchor:
- return m_anchorNode->childNode(m_offset);
+ return m_anchorNode->traverseToChildAt(m_offset);
case PositionIsBeforeAnchor:
return m_anchorNode.get();
case PositionIsAfterAnchor:
return m_anchorNode->nextSibling();
}
ASSERT_NOT_REACHED();
- return 0;
+ return nullptr;
}
Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNode, int offset)
{
- if (anchorNode && editingIgnoresContent(anchorNode)) {
+ if (anchorNode && editingIgnoresContent(*anchorNode)) {
if (offset == 0)
return Position::PositionIsBeforeAnchor;
return Position::PositionIsAfterAnchor;
@@ -305,25 +301,32 @@ Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNo
// FIXME: This method is confusing (does it return anchorNode() or containerNode()?) and should be renamed or removed
Element* Position::element() const
{
- Node* n = anchorNode();
- while (n && !n->isElementNode())
- n = n->parentNode();
- return toElement(n);
+ Node* node = anchorNode();
+ while (node && !is<Element>(*node))
+ node = node->parentNode();
+ return downcast<Element>(node);
}
Position Position::previous(PositionMoveType moveType) const
{
- Node* n = deprecatedNode();
- if (!n)
+ Node* node = deprecatedNode();
+ if (!node)
return *this;
- int o = deprecatedEditingOffset();
+ int offset = deprecatedEditingOffset();
// FIXME: Negative offsets shouldn't be allowed. We should catch this earlier.
- ASSERT(o >= 0);
+ ASSERT(offset >= 0);
- if (o > 0) {
- Node* child = n->childNode(o - 1);
- if (child)
+ if (anchorType() == PositionIsBeforeAnchor) {
+ node = containerNode();
+ if (!node)
+ return *this;
+
+ offset = computeOffsetInContainerNode();
+ }
+
+ if (offset > 0) {
+ if (Node* child = node->traverseToChildAt(offset - 1))
return lastPositionInOrAfterNode(child);
// There are two reasons child might be 0:
@@ -333,35 +336,50 @@ Position Position::previous(PositionMoveType moveType) const
// Going from 1 to 0 is correct.
switch (moveType) {
case CodePoint:
- return createLegacyEditingPosition(n, o - 1);
+ return createLegacyEditingPosition(node, offset - 1);
case Character:
- return createLegacyEditingPosition(n, uncheckedPreviousOffset(n, o));
+ return createLegacyEditingPosition(node, uncheckedPreviousOffset(node, offset));
case BackwardDeletion:
- return createLegacyEditingPosition(n, uncheckedPreviousOffsetForBackwardDeletion(n, o));
+ return createLegacyEditingPosition(node, uncheckedPreviousOffsetForBackwardDeletion(node, offset));
}
}
- ContainerNode* parent = findParent(n);
+ ContainerNode* parent = node->parentNode();
if (!parent)
return *this;
- return createLegacyEditingPosition(parent, n->nodeIndex());
+ if (positionBeforeOrAfterNodeIsCandidate(*node))
+ return positionBeforeNode(node);
+
+ Node* previousSibling = node->previousSibling();
+ if (previousSibling && positionBeforeOrAfterNodeIsCandidate(*previousSibling))
+ return positionAfterNode(previousSibling);
+
+ return createLegacyEditingPosition(parent, node->computeNodeIndex());
}
Position Position::next(PositionMoveType moveType) const
{
ASSERT(moveType != BackwardDeletion);
- Node* n = deprecatedNode();
- if (!n)
+ Node* node = deprecatedNode();
+ if (!node)
return *this;
- int o = deprecatedEditingOffset();
+ int offset = deprecatedEditingOffset();
// FIXME: Negative offsets shouldn't be allowed. We should catch this earlier.
- ASSERT(o >= 0);
+ ASSERT(offset >= 0);
+
+ if (anchorType() == PositionIsAfterAnchor) {
+ node = containerNode();
+ if (!node)
+ return *this;
- Node* child = n->childNode(o);
- if (child || (!n->hasChildNodes() && o < lastOffsetForEditing(n))) {
+ offset = computeOffsetInContainerNode();
+ }
+
+ Node* child = node->traverseToChildAt(offset);
+ if (child || (!node->hasChildNodes() && offset < lastOffsetForEditing(*node))) {
if (child)
return firstPositionInOrBeforeNode(child);
@@ -370,14 +388,21 @@ Position Position::next(PositionMoveType moveType) const
// Going forward one character at a time is correct.
// 2) The new offset is a bogus offset like (<br>, 1), and there is no child.
// Going from 0 to 1 is correct.
- return createLegacyEditingPosition(n, (moveType == Character) ? uncheckedNextOffset(n, o) : o + 1);
+ return createLegacyEditingPosition(node, (moveType == Character) ? uncheckedNextOffset(node, offset) : offset + 1);
}
- ContainerNode* parent = findParent(n);
+ ContainerNode* parent = node->parentNode();
if (!parent)
return *this;
- return createLegacyEditingPosition(parent, n->nodeIndex() + 1);
+ if (isRenderedTable(node) || editingIgnoresContent(*node))
+ return positionAfterNode(node);
+
+ Node* nextSibling = node->nextSibling();
+ if (nextSibling && positionBeforeOrAfterNodeIsCandidate(*nextSibling))
+ return positionBeforeNode(nextSibling);
+
+ return createLegacyEditingPosition(parent, node->computeNodeIndex() + 1);
}
int Position::uncheckedPreviousOffset(const Node* n, int current)
@@ -409,7 +434,7 @@ bool Position::atFirstEditingPositionForNode() const
return true;
case PositionIsAfterChildren:
case PositionIsAfterAnchor:
- return !lastOffsetForEditing(deprecatedNode());
+ return !lastOffsetForEditing(*deprecatedNode());
}
ASSERT_NOT_REACHED();
return false;
@@ -421,7 +446,7 @@ bool Position::atLastEditingPositionForNode() const
return true;
// FIXME: Position after anchor shouldn't be considered as at the first editing position for node
// since that position resides outside of the node.
- return m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || m_offset >= lastOffsetForEditing(deprecatedNode());
+ return m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || m_offset >= lastOffsetForEditing(*deprecatedNode());
}
// A position is considered at editing boundary if one of the following is true:
@@ -448,11 +473,11 @@ bool Position::atEditingBoundary() const
Node* Position::parentEditingBoundary() const
{
if (!m_anchorNode)
- return 0;
+ return nullptr;
Node* documentElement = m_anchorNode->document().documentElement();
if (!documentElement)
- return 0;
+ return nullptr;
Node* boundary = m_anchorNode.get();
while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && m_anchorNode->hasEditableStyle() == boundary->parentNode()->hasEditableStyle())
@@ -466,39 +491,75 @@ bool Position::atStartOfTree() const
{
if (isNull())
return true;
- return !findParent(deprecatedNode()) && m_offset <= 0;
+
+ Node* container = containerNode();
+ if (container && container->parentNode())
+ return false;
+
+ switch (m_anchorType) {
+ case PositionIsOffsetInAnchor:
+ return m_offset <= 0;
+ case PositionIsBeforeAnchor:
+ return !m_anchorNode->previousSibling();
+ case PositionIsAfterAnchor:
+ return false;
+ case PositionIsBeforeChildren:
+ return true;
+ case PositionIsAfterChildren:
+ return !lastOffsetForEditing(*m_anchorNode);
+ }
+ ASSERT_NOT_REACHED();
+ return false;
}
bool Position::atEndOfTree() const
{
if (isNull())
return true;
- return !findParent(deprecatedNode()) && m_offset >= lastOffsetForEditing(deprecatedNode());
+
+ Node* container = containerNode();
+ if (container && container->parentNode())
+ return false;
+
+ switch (m_anchorType) {
+ case PositionIsOffsetInAnchor:
+ return m_offset >= lastOffsetForEditing(*m_anchorNode);
+ case PositionIsBeforeAnchor:
+ return false;
+ case PositionIsAfterAnchor:
+ return !m_anchorNode->nextSibling();
+ case PositionIsBeforeChildren:
+ return !lastOffsetForEditing(*m_anchorNode);
+ case PositionIsAfterChildren:
+ return true;
+ }
+ ASSERT_NOT_REACHED();
+ return false;
}
// return first preceding DOM position rendered at a different location, or "this"
Position Position::previousCharacterPosition(EAffinity affinity) const
{
if (isNull())
- return Position();
+ return { };
Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity));
bool rendered = isCandidate();
- Position currentPos = *this;
- while (!currentPos.atStartOfTree()) {
- currentPos = currentPos.previous();
+ Position currentPosition = *this;
+ while (!currentPosition.atStartOfTree()) {
+ currentPosition = currentPosition.previous();
- if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
+ if (currentPosition.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
return *this;
if (atStartOfLine || !rendered) {
- if (currentPos.isCandidate())
- return currentPos;
- } else if (rendersInDifferentPosition(currentPos))
- return currentPos;
+ if (currentPosition.isCandidate())
+ return currentPosition;
+ } else if (rendersInDifferentPosition(currentPosition))
+ return currentPosition;
}
return *this;
@@ -508,25 +569,25 @@ Position Position::previousCharacterPosition(EAffinity affinity) const
Position Position::nextCharacterPosition(EAffinity affinity) const
{
if (isNull())
- return Position();
+ return { };
Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
- bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity));
+ bool atEndOfLine = isEndOfLine({ *this, affinity });
bool rendered = isCandidate();
- Position currentPos = *this;
- while (!currentPos.atEndOfTree()) {
- currentPos = currentPos.next();
+ Position currentPosition = *this;
+ while (!currentPosition.atEndOfTree()) {
+ currentPosition = currentPosition.next();
- if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
+ if (currentPosition.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
return *this;
if (atEndOfLine || !rendered) {
- if (currentPos.isCandidate())
- return currentPos;
- } else if (rendersInDifferentPosition(currentPos))
- return currentPos;
+ if (currentPosition.isCandidate())
+ return currentPosition;
+ } else if (rendersInDifferentPosition(currentPosition))
+ return currentPosition;
}
return *this;
@@ -545,11 +606,11 @@ static bool endsOfNodeAreVisuallyDistinctPositions(Node* node)
return true;
// Don't include inline tables.
- if (isHTMLTableElement(node))
+ if (is<HTMLTableElement>(*node))
return false;
// There is a VisiblePosition inside an empty inline-block container.
- return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild();
+ return node->renderer()->isReplaced() && canHaveChildrenForEditing(*node) && downcast<RenderBox>(*node->renderer()).height() && !node->firstChild();
}
static Node* enclosingVisualBoundary(Node* node)
@@ -583,85 +644,85 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const
{
Node* startNode = deprecatedNode();
if (!startNode)
- return Position();
+ return { };
// iterate backward from there, looking for a qualified position
Node* boundary = enclosingVisualBoundary(startNode);
// FIXME: PositionIterator should respect Before and After positions.
- PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
- PositionIterator currentPos = lastVisible;
+ PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(*m_anchorNode)) : *this;
+ PositionIterator currentPosition = lastVisible;
bool startEditable = startNode->hasEditableStyle();
Node* lastNode = startNode;
bool boundaryCrossed = false;
- for (; !currentPos.atStart(); currentPos.decrement()) {
- Node* currentNode = currentPos.node();
+ for (; !currentPosition.atStart(); currentPosition.decrement()) {
+ auto& currentNode = *currentPosition.node();
// Don't check for an editability change if we haven't moved to a different node,
// to avoid the expense of computing hasEditableStyle().
- if (currentNode != lastNode) {
+ if (&currentNode != lastNode) {
// Don't change editability.
- bool currentEditable = currentNode->hasEditableStyle();
+ bool currentEditable = currentNode.hasEditableStyle();
if (startEditable != currentEditable) {
if (rule == CannotCrossEditingBoundary)
break;
boundaryCrossed = true;
}
- lastNode = currentNode;
+ lastNode = &currentNode;
}
// If we've moved to a position that is visually distinct, return the last saved position. There
// is code below that terminates early if we're *about* to move to a visually distinct position.
- if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary)
+ if (endsOfNodeAreVisuallyDistinctPositions(&currentNode) && &currentNode != boundary)
return lastVisible;
// skip position in unrendered or invisible node
- RenderObject* renderer = currentNode->renderer();
+ RenderObject* renderer = currentNode.renderer();
if (!renderer || renderer->style().visibility() != VISIBLE)
continue;
if (rule == CanCrossEditingBoundary && boundaryCrossed) {
- lastVisible = currentPos;
+ lastVisible = currentPosition;
break;
}
// track last visible streamer position
- if (isStreamer(currentPos))
- lastVisible = currentPos;
+ if (isStreamer(currentPosition))
+ lastVisible = currentPosition;
// Don't move past a position that is visually distinct. We could rely on code above to terminate and
- // return lastVisible on the next iteration, but we terminate early to avoid doing a nodeIndex() call.
- if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.atStartOfNode())
+ // return lastVisible on the next iteration, but we terminate early to avoid doing a computeNodeIndex() call.
+ if (endsOfNodeAreVisuallyDistinctPositions(&currentNode) && currentPosition.atStartOfNode())
return lastVisible;
// Return position after tables and nodes which have content that can be ignored.
- if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) {
- if (currentPos.atEndOfNode())
- return positionAfterNode(currentNode);
+ if (editingIgnoresContent(currentNode) || isRenderedTable(&currentNode)) {
+ if (currentPosition.atEndOfNode())
+ return positionAfterNode(&currentNode);
continue;
}
// return current position if it is in rendered text
- if (renderer->isText()) {
- auto& textRenderer = toRenderText(*renderer);
+ if (is<RenderText>(*renderer)) {
+ auto& textRenderer = downcast<RenderText>(*renderer);
textRenderer.ensureLineBoxes();
if (!textRenderer.firstTextBox())
continue;
- if (currentNode != startNode) {
+ if (&currentNode != startNode) {
// This assertion fires in layout tests in the case-transform.html test because
// of a mix-up between offsets in the text in the DOM tree with text in the
// render tree which can have a different length due to case transformation.
// Until we resolve that, disable this so we can run the layout tests!
//ASSERT(currentOffset >= renderer->caretMaxOffset());
- return createLegacyEditingPosition(currentNode, renderer->caretMaxOffset());
+ return createLegacyEditingPosition(&currentNode, renderer->caretMaxOffset());
}
- unsigned textOffset = currentPos.offsetInLeafNode();
+ unsigned textOffset = currentPosition.offsetInLeafNode();
auto lastTextBox = textRenderer.lastTextBox();
- for (auto box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
+ for (auto* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
if (textOffset <= box->start() + box->len()) {
if (textOffset > box->start())
- return currentPos;
+ return currentPosition;
continue;
}
@@ -677,7 +738,7 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const
otherBox = otherBox->nextLeafChild();
if (!otherBox)
break;
- if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() > textOffset))
+ if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() > textOffset))
continuesOnNextLine = false;
}
@@ -686,12 +747,12 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const
otherBox = otherBox->prevLeafChild();
if (!otherBox)
break;
- if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() > textOffset))
+ if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() > textOffset))
continuesOnNextLine = false;
}
if (continuesOnNextLine)
- return currentPos;
+ return currentPosition;
}
}
}
@@ -710,86 +771,86 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const
{
Node* startNode = deprecatedNode();
if (!startNode)
- return Position();
+ return { };
// iterate forward from there, looking for a qualified position
Node* boundary = enclosingVisualBoundary(startNode);
// FIXME: PositionIterator should respect Before and After positions.
- PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
- PositionIterator currentPos = lastVisible;
+ PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(*m_anchorNode)) : *this;
+ PositionIterator currentPosition = lastVisible;
bool startEditable = startNode->hasEditableStyle();
Node* lastNode = startNode;
bool boundaryCrossed = false;
- for (; !currentPos.atEnd(); currentPos.increment()) {
- Node* currentNode = currentPos.node();
+ for (; !currentPosition.atEnd(); currentPosition.increment()) {
+ auto& currentNode = *currentPosition.node();
// Don't check for an editability change if we haven't moved to a different node,
// to avoid the expense of computing hasEditableStyle().
- if (currentNode != lastNode) {
+ if (&currentNode != lastNode) {
// Don't change editability.
- bool currentEditable = currentNode->hasEditableStyle();
+ bool currentEditable = currentNode.hasEditableStyle();
if (startEditable != currentEditable) {
if (rule == CannotCrossEditingBoundary)
break;
boundaryCrossed = true;
}
- lastNode = currentNode;
+ lastNode = &currentNode;
}
// stop before going above the body, up into the head
// return the last visible streamer position
- if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode())
+ if (is<HTMLBodyElement>(currentNode) && currentPosition.atEndOfNode())
break;
-
+
// Do not move to a visually distinct position.
- if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary)
+ if (endsOfNodeAreVisuallyDistinctPositions(&currentNode) && &currentNode != boundary)
return lastVisible;
// Do not move past a visually disinct position.
// Note: The first position after the last in a node whose ends are visually distinct
- // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1].
- if (boundary && boundary->parentNode() == currentNode)
+ // positions will be [boundary->parentNode(), originalBlock->computeNodeIndex() + 1].
+ if (boundary && boundary->parentNode() == &currentNode)
return lastVisible;
// skip position in unrendered or invisible node
- RenderObject* renderer = currentNode->renderer();
+ auto* renderer = currentNode.renderer();
if (!renderer || renderer->style().visibility() != VISIBLE)
continue;
if (rule == CanCrossEditingBoundary && boundaryCrossed) {
- lastVisible = currentPos;
+ lastVisible = currentPosition;
break;
}
// track last visible streamer position
- if (isStreamer(currentPos))
- lastVisible = currentPos;
+ if (isStreamer(currentPosition))
+ lastVisible = currentPosition;
// Return position before tables and nodes which have content that can be ignored.
- if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) {
- if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset())
- return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
+ if (editingIgnoresContent(currentNode) || isRenderedTable(&currentNode)) {
+ if (currentPosition.atStartOfNode())
+ return positionBeforeNode(&currentNode);
continue;
}
// return current position if it is in rendered text
- if (renderer->isText()) {
- auto& textRenderer = toRenderText(*renderer);
+ if (is<RenderText>(*renderer)) {
+ auto& textRenderer = downcast<RenderText>(*renderer);
textRenderer.ensureLineBoxes();
if (!textRenderer.firstTextBox())
continue;
- if (currentNode != startNode) {
- ASSERT(currentPos.atStartOfNode());
- return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
+ if (&currentNode != startNode) {
+ ASSERT(currentPosition.atStartOfNode());
+ return createLegacyEditingPosition(&currentNode, renderer->caretMinOffset());
}
- unsigned textOffset = currentPos.offsetInLeafNode();
+ unsigned textOffset = currentPosition.offsetInLeafNode();
auto lastTextBox = textRenderer.lastTextBox();
- for (auto box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
+ for (auto* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
if (textOffset <= box->end()) {
if (textOffset >= box->start())
- return currentPos;
+ return currentPosition;
continue;
}
@@ -805,7 +866,7 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const
otherBox = otherBox->nextLeafChild();
if (!otherBox)
break;
- if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() >= textOffset))
+ if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() >= textOffset))
continuesOnNextLine = false;
}
@@ -814,12 +875,12 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const
otherBox = otherBox->prevLeafChild();
if (!otherBox)
break;
- if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() >= textOffset))
+ if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() >= textOffset))
continuesOnNextLine = false;
}
if (continuesOnNextLine)
- return currentPos;
+ return currentPosition;
}
}
}
@@ -827,6 +888,30 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const
return lastVisible;
}
+unsigned Position::positionCountBetweenPositions(const Position& a, const Position& b)
+{
+ if (a.isNull() || b.isNull())
+ return UINT_MAX;
+
+ Position endPos;
+ Position pos;
+ if (a > b) {
+ endPos = a;
+ pos = b;
+ } else if (a < b) {
+ endPos = b;
+ pos = a;
+ } else
+ return 0;
+
+ unsigned posCount = 0;
+ while (!pos.atEndOfTree() && pos != endPos) {
+ pos = pos.next();
+ ++posCount;
+ }
+ return posCount;
+}
+
static int boundingBoxLogicalHeight(RenderObject *o, const IntRect &rect)
{
return o->style().isHorizontalWritingMode() ? rect.height() : rect.width();
@@ -838,23 +923,23 @@ bool Position::hasRenderedNonAnonymousDescendantsWithHeight(const RenderElement&
for (RenderObject* o = renderer.firstChild(); o && o != stop; o = o->nextInPreOrder()) {
if (!o->nonPseudoNode())
continue;
- if (o->isText()) {
- if (boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox()))
+ if (is<RenderText>(*o)) {
+ if (boundingBoxLogicalHeight(o, downcast<RenderText>(*o).linesBoundingBox()))
return true;
continue;
}
- if (o->isLineBreak()) {
- if (boundingBoxLogicalHeight(o, toRenderLineBreak(o)->linesBoundingBox()))
+ if (is<RenderLineBreak>(*o)) {
+ if (boundingBoxLogicalHeight(o, downcast<RenderLineBreak>(*o).linesBoundingBox()))
return true;
continue;
}
- if (o->isBox()) {
- if (toRenderBox(o)->pixelSnappedLogicalHeight())
+ if (is<RenderBox>(*o)) {
+ if (roundToInt(downcast<RenderBox>(*o).logicalHeight()))
return true;
continue;
}
- if (o->isRenderInline()) {
- const RenderInline& renderInline = toRenderInline(*o);
+ if (is<RenderInline>(*o)) {
+ const RenderInline& renderInline = downcast<RenderInline>(*o);
if (isEmptyInline(renderInline) && boundingBoxLogicalHeight(o, renderInline.linesBoundingBox()))
return true;
continue;
@@ -868,18 +953,6 @@ bool Position::nodeIsUserSelectNone(Node* node)
return node && node->renderer() && node->renderer()->style().userSelect() == SELECT_NONE;
}
-ContainerNode* Position::findParent(const Node* node)
-{
- // FIXME: See http://web.ug/82697
-
-#if ENABLE(SHADOW_DOM)
- if (RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled())
- return node->parentNode();
-#endif
-
- return node->nonShadowBoundaryParentNode();
-}
-
#if ENABLE(USERSELECT_ALL)
bool Position::nodeIsUserSelectAll(const Node* node)
{
@@ -889,7 +962,7 @@ bool Position::nodeIsUserSelectAll(const Node* node)
Node* Position::rootUserSelectAllForNode(Node* node)
{
if (!node || !nodeIsUserSelectAll(node))
- return 0;
+ return nullptr;
Node* parent = node->parentNode();
if (!parent)
return node;
@@ -913,52 +986,53 @@ bool Position::isCandidate() const
{
if (isNull())
return false;
-
- RenderObject* renderer = deprecatedNode()->renderer();
+
+ auto* renderer = deprecatedNode()->renderer();
if (!renderer)
return false;
-
+
if (renderer->style().visibility() != VISIBLE)
return false;
- if (renderer->isBR())
+ if (renderer->isBR()) {
// FIXME: The condition should be m_anchorType == PositionIsBeforeAnchor, but for now we still need to support legacy positions.
return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
+ }
- if (renderer->isText())
- return !nodeIsUserSelectNone(deprecatedNode()) && toRenderText(renderer)->containsCaretOffset(m_offset);
+ if (is<RenderText>(*renderer))
+ return !nodeIsUserSelectNone(deprecatedNode()) && downcast<RenderText>(*renderer).containsCaretOffset(m_offset);
- if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode()))
- return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
+ if (positionBeforeOrAfterNodeIsCandidate(*deprecatedNode())) {
+ return ((atFirstEditingPositionForNode() && m_anchorType == PositionIsBeforeAnchor)
+ || (atLastEditingPositionForNode() && m_anchorType == PositionIsAfterAnchor))
+ && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
+ }
- if (m_anchorNode->hasTagName(htmlTag))
+ if (is<HTMLHtmlElement>(*m_anchorNode))
return false;
-
- if (isRendererReplacedElement(renderer))
- return !nodeIsUserSelectNone(deprecatedNode()) && atFirstEditingPositionForNode();
- if (renderer->isRenderBlockFlow()) {
- RenderBlock& block = toRenderBlock(*renderer);
- if (block.logicalHeight() || m_anchorNode->hasTagName(bodyTag)) {
+ if (is<RenderBlockFlow>(*renderer) || is<RenderGrid>(*renderer) || is<RenderFlexibleBox>(*renderer)) {
+ RenderBlock& block = downcast<RenderBlock>(*renderer);
+ if (block.logicalHeight() || is<HTMLBodyElement>(*m_anchorNode)) {
if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(block))
return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode());
return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
}
- } else
- return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
+ return false;
+ }
- return false;
+ return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
}
bool Position::isRenderedCharacter() const
{
- if (isNull() || !deprecatedNode()->isTextNode())
+ if (!is<Text>(deprecatedNode()))
return false;
-
- RenderText* renderer = toText(deprecatedNode())->renderer();
+
+ RenderText* renderer = downcast<Text>(*deprecatedNode()).renderer();
if (!renderer)
return false;
-
+
return renderer->containsRenderedCharacterOffset(m_offset);
}
@@ -967,69 +1041,68 @@ static bool inSameEnclosingBlockFlowElement(Node* a, Node* b)
return a && b && deprecatedEnclosingBlockFlowElement(a) == deprecatedEnclosingBlockFlowElement(b);
}
-bool Position::rendersInDifferentPosition(const Position &pos) const
+bool Position::rendersInDifferentPosition(const Position& position) const
{
- if (isNull() || pos.isNull())
+ if (isNull() || position.isNull())
return false;
- RenderObject* renderer = deprecatedNode()->renderer();
+ auto* renderer = deprecatedNode()->renderer();
if (!renderer)
return false;
- RenderObject* posRenderer = pos.deprecatedNode()->renderer();
- if (!posRenderer)
+ auto* positionRenderer = position.deprecatedNode()->renderer();
+ if (!positionRenderer)
return false;
- if (renderer->style().visibility() != VISIBLE ||
- posRenderer->style().visibility() != VISIBLE)
+ if (renderer->style().visibility() != VISIBLE || positionRenderer->style().visibility() != VISIBLE)
return false;
- if (deprecatedNode() == pos.deprecatedNode()) {
- if (deprecatedNode()->hasTagName(brTag))
+ if (deprecatedNode() == position.deprecatedNode()) {
+ if (is<HTMLBRElement>(*deprecatedNode()))
return false;
- if (m_offset == pos.deprecatedEditingOffset())
+ if (m_offset == position.deprecatedEditingOffset())
return false;
-
- if (!deprecatedNode()->isTextNode() && !pos.deprecatedNode()->isTextNode()) {
- if (m_offset != pos.deprecatedEditingOffset())
+
+ if (!is<Text>(*deprecatedNode()) && !is<Text>(*position.deprecatedNode())) {
+ if (m_offset != position.deprecatedEditingOffset())
return true;
}
}
-
- if (deprecatedNode()->hasTagName(brTag) && pos.isCandidate())
+
+ if (is<HTMLBRElement>(*deprecatedNode()) && position.isCandidate())
return true;
-
- if (pos.deprecatedNode()->hasTagName(brTag) && isCandidate())
+
+ if (is<HTMLBRElement>(*position.deprecatedNode()) && isCandidate())
return true;
-
- if (!inSameEnclosingBlockFlowElement(deprecatedNode(), pos.deprecatedNode()))
+
+ if (!inSameEnclosingBlockFlowElement(deprecatedNode(), position.deprecatedNode()))
return true;
- if (renderer->isText() && !toRenderText(renderer)->containsCaretOffset(m_offset))
+ if (is<RenderText>(*renderer) && !downcast<RenderText>(*renderer).containsCaretOffset(m_offset))
return false;
- if (posRenderer->isText() && !toRenderText(posRenderer)->containsCaretOffset(pos.m_offset))
+ if (is<RenderText>(*positionRenderer) && !downcast<RenderText>(*positionRenderer).containsCaretOffset(position.m_offset))
return false;
- int thisRenderedOffset = renderer->isText() ? toRenderText(renderer)->countRenderedCharacterOffsetsUntil(m_offset) : m_offset;
- int posRenderedOffset = posRenderer->isText() ? toRenderText(posRenderer)->countRenderedCharacterOffsetsUntil(pos.m_offset) : pos.m_offset;
+ int thisRenderedOffset = is<RenderText>(*renderer) ? downcast<RenderText>(*renderer).countRenderedCharacterOffsetsUntil(m_offset) : m_offset;
+ int positionRenderedOffset = is<RenderText>(*positionRenderer) ? downcast<RenderText>(*positionRenderer).countRenderedCharacterOffsetsUntil(position.m_offset) : position.m_offset;
- if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
+ if (renderer == positionRenderer && thisRenderedOffset == positionRenderedOffset)
return false;
int ignoredCaretOffset;
InlineBox* b1;
getInlineBoxAndOffset(DOWNSTREAM, b1, ignoredCaretOffset);
InlineBox* b2;
- pos.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset);
+ position.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset);
LOG(Editing, "renderer: %p [%p]\n", renderer, b1);
LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset);
- LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2);
- LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset);
- LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(deprecatedNode()), caretMaxOffset(deprecatedNode()));
- LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.deprecatedNode()), caretMaxOffset(pos.deprecatedNode()));
+ LOG(Editing, "posRenderer: %p [%p]\n", positionRenderer, b2);
+ LOG(Editing, "posRenderedOffset: %d\n", positionRenderedOffset);
+ LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(*deprecatedNode()), caretMaxOffset(*deprecatedNode()));
+ LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(*position.deprecatedNode()), caretMaxOffset(*position.deprecatedNode()));
LOG(Editing, "----------------------------------------------------------------------\n");
if (!b1 || !b2) {
@@ -1040,13 +1113,13 @@ bool Position::rendersInDifferentPosition(const Position &pos) const
return true;
}
- if (nextRenderedEditable(deprecatedNode()) == pos.deprecatedNode()
- && thisRenderedOffset == caretMaxOffset(deprecatedNode()) && !posRenderedOffset) {
+ if (nextRenderedEditable(deprecatedNode()) == position.deprecatedNode()
+ && thisRenderedOffset == caretMaxOffset(*deprecatedNode()) && !positionRenderedOffset) {
return false;
}
- if (previousRenderedEditable(deprecatedNode()) == pos.deprecatedNode()
- && !thisRenderedOffset && posRenderedOffset == caretMaxOffset(pos.deprecatedNode())) {
+ if (previousRenderedEditable(deprecatedNode()) == position.deprecatedNode()
+ && !thisRenderedOffset && positionRenderedOffset == caretMaxOffset(*position.deprecatedNode())) {
return false;
}
@@ -1058,21 +1131,21 @@ Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNo
{
ASSERT(isEditablePosition(*this));
if (isNull())
- return Position();
+ return { };
- if (upstream().deprecatedNode()->hasTagName(brTag))
- return Position();
+ if (is<HTMLBRElement>(*upstream().deprecatedNode()))
+ return { };
Position prev = previousCharacterPosition(affinity);
- if (prev != *this && inSameEnclosingBlockFlowElement(deprecatedNode(), prev.deprecatedNode()) && prev.deprecatedNode()->isTextNode()) {
- String string = toText(prev.deprecatedNode())->data();
+ if (prev != *this && inSameEnclosingBlockFlowElement(deprecatedNode(), prev.deprecatedNode()) && is<Text>(*prev.deprecatedNode())) {
+ String string = downcast<Text>(*prev.deprecatedNode()).data();
UChar c = string[prev.deprecatedEditingOffset()];
- if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
+ if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : deprecatedIsCollapsibleWhitespace(c))
if (isEditablePosition(prev))
return prev;
}
- return Position();
+ return { };
}
// This assumes that it starts in editable content.
@@ -1080,16 +1153,16 @@ Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollaps
{
ASSERT(isEditablePosition(*this));
if (isNull())
- return Position();
+ return { };
VisiblePosition v(*this);
UChar c = v.characterAfter();
// The space must not be in another paragraph and it must be editable.
if (!isEndOfParagraph(v) && v.next(CannotCrossEditingBoundary).isNotNull())
- if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
+ if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : deprecatedIsCollapsibleWhitespace(c))
return *this;
- return Position();
+ return { };
}
void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const
@@ -1097,13 +1170,11 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox,
getInlineBoxAndOffset(affinity, primaryDirection(), inlineBox, caretOffset);
}
-static bool isNonTextLeafChild(RenderObject* object)
+static bool isNonTextLeafChild(RenderObject& object)
{
- if (object->isText())
- return false;
- if (toRenderElement(object)->firstChild())
+ if (is<RenderText>(object))
return false;
- return true;
+ return !downcast<RenderElement>(object).firstChild();
}
static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer)
@@ -1111,16 +1182,16 @@ static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer)
RenderBlock* container = renderer->containingBlock();
RenderObject* next = renderer;
while ((next = next->nextInPreOrder(container))) {
- if (next->isRenderBlock())
- return 0;
+ if (is<RenderBlock>(*next))
+ return nullptr;
if (next->isBR())
- return 0;
- if (isNonTextLeafChild(next))
- return 0;
- if (next->isText()) {
- InlineTextBox* match = 0;
+ return nullptr;
+ if (isNonTextLeafChild(*next))
+ return nullptr;
+ if (is<RenderText>(*next)) {
+ InlineTextBox* match = nullptr;
int minOffset = INT_MAX;
- for (InlineTextBox* box = toRenderText(next)->firstTextBox(); box; box = box->nextTextBox()) {
+ for (InlineTextBox* box = downcast<RenderText>(*next).firstTextBox(); box; box = box->nextTextBox()) {
int caretMinOffset = box->caretMinOffset();
if (caretMinOffset < minOffset) {
match = box;
@@ -1131,7 +1202,7 @@ static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer)
return match;
}
}
- return 0;
+ return nullptr;
}
static Position downstreamIgnoringEditingBoundaries(Position position)
@@ -1160,15 +1231,15 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi
RenderObject* renderer = deprecatedNode()->renderer();
if (renderer->isBR())
- inlineBox = !caretOffset ? toRenderLineBreak(renderer)->inlineBoxWrapper() : nullptr;
- else if (renderer->isText()) {
- auto textRenderer = toRenderText(renderer);
- textRenderer->ensureLineBoxes();
+ inlineBox = !caretOffset ? downcast<RenderLineBreak>(*renderer).inlineBoxWrapper() : nullptr;
+ else if (is<RenderText>(*renderer)) {
+ auto& textRenderer = downcast<RenderText>(*renderer);
+ textRenderer.ensureLineBoxes();
InlineTextBox* box;
- InlineTextBox* candidate = 0;
+ InlineTextBox* candidate = nullptr;
- for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+ for (box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
int caretMinOffset = box->caretMinOffset();
int caretMaxOffset = box->caretMaxOffset();
@@ -1187,15 +1258,15 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi
candidate = box;
}
- if (candidate && candidate == textRenderer->lastTextBox() && affinity == DOWNSTREAM) {
- box = searchAheadForBetterMatch(textRenderer);
+ if (candidate && candidate == textRenderer.lastTextBox() && affinity == DOWNSTREAM) {
+ box = searchAheadForBetterMatch(&textRenderer);
if (box)
caretOffset = box->caretMinOffset();
}
inlineBox = box ? box : candidate;
} else {
- inlineBox = 0;
- if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isRenderBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(toRenderBlock(*renderer))) {
+ inlineBox = nullptr;
+ if (canHaveChildrenForEditing(*deprecatedNode()) && is<RenderBlockFlow>(*renderer) && hasRenderedNonAnonymousDescendantsWithHeight(downcast<RenderBlockFlow>(*renderer))) {
// Try a visually equivalent position with possibly opposite editability. This helps in case |this| is in
// an editable block but surrounded by non-editable positions. It acts to negate the logic at the beginning
// of RenderObject::createVisiblePosition().
@@ -1209,8 +1280,8 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi
equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineBox, caretOffset);
return;
}
- if (renderer->isBox()) {
- inlineBox = toRenderBox(renderer)->inlineBoxWrapper();
+ if (is<RenderBox>(*renderer)) {
+ inlineBox = downcast<RenderBox>(*renderer).inlineBoxWrapper();
if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset()))
return;
}
@@ -1310,17 +1381,14 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi
TextDirection Position::primaryDirection() const
{
- TextDirection primaryDirection = LTR;
- for (const RenderObject* r = m_anchorNode->renderer(); r; r = r->parent()) {
- if (r->isRenderBlockFlow()) {
- primaryDirection = toRenderBlockFlow(r)->style().direction();
- break;
- }
- }
-
- return primaryDirection;
+ if (!m_anchorNode->renderer())
+ return LTR;
+ if (auto* blockFlow = lineageOfType<RenderBlockFlow>(*m_anchorNode->renderer()).first())
+ return blockFlow->style().direction();
+ return LTR;
}
+#if ENABLE(TREE_DEBUGGING)
void Position::debugPosition(const char* msg) const
{
@@ -1330,8 +1398,6 @@ void Position::debugPosition(const char* msg) const
fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, deprecatedNode()->nodeName().utf8().data(), deprecatedNode(), m_offset);
}
-#ifndef NDEBUG
-
void Position::formatForDebugger(char* buffer, unsigned length) const
{
StringBuilder result;
@@ -1384,11 +1450,139 @@ void Position::showTreeForThis() const
#endif
+bool Position::equals(const Position& other) const
+{
+ if (!m_anchorNode)
+ return !m_anchorNode == !other.m_anchorNode;
+ if (!other.m_anchorNode)
+ return false;
+
+ switch (anchorType()) {
+ case PositionIsBeforeChildren:
+ ASSERT(!is<Text>(*m_anchorNode));
+ switch (other.anchorType()) {
+ case PositionIsBeforeChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode;
+ case PositionIsAfterChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode && !m_anchorNode->hasChildNodes();
+ case PositionIsOffsetInAnchor:
+ return m_anchorNode == other.m_anchorNode && !other.m_offset;
+ case PositionIsBeforeAnchor:
+ return m_anchorNode->firstChild() == other.m_anchorNode;
+ case PositionIsAfterAnchor:
+ return false;
+ }
+ break;
+ case PositionIsAfterChildren:
+ ASSERT(!is<Text>(*m_anchorNode));
+ switch (other.anchorType()) {
+ case PositionIsBeforeChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode && !m_anchorNode->hasChildNodes();
+ case PositionIsAfterChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode;
+ case PositionIsOffsetInAnchor:
+ return m_anchorNode == other.m_anchorNode && m_anchorNode->countChildNodes() == static_cast<unsigned>(m_offset);
+ case PositionIsBeforeAnchor:
+ return false;
+ case PositionIsAfterAnchor:
+ return m_anchorNode->lastChild() == other.m_anchorNode;
+ }
+ break;
+ case PositionIsOffsetInAnchor:
+ switch (other.anchorType()) {
+ case PositionIsBeforeChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode && !m_offset;
+ case PositionIsAfterChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode && m_offset == static_cast<int>(other.m_anchorNode->countChildNodes());
+ case PositionIsOffsetInAnchor:
+ return m_anchorNode == other.m_anchorNode && m_offset == other.m_offset;
+ case PositionIsBeforeAnchor:
+ return m_anchorNode->traverseToChildAt(m_offset) == other.m_anchorNode;
+ case PositionIsAfterAnchor:
+ return m_offset && m_anchorNode->traverseToChildAt(m_offset - 1) == other.m_anchorNode;
+ }
+ break;
+ case PositionIsBeforeAnchor:
+ switch (other.anchorType()) {
+ case PositionIsBeforeChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode->firstChild();
+ case PositionIsAfterChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return false;
+ case PositionIsOffsetInAnchor:
+ return m_anchorNode == other.m_anchorNode->traverseToChildAt(other.m_offset);
+ case PositionIsBeforeAnchor:
+ return m_anchorNode == other.m_anchorNode;
+ case PositionIsAfterAnchor:
+ return m_anchorNode->previousSibling() == other.m_anchorNode;
+ }
+ break;
+ case PositionIsAfterAnchor:
+ switch (other.anchorType()) {
+ case PositionIsBeforeChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return false;
+ case PositionIsAfterChildren:
+ ASSERT(!is<Text>(*other.m_anchorNode));
+ return m_anchorNode == other.m_anchorNode->lastChild();
+ case PositionIsOffsetInAnchor:
+ return other.m_offset && m_anchorNode == other.m_anchorNode->traverseToChildAt(other.m_offset - 1);
+ case PositionIsBeforeAnchor:
+ return m_anchorNode->nextSibling() == other.m_anchorNode;
+ case PositionIsAfterAnchor:
+ return m_anchorNode == other.m_anchorNode;
+ }
+ break;
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+static TextStream& operator<<(TextStream& stream, Position::AnchorType anchorType)
+{
+ switch (anchorType) {
+ case Position::PositionIsOffsetInAnchor:
+ stream << "offset in anchor";
+ break;
+ case Position::PositionIsBeforeAnchor:
+ stream << "before anchor";
+ break;
+ case Position::PositionIsAfterAnchor:
+ stream << "after anchor";
+ break;
+ case Position::PositionIsBeforeChildren:
+ stream << "before children";
+ break;
+ case Position::PositionIsAfterChildren:
+ stream << "after children";
+ break;
+ }
+ return stream;
+}
+
+TextStream& operator<<(TextStream& stream, const Position& position)
+{
+ TextStream::GroupScope scope(stream);
+ stream << "Position " << &position;
+ stream.dumpProperty("anchor node", position.anchorNode());
+ stream.dumpProperty("offset", position.offsetInContainerNode());
+ stream.dumpProperty("anchor type", position.anchorType());
+
+ return stream;
+}
} // namespace WebCore
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showTree(const WebCore::Position& pos)
{