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 "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)
{
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(nextVisiblePosition(m_deepPosition), m_affinity);
if (!stayInEditableContent || next.isNull())
return next;
Node* highestRoot = highestEditableRoot(deepEquivalent());
if (!next.deepEquivalent().node()->isAncestor(highestRoot))
return VisiblePosition();
if (highestEditableRoot(next.deepEquivalent()) == highestRoot)
return next;
return firstEditablePositionAfterPositionInRoot(next.deepEquivalent(), highestRoot);
}
VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
{
Position pos = previousVisiblePosition(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 || prev.isNull())
return prev;
Node* highestRoot = highestEditableRoot(deepEquivalent());
if (!prev.deepEquivalent().node()->isAncestor(highestRoot))
return VisiblePosition();
if (highestEditableRoot(prev.deepEquivalent()) == highestRoot)
return prev;
return lastEditablePositionBeforePositionInRoot(prev.deepEquivalent(), highestRoot);
}
Position VisiblePosition::previousVisiblePosition(const Position& pos)
{
if (!pos.inRenderedContent()) {
Position current = pos;
while (!current.atStart()) {
current = current.previous(UsingComposedCharacters);
if (current.inRenderedContent())
return current;
}
return Position();
}
Position downstreamStart = pos.downstream();
Position current = pos;
while (!current.atStart()) {
current = current.previous(UsingComposedCharacters);
if (current.inRenderedContent() && downstreamStart != current.downstream())
return current;
}
return Position();
}
Position VisiblePosition::nextVisiblePosition(const Position& pos)
{
if (!pos.inRenderedContent()) {
Position current = pos;
while (!current.atEnd()) {
current = current.next(UsingComposedCharacters);
if (current.inRenderedContent())
return current;
}
return Position();
}
Position downstreamStart = pos.downstream();
Position current = pos;
while (!current.atEnd()) {
current = current.next(UsingComposedCharacters);
if (current.inRenderedContent() && downstreamStart != current.downstream())
return current;
}
return Position();
}
Position VisiblePosition::canonicalPosition(const Position& position)
{
Node* node = position.node();
if (!node)
return Position();
node->document()->updateLayoutIgnorePendingStylesheets();
Position candidate = position.upstream();
if (candidate.inRenderedContent())
return candidate;
candidate = position.downstream();
if (candidate.inRenderedContent())
return candidate;
Position next = nextVisiblePosition(position);
Position prev = previousVisiblePosition(position);
Node* nextNode = next.node();
Node* prevNode = prev.node();
Node* editingRoot = node->rootEditableElement();
if (editingRoot && editingRoot->hasTagName(htmlTag))
return next.isNotNull() ? next : prev;
bool prevIsInSameEditableElement = prevNode && prevNode->rootEditableElement() == editingRoot;
bool nextIsInSameEditableElement = nextNode && nextNode->rootEditableElement() == editingRoot;
if (prevIsInSameEditableElement && !nextIsInSameEditableElement)
return prev;
if (nextIsInSameEditableElement && !prevIsInSameEditableElement)
return next;
if (!nextIsInSameEditableElement && !prevIsInSameEditableElement)
return Position();
Node *originalBlock = node->enclosingBlockFlowElement();
bool nextIsOutsideOriginalBlock = !nextNode->isAncestor(originalBlock) && nextNode != originalBlock;
bool prevIsOutsideOriginalBlock = !prevNode->isAncestor(originalBlock) && prevNode != originalBlock;
if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock)
return prev;
return next;
}
int VisiblePosition::maxOffset(const Node *node)
{
return node->offsetInCharacters() ? (int)static_cast<const CharacterData *>(node)->length() : (int)node->childNodeCount();
}
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];
}
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()->isAncestor(node))
return false;
VisiblePosition previous = visiblePosition.previous();
return previous.isNull() || !previous.deepEquivalent().node()->isAncestor(node);
}
bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node)
{
if (visiblePosition.isNull())
return false;
if (!visiblePosition.deepEquivalent().node()->isAncestor(node))
return false;
VisiblePosition next = visiblePosition.next();
return next.isNull() || !next.deepEquivalent().node()->isAncestor(node);
}
}
#ifndef NDEBUG
void showTree(const WebCore::VisiblePosition* vpos)
{
if (vpos)
vpos->showTreeForThis();
}
void showTree(const WebCore::VisiblePosition& vpos)
{
vpos.showTreeForThis();
}
#endif