diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:41 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:41 -0800 |
commit | 648161bb0edfc3d43db63caed5cc5213bc6cb78f (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /WebCore/editing/VisiblePosition.cpp | |
parent | a65af38181ac7d34544586bdb5cd004de93897ad (diff) | |
download | external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.zip external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.tar.gz external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/editing/VisiblePosition.cpp')
-rw-r--r-- | WebCore/editing/VisiblePosition.cpp | 654 |
1 files changed, 0 insertions, 654 deletions
diff --git a/WebCore/editing/VisiblePosition.cpp b/WebCore/editing/VisiblePosition.cpp deleted file mode 100644 index 626c5b4..0000000 --- a/WebCore/editing/VisiblePosition.cpp +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * 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 - * 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 - * 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 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "VisiblePosition.h" - -#include "CString.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" -#include <stdio.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); - - // When not at a line wrap, make sure to end up with DOWNSTREAM affinity. - 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 -{ - // find first previous DOM position that is visible - Position pos = previousVisuallyDistinctCandidate(m_deepPosition); - - // return null visible position if there is no previous visible position - if (pos.atStart()) - return VisiblePosition(); - - VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM); - ASSERT(prev != *this); - -#ifndef NDEBUG - // we should always be able to make the affinity DOWNSTREAM, because going previous from an - // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!). - if (prev.isNotNull() && m_affinity == UPSTREAM) { - VisiblePosition temp = prev; - temp.setAffinity(UPSTREAM); - ASSERT(inSameLine(temp, prev)); - } -#endif - - if (!stayInEditableContent) - return prev; - - return honorEditableBoundaryAtOrBefore(prev); -} - -Position VisiblePosition::leftVisuallyDistinctCandidate() const -{ - Position p = m_deepPosition; - if (!p.node()) - return Position(); - - Position downstreamStart = p.downstream(); - TextDirection primaryDirection = LTR; - for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) { - if (r->isBlockFlow()) { - primaryDirection = r->style()->direction(); - break; - } - } - - while (true) { - InlineBox* box; - int offset; - p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); - if (!box) - return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); - - RenderObject* renderer = box->object(); - - while (true) { - if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretRightmostOffset()) - return box->direction() == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); - - offset = box->direction() == LTR ? renderer->previousOffset(offset) : renderer->nextOffset(offset); - - int caretMinOffset = box->caretMinOffset(); - int caretMaxOffset = box->caretMaxOffset(); - - if (offset > caretMinOffset && offset < caretMaxOffset) - break; - - if (box->direction() == LTR ? offset < caretMinOffset : offset > caretMaxOffset) { - // Overshot to the left. - InlineBox* prevBox = box->prevLeafChild(); - if (!prevBox) - return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); - - // Reposition at the other logical position corresponding to our edge's visual position and go for another round. - box = prevBox; - renderer = box->object(); - offset = prevBox->caretRightmostOffset(); - continue; - } - - ASSERT(offset == box->caretLeftmostOffset()); - - unsigned char level = box->bidiLevel(); - InlineBox* prevBox = box->prevLeafChild(); - - if (box->direction() == primaryDirection) { - if (!prevBox || prevBox->bidiLevel() >= level) - break; - - level = prevBox->bidiLevel(); - - InlineBox* nextBox = box; - do { - nextBox = nextBox->nextLeafChild(); - } while (nextBox && nextBox->bidiLevel() > level); - - if (nextBox && nextBox->bidiLevel() == level) - break; - - while (InlineBox* prevBox = box->prevLeafChild()) { - if (prevBox->bidiLevel() < level) - break; - box = prevBox; - } - renderer = box->object(); - offset = box->caretRightmostOffset(); - if (box->direction() == primaryDirection) - break; - continue; - } - - if (prevBox) { - box = prevBox; - renderer = box->object(); - offset = box->caretRightmostOffset(); - if (box->bidiLevel() > level) { - do { - prevBox = box->prevLeafChild(); - } while (prevBox && prevBox->bidiLevel() > level); - - if (!prevBox || prevBox->bidiLevel() < level) - continue; - } - } else { - // Trailing edge of a secondary run. Set to the leading edge of the entire run. - while (true) { - while (InlineBox* nextBox = box->nextLeafChild()) { - if (nextBox->bidiLevel() < level) - break; - box = nextBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - while (InlineBox* prevBox = box->prevLeafChild()) { - if (prevBox->bidiLevel() < level) - break; - box = prevBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - } - renderer = box->object(); - offset = primaryDirection == LTR ? box->caretMinOffset() : box->caretMaxOffset(); - } - break; - } - - p = Position(renderer->element(), offset); - - if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd()) - return p; - } -} - -VisiblePosition VisiblePosition::left(bool stayInEditableContent) const -{ - Position pos = leftVisuallyDistinctCandidate(); - if (pos.atStart() || pos.atEnd()) - return VisiblePosition(); - - VisiblePosition left = VisiblePosition(pos, DOWNSTREAM); - ASSERT(left != *this); - - if (!stayInEditableContent) - return left; - - // FIXME: This may need to do something different from "before". - return honorEditableBoundaryAtOrBefore(left); -} - -Position VisiblePosition::rightVisuallyDistinctCandidate() const -{ - Position p = m_deepPosition; - if (!p.node()) - return Position(); - - Position downstreamStart = p.downstream(); - TextDirection primaryDirection = LTR; - for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) { - if (r->isBlockFlow()) { - primaryDirection = r->style()->direction(); - break; - } - } - - while (true) { - InlineBox* box; - int offset; - p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); - if (!box) - return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); - - RenderObject* renderer = box->object(); - - while (true) { - if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretLeftmostOffset()) - return box->direction() == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); - - offset = box->direction() == LTR ? renderer->nextOffset(offset) : renderer->previousOffset(offset); - - int caretMinOffset = box->caretMinOffset(); - int caretMaxOffset = box->caretMaxOffset(); - - if (offset > caretMinOffset && offset < caretMaxOffset) - break; - - if (box->direction() == LTR ? offset > caretMaxOffset : offset < caretMinOffset) { - // Overshot to the right. - InlineBox* nextBox = box->nextLeafChild(); - if (!nextBox) - return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); - - // Reposition at the other logical position corresponding to our edge's visual position and go for another round. - box = nextBox; - renderer = box->object(); - offset = nextBox->caretLeftmostOffset(); - continue; - } - - ASSERT(offset == box->caretRightmostOffset()); - - unsigned char level = box->bidiLevel(); - InlineBox* nextBox = box->nextLeafChild(); - - if (box->direction() == primaryDirection) { - if (!nextBox || nextBox->bidiLevel() >= level) - break; - - level = nextBox->bidiLevel(); - - InlineBox* prevBox = box; - do { - prevBox = prevBox->prevLeafChild(); - } while (prevBox && prevBox->bidiLevel() > level); - - if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA - break; - - // For example, abc 123 ^ CBA - while (InlineBox* nextBox = box->nextLeafChild()) { - if (nextBox->bidiLevel() < level) - break; - box = nextBox; - } - renderer = box->object(); - offset = box->caretLeftmostOffset(); - if (box->direction() == primaryDirection) - break; - continue; - } - - if (nextBox) { - box = nextBox; - renderer = box->object(); - offset = box->caretLeftmostOffset(); - if (box->bidiLevel() > level) { - do { - nextBox = box->nextLeafChild(); - } while (nextBox && nextBox->bidiLevel() > level); - - if (!nextBox || nextBox->bidiLevel() < level) - continue; - } - } else { - // Trailing edge of a secondary run. Set to the leading edge of the entire run. - while (true) { - while (InlineBox* prevBox = box->prevLeafChild()) { - if (prevBox->bidiLevel() < level) - break; - box = prevBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - while (InlineBox* nextBox = box->nextLeafChild()) { - if (nextBox->bidiLevel() < level) - break; - box = nextBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - } - renderer = box->object(); - offset = primaryDirection == LTR ? box->caretMaxOffset() : box->caretMinOffset(); - } - break; - } - - p = Position(renderer->element(), offset); - - if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd()) - return p; - } -} - -VisiblePosition VisiblePosition::right(bool stayInEditableContent) const -{ - Position pos = rightVisuallyDistinctCandidate(); - if (pos.atStart() || pos.atEnd()) - return VisiblePosition(); - - VisiblePosition right = VisiblePosition(pos, DOWNSTREAM); - ASSERT(right != *this); - - if (!stayInEditableContent) - return right; - - // FIXME: This may need to do something different from "after". - return honorEditableBoundaryAtOrAfter(right); -} - -VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePosition &pos) const -{ - if (pos.isNull()) - return pos; - - Node* highestRoot = highestEditableRoot(deepEquivalent()); - - // Return empty position if pos is not somewhere inside the editable region containing this position - if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot)) - return VisiblePosition(); - - // Return pos itself if the two are from the very same editable region, or both are non-editable - // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement - // to it is allowed. Selection::adjustForEditableContent has this problem too. - if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) - return pos; - - // Return empty position if this position is non-editable, but pos is editable - // FIXME: Move to the previous non-editable region. - if (!highestRoot) - return VisiblePosition(); - - // Return the last position before pos that is in the same editable region as this position - return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot); -} - -VisiblePosition VisiblePosition::honorEditableBoundaryAtOrAfter(const VisiblePosition &pos) const -{ - if (pos.isNull()) - return pos; - - Node* highestRoot = highestEditableRoot(deepEquivalent()); - - // Return empty position if pos is not somewhere inside the editable region containing this position - if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot)) - return VisiblePosition(); - - // Return pos itself if the two are from the very same editable region, or both are non-editable - // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement - // to it is allowed. Selection::adjustForEditableContent has this problem too. - if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) - return pos; - - // Return empty position if this position is non-editable, but pos is editable - // FIXME: Move to the next non-editable region. - if (!highestRoot) - return VisiblePosition(); - - // Return the next position after pos that is in the same editable region as this position - 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) -{ - // FIXME (9535): Canonicalizing to the leftmost candidate means that if we're at a line wrap, we will - // ask renderers to paint downstream carets for other renderers. - // To fix this, we need to either a) add code to all paintCarets to pass the responsibility off to - // the appropriate renderer for VisiblePosition's like these, or b) canonicalize to the rightmost candidate - // unless the affinity is upstream. - 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; - - // When neither upstream or downstream gets us to a candidate (upstream/downstream won't leave - // blocks or enter new ones), we search forward and backward until we find one. - Position next = canonicalizeCandidate(nextCandidate(position)); - Position prev = canonicalizeCandidate(previousCandidate(position)); - Node* nextNode = next.node(); - Node* prevNode = prev.node(); - - // The new position must be in the same editable element. Enforce that first. - // Unless the descent is from a non-editable html element to an editable body. - if (node->hasTagName(htmlTag) && !node->isContentEditable()) - return next.isNotNull() ? next : prev; - - Node* editingRoot = editableRootForPosition(position); - - // If the html element is editable, descending into its body will look like a descent - // from non-editable to editable content since rootEditableElement() always stops at the body. - 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(); - - // The new position should be in the same block flow element. Favor that. - 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 -{ - // We canonicalize to the first of two equivalent candidates, but the second of the two candidates - // is the one that will be inside the text node containing the character after this visible position. - 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 -{ - Node* node = m_deepPosition.node(); - if (!node) - return IntRect(); - - RenderObject* renderer = node->renderer(); - if (!renderer) - return IntRect(); - - InlineBox* inlineBox; - int caretOffset; - getInlineBoxAndOffset(inlineBox, caretOffset); - - if (inlineBox) - renderer = inlineBox->object(); - - return renderer->caretRect(inlineBox, caretOffset); -} - -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().utf8().data(), 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 Range::create(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); -} - -} // namespace WebCore - -#ifndef NDEBUG - -void showTree(const WebCore::VisiblePosition* vpos) -{ - if (vpos) - vpos->showTreeForThis(); -} - -void showTree(const WebCore::VisiblePosition& vpos) -{ - vpos.showTreeForThis(); -} - -#endif |