Index: third_party/WebKit/Source/core/editing/VisibleSelection.cpp |
diff --git a/third_party/WebKit/Source/core/editing/VisibleSelection.cpp b/third_party/WebKit/Source/core/editing/VisibleSelection.cpp |
index 7c92aedaa22475018ccdb02c82e6ae8896b64974..89729a709525abcb7eeb12cc0fe823870dd0f974 100644 |
--- a/third_party/WebKit/Source/core/editing/VisibleSelection.cpp |
+++ b/third_party/WebKit/Source/core/editing/VisibleSelection.cpp |
@@ -30,7 +30,6 @@ |
#include "core/dom/Element.h" |
#include "core/dom/Range.h" |
#include "core/editing/EditingUtilities.h" |
-#include "core/editing/SelectionAdjuster.h" |
#include "core/editing/iterators/CharacterIterator.h" |
#include "platform/geometry/LayoutPoint.h" |
#include "wtf/Assertions.h" |
@@ -40,6 +39,179 @@ |
namespace blink { |
+namespace { |
+ |
+enum class AdjustDirection { kAdjustNone, kAdjustStart, kAdjustEnd }; |
+ |
+Node* enclosingShadowHost(Node* node) { |
+ for (Node* runner = node; runner; |
+ runner = FlatTreeTraversal::parent(*runner)) { |
+ if (isShadowHost(runner)) |
+ return runner; |
+ } |
+ return nullptr; |
+} |
+ |
+bool isEnclosedBy(const PositionInFlatTree& position, const Node& node) { |
+ DCHECK(position.isNotNull()); |
+ Node* anchorNode = position.anchorNode(); |
+ if (anchorNode == node) |
+ return !position.isAfterAnchor() && !position.isBeforeAnchor(); |
+ |
+ return FlatTreeTraversal::isDescendantOf(*anchorNode, node); |
+} |
+ |
+bool isSelectionBoundary(const Node& node) { |
+ return isHTMLTextAreaElement(node) || isHTMLInputElement(node) || |
+ isHTMLSelectElement(node); |
+} |
+ |
+Node* enclosingShadowHostForStart(const PositionInFlatTree& position) { |
+ Node* node = position.nodeAsRangeFirstNode(); |
+ if (!node) |
+ return nullptr; |
+ Node* shadowHost = enclosingShadowHost(node); |
+ if (!shadowHost) |
+ return nullptr; |
+ if (!isEnclosedBy(position, *shadowHost)) |
+ return nullptr; |
+ return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; |
+} |
+ |
+Node* enclosingShadowHostForEnd(const PositionInFlatTree& position) { |
+ Node* node = position.nodeAsRangeLastNode(); |
+ if (!node) |
+ return nullptr; |
+ Node* shadowHost = enclosingShadowHost(node); |
+ if (!shadowHost) |
+ return nullptr; |
+ if (!isEnclosedBy(position, *shadowHost)) |
+ return nullptr; |
+ return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; |
+} |
+ |
+PositionInFlatTree adjustPositionInFlatTreeForStart( |
+ const PositionInFlatTree& position, |
+ Node* shadowHost) { |
+ if (isEnclosedBy(position, *shadowHost)) { |
+ if (position.isBeforeChildren()) |
+ return PositionInFlatTree::beforeNode(shadowHost); |
+ return PositionInFlatTree::afterNode(shadowHost); |
+ } |
+ |
+ // We use |firstChild|'s after instead of beforeAllChildren for backward |
+ // compatibility. The positions are same but the anchors would be different, |
+ // and selection painting uses anchor nodes. |
+ if (Node* firstChild = FlatTreeTraversal::firstChild(*shadowHost)) |
+ return PositionInFlatTree::beforeNode(firstChild); |
+ return PositionInFlatTree(); |
+} |
+ |
+Position adjustPositionForEnd(const Position& currentPosition, |
+ Node* startContainerNode) { |
+ TreeScope& treeScope = startContainerNode->treeScope(); |
+ |
+ DCHECK(currentPosition.computeContainerNode()->treeScope() != treeScope); |
+ |
+ if (Node* ancestor = treeScope.ancestorInThisScope( |
+ currentPosition.computeContainerNode())) { |
+ if (ancestor->contains(startContainerNode)) |
+ return Position::afterNode(ancestor); |
+ return Position::beforeNode(ancestor); |
+ } |
+ |
+ if (Node* lastChild = treeScope.rootNode().lastChild()) |
+ return Position::afterNode(lastChild); |
+ |
+ return Position(); |
+} |
+ |
+PositionInFlatTree adjustPositionInFlatTreeForEnd( |
+ const PositionInFlatTree& position, |
+ Node* shadowHost) { |
+ if (isEnclosedBy(position, *shadowHost)) { |
+ if (position.isAfterChildren()) |
+ return PositionInFlatTree::afterNode(shadowHost); |
+ return PositionInFlatTree::beforeNode(shadowHost); |
+ } |
+ |
+ // We use |lastChild|'s after instead of afterAllChildren for backward |
+ // compatibility. The positions are same but the anchors would be different, |
+ // and selection painting uses anchor nodes. |
+ if (Node* lastChild = FlatTreeTraversal::lastChild(*shadowHost)) |
+ return PositionInFlatTree::afterNode(lastChild); |
+ return PositionInFlatTree(); |
+} |
+ |
+Position adjustPositionForStart(const Position& currentPosition, |
+ Node* endContainerNode) { |
+ TreeScope& treeScope = endContainerNode->treeScope(); |
+ |
+ DCHECK(currentPosition.computeContainerNode()->treeScope() != treeScope); |
+ |
+ if (Node* ancestor = treeScope.ancestorInThisScope( |
+ currentPosition.computeContainerNode())) { |
+ if (ancestor->contains(endContainerNode)) |
+ return Position::beforeNode(ancestor); |
+ return Position::afterNode(ancestor); |
+ } |
+ |
+ if (Node* firstChild = treeScope.rootNode().firstChild()) |
+ return Position::beforeNode(firstChild); |
+ |
+ return Position(); |
+} |
+ |
+std::pair<AdjustDirection, Position> adjustmentToAvoidCrossingShadowBoundaries( |
+ const VisibleSelection& selection) { |
+ // Note: |m_selectionType| isn't computed yet. |
+ DCHECK(selection.base().isNotNull()); |
+ DCHECK(selection.extent().isNotNull()); |
+ DCHECK(selection.start().isNotNull()); |
+ DCHECK(selection.end().isNotNull()); |
+ |
+ // TODO(hajimehoshi): Checking treeScope is wrong when a node is |
+ // distributed, but we leave it as it is for backward compatibility. |
+ if (selection.start().anchorNode()->treeScope() == |
+ selection.end().anchorNode()->treeScope()) |
+ return std::make_pair(AdjustDirection::kAdjustNone, Position()); |
+ |
+ if (selection.isBaseFirst()) { |
+ const Position& newEnd = adjustPositionForEnd( |
+ selection.end(), selection.start().computeContainerNode()); |
+ return std::make_pair(AdjustDirection::kAdjustEnd, newEnd); |
+ } |
+ |
+ const Position& newStart = adjustPositionForStart( |
+ selection.start(), selection.end().computeContainerNode()); |
+ return std::make_pair(AdjustDirection::kAdjustStart, newStart); |
+} |
+ |
+// This function is called twice. The first is called when |m_start| and |m_end| |
+// or |m_extent| are same, and the second when |m_start| and |m_end| are changed |
+// after downstream/upstream. |
+std::pair<AdjustDirection, PositionInFlatTree> |
+adjustmentToAvoidCrossingShadowBoundaries( |
+ const VisibleSelectionInFlatTree& selection) { |
+ Node* const shadowHostStart = enclosingShadowHostForStart(selection.start()); |
+ Node* const shadowHostEnd = enclosingShadowHostForEnd(selection.end()); |
+ if (shadowHostStart == shadowHostEnd) |
+ return std::make_pair(AdjustDirection::kAdjustNone, PositionInFlatTree()); |
+ |
+ if (selection.isBaseFirst()) { |
+ Node* const shadowHost = shadowHostStart ? shadowHostStart : shadowHostEnd; |
+ const PositionInFlatTree& newEnd = |
+ adjustPositionInFlatTreeForEnd(selection.end(), shadowHost); |
+ return std::make_pair(AdjustDirection::kAdjustEnd, newEnd); |
+ } |
+ Node* const shadowHost = shadowHostEnd ? shadowHostEnd : shadowHostStart; |
+ const PositionInFlatTree& newStart = |
+ adjustPositionInFlatTreeForStart(selection.start(), shadowHost); |
+ return std::make_pair(AdjustDirection::kAdjustStart, newStart); |
+} |
+ |
+} // namespace |
+ |
template <typename Strategy> |
VisibleSelectionTemplate<Strategy>::VisibleSelectionTemplate() |
: m_affinity(TextAffinity::Downstream), |