summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing/VisiblePosition.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/editing/VisiblePosition.cpp')
-rw-r--r--Source/WebCore/editing/VisiblePosition.cpp193
1 files changed, 133 insertions, 60 deletions
diff --git a/Source/WebCore/editing/VisiblePosition.cpp b/Source/WebCore/editing/VisiblePosition.cpp
index 9155aaef3..bda4a1476 100644
--- a/Source/WebCore/editing/VisiblePosition.cpp
+++ b/Source/WebCore/editing/VisiblePosition.cpp
@@ -11,10 +11,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
@@ -30,6 +30,7 @@
#include "Document.h"
#include "FloatQuad.h"
#include "HTMLElement.h"
+#include "HTMLHtmlElement.h"
#include "HTMLNames.h"
#include "InlineTextBox.h"
#include "Logging.h"
@@ -37,6 +38,7 @@
#include "RenderBlock.h"
#include "RootInlineBox.h"
#include "Text.h"
+#include "TextStream.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
#include <stdio.h>
@@ -62,8 +64,10 @@ void VisiblePosition::init(const Position& position, EAffinity affinity)
m_affinity = DOWNSTREAM;
}
-VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule) const
+VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
// FIXME: Support CanSkipEditingBoundary
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity);
@@ -71,19 +75,24 @@ VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule) const
if (rule == CanCrossEditingBoundary)
return next;
- return honorEditingBoundaryAtOrAfter(next);
+ return honorEditingBoundaryAtOrAfter(next, reachedBoundary);
}
-VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) const
+VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
// FIXME: Support CanSkipEditingBoundary
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
// 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.atStartOfTree())
+ if (pos.atStartOfTree()) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
ASSERT(prev != *this);
@@ -101,7 +110,7 @@ VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) cons
if (rule == CanCrossEditingBoundary)
return prev;
- return honorEditingBoundaryAtOrBefore(prev);
+ return honorEditingBoundaryAtOrBefore(prev, reachedBoundary);
}
Position VisiblePosition::leftVisuallyDistinctCandidate() const
@@ -173,7 +182,7 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
if (box->direction() == primaryDirection) {
if (!prevBox) {
- InlineBox* logicalStart = 0;
+ InlineBox* logicalStart = nullptr;
if (primaryDirection == LTR ? box->root().getLogicalStartBoxWithNode(logicalStart) : box->root().getLogicalEndBoxWithNode(logicalStart)) {
box = logicalStart;
renderer = &box->renderer();
@@ -252,12 +261,17 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
}
}
-VisiblePosition VisiblePosition::left(bool stayInEditableContent) const
+VisiblePosition VisiblePosition::left(bool stayInEditableContent, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
Position pos = leftVisuallyDistinctCandidate();
// FIXME: Why can't we move left from the last position in a tree?
- if (pos.atStartOfTree() || pos.atEndOfTree())
+ if (pos.atStartOfTree() || pos.atEndOfTree()) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
VisiblePosition left = VisiblePosition(pos, DOWNSTREAM);
ASSERT(left != *this);
@@ -266,7 +280,7 @@ VisiblePosition VisiblePosition::left(bool stayInEditableContent) const
return left;
// FIXME: This may need to do something different from "before".
- return honorEditingBoundaryAtOrBefore(left);
+ return honorEditingBoundaryAtOrBefore(left, reachedBoundary);
}
Position VisiblePosition::rightVisuallyDistinctCandidate() const
@@ -338,7 +352,7 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
if (box->direction() == primaryDirection) {
if (!nextBox) {
- InlineBox* logicalEnd = 0;
+ InlineBox* logicalEnd = nullptr;
if (primaryDirection == LTR ? box->root().getLogicalEndBoxWithNode(logicalEnd) : box->root().getLogicalStartBoxWithNode(logicalEnd)) {
box = logicalEnd;
renderer = &box->renderer();
@@ -420,12 +434,17 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
}
}
-VisiblePosition VisiblePosition::right(bool stayInEditableContent) const
+VisiblePosition VisiblePosition::right(bool stayInEditableContent, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
Position pos = rightVisuallyDistinctCandidate();
// FIXME: Why can't we move left from the last position in a tree?
- if (pos.atStartOfTree() || pos.atEndOfTree())
+ if (pos.atStartOfTree() || pos.atEndOfTree()) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
VisiblePosition right = VisiblePosition(pos, DOWNSTREAM);
ASSERT(right != *this);
@@ -434,56 +453,78 @@ VisiblePosition VisiblePosition::right(bool stayInEditableContent) const
return right;
// FIXME: This may need to do something different from "after".
- return honorEditingBoundaryAtOrAfter(right);
+ return honorEditingBoundaryAtOrAfter(right, reachedBoundary);
}
-VisiblePosition VisiblePosition::honorEditingBoundaryAtOrBefore(const VisiblePosition &pos) const
+VisiblePosition VisiblePosition::honorEditingBoundaryAtOrBefore(const VisiblePosition& position, bool* reachedBoundary) const
{
- if (pos.isNull())
- return pos;
+ if (reachedBoundary)
+ *reachedBoundary = false;
+ if (position.isNull())
+ return position;
- Node* highestRoot = highestEditableRoot(deepEquivalent());
+ auto* highestRoot = highestEditableRoot(deepEquivalent());
// Return empty position if pos is not somewhere inside the editable region containing this position
- if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(highestRoot))
+ if (highestRoot && !position.deepEquivalent().deprecatedNode()->isDescendantOf(*highestRoot)) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
-
- // Return pos itself if the two are from the very same editable region, or both are non-editable
+ }
+
+ // Return position 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. VisibleSelection::adjustForEditableContent has this problem too.
- if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
- return pos;
+ if (highestEditableRoot(position.deepEquivalent()) == highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = *this == position;
+ return position;
+ }
// Return empty position if this position is non-editable, but pos is editable
// FIXME: Move to the previous non-editable region.
- if (!highestRoot)
+ if (!highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
// Return the last position before pos that is in the same editable region as this position
- return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot);
+ return lastEditablePositionBeforePositionInRoot(position.deepEquivalent(), highestRoot);
}
-VisiblePosition VisiblePosition::honorEditingBoundaryAtOrAfter(const VisiblePosition &pos) const
+VisiblePosition VisiblePosition::honorEditingBoundaryAtOrAfter(const VisiblePosition &pos, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
if (pos.isNull())
return pos;
- Node* highestRoot = highestEditableRoot(deepEquivalent());
+ auto* highestRoot = highestEditableRoot(deepEquivalent());
// Return empty position if pos is not somewhere inside the editable region containing this position
- if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(highestRoot))
+ if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(*highestRoot)) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
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. VisibleSelection::adjustForEditableContent has this problem too.
- if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
+ if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = *this == pos;
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)
+ if (!highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
// Return the next position after pos that is in the same editable region as this position
return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot);
@@ -536,14 +577,17 @@ Position VisiblePosition::canonicalPosition(const Position& passedPosition)
// 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 && node->hasTagName(htmlTag) && !node->hasEditableStyle() && node->document().body() && node->document().body()->hasEditableStyle())
- return next.isNotNull() ? next : prev;
+ if (is<HTMLHtmlElement>(node) && !node->hasEditableStyle()) {
+ auto* body = node->document().bodyOrFrameset();
+ if (body && body->hasEditableStyle())
+ 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.deprecatedNode()->isDocumentNode())
+ if ((editingRoot && editingRoot->hasTagName(htmlTag)) || (node && (node->isDocumentNode() || node->isShadowRoot())))
return next.isNotNull() ? next : prev;
bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot;
@@ -590,15 +634,14 @@ UChar32 VisiblePosition::characterAfter() const
return 0;
UChar32 ch;
- const UChar* characters = textNode->data().deprecatedCharacters();
- U16_NEXT(characters, offset, length, ch);
+ U16_NEXT(textNode->data(), offset, length, ch);
return ch;
}
LayoutRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
{
if (m_deepPosition.isNull()) {
- renderer = 0;
+ renderer = nullptr;
return IntRect();
}
Node* node = m_deepPosition.anchorNode();
@@ -617,14 +660,11 @@ LayoutRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
return renderer->localCaretRect(inlineBox, caretOffset);
}
-IntRect VisiblePosition::absoluteCaretBounds() const
+IntRect VisiblePosition::absoluteCaretBounds(bool* insideFixed) const
{
- RenderObject* renderer;
- LayoutRect localRect = localCaretRect(renderer);
- if (localRect.isEmpty() || !renderer)
- return IntRect();
-
- return renderer->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
+ RenderBlock* renderer = nullptr;
+ LayoutRect localRect = localCaretRectInRendererForCaretPainting(*this, renderer);
+ return absoluteBoundsForLocalCaretRect(renderer, localRect, insideFixed);
}
int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const
@@ -644,7 +684,7 @@ int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const
return containingBlock->isHorizontalWritingMode() ? caretPoint.x() : caretPoint.y();
}
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void VisiblePosition::debugPosition(const char* msg) const
{
@@ -668,15 +708,15 @@ void VisiblePosition::showTreeForThis() const
#endif
-PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition &end)
+RefPtr<Range> makeRange(const VisiblePosition& start, const VisiblePosition& end)
{
if (start.isNull() || end.isNull())
- return 0;
+ return nullptr;
Position s = start.deepEquivalent().parentAnchoredEquivalent();
Position e = end.deepEquivalent().parentAnchoredEquivalent();
if (s.isNull() || e.isNull())
- return 0;
+ return nullptr;
return Range::create(s.containerNode()->document(), s.containerNode(), s.offsetInContainerNode(), e.containerNode(), e.offsetInContainerNode());
}
@@ -691,31 +731,35 @@ VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity)
return VisiblePosition(r->endPosition(), affinity);
}
-bool setStart(Range *r, const VisiblePosition &visiblePosition)
+bool setStart(Range* range, const VisiblePosition& visiblePosition)
{
- if (!r)
+ if (!range)
return false;
+
Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
- int code = 0;
- r->setStart(p.containerNode(), p.offsetInContainerNode(), code);
- return code == 0;
+ if (!p.containerNode())
+ return false;
+
+ return !range->setStart(*p.containerNode(), p.offsetInContainerNode()).hasException();
}
-bool setEnd(Range *r, const VisiblePosition &visiblePosition)
+bool setEnd(Range* range, const VisiblePosition& visiblePosition)
{
- if (!r)
+ if (!range)
return false;
+
Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
- int code = 0;
- r->setEnd(p.containerNode(), p.offsetInContainerNode(), code);
- return code == 0;
+ if (!p.containerNode())
+ return false;
+
+ return !range->setEnd(*p.containerNode(), p.offsetInContainerNode()).hasException();
}
// FIXME: Maybe this should be deprecated too, like the underlying function?
Element* enclosingBlockFlowElement(const VisiblePosition& visiblePosition)
{
if (visiblePosition.isNull())
- return NULL;
+ return nullptr;
return deprecatedEnclosingBlockFlowElement(visiblePosition.deepEquivalent().deprecatedNode());
}
@@ -744,9 +788,38 @@ bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const N
return next.isNull() || !next.deepEquivalent().deprecatedNode()->isDescendantOf(node);
}
+bool VisiblePosition::equals(const VisiblePosition& other) const
+{
+ return m_affinity == other.m_affinity && m_deepPosition.equals(other.m_deepPosition);
+}
+
+TextStream& operator<<(TextStream& stream, EAffinity affinity)
+{
+ switch (affinity) {
+ case UPSTREAM:
+ stream << "upstream";
+ break;
+ case DOWNSTREAM:
+ stream << "downstream";
+ break;
+ }
+ return stream;
+}
+
+TextStream& operator<<(TextStream& stream, const VisiblePosition& visiblePosition)
+{
+ TextStream::GroupScope scope(stream);
+ stream << "VisiblePosition " << &visiblePosition;
+
+ stream.dumpProperty("position", visiblePosition.deepEquivalent());
+ stream.dumpProperty("affinity", visiblePosition.affinity());
+
+ return stream;
+}
+
} // namespace WebCore
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showTree(const WebCore::VisiblePosition* vpos)
{