VisiblePosition.cpp [plain text]
#include "config.h"
#include "VisiblePosition.h"
#include "Document.h"
#include "Element.h"
#include "HTMLNames.h"
#include "InlineTextBox.h"
#include "Logging.h"
#include "Range.h"
#include "Text.h"
#include "htmlediting.h"
#include "visible_units.h"
namespace WebCore {
using namespace HTMLNames;
VisiblePosition::VisiblePosition(const Position &pos, EAffinity affinity)
{
init(pos, affinity);
}
VisiblePosition::VisiblePosition(Node *node, int offset, EAffinity affinity)
{
ASSERT(offset >= 0);
init(Position(node, offset), affinity);
}
void VisiblePosition::init(const Position& position, EAffinity affinity)
{
m_affinity = affinity;
m_deepPosition = canonicalPosition(position);
if (m_affinity == UPSTREAM && (isNull() || inSameLine(VisiblePosition(position, DOWNSTREAM), *this)))
m_affinity = DOWNSTREAM;
}
VisiblePosition VisiblePosition::next(bool stayInEditableContent) const
{
VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity);
if (!stayInEditableContent)
return next;
return honorEditableBoundaryAtOrAfter(next);
}
VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
{
Position pos = previousVisuallyDistinctCandidate(m_deepPosition);
if (pos.atStart())
return VisiblePosition();
VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
ASSERT(prev != *this);
#ifndef NDEBUG
if (prev.isNotNull() && m_affinity == UPSTREAM) {
VisiblePosition temp = prev;
temp.setAffinity(UPSTREAM);
ASSERT(inSameLine(temp, prev));
}
#endif
if (!stayInEditableContent)
return prev;
return honorEditableBoundaryAtOrBefore(prev);
}
VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePosition &pos) const
{
if (pos.isNull())
return pos;
Node* highestRoot = highestEditableRoot(deepEquivalent());
if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot))
return VisiblePosition();
if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
return pos;
if (!highestRoot)
return VisiblePosition();
return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot);
}
VisiblePosition VisiblePosition::honorEditableBoundaryAtOrAfter(const VisiblePosition &pos) const
{
if (pos.isNull())
return pos;
Node* highestRoot = highestEditableRoot(deepEquivalent());
if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot))
return VisiblePosition();
if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
return pos;
if (!highestRoot)
return VisiblePosition();
return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot);
}
Position canonicalizeCandidate(const Position& candidate)
{
if (candidate.isNull())
return Position();
ASSERT(candidate.isCandidate());
Position upstream = candidate.upstream();
if (upstream.isCandidate())
return upstream;
return candidate;
}
Position VisiblePosition::canonicalPosition(const Position& position)
{
Node* node = position.node();
if (!node)
return Position();
node->document()->updateLayoutIgnorePendingStylesheets();
Position candidate = position.upstream();
if (candidate.isCandidate())
return candidate;
candidate = position.downstream();
if (candidate.isCandidate())
return candidate;
Position next = canonicalizeCandidate(nextCandidate(position));
Position prev = canonicalizeCandidate(previousCandidate(position));
Node* nextNode = next.node();
Node* prevNode = prev.node();
if (node->hasTagName(htmlTag) && !node->isContentEditable())
return next.isNotNull() ? next : prev;
Node* editingRoot = editableRootForPosition(position);
if (editingRoot && editingRoot->hasTagName(htmlTag) || position.node()->isDocumentNode())
return next.isNotNull() ? next : prev;
bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot;
bool nextIsInSameEditableElement = nextNode && editableRootForPosition(next) == editingRoot;
if (prevIsInSameEditableElement && !nextIsInSameEditableElement)
return prev;
if (nextIsInSameEditableElement && !prevIsInSameEditableElement)
return next;
if (!nextIsInSameEditableElement && !prevIsInSameEditableElement)
return Position();
Node *originalBlock = node->enclosingBlockFlowElement();
bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock;
bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock;
if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock)
return prev;
return next;
}
UChar VisiblePosition::characterAfter() const
{
Position pos = m_deepPosition.downstream();
Node* node = pos.node();
if (!node || !node->isTextNode())
return 0;
Text* textNode = static_cast<Text*>(pos.node());
int offset = pos.offset();
if ((unsigned)offset >= textNode->length())
return 0;
return textNode->data()[offset];
}
IntRect VisiblePosition::caretRect() const
{
if (!m_deepPosition.node() || !m_deepPosition.node()->renderer())
return IntRect();
return m_deepPosition.node()->renderer()->caretRect(m_deepPosition.offset(), m_affinity);
}
void VisiblePosition::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, m_deepPosition.node()->nodeName().deprecatedString().latin1(), m_deepPosition.node(), m_deepPosition.offset());
}
#ifndef NDEBUG
void VisiblePosition::formatForDebugger(char* buffer, unsigned length) const
{
m_deepPosition.formatForDebugger(buffer, length);
}
void VisiblePosition::showTreeForThis() const
{
m_deepPosition.showTreeForThis();
}
#endif
PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition &end)
{
Position s = rangeCompliantEquivalent(start);
Position e = rangeCompliantEquivalent(end);
return new Range(s.node()->document(), s.node(), s.offset(), e.node(), e.offset());
}
VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity)
{
int exception = 0;
return VisiblePosition(r->startContainer(exception), r->startOffset(exception), affinity);
}
VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity)
{
int exception = 0;
return VisiblePosition(r->endContainer(exception), r->endOffset(exception), affinity);
}
bool setStart(Range *r, const VisiblePosition &visiblePosition)
{
if (!r)
return false;
Position p = rangeCompliantEquivalent(visiblePosition);
int code = 0;
r->setStart(p.node(), p.offset(), code);
return code == 0;
}
bool setEnd(Range *r, const VisiblePosition &visiblePosition)
{
if (!r)
return false;
Position p = rangeCompliantEquivalent(visiblePosition);
int code = 0;
r->setEnd(p.node(), p.offset(), code);
return code == 0;
}
Node *enclosingBlockFlowElement(const VisiblePosition &visiblePosition)
{
if (visiblePosition.isNull())
return NULL;
return visiblePosition.deepEquivalent().node()->enclosingBlockFlowElement();
}
bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node)
{
if (visiblePosition.isNull())
return false;
if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node))
return false;
VisiblePosition previous = visiblePosition.previous();
return previous.isNull() || !previous.deepEquivalent().node()->isDescendantOf(node);
}
bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node)
{
if (visiblePosition.isNull())
return false;
if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node))
return false;
VisiblePosition next = visiblePosition.next();
return next.isNull() || !next.deepEquivalent().node()->isDescendantOf(node);
}
}
#ifndef NDEBUG
void showTree(const WebCore::VisiblePosition* vpos)
{
if (vpos)
vpos->showTreeForThis();
}
void showTree(const WebCore::VisiblePosition& vpos)
{
vpos.showTreeForThis();
}
#endif