| Index: Source/core/editing/VisibleUnits.cpp
|
| diff --git a/Source/core/editing/VisibleUnits.cpp b/Source/core/editing/VisibleUnits.cpp
|
| index 5ea7f0761594e76895bff49edc5582112b48ebb1..b9362b681ce198aea1eaac76c0670be0b7f40ae5 100644
|
| --- a/Source/core/editing/VisibleUnits.cpp
|
| +++ b/Source/core/editing/VisibleUnits.cpp
|
| @@ -66,6 +66,106 @@ namespace blink {
|
| using namespace HTMLNames;
|
| using namespace WTF::Unicode;
|
|
|
| +template <typename PositionType>
|
| +static PositionType canonicalizeCandidate(const PositionType& candidate)
|
| +{
|
| + if (candidate.isNull())
|
| + return PositionType();
|
| + ASSERT(isVisuallyEquivalentCandidate(candidate));
|
| + PositionType upstream = mostBackwardCaretPosition(candidate);
|
| + if (isVisuallyEquivalentCandidate(upstream))
|
| + return upstream;
|
| + return candidate;
|
| +}
|
| +
|
| +template <typename PositionType>
|
| +static PositionType canonicalPosition(const PositionType& passedPosition)
|
| +{
|
| + // Sometimes updating selection positions can be extremely expensive and
|
| + // occur frequently. Often calling preventDefault on mousedown events can
|
| + // avoid doing unnecessary text selection work. http://crbug.com/472258.
|
| + TRACE_EVENT0("blink", "VisiblePosition::canonicalPosition");
|
| +
|
| + // The updateLayout call below can do so much that even the position passed
|
| + // in to us might get changed as a side effect. Specifically, there are code
|
| + // paths that pass selection endpoints, and updateLayout can change the
|
| + // selection.
|
| + PositionType position = passedPosition;
|
| +
|
| + // FIXME (9535): Canonicalizing to the leftmost candidate means that if
|
| + // we're at a line wrap, we will ask layoutObjects to paint downstream
|
| + // carets for other layoutObjects. To fix this, we need to either a) add
|
| + // code to all paintCarets to pass the responsibility off to the appropriate
|
| + // layoutObject for VisiblePosition's like these, or b) canonicalize to the
|
| + // rightmost candidate unless the affinity is upstream.
|
| + if (position.isNull())
|
| + return PositionType();
|
| +
|
| + ASSERT(position.document());
|
| + position.document()->updateLayoutIgnorePendingStylesheets();
|
| +
|
| + Node* node = position.computeContainerNode();
|
| +
|
| + PositionType candidate = mostBackwardCaretPosition(position);
|
| + if (isVisuallyEquivalentCandidate(candidate))
|
| + return candidate;
|
| + candidate = mostForwardCaretPosition(position);
|
| + if (isVisuallyEquivalentCandidate(candidate))
|
| + 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.
|
| + PositionType next = canonicalizeCandidate(nextCandidate(position));
|
| + PositionType prev = canonicalizeCandidate(previousCandidate(position));
|
| + Node* nextNode = next.anchorNode();
|
| + Node* prevNode = prev.anchorNode();
|
| +
|
| + // 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 (isHTMLHtmlElement(node) && !node->hasEditableStyle() && node->document().body() && node->document().body()->hasEditableStyle())
|
| + return next.isNotNull() ? next : prev;
|
| +
|
| + Element* 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
|
| + // |rootEditableElementOf()| always stops at the body.
|
| + if (isHTMLHtmlElement(editingRoot) || position.anchorNode()->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 PositionType();
|
| +
|
| + // The new position should be in the same block flow element. Favor that.
|
| + Element* originalBlock = node ? enclosingBlockFlowElement(*node) : 0;
|
| + bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock;
|
| + bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock;
|
| + if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock)
|
| + return prev;
|
| +
|
| + return next;
|
| +}
|
| +
|
| +Position canonicalPositionOf(const Position& position)
|
| +{
|
| + return canonicalPosition(position);
|
| +}
|
| +
|
| +PositionInComposedTree canonicalPositionOf(const PositionInComposedTree& position)
|
| +{
|
| + return canonicalPosition(position);
|
| +}
|
| +
|
| static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
|
| {
|
| bool editable = node->hasEditableStyle(editableType);
|
|
|