| 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 a30f178610c8eede18e70b4fa4cfe6f76ee7b721..10a702562f5f21f4b2e96cf80b2d4582bd1f1942 100644
|
| --- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
|
| @@ -110,17 +110,16 @@
|
| return m_frameCaret->displayItemClient();
|
| }
|
|
|
| -Document& FrameSelection::document() const {
|
| +const Document& FrameSelection::document() const {
|
| DCHECK(lifecycleContext());
|
| return *lifecycleContext();
|
| }
|
|
|
| -bool FrameSelection::isHandleVisible() const {
|
| - return selectionInDOMTree().isHandleVisible();
|
| -}
|
| -
|
| -// TODO(yosin): We should replace |visibleSelection<EditingStrategy>()| to
|
| -// |computeVisibleSelectionInDOMTree()|.
|
| +Document& FrameSelection::document() {
|
| + 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.
|
| @@ -130,29 +129,12 @@
|
| return m_selectionEditor->visibleSelection<EditingStrategy>();
|
| }
|
|
|
| -// TODO(yosin): We should replace
|
| -// |visibleSelection<EditingInFlatTreeStrategy>()| with
|
| -// |computeVisibleSelectionInFlatTree()|.
|
| template <>
|
| const VisibleSelectionInFlatTree&
|
| FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const {
|
| return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>();
|
| }
|
|
|
| -const VisibleSelection& FrameSelection::computeVisibleSelectionInDOMTree()
|
| - const {
|
| - return m_selectionEditor->computeVisibleSelectionInDOMTree();
|
| -}
|
| -
|
| -const VisibleSelectionInFlatTree&
|
| -FrameSelection::computeVisibleSelectionInFlatTree() const {
|
| - return m_selectionEditor->computeVisibleSelectionInFlatTree();
|
| -}
|
| -
|
| -const SelectionInDOMTree& FrameSelection::selectionInDOMTree() const {
|
| - return m_selectionEditor->selectionInDOMTree();
|
| -}
|
| -
|
| Element* FrameSelection::rootEditableElementOrDocumentElement() const {
|
| Element* selectionRoot = selection().rootEditableElement();
|
| return selectionRoot ? selectionRoot : document().documentElement();
|
| @@ -167,13 +149,8 @@
|
| return node ? &node->treeScope().rootNode() : 0;
|
| }
|
|
|
| -// TODO(yosin): We should rename |FrameSelection::selection()| to
|
| -// |selectionDeprecated()|.
|
| const VisibleSelection& FrameSelection::selection() const {
|
| - // TODO(yosin): We should hoist updateStyleAndLayoutIgnorePendingStylesheets
|
| - // to caller. See http://crbug.com/590369 for more details.
|
| - document().updateStyleAndLayoutIgnorePendingStylesheets();
|
| - return computeVisibleSelectionInDOMTree();
|
| + return visibleSelection<EditingStrategy>();
|
| }
|
|
|
| const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const {
|
| @@ -197,22 +174,29 @@
|
| setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered);
|
| }
|
|
|
| -void FrameSelection::setSelection(const SelectionInDOMTree& passedSelection,
|
| - SetSelectionOptions options,
|
| - CursorAlignOnScroll align,
|
| - TextGranularity granularity) {
|
| +template <typename Strategy>
|
| +void FrameSelection::setSelectionAlgorithm(
|
| + const VisibleSelectionTemplate<Strategy>& newSelection,
|
| + HandleVisibility handleVisibility,
|
| + SetSelectionOptions options,
|
| + CursorAlignOnScroll align,
|
| + TextGranularity granularity) {
|
| DCHECK(isAvailable());
|
| - passedSelection.assertValidFor(document());
|
| -
|
| - SelectionInDOMTree::Builder builder(passedSelection);
|
| - if (shouldAlwaysUseDirectionalSelection(m_frame))
|
| - builder.setIsDirectional(true);
|
| - SelectionInDOMTree newSelection = builder.build();
|
| + DCHECK(newSelection.isValidFor(document()));
|
| + const Document& currentDocument = document();
|
| 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;
|
|
|
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to
|
| @@ -223,28 +207,23 @@
|
| if (shouldClearTypingStyle)
|
| m_frame->editor().clearTypingStyle();
|
|
|
| - const SelectionInDOMTree oldSelectionInDOMTree =
|
| - m_selectionEditor->selectionInDOMTree();
|
| - if (oldSelectionInDOMTree == newSelection)
|
| - return;
|
| - m_selectionEditor->setSelection(newSelection);
|
| + if (m_selectionEditor->visibleSelection<Strategy>() == s &&
|
| + m_handleVisibility == handleVisibility) {
|
| + // Even if selection was not changed, selection offsets may have been
|
| + // changed.
|
| + notifyLayoutObjectOfSelectionChange(userTriggered);
|
| + return;
|
| + }
|
| +
|
| + const VisibleSelectionTemplate<Strategy> oldSelection =
|
| + visibleSelection<Strategy>();
|
| + const Position& oldSelectionStart = selection().start();
|
| +
|
| + m_handleVisibility = handleVisibility;
|
| + m_selectionEditor->setVisibleSelection(s, options);
|
| scheduleVisualUpdateForPaintInvalidationIfNeeded();
|
|
|
| - // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets
|
| - // needs to be audited. see http://crbug.com/590369 for more details.
|
| - document().updateStyleAndLayoutIgnorePendingStylesheets();
|
| -
|
| - 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 (!computeVisibleSelectionInDOMTree().isNone() &&
|
| - !(options & DoNotSetFocus)) {
|
| + if (!s.isNone() && !(options & DoNotSetFocus)) {
|
| setFocusedNodeIfNeeded();
|
| // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and
|
| // "FocusIn", |m_frame| may associate to another document.
|
| @@ -264,7 +243,6 @@
|
| // 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) {
|
| @@ -273,8 +251,16 @@
|
| // 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>())
|
| + return;
|
| +
|
| + m_frame->editor().respondToChangedSelection(oldSelectionStart, options);
|
| if (userTriggered == UserTriggered) {
|
| ScrollAlignment alignment;
|
|
|
| @@ -287,11 +273,6 @@
|
| ? ScrollAlignment::alignTopAlways
|
| : ScrollAlignment::alignToEdgeIfNeeded;
|
|
|
| - // TODO(editing-dev): The use of
|
| - // updateStyleAndLayoutIgnorePendingStylesheets
|
| - // needs to be audited. See http://crbug.com/590369 for more details.
|
| - document().updateStyleAndLayoutIgnorePendingStylesheets();
|
| -
|
| revealSelection(alignment, RevealExtent);
|
| }
|
|
|
| @@ -302,20 +283,44 @@
|
| 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),
|
| + newSelection.isHandleVisible() ? HandleVisibility::Visible
|
| + : HandleVisibility::NotVisible,
|
| + 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) {
|
| - newSelection.assertValidFor(document());
|
| - SelectionInDOMTree::Builder builder;
|
| - builder.setAffinity(newSelection.affinity())
|
| - .setBaseAndExtent(toPositionInDOMTree(newSelection.base()),
|
| - toPositionInDOMTree(newSelection.extent()))
|
| - .setGranularity(newSelection.granularity())
|
| - .setIsDirectional(newSelection.isDirectional())
|
| - .setIsHandleVisible(newSelection.isHandleVisible())
|
| - .setHasTrailingWhitespace(newSelection.hasTrailingWhitespace());
|
| - return setSelection(builder.build(), options, align, 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),
|
| + newSelection.isHandleVisible() ? HandleVisibility::Visible
|
| + : HandleVisibility::NotVisible,
|
| + options, align, granularity);
|
| }
|
|
|
| void FrameSelection::setSelection(const VisibleSelection& newSelection,
|
| @@ -323,16 +328,13 @@
|
| SetSelectionOptions options,
|
| CursorAlignOnScroll align,
|
| TextGranularity granularity) {
|
| - setSelection(
|
| - SelectionInDOMTree::Builder(newSelection.asSelection())
|
| - .setIsHandleVisible(handleVisibility == HandleVisibility::Visible)
|
| - .build(),
|
| - options, align, granularity);
|
| + setSelectionAlgorithm<EditingStrategy>(newSelection, handleVisibility,
|
| + options, align, granularity);
|
| }
|
|
|
| void FrameSelection::setSelection(const VisibleSelection& newSelection,
|
| SetSelectionOptions options) {
|
| - setSelection(newSelection.asSelection(), options);
|
| + setSelection(newSelection, HandleVisibility::NotVisible, options);
|
| }
|
|
|
| void FrameSelection::setSelection(
|
| @@ -341,22 +343,30 @@
|
| SetSelectionOptions options,
|
| CursorAlignOnScroll align,
|
| TextGranularity granularity) {
|
| - setSelection(
|
| - SelectionInFlatTree::Builder(newSelection.asSelection())
|
| - .setIsHandleVisible(handleVisibility == HandleVisibility::Visible)
|
| - .build(),
|
| - options, align, granularity);
|
| + setSelectionAlgorithm<EditingInFlatTreeStrategy>(
|
| + newSelection, handleVisibility, options, align, granularity);
|
| }
|
|
|
| void FrameSelection::setSelection(
|
| const VisibleSelectionInFlatTree& newSelection,
|
| SetSelectionOptions options) {
|
| - setSelection(newSelection.asSelection(), options);
|
| -}
|
| -
|
| -// TODO(yosin): We should move |computePositionForChildrenRemoval()| to
|
| -// "SelectionEditor.cpp" since it used only in
|
| -// |SelectionEditor::nodeChildrenWillBeRemoved()|.
|
| + setSelection(newSelection, HandleVisibility::NotVisible, options);
|
| +}
|
| +
|
| +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();
|
| @@ -366,95 +376,121 @@
|
| }
|
|
|
| void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) {
|
| - if (!container.inActiveDocument())
|
| - return;
|
| + 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)
|
| + return;
|
| + if (selection().isBaseFirst())
|
| + m_selectionEditor->setWithoutValidation(newStart, newEnd);
|
| + else
|
| + m_selectionEditor->setWithoutValidation(newEnd, newStart);
|
| + 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()));
|
| +}
|
| +
|
| +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);
|
| +
|
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to
|
| // |Editor| class.
|
| if (!document().isRunningExecCommand())
|
| TypingCommand::closeTyping(m_frame);
|
| }
|
|
|
| -// TODO(yosin): We should move |SelectionEditor::nodeChildrenWillBeRemoved()|
|
| -// to "SelectionEditor.cpp".
|
| -void SelectionEditor::nodeChildrenWillBeRemoved(ContainerNode& container) {
|
| - if (m_selection.isNone())
|
| - return;
|
| - const Position oldBase = m_selection.m_base;
|
| - const Position oldExtent = m_selection.m_extent;
|
| - const Position& newBase =
|
| - computePositionForChildrenRemoval(oldBase, container);
|
| - const Position& newExtent =
|
| - computePositionForChildrenRemoval(oldExtent, container);
|
| - if (newBase == oldBase && newExtent == oldExtent)
|
| - return;
|
| - m_selection = SelectionInDOMTree::Builder()
|
| - .setBaseAndExtent(newBase, newExtent)
|
| - .build();
|
| - markCacheDirty();
|
| -}
|
| -
|
| -// TODO(yosin): We should move |computePositionForChildrenRemoval()| with
|
| -// |nodeWillBeRemoved()| to "SelectionEditor.cpp".
|
| -static Position computePositionForNodeRemoval(const Position& position,
|
| - Node& nodeToBeRemoved) {
|
| - Position result = position;
|
| - // TODO(yosin): We should rename |updatePositionForNodeRemoval()|
|
| - // to |computePositionForNodeRemoval()| to avoid using output parameter.
|
| - updatePositionForNodeRemoval(result, nodeToBeRemoved);
|
| - return result;
|
| -}
|
| -
|
| -// TODO(yosin): We should move |nodeWillBeRemoved()| to
|
| -// "SelectionEditor.cpp".
|
| -void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) {
|
| - if (m_selection.isNone())
|
| - return;
|
| - const Position oldBase = m_selection.m_base;
|
| - const Position oldExtent = m_selection.m_extent;
|
| - const Position& newBase =
|
| - computePositionForNodeRemoval(oldBase, nodeToBeRemoved);
|
| - const Position& newExtent =
|
| - computePositionForNodeRemoval(oldExtent, nodeToBeRemoved);
|
| - if (newBase == oldBase && newExtent == oldExtent)
|
| - return;
|
| - m_selection = SelectionInDOMTree::Builder()
|
| - .setBaseAndExtent(newBase, newExtent)
|
| - .build();
|
| - markCacheDirty();
|
| -}
|
| -
|
| -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);
|
| -}
|
| -
|
| -// TODO(yosin): We should move |updatePositionAfterAdoptingTextReplacement()|
|
| -// to "SelectionEditor.cpp" since it used only in
|
| -// |SelectionEditor::didUpdateCharacterData()|.
|
| static Position updatePositionAfterAdoptingTextReplacement(
|
| const Position& position,
|
| CharacterData* node,
|
| unsigned offset,
|
| unsigned oldLength,
|
| unsigned newLength) {
|
| - if (position.anchorNode() != node)
|
| + if (!position.anchorNode() || position.anchorNode() != node ||
|
| + !position.isOffsetInAnchor())
|
| return position;
|
| -
|
| - if (position.isBeforeAnchor()) {
|
| - return updatePositionAfterAdoptingTextReplacement(
|
| - Position(node, 0), node, offset, oldLength, newLength);
|
| - }
|
| - if (position.isAfterAnchor()) {
|
| - return updatePositionAfterAdoptingTextReplacement(
|
| - Position(node, oldLength), node, offset, oldLength, newLength);
|
| - }
|
|
|
| // See:
|
| // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
|
| @@ -478,89 +514,69 @@
|
| if (positionOffset > node->length())
|
| positionOffset = node->length();
|
|
|
| - return Position(node, positionOffset);
|
| -}
|
| -
|
| -// TODO(yosin): We should move |didUpdateCharacterData()| to
|
| -// "SelectionEditor.cpp".
|
| -void SelectionEditor::didUpdateCharacterData(CharacterData* node,
|
| - unsigned offset,
|
| - unsigned oldLength,
|
| - unsigned newLength) {
|
| + // 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 (m_selection.isNone() || !node || !node->isConnected()) {
|
| - didFinishDOMMutation();
|
| - return;
|
| - }
|
| - const Position& newBase = updatePositionAfterAdoptingTextReplacement(
|
| - m_selection.m_base, node, offset, oldLength, newLength);
|
| - const Position& newExtent = updatePositionAfterAdoptingTextReplacement(
|
| - m_selection.m_extent, node, offset, oldLength, newLength);
|
| - didFinishTextChange(newBase, newExtent);
|
| -}
|
| -
|
| -// TODO(yosin): We should move |updatePostionAfterAdoptingTextNodesMerged()|
|
| -// to "SelectionEditor.cpp" since it used only in
|
| -// |SelectionEditor::didMergeTextNodes()|.
|
| -// TODO(yosin): We should introduce |Position(const Text&, int)| to avoid
|
| -// |const_cast<Text*>|.
|
| + 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& mergedNode,
|
| const NodeWithIndex& nodeToBeRemovedWithIndex,
|
| - unsigned oldLength) {
|
| - Node* const anchorNode = position.anchorNode();
|
| - const Node& nodeToBeRemoved = nodeToBeRemovedWithIndex.node();
|
| - switch (position.anchorType()) {
|
| - case PositionAnchorType::BeforeChildren:
|
| - case PositionAnchorType::AfterChildren:
|
| - return position;
|
| - case PositionAnchorType::BeforeAnchor:
|
| - if (anchorNode == nodeToBeRemoved)
|
| - return Position(const_cast<Text*>(&mergedNode), mergedNode.length());
|
| - return position;
|
| - case PositionAnchorType::AfterAnchor:
|
| - if (anchorNode == nodeToBeRemoved)
|
| - return Position(const_cast<Text*>(&mergedNode), mergedNode.length());
|
| - if (anchorNode == mergedNode)
|
| - return Position(const_cast<Text*>(&mergedNode), oldLength);
|
| - return position;
|
| - case PositionAnchorType::OffsetInAnchor: {
|
| - const int offset = position.offsetInContainerNode();
|
| - if (anchorNode == nodeToBeRemoved)
|
| - return Position(const_cast<Text*>(&mergedNode), oldLength + offset);
|
| - if (anchorNode == nodeToBeRemoved.parentNode() &&
|
| - offset == nodeToBeRemovedWithIndex.index()) {
|
| - return Position(const_cast<Text*>(&mergedNode), oldLength);
|
| - }
|
| - return position;
|
| - }
|
| - }
|
| - NOTREACHED() << position;
|
| - return position;
|
| -}
|
| -
|
| -// TODO(yosin): We should move |SelectionEditor::didMergeTextNodes()| to
|
| -// "SelectionEditor.cpp".
|
| -void SelectionEditor::didMergeTextNodes(
|
| - const Text& mergedNode,
|
| - const NodeWithIndex& nodeToBeRemovedWithIndex,
|
| - unsigned oldLength) {
|
| - if (m_selection.isNone()) {
|
| - didFinishDOMMutation();
|
| - return;
|
| - }
|
| - const Position& newBase = updatePostionAfterAdoptingTextNodesMerged(
|
| - m_selection.m_base, mergedNode, nodeToBeRemovedWithIndex, oldLength);
|
| - const Position& newExtent = updatePostionAfterAdoptingTextNodesMerged(
|
| - m_selection.m_extent, mergedNode, nodeToBeRemovedWithIndex, oldLength);
|
| - didFinishTextChange(newBase, newExtent);
|
| -}
|
| -
|
| -// TODO(yosin): We should move |updatePostionAfterAdoptingTextNodeSplit()|
|
| -// to "SelectionEditor.cpp" since it used only in
|
| -// |SelectionEditor::didSplitTextNode()|.
|
| + unsigned offset) {
|
| + const Text& oldNode = toText(nodeToBeRemovedWithIndex.node());
|
| + 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) {
|
| @@ -578,18 +594,37 @@
|
| return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
|
| }
|
|
|
| -// TODO(yosin): We should move |SelectionEditor::didSplitTextNode()| to
|
| -// "SelectionEditor.cpp".
|
| -void SelectionEditor::didSplitTextNode(const Text& oldNode) {
|
| - if (m_selection.isNone() || !oldNode.isConnected()) {
|
| - didFinishDOMMutation();
|
| - return;
|
| - }
|
| - const Position& newBase =
|
| - updatePostionAfterAdoptingTextNodeSplit(m_selection.m_base, oldNode);
|
| - const Position& newExtent =
|
| - updatePostionAfterAdoptingTextNodeSplit(m_selection.m_extent, oldNode);
|
| - didFinishTextChange(newBase, newExtent);
|
| +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())
|
| + 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,6 +731,7 @@
|
| view.clearSelection();
|
|
|
| m_frame->editor().clearTypingStyle();
|
| + m_selectionEditor->documentDetached(*document);
|
| }
|
|
|
| void FrameSelection::clearPreviousCaretVisualRect(const LayoutBlock& block) {
|
| @@ -718,7 +754,8 @@
|
| }
|
|
|
| bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const {
|
| - DCHECK_GE(document().lifecycle().state(), DocumentLifecycle::LayoutClean);
|
| + DCHECK(selection().isValidFor(document()));
|
| +
|
| bool result = m_frameCaret->shouldPaintCaret(block);
|
| DCHECK(!result || (isCaret() && hasEditableStyle()));
|
| return result;
|
| @@ -1038,8 +1075,10 @@
|
| }
|
|
|
| void FrameSelection::updateAppearance() {
|
| - DCHECK(!m_frame->contentLayoutItem().isNull());
|
| - m_frameCaret->scheduleVisualUpdateForPaintInvalidationIfNeeded();
|
| + m_frameCaret->updateAppearance();
|
| +
|
| + if (m_frame->contentLayoutItem().isNull())
|
| + return;
|
| m_pendingSelection->setHasPendingSelection();
|
| }
|
|
|
| @@ -1242,6 +1281,10 @@
|
| 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());
|
| @@ -1352,19 +1395,18 @@
|
| void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition,
|
| const VisiblePosition& extentPosition,
|
| TextGranularity granularity) {
|
| - SelectionInDOMTree newSelection =
|
| + VisibleSelection newSelection = createVisibleSelection(
|
| SelectionInDOMTree::Builder()
|
| .setBaseAndExtentDeprecated(basePosition.deepEquivalent(),
|
| extentPosition.deepEquivalent())
|
| .setAffinity(basePosition.affinity())
|
| .setGranularity(granularity)
|
| - .setIsHandleVisible(isHandleVisible())
|
| - .build();
|
| + .build());
|
|
|
| if (newSelection.isNone())
|
| return;
|
|
|
| - setSelection(newSelection, CloseTyping | ClearTypingStyle,
|
| + setSelection(newSelection, m_handleVisibility, CloseTyping | ClearTypingStyle,
|
| CursorAlignOnScroll::IfNeeded, granularity);
|
| }
|
|
|
|
|