| Index: third_party/WebKit/Source/core/editing/FrameSelection.cpp
|
| diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
|
| index 5cf2922249fa15ffd7d49687da53058e28b18ffc..96318698ec45a22f4467add9a25589e686dc38c5 100644
|
| --- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
|
| @@ -92,6 +92,10 @@ static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) {
|
| return frame->editor().behavior().shouldConsiderSelectionAsDirectional();
|
| }
|
|
|
| +FrameSelection* FrameSelection::create(LocalFrame& frame) {
|
| + return new FrameSelection(frame);
|
| +}
|
| +
|
| FrameSelection::FrameSelection(LocalFrame& frame)
|
| : m_frame(frame),
|
| m_pendingSelection(PendingSelection::create(*this)),
|
| @@ -106,32 +110,26 @@ FrameSelection::~FrameSelection() {}
|
|
|
| const DisplayItemClient& FrameSelection::caretDisplayItemClientForTesting()
|
| const {
|
| - return *m_frameCaret;
|
| -}
|
| -
|
| -const Document& FrameSelection::document() const {
|
| - DCHECK(m_document);
|
| - return *m_document;
|
| + return m_frameCaret->displayItemClient();
|
| }
|
|
|
| -Document& FrameSelection::document() {
|
| - DCHECK(m_document);
|
| - return *m_document;
|
| +Document& FrameSelection::document() const {
|
| + DCHECK(lifecycleContext());
|
| + return *lifecycleContext();
|
| }
|
|
|
| -// TODO(yosin): To avoid undefined symbols in clang, we explicitly
|
| -// have specialized version of |FrameSelection::visibleSelection<Strategy>|
|
| -// before |FrameSelection::selection()| which refers this.
|
| -template <>
|
| -const VisibleSelection& FrameSelection::visibleSelection<EditingStrategy>()
|
| +const VisibleSelection& FrameSelection::computeVisibleSelectionInDOMTree()
|
| const {
|
| - return m_selectionEditor->visibleSelection<EditingStrategy>();
|
| + return m_selectionEditor->computeVisibleSelectionInDOMTree();
|
| }
|
|
|
| -template <>
|
| const VisibleSelectionInFlatTree&
|
| -FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const {
|
| - return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>();
|
| +FrameSelection::computeVisibleSelectionInFlatTree() const {
|
| + return m_selectionEditor->computeVisibleSelectionInFlatTree();
|
| +}
|
| +
|
| +const SelectionInDOMTree& FrameSelection::selectionInDOMTree() const {
|
| + return m_selectionEditor->selectionInDOMTree();
|
| }
|
|
|
| Element* FrameSelection::rootEditableElementOrDocumentElement() const {
|
| @@ -148,12 +146,13 @@ ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const {
|
| return node ? &node->treeScope().rootNode() : 0;
|
| }
|
|
|
| +// TODO(yosin): We should rename |FrameSelection::selection()| to
|
| +// |selectionDeprecated()|.
|
| const VisibleSelection& FrameSelection::selection() const {
|
| - return visibleSelection<EditingStrategy>();
|
| -}
|
| -
|
| -const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const {
|
| - return visibleSelection<EditingInFlatTreeStrategy>();
|
| + // TODO(yosin): We should hoist updateStyleAndLayoutIgnorePendingStylesheets
|
| + // to caller. See http://crbug.com/590369 for more details.
|
| + document().updateStyleAndLayoutIgnorePendingStylesheets();
|
| + return computeVisibleSelectionInDOMTree();
|
| }
|
|
|
| void FrameSelection::moveCaretSelection(const IntPoint& point) {
|
| @@ -172,27 +171,22 @@ void FrameSelection::moveCaretSelection(const IntPoint& point) {
|
| setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered);
|
| }
|
|
|
| -template <typename Strategy>
|
| -void FrameSelection::setSelectionAlgorithm(
|
| - const VisibleSelectionTemplate<Strategy>& newSelection,
|
| - SetSelectionOptions options,
|
| - CursorAlignOnScroll align,
|
| - TextGranularity granularity) {
|
| +void FrameSelection::setSelection(const SelectionInDOMTree& passedSelection,
|
| + SetSelectionOptions options,
|
| + CursorAlignOnScroll align,
|
| + TextGranularity granularity) {
|
| DCHECK(isAvailable());
|
| - DCHECK(newSelection.isValidFor(document()));
|
| - const Document& currentDocument = document();
|
| + passedSelection.assertValidFor(document());
|
| +
|
| + SelectionInDOMTree::Builder builder(passedSelection);
|
| + if (shouldAlwaysUseDirectionalSelection(m_frame))
|
| + builder.setIsDirectional(true);
|
| + SelectionInDOMTree newSelection = builder.build();
|
| if (m_granularityStrategy &&
|
| (options & FrameSelection::DoNotClearStrategy) == 0)
|
| m_granularityStrategy->Clear();
|
| bool closeTyping = options & CloseTyping;
|
| bool shouldClearTypingStyle = options & ClearTypingStyle;
|
| - EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
|
| -
|
| - // TODO(editing-dev): We should rename variable |s| to another name to avoid
|
| - // using one letter variable name.
|
| - VisibleSelectionTemplate<Strategy> s = newSelection;
|
| - if (shouldAlwaysUseDirectionalSelection(m_frame))
|
| - s.setIsDirectional(true);
|
|
|
| m_granularity = granularity;
|
|
|
| @@ -204,22 +198,26 @@ void FrameSelection::setSelectionAlgorithm(
|
| if (shouldClearTypingStyle)
|
| clearTypingStyle();
|
|
|
| - if (m_selectionEditor->visibleSelection<Strategy>() == s) {
|
| - // Even if selection was not changed, selection offsets may have been
|
| - // changed.
|
| - m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
|
| - notifyLayoutObjectOfSelectionChange(userTriggered);
|
| + const SelectionInDOMTree oldSelectionInDOMTree =
|
| + m_selectionEditor->selectionInDOMTree();
|
| + if (oldSelectionInDOMTree == newSelection)
|
| return;
|
| - }
|
| -
|
| - const VisibleSelectionTemplate<Strategy> oldSelection =
|
| - visibleSelection<Strategy>();
|
| - const Position& oldSelectionStart = selection().start();
|
| + m_selectionEditor->setSelection(newSelection);
|
|
|
| - m_selectionEditor->setVisibleSelection(s, options);
|
| - m_frameCaret->setCaretRectNeedsUpdate();
|
| + // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets
|
| + // needs to be audited. see http://crbug.com/590369 for more details.
|
| + document().updateStyleAndLayoutIgnorePendingStylesheets();
|
|
|
| - if (!s.isNone() && !(options & DoNotSetFocus)) {
|
| + const Document& currentDocument = document();
|
| + // TODO(yosin): We should get rid of unsued |options| for
|
| + // |Editor::respondToChangedSelection()|.
|
| + // Note: Since, setting focus can modify DOM tree, we should use
|
| + // |oldSelection| before setting focus
|
| + m_frame->editor().respondToChangedSelection(
|
| + createVisibleSelection(oldSelectionInDOMTree).start(), options);
|
| + DCHECK_EQ(currentDocument, document());
|
| +
|
| + if (!selection().isNone() && !(options & DoNotSetFocus)) {
|
| setFocusedNodeIfNeeded();
|
| // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and
|
| // "FocusIn", |m_frame| may associate to another document.
|
| @@ -230,18 +228,12 @@ void FrameSelection::setSelectionAlgorithm(
|
| return;
|
| }
|
| }
|
| -
|
| - if (!(options & DoNotUpdateAppearance)) {
|
| - // Hits in
|
| - // compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html
|
| - DisableCompositingQueryAsserts disabler;
|
| - m_frameCaret->stopCaretBlinkTimer();
|
| - updateAppearance();
|
| - }
|
| + updateAppearance();
|
|
|
| // Always clear the x position used for vertical arrow navigation.
|
| // It will be restored by the vertical arrow navigation code if necessary.
|
| m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
|
| + // TODO(yosin): Can we move this to at end of this function?
|
| // This may dispatch a synchronous focus-related events.
|
| selectFrameElementInParentIfFullySelected();
|
| if (!isAvailable() || document() != currentDocument) {
|
| @@ -250,17 +242,8 @@ void FrameSelection::setSelectionAlgorithm(
|
| // reach here.
|
| return;
|
| }
|
| + EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
|
| notifyLayoutObjectOfSelectionChange(userTriggered);
|
| - // If the selections are same in the DOM tree but not in the flat tree,
|
| - // don't fire events. For example, if the selection crosses shadow tree
|
| - // boundary, selection for the DOM tree is shrunk while that for the
|
| - // flat tree is not. Additionally, this case occurs in some edge cases.
|
| - // See also: editing/pasteboard/4076267-3.html
|
| - if (oldSelection == m_selectionEditor->visibleSelection<Strategy>()) {
|
| - m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
|
| - return;
|
| - }
|
| - m_frame->editor().respondToChangedSelection(oldSelectionStart, options);
|
| if (userTriggered == UserTriggered) {
|
| ScrollAlignment alignment;
|
|
|
| @@ -273,6 +256,10 @@ void FrameSelection::setSelectionAlgorithm(
|
| ? ScrollAlignment::alignTopAlways
|
| : ScrollAlignment::alignToEdgeIfNeeded;
|
|
|
| + // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
|
| + // needs to be audited. See http://crbug.com/590369 for more details.
|
| + document().updateStyleAndLayoutIgnorePendingStylesheets();
|
| +
|
| revealSelection(alignment, RevealExtent);
|
| }
|
|
|
| @@ -283,48 +270,26 @@ void FrameSelection::setSelectionAlgorithm(
|
| Event::create(EventTypeNames::selectionchange));
|
| }
|
|
|
| -// TODO(yosin): We will make |selectionInDOMTree| version of |SetSelection()|
|
| -// as primary function instead of wrapper.
|
| -void FrameSelection::setSelection(const SelectionInDOMTree& newSelection,
|
| - SetSelectionOptions options,
|
| - CursorAlignOnScroll align,
|
| - TextGranularity granularity) {
|
| - if (!newSelection.isNone()) {
|
| - // TODO(editing-dev): The use of
|
| - // updateStyleAndLayoutIgnorePendingStylesheets
|
| - // needs to be audited. See http://crbug.com/590369 for more details.
|
| - newSelection.base()
|
| - .document()
|
| - ->updateStyleAndLayoutIgnorePendingStylesheets();
|
| - }
|
| - setSelection(createVisibleSelection(newSelection), options, align,
|
| - granularity);
|
| -}
|
| -
|
| -// TODO(yosin): We will make |selectionInFlatTree| version of |SetSelection()|
|
| -// as primary function instead of wrapper.
|
| void FrameSelection::setSelection(const SelectionInFlatTree& newSelection,
|
| SetSelectionOptions options,
|
| CursorAlignOnScroll align,
|
| TextGranularity granularity) {
|
| - if (!newSelection.isNone()) {
|
| - // TODO(editing-dev): The use of
|
| - // updateStyleAndLayoutIgnorePendingStylesheets
|
| - // needs to be audited. See http://crbug.com/590369 for more details.
|
| - newSelection.base()
|
| - .document()
|
| - ->updateStyleAndLayoutIgnorePendingStylesheets();
|
| - }
|
| - setSelection(createVisibleSelection(newSelection), options, align,
|
| - granularity);
|
| + newSelection.assertValidFor(document());
|
| + SelectionInDOMTree::Builder builder;
|
| + builder.setAffinity(newSelection.affinity())
|
| + .setBaseAndExtent(toPositionInDOMTree(newSelection.base()),
|
| + toPositionInDOMTree(newSelection.extent()))
|
| + .setGranularity(newSelection.granularity())
|
| + .setIsDirectional(newSelection.isDirectional())
|
| + .setHasTrailingWhitespace(newSelection.hasTrailingWhitespace());
|
| + return setSelection(builder.build(), options, align, granularity);
|
| }
|
|
|
| void FrameSelection::setSelection(const VisibleSelection& newSelection,
|
| SetSelectionOptions options,
|
| CursorAlignOnScroll align,
|
| TextGranularity granularity) {
|
| - setSelectionAlgorithm<EditingStrategy>(newSelection, options, align,
|
| - granularity);
|
| + setSelection(newSelection.asSelection(), options, align, granularity);
|
| }
|
|
|
| void FrameSelection::setSelection(
|
| @@ -332,282 +297,29 @@ void FrameSelection::setSelection(
|
| SetSelectionOptions options,
|
| CursorAlignOnScroll align,
|
| TextGranularity granularity) {
|
| - setSelectionAlgorithm<EditingInFlatTreeStrategy>(newSelection, options, align,
|
| - granularity);
|
| -}
|
| -
|
| -static bool removingNodeRemovesPosition(Node& node, const Position& position) {
|
| - if (!position.anchorNode())
|
| - return false;
|
| -
|
| - if (position.anchorNode() == node)
|
| - return true;
|
| -
|
| - if (!node.isElementNode())
|
| - return false;
|
| -
|
| - Element& element = toElement(node);
|
| - return element.isShadowIncludingInclusiveAncestorOf(position.anchorNode());
|
| -}
|
| -
|
| -static Position computePositionForChildrenRemoval(const Position& position,
|
| - ContainerNode& container) {
|
| - Node* node = position.computeContainerNode();
|
| - if (container.containsIncludingHostElements(*node))
|
| - return Position::firstPositionInNode(&container);
|
| - return position;
|
| + setSelection(newSelection.asSelection(), options, align, granularity);
|
| }
|
|
|
| void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) {
|
| - if (isNone() || !container.inActiveDocument())
|
| - return;
|
| - const Position& oldStart = selection().start();
|
| - const Position& newStart =
|
| - computePositionForChildrenRemoval(oldStart, container);
|
| - const Position& oldEnd = selection().end();
|
| - const Position& newEnd = computePositionForChildrenRemoval(oldEnd, container);
|
| - const Position& oldBase = selection().base();
|
| - const Position& newBase =
|
| - computePositionForChildrenRemoval(oldBase, container);
|
| - const Position& oldExtent = selection().extent();
|
| - const Position& newExtent =
|
| - computePositionForChildrenRemoval(oldExtent, container);
|
| - if (newStart == oldStart && newEnd == oldEnd && newBase == oldBase &&
|
| - newExtent == oldExtent)
|
| + if (!container.inActiveDocument())
|
| return;
|
| - if (selection().isBaseFirst())
|
| - m_selectionEditor->setWithoutValidation(newStart, newEnd);
|
| - else
|
| - m_selectionEditor->setWithoutValidation(newEnd, newStart);
|
| - m_frameCaret->setCaretRectNeedsUpdate();
|
| - if (document().isRunningExecCommand())
|
| - return;
|
| - TypingCommand::closeTyping(m_frame);
|
| -}
|
| -
|
| -void FrameSelection::nodeWillBeRemoved(Node& node) {
|
| - // There can't be a selection inside a fragment, so if a fragment's node is
|
| - // being removed, the selection in the document that created the fragment
|
| - // needs no adjustment.
|
| - if (isNone() || !node.inActiveDocument())
|
| - return;
|
| -
|
| - respondToNodeModification(
|
| - node, removingNodeRemovesPosition(node, selection().base()),
|
| - removingNodeRemovesPosition(node, selection().extent()),
|
| - removingNodeRemovesPosition(node, selection().start()),
|
| - removingNodeRemovesPosition(node, selection().end()));
|
| -
|
| - m_frameCaret->nodeWillBeRemoved(node);
|
| -}
|
| -
|
| -static SelectionState selectionStateOf(const Node& node) {
|
| - const LayoutObject* layoutObject = node.layoutObject();
|
| - if (!layoutObject)
|
| - return SelectionNone;
|
| - return layoutObject->getSelectionState();
|
| -}
|
| -
|
| -void FrameSelection::respondToNodeModification(Node& node,
|
| - bool baseRemoved,
|
| - bool extentRemoved,
|
| - bool startRemoved,
|
| - bool endRemoved) {
|
| - DCHECK(node.document().isActive()) << node;
|
| -
|
| - bool clearLayoutTreeSelection = false;
|
| - bool clearDOMTreeSelection = false;
|
| -
|
| - if (startRemoved || endRemoved) {
|
| - Position start = selection().start();
|
| - Position end = selection().end();
|
| - if (startRemoved)
|
| - updatePositionForNodeRemoval(start, node);
|
| - if (endRemoved)
|
| - updatePositionForNodeRemoval(end, node);
|
| -
|
| - if (Position::commonAncestorTreeScope(start, end) && start.isNotNull() &&
|
| - end.isNotNull()) {
|
| - if (selection().isBaseFirst())
|
| - m_selectionEditor->setWithoutValidation(start, end);
|
| - else
|
| - m_selectionEditor->setWithoutValidation(end, start);
|
| - } else {
|
| - clearDOMTreeSelection = true;
|
| - }
|
| -
|
| - clearLayoutTreeSelection = true;
|
| - } else if (baseRemoved || extentRemoved) {
|
| - // The base and/or extent are about to be removed, but the start and end
|
| - // aren't. Change the base and extent to the start and end, but don't
|
| - // re-validate the selection, since doing so could move the start and end
|
| - // into the node that is about to be removed.
|
| - if (selection().isBaseFirst())
|
| - m_selectionEditor->setWithoutValidation(selection().start(),
|
| - selection().end());
|
| - else
|
| - m_selectionEditor->setWithoutValidation(selection().end(),
|
| - selection().start());
|
| - } else if (selectionStateOf(node) != SelectionNone) {
|
| - // When node to be removed is part of selection, we invalidate
|
| - // selection to paint again.
|
| - // TODO(yosin): We should paint changed area only rather than whole
|
| - // selected range.
|
| - clearLayoutTreeSelection = true;
|
| - }
|
| -
|
| - if (clearLayoutTreeSelection)
|
| - selection().start().document()->layoutViewItem().clearSelection();
|
| -
|
| - if (clearDOMTreeSelection)
|
| - setSelection(SelectionInDOMTree(), DoNotSetFocus);
|
| - m_frameCaret->setCaretRectNeedsUpdate();
|
| -
|
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to
|
| // |Editor| class.
|
| if (!document().isRunningExecCommand())
|
| TypingCommand::closeTyping(m_frame);
|
| }
|
|
|
| -static Position updatePositionAfterAdoptingTextReplacement(
|
| - const Position& position,
|
| - CharacterData* node,
|
| - unsigned offset,
|
| - unsigned oldLength,
|
| - unsigned newLength) {
|
| - if (!position.anchorNode() || position.anchorNode() != node ||
|
| - !position.isOffsetInAnchor())
|
| - return position;
|
| -
|
| - // See:
|
| - // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
|
| - DCHECK_GE(position.offsetInContainerNode(), 0);
|
| - unsigned positionOffset =
|
| - static_cast<unsigned>(position.offsetInContainerNode());
|
| - // Replacing text can be viewed as a deletion followed by insertion.
|
| - if (positionOffset >= offset && positionOffset <= offset + oldLength)
|
| - positionOffset = offset;
|
| -
|
| - // Adjust the offset if the position is after the end of the deleted contents
|
| - // (positionOffset > offset + oldLength) to avoid having a stale offset.
|
| - if (positionOffset > offset + oldLength)
|
| - positionOffset = positionOffset - oldLength + newLength;
|
| -
|
| - // Due to case folding
|
| - // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText
|
| - // length may be different from Text length. A correct implementation would
|
| - // translate the LayoutText offset to a Text offset; this is just a safety
|
| - // precaution to avoid offset values that run off the end of the Text.
|
| - if (positionOffset > node->length())
|
| - positionOffset = node->length();
|
| -
|
| - // CharacterNode in VisibleSelection must be Text node, because Comment
|
| - // and ProcessingInstruction node aren't visible.
|
| - return Position(toText(node), positionOffset);
|
| -}
|
| -
|
| -void FrameSelection::didUpdateCharacterData(CharacterData* node,
|
| - unsigned offset,
|
| - unsigned oldLength,
|
| - unsigned newLength) {
|
| - // The fragment check is a performance optimization. See
|
| - // http://trac.webkit.org/changeset/30062.
|
| - if (isNone() || !node || !node->isConnected())
|
| - return;
|
| -
|
| - Position base = updatePositionAfterAdoptingTextReplacement(
|
| - selection().base(), node, offset, oldLength, newLength);
|
| - Position extent = updatePositionAfterAdoptingTextReplacement(
|
| - selection().extent(), node, offset, oldLength, newLength);
|
| - Position start = updatePositionAfterAdoptingTextReplacement(
|
| - selection().start(), node, offset, oldLength, newLength);
|
| - Position end = updatePositionAfterAdoptingTextReplacement(
|
| - selection().end(), node, offset, oldLength, newLength);
|
| - updateSelectionIfNeeded(base, extent, start, end);
|
| -}
|
| -
|
| -static Position updatePostionAfterAdoptingTextNodesMerged(
|
| - const Position& position,
|
| - const Text& oldNode,
|
| - unsigned offset) {
|
| - if (!position.anchorNode() || !position.isOffsetInAnchor())
|
| - return position;
|
| -
|
| - DCHECK_GE(position.offsetInContainerNode(), 0);
|
| - unsigned positionOffset =
|
| - static_cast<unsigned>(position.offsetInContainerNode());
|
| -
|
| - if (position.anchorNode() == &oldNode)
|
| - return Position(toText(oldNode.previousSibling()), positionOffset + offset);
|
| -
|
| - if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset)
|
| - return Position(toText(oldNode.previousSibling()), offset);
|
| -
|
| - return position;
|
| -}
|
| -
|
| -void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset) {
|
| - if (isNone() || !oldNode.isConnected())
|
| - return;
|
| - Position base = updatePostionAfterAdoptingTextNodesMerged(selection().base(),
|
| - oldNode, offset);
|
| - Position extent = updatePostionAfterAdoptingTextNodesMerged(
|
| - selection().extent(), oldNode, offset);
|
| - Position start = updatePostionAfterAdoptingTextNodesMerged(
|
| - selection().start(), oldNode, offset);
|
| - Position end = updatePostionAfterAdoptingTextNodesMerged(selection().end(),
|
| - oldNode, offset);
|
| - updateSelectionIfNeeded(base, extent, start, end);
|
| -}
|
| -
|
| -static Position updatePostionAfterAdoptingTextNodeSplit(
|
| - const Position& position,
|
| - const Text& oldNode) {
|
| - if (!position.anchorNode() || position.anchorNode() != &oldNode ||
|
| - !position.isOffsetInAnchor())
|
| - return position;
|
| - // See:
|
| - // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
|
| - DCHECK_GE(position.offsetInContainerNode(), 0);
|
| - unsigned positionOffset =
|
| - static_cast<unsigned>(position.offsetInContainerNode());
|
| - unsigned oldLength = oldNode.length();
|
| - if (positionOffset <= oldLength)
|
| - return position;
|
| - return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
|
| -}
|
| -
|
| -void FrameSelection::didSplitTextNode(const Text& oldNode) {
|
| - if (isNone() || !oldNode.isConnected())
|
| - return;
|
| - Position base =
|
| - updatePostionAfterAdoptingTextNodeSplit(selection().base(), oldNode);
|
| - Position extent =
|
| - updatePostionAfterAdoptingTextNodeSplit(selection().extent(), oldNode);
|
| - Position start =
|
| - updatePostionAfterAdoptingTextNodeSplit(selection().start(), oldNode);
|
| - Position end =
|
| - updatePostionAfterAdoptingTextNodeSplit(selection().end(), oldNode);
|
| - updateSelectionIfNeeded(base, extent, start, end);
|
| -}
|
| -
|
| -void FrameSelection::updateSelectionIfNeeded(const Position& base,
|
| - const Position& extent,
|
| - const Position& start,
|
| - const Position& end) {
|
| - if (base == selection().base() && extent == selection().extent() &&
|
| - start == selection().start() && end == selection().end())
|
| +void FrameSelection::nodeWillBeRemoved(Node& node) {
|
| + // There can't be a selection inside a fragment, so if a fragment's node is
|
| + // being removed,
|
| + // the selection in the document that created the fragment needs no
|
| + // adjustment.
|
| + if (!node.inActiveDocument())
|
| return;
|
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to
|
| // |Editor| class.
|
| if (!document().isRunningExecCommand())
|
| TypingCommand::closeTyping(m_frame);
|
| - VisibleSelection newSelection;
|
| - if (selection().isBaseFirst())
|
| - newSelection.setWithoutValidation(start, end);
|
| - else
|
| - newSelection.setWithoutValidation(end, start);
|
| - setSelection(newSelection, DoNotSetFocus);
|
| }
|
|
|
| void FrameSelection::didChangeFocus() {
|
| @@ -696,20 +408,17 @@ void FrameSelection::clear() {
|
| m_granularity = CharacterGranularity;
|
| if (m_granularityStrategy)
|
| m_granularityStrategy->Clear();
|
| - setSelection(SelectionInDOMTree());
|
| + setSelection(VisibleSelection());
|
| }
|
|
|
| void FrameSelection::documentAttached(Document* document) {
|
| DCHECK(document);
|
| - DCHECK(!m_document) << "FrameSelection is already attached to " << m_document;
|
| - m_document = document;
|
| - m_useSecureKeyboardEntryWhenActive = false;
|
| + m_frameCaret->documentAttached(document);
|
| m_selectionEditor->documentAttached(document);
|
| + setContext(document);
|
| }
|
|
|
| -void FrameSelection::documentDetached(const Document& document) {
|
| - DCHECK_EQ(m_document, document);
|
| - m_document = nullptr;
|
| +void FrameSelection::contextDestroyed(Document*) {
|
| m_granularity = CharacterGranularity;
|
|
|
| LayoutViewItem view = m_frame->contentLayoutItem();
|
| @@ -717,19 +426,18 @@ void FrameSelection::documentDetached(const Document& document) {
|
| view.clearSelection();
|
|
|
| clearTypingStyle();
|
| - m_selectionEditor->documentDetached(document);
|
| - m_frameCaret->documentDetached();
|
| }
|
|
|
| LayoutBlock* FrameSelection::caretLayoutObject() const {
|
| - DCHECK(selection().isValidFor(document()));
|
| - if (!isCaret())
|
| + DCHECK(isAvailable());
|
| + const VisibleSelection& selection = computeVisibleSelectionInDOMTree();
|
| + if (!selection.isCaret())
|
| return nullptr;
|
| - return CaretBase::caretLayoutObject(selection().start().anchorNode());
|
| + return CaretBase::caretLayoutObject(selection.start().anchorNode());
|
| }
|
|
|
| IntRect FrameSelection::absoluteCaretBounds() {
|
| - DCHECK(selection().isValidFor(*m_frame->document()));
|
| + DCHECK(isAvailable());
|
| return m_frameCaret->absoluteCaretBounds();
|
| }
|
|
|
| @@ -737,22 +445,18 @@ void FrameSelection::invalidateCaretRect(bool forceInvalidation) {
|
| m_frameCaret->invalidateCaretRect(forceInvalidation);
|
| }
|
|
|
| -void FrameSelection::dataWillChange(const CharacterData& node) {
|
| - m_frameCaret->dataWillChange(node);
|
| -}
|
| -
|
| void FrameSelection::paintCaret(GraphicsContext& context,
|
| const LayoutPoint& paintOffset) {
|
| m_frameCaret->paintCaret(context, paintOffset);
|
| }
|
|
|
| bool FrameSelection::contains(const LayoutPoint& point) {
|
| - if (document().layoutViewItem().isNull())
|
| + if (!isAvailable())
|
| return false;
|
|
|
| // Treat a collapsed selection like no selection.
|
| const VisibleSelectionInFlatTree& visibleSelection =
|
| - this->visibleSelection<EditingInFlatTreeStrategy>();
|
| + computeVisibleSelectionInFlatTree();
|
| if (!visibleSelection.isRange())
|
| return false;
|
|
|
| @@ -783,10 +487,12 @@ bool FrameSelection::contains(const LayoutPoint& point) {
|
|
|
| // Workaround for the fact that it's hard to delete a frame.
|
| // Call this after doing user-triggered selections to make it easy to delete the
|
| -// frame you entirely selected. Can't do this implicitly as part of every
|
| -// setSelection call because in some contexts it might not be good for the focus
|
| -// to move to another frame. So instead we call it from places where we are
|
| -// selecting with the mouse or the keyboard after setting the selection.
|
| +// frame you entirely selected.
|
| +// Can't do this implicitly as part of every setSelection call because in some
|
| +// contexts it might not be good
|
| +// for the focus to move to another frame. So instead we call it from places
|
| +// where we are selecting with the
|
| +// mouse or the keyboard after setting the selection.
|
| void FrameSelection::selectFrameElementInParentIfFullySelected() {
|
| // Find the parent frame; if there is none, then we have nothing to do.
|
| Frame* parent = m_frame->tree().parent();
|
| @@ -912,6 +618,18 @@ void FrameSelection::selectAll() {
|
| notifyLayoutObjectOfSelectionChange(UserTriggered);
|
| }
|
|
|
| +bool FrameSelection::setSelectedRange(Range* range,
|
| + TextAffinity affinity,
|
| + SelectionDirectionalMode directional,
|
| + SetSelectionOptions options) {
|
| + if (!range || !range->isConnected())
|
| + return false;
|
| + DCHECK_EQ(range->startContainer()->document(),
|
| + range->endContainer()->document());
|
| + return setSelectedRange(EphemeralRange(range), affinity, directional,
|
| + options);
|
| +}
|
| +
|
| bool FrameSelection::setSelectedRange(const EphemeralRange& range,
|
| TextAffinity affinity,
|
| SelectionDirectionalMode directional,
|
| @@ -1001,9 +719,8 @@ void FrameSelection::pageActivationChanged() {
|
| }
|
|
|
| void FrameSelection::updateSecureKeyboardEntryIfActive() {
|
| - if (!isFocusedAndActive())
|
| - return;
|
| - setUseSecureKeyboardEntry(m_useSecureKeyboardEntryWhenActive);
|
| + if (isFocusedAndActive())
|
| + setUseSecureKeyboardEntry(m_useSecureKeyboardEntryWhenActive);
|
| }
|
|
|
| void FrameSelection::setUseSecureKeyboardEntryWhenActive(
|
| @@ -1042,11 +759,15 @@ void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) {
|
| return m_pendingSelection->commit(layoutView);
|
| }
|
|
|
| -void FrameSelection::updateAppearance() {
|
| - m_frameCaret->updateAppearance();
|
| +void FrameSelection::didLayout() {
|
| + DCHECK_GE(document().lifecycle().state(),
|
| + DocumentLifecycle::AfterPerformLayout);
|
| + updateAppearance();
|
| +}
|
|
|
| - if (m_frame->contentLayoutItem().isNull())
|
| - return;
|
| +void FrameSelection::updateAppearance() {
|
| + DCHECK(!m_frame->contentLayoutItem().isNull());
|
| + m_frameCaret->setCaretRectNeedsUpdate();
|
| m_pendingSelection->setHasPendingSelection();
|
| }
|
|
|
| @@ -1093,7 +814,7 @@ void FrameSelection::setFocusedNodeIfNeeded() {
|
| static String extractSelectedText(const FrameSelection& selection,
|
| TextIteratorBehavior behavior) {
|
| const VisibleSelectionInFlatTree& visibleSelection =
|
| - selection.visibleSelection<EditingInFlatTreeStrategy>();
|
| + selection.computeVisibleSelectionInFlatTree();
|
| const EphemeralRangeInFlatTree& range =
|
| visibleSelection.toNormalizedEphemeralRange();
|
| // We remove '\0' characters because they are not visibly rendered to the
|
| @@ -1103,7 +824,7 @@ static String extractSelectedText(const FrameSelection& selection,
|
|
|
| String FrameSelection::selectedHTMLForClipboard() const {
|
| const VisibleSelectionInFlatTree& visibleSelection =
|
| - this->visibleSelection<EditingInFlatTreeStrategy>();
|
| + computeVisibleSelectionInFlatTree();
|
| const EphemeralRangeInFlatTree& range =
|
| visibleSelection.toNormalizedEphemeralRange();
|
| return createMarkup(range.startPosition(), range.endPosition(),
|
| @@ -1242,10 +963,6 @@ void FrameSelection::setSelectionFromNone() {
|
| return;
|
| if (HTMLBodyElement* body =
|
| Traversal<HTMLBodyElement>::firstChild(*documentElement)) {
|
| - // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
|
| - // needs to be audited. See http://crbug.com/590369 for more details.
|
| - document->updateStyleAndLayoutIgnorePendingStylesheets();
|
| -
|
| setSelection(SelectionInDOMTree::Builder()
|
| .collapse(firstPositionInOrBeforeNode(body))
|
| .build());
|
| @@ -1270,6 +987,29 @@ void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor) {
|
| m_frameCaret->setShouldShowBlockCursor(shouldShowBlockCursor);
|
| }
|
|
|
| +template <typename Strategy>
|
| +VisibleSelectionTemplate<Strategy> FrameSelection::validateSelection(
|
| + const VisibleSelectionTemplate<Strategy>& selection) {
|
| + if (selection.isNone())
|
| + return selection;
|
| +
|
| + const PositionTemplate<Strategy> base = selection.base();
|
| + const PositionTemplate<Strategy> extent = selection.extent();
|
| + bool isBaseValid = base.document() == document();
|
| + bool isExtentValid = extent.document() == document();
|
| +
|
| + if (isBaseValid && isExtentValid)
|
| + return selection;
|
| +
|
| + VisibleSelectionTemplate<Strategy> newSelection;
|
| + if (isBaseValid) {
|
| + newSelection.setWithoutValidation(base, base);
|
| + } else if (isExtentValid) {
|
| + newSelection.setWithoutValidation(extent, extent);
|
| + }
|
| + return newSelection;
|
| +}
|
| +
|
| #ifndef NDEBUG
|
|
|
| void FrameSelection::showTreeForThis() const {
|
| @@ -1279,12 +1019,12 @@ void FrameSelection::showTreeForThis() const {
|
| #endif
|
|
|
| DEFINE_TRACE(FrameSelection) {
|
| - visitor->trace(m_document);
|
| visitor->trace(m_frame);
|
| visitor->trace(m_pendingSelection);
|
| visitor->trace(m_selectionEditor);
|
| visitor->trace(m_typingStyle);
|
| visitor->trace(m_frameCaret);
|
| + SynchronousMutationObserver::trace(visitor);
|
| }
|
|
|
| void FrameSelection::scheduleVisualUpdate() const {
|
| @@ -1305,8 +1045,7 @@ bool FrameSelection::selectWordAroundPosition(const VisiblePosition& position) {
|
| .collapse(start.toPositionWithAffinity())
|
| .extend(end.deepEquivalent())
|
| .build(),
|
| - CloseTyping | ClearTypingStyle,
|
| - CursorAlignOnScroll::IfNeeded, WordGranularity);
|
| + WordGranularity);
|
| return true;
|
| }
|
| }
|
| @@ -1363,8 +1102,7 @@ void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition,
|
| if (newSelection.isNone())
|
| return;
|
|
|
| - setSelection(newSelection, CloseTyping | ClearTypingStyle,
|
| - CursorAlignOnScroll::IfNeeded, granularity);
|
| + setSelection(newSelection, granularity);
|
| }
|
|
|
| void FrameSelection::updateIfNeeded() {
|
|
|