This source file includes following definitions.
- nextRenderedEditable
- previousRenderedEditable
- m_isLegacyEditingPosition
- m_isLegacyEditingPosition
- m_isLegacyEditingPosition
- m_isLegacyEditingPosition
- moveToPosition
- moveToOffset
- containerNode
- containerText
- computeOffsetInContainerNode
- offsetForPositionAfterAnchor
- parentAnchoredEquivalent
- computeNodeBeforePosition
- computeNodeAfterPosition
- anchorTypeForLegacyEditingPosition
- element
- computedStyle
- previous
- next
- uncheckedPreviousOffset
- uncheckedPreviousOffsetForBackwardDeletion
- uncheckedNextOffset
- atFirstEditingPositionForNode
- atLastEditingPositionForNode
- atEditingBoundary
- parentEditingBoundary
- atStartOfTree
- atEndOfTree
- renderedOffset
- previousCharacterPosition
- nextCharacterPosition
- endsOfNodeAreVisuallyDistinctPositions
- enclosingVisualBoundary
- isStreamer
- upstream
- downstream
- boundingBoxLogicalHeight
- hasRenderedNonAnonymousDescendantsWithHeight
- nodeIsUserSelectNone
- nodeIsUserSelectAll
- rootUserSelectAllForNode
- isCandidate
- inRenderedText
- isRenderedCharacter
- rendersInDifferentPosition
- leadingWhitespacePosition
- trailingWhitespacePosition
- getInlineBoxAndOffset
- isNonTextLeafChild
- searchAheadForBetterMatch
- downstreamIgnoringEditingBoundaries
- upstreamIgnoringEditingBoundaries
- getInlineBoxAndOffset
- primaryDirection
- debugPosition
- formatForDebugger
- showAnchorTypeAndOffset
- showTreeForThis
- showTree
- showTree
#include "config.h"
#include "core/dom/Position.h"
#include <stdio.h>
#include "HTMLNames.h"
#include "core/css/CSSComputedStyleDeclaration.h"
#include "core/dom/PositionIterator.h"
#include "core/dom/Text.h"
#include "core/editing/TextIterator.h"
#include "core/editing/VisiblePosition.h"
#include "core/editing/VisibleUnits.h"
#include "core/editing/htmlediting.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLTableElement.h"
#include "core/rendering/InlineIterator.h"
#include "core/rendering/InlineTextBox.h"
#include "core/rendering/RenderBlock.h"
#include "core/rendering/RenderInline.h"
#include "core/rendering/RenderText.h"
#include "platform/Logging.h"
#include "wtf/text/CString.h"
#include "wtf/unicode/CharacterNames.h"
namespace WebCore {
using namespace HTMLNames;
static Node* nextRenderedEditable(Node* node)
{
while ((node = node->nextLeafNode())) {
RenderObject* renderer = node->renderer();
if (!renderer)
continue;
if (!node->rendererIsEditable())
continue;
if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox()))
return node;
}
return 0;
}
static Node* previousRenderedEditable(Node* node)
{
while ((node = node->previousLeafNode())) {
RenderObject* renderer = node->renderer();
if (!renderer)
continue;
if (!node->rendererIsEditable())
continue;
if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox()))
return node;
}
return 0;
}
Position::Position(PassRefPtr<Node> anchorNode, LegacyEditingOffset offset)
: m_anchorNode(anchorNode)
, m_offset(offset.value())
, m_anchorType(anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset))
, m_isLegacyEditingPosition(true)
{
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
}
Position::Position(PassRefPtr<Node> anchorNode, AnchorType anchorType)
: m_anchorNode(anchorNode)
, m_offset(0)
, m_anchorType(anchorType)
, m_isLegacyEditingPosition(false)
{
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
ASSERT(anchorType != PositionIsOffsetInAnchor);
ASSERT(!((anchorType == PositionIsBeforeChildren || anchorType == PositionIsAfterChildren)
&& (m_anchorNode->isTextNode() || editingIgnoresContent(m_anchorNode.get()))));
}
Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorType)
: m_anchorNode(anchorNode)
, m_offset(offset)
, m_anchorType(anchorType)
, m_isLegacyEditingPosition(false)
{
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
ASSERT(anchorType == PositionIsOffsetInAnchor);
}
Position::Position(PassRefPtr<Text> textNode, unsigned offset)
: m_anchorNode(textNode)
, m_offset(static_cast<int>(offset))
, m_anchorType(PositionIsOffsetInAnchor)
, m_isLegacyEditingPosition(false)
{
ASSERT(m_anchorNode);
}
void Position::moveToPosition(PassRefPtr<Node> node, int offset)
{
ASSERT(!editingIgnoresContent(node.get()));
ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition);
m_anchorNode = node;
m_offset = offset;
if (m_isLegacyEditingPosition)
m_anchorType = anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset);
}
void Position::moveToOffset(int offset)
{
ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition);
m_offset = offset;
if (m_isLegacyEditingPosition)
m_anchorType = anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset);
}
Node* Position::containerNode() const
{
if (!m_anchorNode)
return 0;
switch (anchorType()) {
case PositionIsBeforeChildren:
case PositionIsAfterChildren:
case PositionIsOffsetInAnchor:
return m_anchorNode.get();
case PositionIsBeforeAnchor:
case PositionIsAfterAnchor:
return m_anchorNode->parentNode();
}
ASSERT_NOT_REACHED();
return 0;
}
Text* Position::containerText() const
{
switch (anchorType()) {
case PositionIsOffsetInAnchor:
return m_anchorNode && m_anchorNode->isTextNode() ? toText(m_anchorNode) : 0;
case PositionIsBeforeAnchor:
case PositionIsAfterAnchor:
return 0;
case PositionIsBeforeChildren:
case PositionIsAfterChildren:
ASSERT(!m_anchorNode || !m_anchorNode->isTextNode());
return 0;
}
ASSERT_NOT_REACHED();
return 0;
}
int Position::computeOffsetInContainerNode() const
{
if (!m_anchorNode)
return 0;
switch (anchorType()) {
case PositionIsBeforeChildren:
return 0;
case PositionIsAfterChildren:
return lastOffsetInNode(m_anchorNode.get());
case PositionIsOffsetInAnchor:
return minOffsetForNode(m_anchorNode.get(), m_offset);
case PositionIsBeforeAnchor:
return m_anchorNode->nodeIndex();
case PositionIsAfterAnchor:
return m_anchorNode->nodeIndex() + 1;
}
ASSERT_NOT_REACHED();
return 0;
}
int Position::offsetForPositionAfterAnchor() const
{
ASSERT(m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren);
ASSERT(!m_isLegacyEditingPosition);
return lastOffsetForEditing(m_anchorNode.get());
}
Position Position::parentAnchoredEquivalent() const
{
if (!m_anchorNode)
return Position();
if (m_offset <= 0 && (m_anchorType != PositionIsAfterAnchor && m_anchorType != PositionIsAfterChildren)) {
if (m_anchorNode->parentNode() && (editingIgnoresContent(m_anchorNode.get()) || isRenderedTableElement(m_anchorNode.get())))
return positionInParentBeforeNode(*m_anchorNode);
return Position(m_anchorNode.get(), 0, PositionIsOffsetInAnchor);
}
if (!m_anchorNode->offsetInCharacters()
&& (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->countChildren())
&& (editingIgnoresContent(m_anchorNode.get()) || isRenderedTableElement(m_anchorNode.get()))
&& containerNode()) {
return positionInParentAfterNode(*m_anchorNode);
}
return Position(containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor);
}
Node* Position::computeNodeBeforePosition() const
{
if (!m_anchorNode)
return 0;
switch (anchorType()) {
case PositionIsBeforeChildren:
return 0;
case PositionIsAfterChildren:
return m_anchorNode->lastChild();
case PositionIsOffsetInAnchor:
return m_anchorNode->traverseToChildAt(m_offset - 1);
case PositionIsBeforeAnchor:
return m_anchorNode->previousSibling();
case PositionIsAfterAnchor:
return m_anchorNode.get();
}
ASSERT_NOT_REACHED();
return 0;
}
Node* Position::computeNodeAfterPosition() const
{
if (!m_anchorNode)
return 0;
switch (anchorType()) {
case PositionIsBeforeChildren:
return m_anchorNode->firstChild();
case PositionIsAfterChildren:
return 0;
case PositionIsOffsetInAnchor:
return m_anchorNode->traverseToChildAt(m_offset);
case PositionIsBeforeAnchor:
return m_anchorNode.get();
case PositionIsAfterAnchor:
return m_anchorNode->nextSibling();
}
ASSERT_NOT_REACHED();
return 0;
}
Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNode, int offset)
{
if (anchorNode && editingIgnoresContent(anchorNode)) {
if (offset == 0)
return Position::PositionIsBeforeAnchor;
return Position::PositionIsAfterAnchor;
}
return Position::PositionIsOffsetInAnchor;
}
Element* Position::element() const
{
Node* n = anchorNode();
while (n && !n->isElementNode())
n = n->parentNode();
return toElement(n);
}
PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const
{
Element* elem = element();
if (!elem)
return nullptr;
return CSSComputedStyleDeclaration::create(elem);
}
Position Position::previous(PositionMoveType moveType) const
{
Node* node = deprecatedNode();
if (!node)
return *this;
int offset = deprecatedEditingOffset();
ASSERT(offset >= 0);
if (offset > 0) {
if (Node* child = node->traverseToChildAt(offset - 1))
return lastPositionInOrAfterNode(child);
switch (moveType) {
case CodePoint:
return createLegacyEditingPosition(node, offset - 1);
case Character:
return createLegacyEditingPosition(node, uncheckedPreviousOffset(node, offset));
case BackwardDeletion:
return createLegacyEditingPosition(node, uncheckedPreviousOffsetForBackwardDeletion(node, offset));
}
}
if (ContainerNode* parent = node->parentNode())
return createLegacyEditingPosition(parent, node->nodeIndex());
return *this;
}
Position Position::next(PositionMoveType moveType) const
{
ASSERT(moveType != BackwardDeletion);
Node* node = deprecatedNode();
if (!node)
return *this;
int offset = deprecatedEditingOffset();
ASSERT(offset >= 0);
if (Node* child = node->traverseToChildAt(offset))
return firstPositionInOrBeforeNode(child);
if (!node->hasChildren() && offset < lastOffsetForEditing(node)) {
return createLegacyEditingPosition(node, (moveType == Character) ? uncheckedNextOffset(node, offset) : offset + 1);
}
if (ContainerNode* parent = node->parentNode())
return createLegacyEditingPosition(parent, node->nodeIndex() + 1);
return *this;
}
int Position::uncheckedPreviousOffset(const Node* n, int current)
{
return n->renderer() ? n->renderer()->previousOffset(current) : current - 1;
}
int Position::uncheckedPreviousOffsetForBackwardDeletion(const Node* n, int current)
{
return n->renderer() ? n->renderer()->previousOffsetForBackwardDeletion(current) : current - 1;
}
int Position::uncheckedNextOffset(const Node* n, int current)
{
return n->renderer() ? n->renderer()->nextOffset(current) : current + 1;
}
bool Position::atFirstEditingPositionForNode() const
{
if (isNull())
return true;
switch (m_anchorType) {
case PositionIsOffsetInAnchor:
return m_offset <= 0;
case PositionIsBeforeChildren:
case PositionIsBeforeAnchor:
return true;
case PositionIsAfterChildren:
case PositionIsAfterAnchor:
return !lastOffsetForEditing(deprecatedNode());
}
ASSERT_NOT_REACHED();
return false;
}
bool Position::atLastEditingPositionForNode() const
{
if (isNull())
return true;
return m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || m_offset >= lastOffsetForEditing(deprecatedNode());
}
bool Position::atEditingBoundary() const
{
Position nextPosition = downstream(CanCrossEditingBoundary);
if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable())
return true;
Position prevPosition = upstream(CanCrossEditingBoundary);
if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable())
return true;
return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable()
&& prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable();
}
Node* Position::parentEditingBoundary() const
{
if (!m_anchorNode)
return 0;
Node* documentElement = m_anchorNode->document().documentElement();
if (!documentElement)
return 0;
Node* boundary = m_anchorNode.get();
while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && m_anchorNode->rendererIsEditable() == boundary->parentNode()->rendererIsEditable())
boundary = boundary->nonShadowBoundaryParentNode();
return boundary;
}
bool Position::atStartOfTree() const
{
if (isNull())
return true;
return !deprecatedNode()->parentNode() && m_offset <= 0;
}
bool Position::atEndOfTree() const
{
if (isNull())
return true;
return !deprecatedNode()->parentNode() && m_offset >= lastOffsetForEditing(deprecatedNode());
}
int Position::renderedOffset() const
{
if (!deprecatedNode()->isTextNode())
return m_offset;
if (!deprecatedNode()->renderer())
return m_offset;
int result = 0;
RenderText* textRenderer = toRenderText(deprecatedNode()->renderer());
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
int start = box->start();
int end = box->start() + box->len();
if (m_offset < start)
return result;
if (m_offset <= end) {
result += m_offset - start;
return result;
}
result += box->len();
}
return result;
}
Position Position::previousCharacterPosition(EAffinity affinity) const
{
if (isNull())
return Position();
Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity));
bool rendered = isCandidate();
Position currentPos = *this;
while (!currentPos.atStartOfTree()) {
currentPos = currentPos.previous();
if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
return *this;
if (atStartOfLine || !rendered) {
if (currentPos.isCandidate())
return currentPos;
} else if (rendersInDifferentPosition(currentPos))
return currentPos;
}
return *this;
}
Position Position::nextCharacterPosition(EAffinity affinity) const
{
if (isNull())
return Position();
Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity));
bool rendered = isCandidate();
Position currentPos = *this;
while (!currentPos.atEndOfTree()) {
currentPos = currentPos.next();
if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
return *this;
if (atEndOfLine || !rendered) {
if (currentPos.isCandidate())
return currentPos;
} else if (rendersInDifferentPosition(currentPos))
return currentPos;
}
return *this;
}
static bool endsOfNodeAreVisuallyDistinctPositions(Node* node)
{
if (!node || !node->renderer())
return false;
if (!node->renderer()->isInline())
return true;
if (isHTMLTableElement(*node))
return false;
if (isHTMLMarqueeElement(*node))
return true;
return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild();
}
static Node* enclosingVisualBoundary(Node* node)
{
while (node && !endsOfNodeAreVisuallyDistinctPositions(node))
node = node->parentNode();
return node;
}
static bool isStreamer(const PositionIterator& pos)
{
if (!pos.node())
return true;
if (isAtomicNode(pos.node()))
return true;
return pos.atStartOfNode();
}
Position Position::upstream(EditingBoundaryCrossingRule rule) const
{
Node* startNode = deprecatedNode();
if (!startNode)
return Position();
Node* boundary = enclosingVisualBoundary(startNode);
PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
PositionIterator currentPos = lastVisible;
bool startEditable = startNode->rendererIsEditable();
Node* lastNode = startNode;
bool boundaryCrossed = false;
for (; !currentPos.atStart(); currentPos.decrement()) {
Node* currentNode = currentPos.node();
if (currentNode != lastNode) {
bool currentEditable = currentNode->rendererIsEditable();
if (startEditable != currentEditable) {
if (rule == CannotCrossEditingBoundary)
break;
boundaryCrossed = true;
}
lastNode = currentNode;
}
if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary)
return lastVisible;
RenderObject* renderer = currentNode->renderer();
if (!renderer || renderer->style()->visibility() != VISIBLE)
continue;
if (rule == CanCrossEditingBoundary && boundaryCrossed) {
lastVisible = currentPos;
break;
}
if (isStreamer(currentPos))
lastVisible = currentPos;
if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.atStartOfNode())
return lastVisible;
if (editingIgnoresContent(currentNode) || isRenderedTableElement(currentNode)) {
if (currentPos.atEndOfNode())
return positionAfterNode(currentNode);
continue;
}
if (renderer->isText() && toRenderText(renderer)->firstTextBox()) {
if (currentNode != startNode) {
return createLegacyEditingPosition(currentNode, renderer->caretMaxOffset());
}
unsigned textOffset = currentPos.offsetInLeafNode();
RenderText* textRenderer = toRenderText(renderer);
InlineTextBox* lastTextBox = textRenderer->lastTextBox();
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (textOffset <= box->start() + box->len()) {
if (textOffset > box->start())
return currentPos;
continue;
}
if (box == lastTextBox || textOffset != box->start() + box->len() + 1)
continue;
bool continuesOnNextLine = true;
InlineBox* otherBox = box;
while (continuesOnNextLine) {
otherBox = otherBox->nextLeafChild();
if (!otherBox)
break;
if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() > textOffset))
continuesOnNextLine = false;
}
otherBox = box;
while (continuesOnNextLine) {
otherBox = otherBox->prevLeafChild();
if (!otherBox)
break;
if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() > textOffset))
continuesOnNextLine = false;
}
if (continuesOnNextLine)
return currentPos;
}
}
}
return lastVisible;
}
Position Position::downstream(EditingBoundaryCrossingRule rule) const
{
Node* startNode = deprecatedNode();
if (!startNode)
return Position();
Node* boundary = enclosingVisualBoundary(startNode);
PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
PositionIterator currentPos = lastVisible;
bool startEditable = startNode->rendererIsEditable();
Node* lastNode = startNode;
bool boundaryCrossed = false;
for (; !currentPos.atEnd(); currentPos.increment()) {
Node* currentNode = currentPos.node();
if (currentNode != lastNode) {
bool currentEditable = currentNode->rendererIsEditable();
if (startEditable != currentEditable) {
if (rule == CannotCrossEditingBoundary)
break;
boundaryCrossed = true;
}
lastNode = currentNode;
}
if (isHTMLBodyElement(*currentNode) && currentPos.atEndOfNode())
break;
if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary)
return lastVisible;
if (boundary && boundary->parentNode() == currentNode)
return lastVisible;
RenderObject* renderer = currentNode->renderer();
if (!renderer || renderer->style()->visibility() != VISIBLE)
continue;
if (rule == CanCrossEditingBoundary && boundaryCrossed) {
lastVisible = currentPos;
break;
}
if (isStreamer(currentPos))
lastVisible = currentPos;
if (editingIgnoresContent(currentNode) || isRenderedTableElement(currentNode)) {
if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset())
return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
continue;
}
if (renderer->isText() && toRenderText(renderer)->firstTextBox()) {
if (currentNode != startNode) {
ASSERT(currentPos.atStartOfNode());
return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
}
unsigned textOffset = currentPos.offsetInLeafNode();
RenderText* textRenderer = toRenderText(renderer);
InlineTextBox* lastTextBox = textRenderer->lastTextBox();
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (textOffset <= box->end()) {
if (textOffset >= box->start())
return currentPos;
continue;
}
if (box == lastTextBox || textOffset != box->start() + box->len())
continue;
bool continuesOnNextLine = true;
InlineBox* otherBox = box;
while (continuesOnNextLine) {
otherBox = otherBox->nextLeafChild();
if (!otherBox)
break;
if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() >= textOffset))
continuesOnNextLine = false;
}
otherBox = box;
while (continuesOnNextLine) {
otherBox = otherBox->prevLeafChild();
if (!otherBox)
break;
if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() >= textOffset))
continuesOnNextLine = false;
}
if (continuesOnNextLine)
return currentPos;
}
}
}
return lastVisible;
}
static int boundingBoxLogicalHeight(RenderObject *o, const IntRect &rect)
{
return o->style()->isHorizontalWritingMode() ? rect.height() : rect.width();
}
bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* renderer)
{
RenderObject* stop = renderer->nextInPreOrderAfterChildren();
for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextInPreOrder())
if (o->nonPseudoNode()) {
if ((o->isText() && boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox()))
|| (o->isBox() && toRenderBox(o)->pixelSnappedLogicalHeight())
|| (o->isRenderInline() && isEmptyInline(o) && boundingBoxLogicalHeight(o, toRenderInline(o)->linesBoundingBox())))
return true;
}
return false;
}
bool Position::nodeIsUserSelectNone(Node* node)
{
return node && node->renderer() && !node->renderer()->isSelectable();
}
bool Position::nodeIsUserSelectAll(const Node* node)
{
return RuntimeEnabledFeatures::userSelectAllEnabled() && node && node->renderer() && node->renderer()->style()->userSelect() == SELECT_ALL;
}
Node* Position::rootUserSelectAllForNode(Node* node)
{
if (!node || !nodeIsUserSelectAll(node))
return 0;
Node* parent = node->parentNode();
if (!parent)
return node;
Node* candidateRoot = node;
while (parent) {
if (!parent->renderer()) {
parent = parent->parentNode();
continue;
}
if (!nodeIsUserSelectAll(parent))
break;
candidateRoot = parent;
parent = candidateRoot->parentNode();
}
return candidateRoot;
}
bool Position::isCandidate() const
{
if (isNull())
return false;
RenderObject* renderer = deprecatedNode()->renderer();
if (!renderer)
return false;
if (renderer->style()->visibility() != VISIBLE)
return false;
if (renderer->isBR())
return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
if (renderer->isText())
return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText();
if (isRenderedTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode()))
return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
if (isHTMLHtmlElement(*m_anchorNode))
return false;
if (renderer->isRenderBlockFlow()) {
if (toRenderBlock(renderer)->logicalHeight() || isHTMLBodyElement(*m_anchorNode)) {
if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer))
return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode());
return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
}
} else {
LocalFrame* frame = m_anchorNode->document().frame();
bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsingEnabled();
return (caretBrowsing || m_anchorNode->rendererIsEditable()) && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
}
return false;
}
bool Position::inRenderedText() const
{
if (isNull() || !deprecatedNode()->isTextNode())
return false;
RenderObject* renderer = deprecatedNode()->renderer();
if (!renderer)
return false;
RenderText *textRenderer = toRenderText(renderer);
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (m_offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) {
return false;
}
if (box->containsCaretOffset(m_offset))
return m_offset == 0 || m_offset == textRenderer->nextOffset(textRenderer->previousOffset(m_offset));
}
return false;
}
bool Position::isRenderedCharacter() const
{
if (isNull() || !deprecatedNode()->isTextNode())
return false;
RenderObject* renderer = deprecatedNode()->renderer();
if (!renderer)
return false;
RenderText* textRenderer = toRenderText(renderer);
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (m_offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) {
return false;
}
if (m_offset >= static_cast<int>(box->start()) && m_offset < static_cast<int>(box->start() + box->len()))
return true;
}
return false;
}
bool Position::rendersInDifferentPosition(const Position &pos) const
{
if (isNull() || pos.isNull())
return false;
RenderObject* renderer = deprecatedNode()->renderer();
if (!renderer)
return false;
RenderObject* posRenderer = pos.deprecatedNode()->renderer();
if (!posRenderer)
return false;
if (renderer->style()->visibility() != VISIBLE ||
posRenderer->style()->visibility() != VISIBLE)
return false;
if (deprecatedNode() == pos.deprecatedNode()) {
if (isHTMLBRElement(*deprecatedNode()))
return false;
if (m_offset == pos.deprecatedEditingOffset())
return false;
if (!deprecatedNode()->isTextNode() && !pos.deprecatedNode()->isTextNode()) {
if (m_offset != pos.deprecatedEditingOffset())
return true;
}
}
if (isHTMLBRElement(*deprecatedNode()) && pos.isCandidate())
return true;
if (isHTMLBRElement(*pos.deprecatedNode()) && isCandidate())
return true;
if (deprecatedNode()->enclosingBlockFlowElement() != pos.deprecatedNode()->enclosingBlockFlowElement())
return true;
if (deprecatedNode()->isTextNode() && !inRenderedText())
return false;
if (pos.deprecatedNode()->isTextNode() && !pos.inRenderedText())
return false;
int thisRenderedOffset = renderedOffset();
int posRenderedOffset = pos.renderedOffset();
if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
return false;
int ignoredCaretOffset;
InlineBox* b1;
getInlineBoxAndOffset(DOWNSTREAM, b1, ignoredCaretOffset);
InlineBox* b2;
pos.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset);
WTF_LOG(Editing, "renderer: %p [%p]\n", renderer, b1);
WTF_LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset);
WTF_LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2);
WTF_LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset);
WTF_LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(deprecatedNode()), caretMaxOffset(deprecatedNode()));
WTF_LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.deprecatedNode()), caretMaxOffset(pos.deprecatedNode()));
WTF_LOG(Editing, "----------------------------------------------------------------------\n");
if (!b1 || !b2) {
return false;
}
if (b1->root() != b2->root()) {
return true;
}
if (nextRenderedEditable(deprecatedNode()) == pos.deprecatedNode()
&& thisRenderedOffset == caretMaxOffset(deprecatedNode()) && !posRenderedOffset) {
return false;
}
if (previousRenderedEditable(deprecatedNode()) == pos.deprecatedNode()
&& !thisRenderedOffset && posRenderedOffset == caretMaxOffset(pos.deprecatedNode())) {
return false;
}
return true;
}
Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNonCollapsibleWhitespace) const
{
ASSERT(isEditablePosition(*this, ContentIsEditable, DoNotUpdateStyle));
if (isNull())
return Position();
if (isHTMLBRElement(*upstream().deprecatedNode()))
return Position();
Position prev = previousCharacterPosition(affinity);
if (prev != *this && prev.deprecatedNode()->inSameContainingBlockFlowElement(deprecatedNode()) && prev.deprecatedNode()->isTextNode()) {
String string = toText(prev.deprecatedNode())->data();
UChar c = string[prev.deprecatedEditingOffset()];
if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
if (isEditablePosition(prev))
return prev;
}
return Position();
}
Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace) const
{
ASSERT(isEditablePosition(*this, ContentIsEditable, DoNotUpdateStyle));
if (isNull())
return Position();
VisiblePosition v(*this);
UChar c = v.characterAfter();
if (!isEndOfParagraph(v) && v.next(CannotCrossEditingBoundary).isNotNull())
if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
return *this;
return Position();
}
void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const
{
getInlineBoxAndOffset(affinity, primaryDirection(), inlineBox, caretOffset);
}
static bool isNonTextLeafChild(RenderObject* object)
{
if (object->firstChild())
return false;
if (object->isText())
return false;
return true;
}
static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer)
{
RenderBlock* container = renderer->containingBlock();
RenderObject* next = renderer;
while ((next = next->nextInPreOrder(container))) {
if (next->isRenderBlock())
return 0;
if (next->isBR())
return 0;
if (isNonTextLeafChild(next))
return 0;
if (next->isText()) {
InlineTextBox* match = 0;
int minOffset = INT_MAX;
for (InlineTextBox* box = toRenderText(next)->firstTextBox(); box; box = box->nextTextBox()) {
int caretMinOffset = box->caretMinOffset();
if (caretMinOffset < minOffset) {
match = box;
minOffset = caretMinOffset;
}
}
if (match)
return match;
}
}
return 0;
}
static Position downstreamIgnoringEditingBoundaries(Position position)
{
Position lastPosition;
while (position != lastPosition) {
lastPosition = position;
position = position.downstream(CanCrossEditingBoundary);
}
return position;
}
static Position upstreamIgnoringEditingBoundaries(Position position)
{
Position lastPosition;
while (position != lastPosition) {
lastPosition = position;
position = position.upstream(CanCrossEditingBoundary);
}
return position;
}
void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const
{
caretOffset = deprecatedEditingOffset();
RenderObject* renderer = deprecatedNode()->renderer();
if (!renderer->isText()) {
inlineBox = 0;
if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isRenderBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) {
Position equivalent = downstreamIgnoringEditingBoundaries(*this);
if (equivalent == *this) {
equivalent = upstreamIgnoringEditingBoundaries(*this);
if (equivalent == *this || downstreamIgnoringEditingBoundaries(equivalent) == *this)
return;
}
equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineBox, caretOffset);
return;
}
if (renderer->isBox()) {
inlineBox = toRenderBox(renderer)->inlineBoxWrapper();
if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset()))
return;
}
} else {
RenderText* textRenderer = toRenderText(renderer);
InlineTextBox* box;
InlineTextBox* candidate = 0;
for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
int caretMinOffset = box->caretMinOffset();
int caretMaxOffset = box->caretMaxOffset();
if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset || (caretOffset == caretMaxOffset && box->isLineBreak()))
continue;
if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset) {
inlineBox = box;
return;
}
if (((caretOffset == caretMaxOffset) ^ (affinity == DOWNSTREAM))
|| ((caretOffset == caretMinOffset) ^ (affinity == UPSTREAM))
|| (caretOffset == caretMaxOffset && box->nextLeafChild() && box->nextLeafChild()->isLineBreak()))
break;
candidate = box;
}
if (candidate && candidate == textRenderer->lastTextBox() && affinity == DOWNSTREAM) {
box = searchAheadForBetterMatch(textRenderer);
if (box)
caretOffset = box->caretMinOffset();
}
inlineBox = box ? box : candidate;
}
if (!inlineBox)
return;
unsigned char level = inlineBox->bidiLevel();
if (inlineBox->direction() == primaryDirection) {
if (caretOffset == inlineBox->caretRightmostOffset()) {
InlineBox* nextBox = inlineBox->nextLeafChild();
if (!nextBox || nextBox->bidiLevel() >= level)
return;
level = nextBox->bidiLevel();
InlineBox* prevBox = inlineBox;
do {
prevBox = prevBox->prevLeafChild();
} while (prevBox && prevBox->bidiLevel() > level);
if (prevBox && prevBox->bidiLevel() == level)
return;
while (InlineBox* nextBox = inlineBox->nextLeafChild()) {
if (nextBox->bidiLevel() < level)
break;
inlineBox = nextBox;
}
caretOffset = inlineBox->caretRightmostOffset();
} else {
InlineBox* prevBox = inlineBox->prevLeafChild();
if (!prevBox || prevBox->bidiLevel() >= level)
return;
level = prevBox->bidiLevel();
InlineBox* nextBox = inlineBox;
do {
nextBox = nextBox->nextLeafChild();
} while (nextBox && nextBox->bidiLevel() > level);
if (nextBox && nextBox->bidiLevel() == level)
return;
while (InlineBox* prevBox = inlineBox->prevLeafChild()) {
if (prevBox->bidiLevel() < level)
break;
inlineBox = prevBox;
}
caretOffset = inlineBox->caretLeftmostOffset();
}
return;
}
if (caretOffset == inlineBox->caretLeftmostOffset()) {
InlineBox* prevBox = inlineBox->prevLeafChildIgnoringLineBreak();
if (!prevBox || prevBox->bidiLevel() < level) {
while (InlineBox* nextBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
if (nextBox->bidiLevel() < level)
break;
inlineBox = nextBox;
}
caretOffset = inlineBox->caretRightmostOffset();
} else if (prevBox->bidiLevel() > level) {
while (InlineBox* tertiaryBox = inlineBox->prevLeafChildIgnoringLineBreak()) {
if (tertiaryBox->bidiLevel() <= level)
break;
inlineBox = tertiaryBox;
}
caretOffset = inlineBox->caretLeftmostOffset();
}
} else {
InlineBox* nextBox = inlineBox->nextLeafChildIgnoringLineBreak();
if (!nextBox || nextBox->bidiLevel() < level) {
while (InlineBox* prevBox = inlineBox->prevLeafChildIgnoringLineBreak()) {
if (prevBox->bidiLevel() < level)
break;
inlineBox = prevBox;
}
caretOffset = inlineBox->caretLeftmostOffset();
} else if (nextBox->bidiLevel() > level) {
while (InlineBox* tertiaryBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
if (tertiaryBox->bidiLevel() <= level)
break;
inlineBox = tertiaryBox;
}
caretOffset = inlineBox->caretRightmostOffset();
}
}
}
TextDirection Position::primaryDirection() const
{
TextDirection primaryDirection = LTR;
for (const RenderObject* r = m_anchorNode->renderer(); r; r = r->parent()) {
if (r->isRenderBlockFlow()) {
primaryDirection = r->style()->direction();
break;
}
}
return primaryDirection;
}
void Position::debugPosition(const char* msg) const
{
if (isNull())
fprintf(stderr, "Position [%s]: null\n", msg);
else
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;
if (isNull())
result.appendLiteral("<null>");
else {
char s[1024];
result.appendLiteral("offset ");
result.appendNumber(m_offset);
result.appendLiteral(" of ");
deprecatedNode()->formatForDebugger(s, sizeof(s));
result.append(s);
}
strncpy(buffer, result.toString().utf8().data(), length - 1);
}
void Position::showAnchorTypeAndOffset() const
{
if (m_isLegacyEditingPosition)
fputs("legacy, ", stderr);
switch (anchorType()) {
case PositionIsOffsetInAnchor:
fputs("offset", stderr);
break;
case PositionIsBeforeChildren:
fputs("beforeChildren", stderr);
break;
case PositionIsAfterChildren:
fputs("afterChildren", stderr);
break;
case PositionIsBeforeAnchor:
fputs("before", stderr);
break;
case PositionIsAfterAnchor:
fputs("after", stderr);
break;
}
fprintf(stderr, ", offset:%d\n", m_offset);
}
void Position::showTreeForThis() const
{
if (anchorNode()) {
anchorNode()->showTreeForThis();
showAnchorTypeAndOffset();
}
}
#endif
}
#ifndef NDEBUG
void showTree(const WebCore::Position& pos)
{
pos.showTreeForThis();
}
void showTree(const WebCore::Position* pos)
{
if (pos)
pos->showTreeForThis();
}
#endif