text: /*
   * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    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
   * 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
   * 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
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "sky/engine/config.h"
  #include "sky/engine/core/editing/FrameSelection.h"
  
  #include <stdio.h>
  #include "sky/engine/bindings/exception_state.h"
  #include "sky/engine/core/css/StylePropertySet.h"
  #include "sky/engine/core/dom/CharacterData.h"
  #include "sky/engine/core/dom/Document.h"
  #include "sky/engine/core/dom/Element.h"
  #include "sky/engine/core/dom/ElementTraversal.h"
  #include "sky/engine/core/dom/NodeTraversal.h"
  #include "sky/engine/core/dom/Text.h"
  #include "sky/engine/core/editing/Editor.h"
  #include "sky/engine/core/editing/InputMethodController.h"
  #include "sky/engine/core/editing/RenderedPosition.h"
  #include "sky/engine/core/editing/SpellChecker.h"
  #include "sky/engine/core/editing/TextIterator.h"
  #include "sky/engine/core/editing/TypingCommand.h"
  #include "sky/engine/core/editing/VisibleUnits.h"
  #include "sky/engine/core/editing/htmlediting.h"
  #include "sky/engine/core/events/Event.h"
  #include "sky/engine/core/frame/FrameView.h"
  #include "sky/engine/core/frame/LocalDOMWindow.h"
  #include "sky/engine/core/frame/LocalFrame.h"
  #include "sky/engine/core/frame/Settings.h"
  #include "sky/engine/core/page/EditorClient.h"
  #include "sky/engine/core/page/EventHandler.h"
  #include "sky/engine/core/page/FocusController.h"
  #include "sky/engine/core/page/Page.h"
  #include "sky/engine/core/rendering/HitTestRequest.h"
  #include "sky/engine/core/rendering/HitTestResult.h"
  #include "sky/engine/core/rendering/InlineTextBox.h"
  #include "sky/engine/core/rendering/RenderLayer.h"
  #include "sky/engine/core/rendering/RenderText.h"
  #include "sky/engine/core/rendering/RenderTheme.h"
  #include "sky/engine/core/rendering/RenderView.h"
  #include "sky/engine/platform/geometry/FloatQuad.h"
  #include "sky/engine/platform/graphics/GraphicsContext.h"
  #include "sky/engine/wtf/text/CString.h"
  
  #define EDIT_DEBUG 0
  
  namespace blink {
  
  static inline LayoutUnit NoXPosForVerticalArrowNavigation()
  {
      return LayoutUnit::min();
  }
  
  static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
  {
      return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
  }
  
  FrameSelection::FrameSelection(LocalFrame* frame)
      : m_frame(frame)
      , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
      , m_observingVisibleSelection(false)
      , m_granularity(CharacterGranularity)
      , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
      , m_shouldPaintCaret(true)
      , m_isCaretBlinkingSuspended(false)
      , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
      , m_shouldShowBlockCursor(false)
  {
      if (shouldAlwaysUseDirectionalSelection(m_frame))
          m_selection.setIsDirectional(true);
  }
  
  FrameSelection::~FrameSelection()
  {
  #if !ENABLE(OILPAN)
      // Oilpan: No need to clear out VisibleSelection observer;
      // it is finalized as a part object of FrameSelection.
      stopObservingVisibleSelectionChangeIfNecessary();
  #endif
  }
  
  ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const
  {
      Element* selectionRoot = m_selection.rootEditableElement();
      if (selectionRoot)
          return selectionRoot;
  
      Node* node = m_selection.base().containerNode();
      return node ? &node->treeScope().rootNode() : 0;
  }
  
  void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
  {
      SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
      setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
  }
  
  void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
  {
      const bool selectionHasDirection = true;
      SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
      setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
  }
  
  void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
  {
      SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
      setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
  }
  
  static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
  {
      RenderedPosition base(visibleBase);
      RenderedPosition extent(visibleExtent);
  
      if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
          return;
  
      if (base.atLeftBoundaryOfBidiRun()) {
          if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
              && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
              visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun());
              return;
          }
          return;
      }
  
      if (base.atRightBoundaryOfBidiRun()) {
          if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
              && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
              visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun());
              return;
          }
          return;
      }
  
      if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
          visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun());
          return;
      }
  
      if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
          visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun());
          return;
      }
  }
  
  void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
      EndPointsAdjustmentMode endpointsAdjustmentMode)
  {
      VisibleSelection newSelection = passedNewSelection;
      bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
  
      VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
      VisiblePosition newBase = base;
      VisiblePosition extent = newSelection.visibleExtent();
      VisiblePosition newExtent = extent;
      if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
          adjustEndpointsAtBidiBoundary(newBase, newExtent);
  
      if (newBase != base || newExtent != extent) {
          m_originalBase = base;
          newSelection.setBase(newBase);
          newSelection.setExtent(newExtent);
      } else if (m_originalBase.isNotNull()) {
          if (m_selection.base() == newSelection.base())
              newSelection.setBase(m_originalBase);
          m_originalBase.clear();
      }
  
      newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
      if (m_selection == newSelection)
          return;
  
      setSelection(newSelection, granularity);
  }
  
  void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
  {
      bool closeTyping = options & CloseTyping;
      bool shouldClearTypingStyle = options & ClearTypingStyle;
      EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
  
      VisibleSelection s = validateSelection(newSelection);
      if (shouldAlwaysUseDirectionalSelection(m_frame))
          s.setIsDirectional(true);
  
      if (!m_frame) {
          m_selection = s;
          return;
      }
  
      // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
      // if document->frame() == m_frame we can get into an infinite loop
      if (s.base().anchorNode()) {
          Document& document = *s.base().document();
          if (document.frame() && document.frame() != m_frame && document != m_frame->document()) {
              RefPtr<LocalFrame> guard = document.frame();
              document.frame()->selection().setSelection(s, options, align, granularity);
              // It's possible that during the above set selection, this FrameSelection has been modified by
              // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
              // the frame is about to be destroyed. If this is the case, clear our selection.
              if (guard->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange())
                  clear();
              return;
          }
      }
  
      m_granularity = granularity;
  
      if (closeTyping)
          TypingCommand::closeTyping(m_frame);
  
      if (shouldClearTypingStyle)
          clearTypingStyle();
  
      if (m_selection == s) {
          // Even if selection was not changed, selection offsets may have been changed.
          m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
          notifyRendererOfSelectionChange(userTriggered);
          return;
      }
  
      VisibleSelection oldSelection = m_selection;
  
      m_selection = s;
      setCaretRectNeedsUpdate();
  
      if (!s.isNone() && !(options & DoNotSetFocus))
          setFocusedNodeIfNeeded();
  
      if (!(options & DoNotUpdateAppearance)) {
          updateAppearance(ResetCaretBlink);
      }
  
      // 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();
      notifyRendererOfSelectionChange(userTriggered);
      m_frame->editor().respondToChangedSelection(oldSelection, options);
      if (userTriggered == UserTriggered) {
          ScrollAlignment alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
          revealSelection(alignment, RevealExtent);
      }
  
      notifyAccessibilityForSelectionChange();
  
      m_frame->domWindow()->enqueueDocumentEvent(Event::create(EventTypeNames::selectionchange));
  }
  
  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.containsIncludingShadowDOM(position.anchorNode());
  }
  
  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, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
          removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
  }
  
  void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
  {
      ASSERT(node.document().isActive());
  
      bool clearRenderTreeSelection = false;
      bool clearDOMTreeSelection = false;
  
      if (startRemoved || endRemoved) {
          Position start = m_selection.start();
          Position end = m_selection.end();
          if (startRemoved)
              updatePositionForNodeRemoval(start, node);
          if (endRemoved)
              updatePositionForNodeRemoval(end, node);
  
          if (start.isNotNull() && end.isNotNull()) {
              if (m_selection.isBaseFirst())
                  m_selection.setWithoutValidation(start, end);
              else
                  m_selection.setWithoutValidation(end, start);
          } else
              clearDOMTreeSelection = true;
  
          clearRenderTreeSelection = 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 (m_selection.isBaseFirst())
              m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
          else
              m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
      } else if (RefPtr<Range> range = m_selection.firstRange()) {
          TrackExceptionState exceptionState;
          Range::CompareResults compareResult = range->compareNode(&node, exceptionState);
          if (!exceptionState.had_exception() && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
              // If we did nothing here, when this node's renderer was destroyed, the rect that it
              // occupied would be invalidated, but, selection gaps that change as a result of
              // the removal wouldn't be invalidated.
              // FIXME: Don't do so much unnecessary invalidation.
              clearRenderTreeSelection = true;
          }
      }
  
      if (clearRenderTreeSelection)
          m_selection.start().document()->renderView()->clearSelection();
  
      if (clearDOMTreeSelection)
          setSelection(VisibleSelection(), DoNotSetFocus);
  }
  
  static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
  {
      if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
          return position;
  
      // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
      ASSERT(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;
  
      ASSERT_WITH_SECURITY_IMPLICATION(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->inDocument())
          return;
  
      Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength);
      Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength);
      Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength);
      Position end = updatePositionAfterAdoptingTextReplacement(m_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.anchorType() != Position::PositionIsOffsetInAnchor)
          return position;
  
      ASSERT(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.inDocument())
          return;
      Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset);
      Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset);
      Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset);
      Position end = updatePostionAfterAdoptingTextNodesMerged(m_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.anchorType() != Position::PositionIsOffsetInAnchor)
          return position;
      // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
      ASSERT(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.inDocument())
          return;
      Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode);
      Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode);
      Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode);
      Position end = updatePostionAfterAdoptingTextNodeSplit(m_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 == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end())
          return;
      VisibleSelection newSelection;
      if (m_selection.isBaseFirst())
          newSelection.setWithoutValidation(start, end);
      else
          newSelection.setWithoutValidation(end, start);
      setSelection(newSelection, DoNotSetFocus);
  }
  
  TextDirection FrameSelection::directionOfEnclosingBlock()
  {
      return blink::directionOfEnclosingBlock(m_selection.extent());
  }
  
  TextDirection FrameSelection::directionOfSelection()
  {
      InlineBox* startBox = 0;
      InlineBox* endBox = 0;
      int unusedOffset;
      // Cache the VisiblePositions because visibleStart() and visibleEnd()
      // can cause layout, which has the potential to invalidate lineboxes.
      VisiblePosition startPosition = m_selection.visibleStart();
      VisiblePosition endPosition = m_selection.visibleEnd();
      if (startPosition.isNotNull())
          startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
      if (endPosition.isNotNull())
          endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
      if (startBox && endBox && startBox->direction() == endBox->direction())
          return startBox->direction();
  
      return directionOfEnclosingBlock();
  }
  
  void FrameSelection::didChangeFocus()
  {
      updateAppearance();
  }
  
  void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
  {
      if (alter != AlterationExtend)
          return;
  
      Position start = m_selection.start();
      Position end = m_selection.end();
  
      bool baseIsStart = true;
  
      if (m_selection.isDirectional()) {
          // Make base and extent match start and end so we extend the user-visible selection.
          // This only matters for cases where base and extend point to different positions than
          // start and end (e.g. after a double-click to select a word).
          if (m_selection.isBaseFirst())
              baseIsStart = true;
          else
              baseIsStart = false;
      } else {
          switch (direction) {
          case DirectionRight:
              if (directionOfSelection() == LTR)
                  baseIsStart = true;
              else
                  baseIsStart = false;
              break;
          case DirectionForward:
              baseIsStart = true;
              break;
          case DirectionLeft:
              if (directionOfSelection() == LTR)
                  baseIsStart = false;
              else
                  baseIsStart = true;
              break;
          case DirectionBackward:
              baseIsStart = false;
              break;
          }
      }
      if (baseIsStart) {
          m_selection.setBase(start);
          m_selection.setExtent(end);
      } else {
          m_selection.setBase(end);
          m_selection.setExtent(start);
      }
  }
  
  VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
  {
      // FIXME: VisibleSelection should be fixed to ensure as an invariant that
      // base/extent always point to the same nodes as start/end, but which points
      // to which depends on the value of isBaseFirst. Then this can be changed
      // to just return m_sel.extent().
      return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
  }
  
  VisiblePosition FrameSelection::startForPlatform() const
  {
      return positionForPlatform(true);
  }
  
  VisiblePosition FrameSelection::endForPlatform() const
  {
      return positionForPlatform(false);
  }
  
  VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
  {
      VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
  
      if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
          // In order to skip spaces when moving right, we advance one
          // word further and then move one word back. Given the
          // semantics of previousWordPosition() this will put us at the
          // beginning of the word following.
          VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
          if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
              positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
  
          bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
          if (movingBackwardsMovedPositionToStartOfCurrentWord)
              positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
      }
      return positionAfterCurrentWord;
  }
  
  static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
  {
      if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
          pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
  }
  
  VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
  {
      VisiblePosition pos(m_selection.extent(), m_selection.affinity());
  
      // The difference between modifyExtendingRight and modifyExtendingForward is:
      // modifyExtendingForward always extends forward logically.
      // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
      // it extends forward logically if the enclosing block is LTR direction,
      // but it extends backward logically if the enclosing block is RTL direction.
      switch (granularity) {
      case CharacterGranularity:
          if (directionOfEnclosingBlock() == LTR)
              pos = pos.next(CanSkipOverEditingBoundary);
          else
              pos = pos.previous(CanSkipOverEditingBoundary);
          break;
      case WordGranularity:
          if (directionOfEnclosingBlock() == LTR)
              pos = nextWordPositionForPlatform(pos);
          else
              pos = previousWordPosition(pos);
          break;
      case LineBoundary:
          if (directionOfEnclosingBlock() == LTR)
              pos = modifyExtendingForward(granularity);
          else
              pos = modifyExtendingBackward(granularity);
          break;
      case SentenceGranularity:
      case LineGranularity:
      case ParagraphGranularity:
      case SentenceBoundary:
      case ParagraphBoundary:
      case DocumentBoundary:
          // FIXME: implement all of the above?
          pos = modifyExtendingForward(granularity);
          break;
      }
      adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
  {
      VisiblePosition pos(m_selection.extent(), m_selection.affinity());
      switch (granularity) {
      case CharacterGranularity:
          pos = pos.next(CanSkipOverEditingBoundary);
          break;
      case WordGranularity:
          pos = nextWordPositionForPlatform(pos);
          break;
      case SentenceGranularity:
          pos = nextSentencePosition(pos);
          break;
      case LineGranularity:
          pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
          break;
      case ParagraphGranularity:
          pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
          break;
      case SentenceBoundary:
          pos = endOfSentence(endForPlatform());
          break;
      case LineBoundary:
          pos = logicalEndOfLine(endForPlatform());
          break;
      case ParagraphBoundary:
          pos = endOfParagraph(endForPlatform());
          break;
      case DocumentBoundary:
          pos = endForPlatform();
          if (isEditablePosition(pos.deepEquivalent()))
              pos = endOfEditableContent(pos);
          else
              pos = endOfDocument(pos);
          break;
      }
      adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
  {
      VisiblePosition pos;
      switch (granularity) {
      case CharacterGranularity:
          if (isRange()) {
              if (directionOfSelection() == LTR)
                  pos = VisiblePosition(m_selection.end(), m_selection.affinity());
              else
                  pos = VisiblePosition(m_selection.start(), m_selection.affinity());
          } else
              pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
          break;
      case WordGranularity: {
          bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
          pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
          break;
      }
      case SentenceGranularity:
      case LineGranularity:
      case ParagraphGranularity:
      case SentenceBoundary:
      case ParagraphBoundary:
      case DocumentBoundary:
          // FIXME: Implement all of the above.
          pos = modifyMovingForward(granularity);
          break;
      case LineBoundary:
          pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
          break;
      }
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
  {
      VisiblePosition pos;
      // FIXME: Stay in editable content for the less common granularities.
      switch (granularity) {
      case CharacterGranularity:
          if (isRange())
              pos = VisiblePosition(m_selection.end(), m_selection.affinity());
          else
              pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary);
          break;
      case WordGranularity:
          pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity()));
          break;
      case SentenceGranularity:
          pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
          break;
      case LineGranularity: {
          // down-arrowing from a range selection that ends at the start of a line needs
          // to leave the selection at that line start (no need to call nextLinePosition!)
          pos = endForPlatform();
          if (!isRange() || !isStartOfLine(pos))
              pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
          break;
      }
      case ParagraphGranularity:
          pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
          break;
      case SentenceBoundary:
          pos = endOfSentence(endForPlatform());
          break;
      case LineBoundary:
          pos = logicalEndOfLine(endForPlatform());
          break;
      case ParagraphBoundary:
          pos = endOfParagraph(endForPlatform());
          break;
      case DocumentBoundary:
          pos = endForPlatform();
          if (isEditablePosition(pos.deepEquivalent()))
              pos = endOfEditableContent(pos);
          else
              pos = endOfDocument(pos);
          break;
      }
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
  {
      VisiblePosition pos(m_selection.extent(), m_selection.affinity());
  
      // The difference between modifyExtendingLeft and modifyExtendingBackward is:
      // modifyExtendingBackward always extends backward logically.
      // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
      // it extends backward logically if the enclosing block is LTR direction,
      // but it extends forward logically if the enclosing block is RTL direction.
      switch (granularity) {
      case CharacterGranularity:
          if (directionOfEnclosingBlock() == LTR)
              pos = pos.previous(CanSkipOverEditingBoundary);
          else
              pos = pos.next(CanSkipOverEditingBoundary);
          break;
      case WordGranularity:
          if (directionOfEnclosingBlock() == LTR)
              pos = previousWordPosition(pos);
          else
              pos = nextWordPositionForPlatform(pos);
          break;
      case LineBoundary:
          if (directionOfEnclosingBlock() == LTR)
              pos = modifyExtendingBackward(granularity);
          else
              pos = modifyExtendingForward(granularity);
          break;
      case SentenceGranularity:
      case LineGranularity:
      case ParagraphGranularity:
      case SentenceBoundary:
      case ParagraphBoundary:
      case DocumentBoundary:
          pos = modifyExtendingBackward(granularity);
          break;
      }
      adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
  {
      VisiblePosition pos(m_selection.extent(), m_selection.affinity());
  
      // Extending a selection backward by word or character from just after a table selects
      // the table.  This "makes sense" from the user perspective, esp. when deleting.
      // It was done here instead of in VisiblePosition because we want VPs to iterate
      // over everything.
      switch (granularity) {
      case CharacterGranularity:
          pos = pos.previous(CanSkipOverEditingBoundary);
          break;
      case WordGranularity:
          pos = previousWordPosition(pos);
          break;
      case SentenceGranularity:
          pos = previousSentencePosition(pos);
          break;
      case LineGranularity:
          pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
          break;
      case ParagraphGranularity:
          pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
          break;
      case SentenceBoundary:
          pos = startOfSentence(startForPlatform());
          break;
      case LineBoundary:
          pos = logicalStartOfLine(startForPlatform());
          break;
      case ParagraphBoundary:
          pos = startOfParagraph(startForPlatform());
          break;
      case DocumentBoundary:
          pos = startForPlatform();
          if (isEditablePosition(pos.deepEquivalent()))
              pos = startOfEditableContent(pos);
          else
              pos = startOfDocument(pos);
          break;
      }
      adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
  {
      VisiblePosition pos;
      switch (granularity) {
      case CharacterGranularity:
          if (isRange())
              if (directionOfSelection() == LTR)
                  pos = VisiblePosition(m_selection.start(), m_selection.affinity());
              else
                  pos = VisiblePosition(m_selection.end(), m_selection.affinity());
          else
              pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
          break;
      case WordGranularity: {
          bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
          pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
          break;
      }
      case SentenceGranularity:
      case LineGranularity:
      case ParagraphGranularity:
      case SentenceBoundary:
      case ParagraphBoundary:
      case DocumentBoundary:
          // FIXME: Implement all of the above.
          pos = modifyMovingBackward(granularity);
          break;
      case LineBoundary:
          pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
          break;
      }
      return pos;
  }
  
  VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
  {
      VisiblePosition pos;
      switch (granularity) {
      case CharacterGranularity:
          if (isRange())
              pos = VisiblePosition(m_selection.start(), m_selection.affinity());
          else
              pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary);
          break;
      case WordGranularity:
          pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
          break;
      case SentenceGranularity:
          pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
          break;
      case LineGranularity:
          pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
          break;
      case ParagraphGranularity:
          pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
          break;
      case SentenceBoundary:
          pos = startOfSentence(startForPlatform());
          break;
      case LineBoundary:
          pos = logicalStartOfLine(startForPlatform());
          break;
      case ParagraphBoundary:
          pos = startOfParagraph(startForPlatform());
          break;
      case DocumentBoundary:
          pos = startForPlatform();
          if (isEditablePosition(pos.deepEquivalent()))
              pos = startOfEditableContent(pos);
          else
              pos = startOfDocument(pos);
          break;
      }
      return pos;
  }
  
  static bool isBoundary(TextGranularity granularity)
  {
      return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
  }
  
  bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
  {
      if (userTriggered == UserTriggered) {
          OwnPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
          trialFrameSelection->setSelection(m_selection);
          trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered);
  
          if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
              return false;
      }
  
      willBeModified(alter, direction);
  
      VisiblePosition originalStartPosition = m_selection.visibleStart();
      VisiblePosition position;
      switch (direction) {
      case DirectionRight:
          if (alter == AlterationMove)
              position = modifyMovingRight(granularity);
          else
              position = modifyExtendingRight(granularity);
          break;
      case DirectionForward:
          if (alter == AlterationExtend)
              position = modifyExtendingForward(granularity);
          else
              position = modifyMovingForward(granularity);
          break;
      case DirectionLeft:
          if (alter == AlterationMove)
              position = modifyMovingLeft(granularity);
          else
              position = modifyExtendingLeft(granularity);
          break;
      case DirectionBackward:
          if (alter == AlterationExtend)
              position = modifyExtendingBackward(granularity);
          else
              position = modifyMovingBackward(granularity);
          break;
      }
  
      if (position.isNull())
          return false;
  
      // Some of the above operations set an xPosForVerticalArrowNavigation.
      // Setting a selection will clear it, so save it to possibly restore later.
      // Note: the START position type is arbitrary because it is unused, it would be
      // the requested position type if there were no xPosForVerticalArrowNavigation set.
      LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
      m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
  
      switch (alter) {
      case AlterationMove:
          moveTo(position, userTriggered);
          break;
      case AlterationExtend:
  
          if (!m_selection.isCaret()
              && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
              && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
              // Don't let the selection go across the base position directly. Needed to match mac
              // behavior when, for instance, word-selecting backwards starting with the caret in
              // the middle of a word and then word-selecting forward, leaving the caret in the
              // same place where it was, instead of directly selecting to the end of the word.
              VisibleSelection newSelection = m_selection;
              newSelection.setExtent(position);
              if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
                  position = m_selection.visibleBase();
          }
  
          // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
          // base in place and moving the extent. Matches NSTextView.
          if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
              setExtent(position, userTriggered);
          else {
              TextDirection textDirection = directionOfEnclosingBlock();
              if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
                  setEnd(position, userTriggered);
              else
                  setStart(position, userTriggered);
          }
          break;
      }
  
      if (granularity == LineGranularity || granularity == ParagraphGranularity)
          m_xPosForVerticalArrowNavigation = x;
  
      if (userTriggered == UserTriggered)
          m_granularity = CharacterGranularity;
  
      setCaretRectNeedsUpdate();
  
      return true;
  }
  
  // FIXME: Maybe baseline would be better?
  static bool absoluteCaretY(const VisiblePosition &c, int &y)
  {
      IntRect rect = c.absoluteCaretBounds();
      if (rect.isEmpty())
          return false;
      y = rect.y() + rect.height() / 2;
      return true;
  }
  
  bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
  {
      if (!verticalDistance)
          return false;
  
      if (userTriggered == UserTriggered) {
          OwnPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
          trialFrameSelection->setSelection(m_selection);
          trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered);
      }
  
      willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
  
      VisiblePosition pos;
      LayoutUnit xPos = 0;
      switch (alter) {
      case AlterationMove:
          pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
          xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
          m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
          break;
      case AlterationExtend:
          pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
          xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
          m_selection.setAffinity(DOWNSTREAM);
          break;
      }
  
      int startY;
      if (!absoluteCaretY(pos, startY))
          return false;
      if (direction == DirectionUp)
          startY = -startY;
      int lastY = startY;
  
      VisiblePosition result;
      VisiblePosition next;
      for (VisiblePosition p = pos; ; p = next) {
          if (direction == DirectionUp)
              next = previousLinePosition(p, xPos);
          else
              next = nextLinePosition(p, xPos);
  
          if (next.isNull() || next == p)
              break;
          int nextY;
          if (!absoluteCaretY(next, nextY))
              break;
          if (direction == DirectionUp)
              nextY = -nextY;
          if (nextY - startY > static_cast<int>(verticalDistance))
              break;
          if (nextY >= lastY) {
              lastY = nextY;
              result = next;
          }
      }
  
      if (result.isNull())
          return false;
  
      switch (alter) {
      case AlterationMove:
          moveTo(result, userTriggered, align);
          break;
      case AlterationExtend:
          setExtent(result, userTriggered);
          break;
      }
  
      if (userTriggered == UserTriggered)
          m_granularity = CharacterGranularity;
  
      m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
  
      return true;
  }
  
  LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
  {
      LayoutUnit x = 0;
  
      if (isNone())
          return x;
  
      Position pos;
      switch (type) {
      case START:
          pos = m_selection.start();
          break;
      case END:
          pos = m_selection.end();
          break;
      case BASE:
          pos = m_selection.base();
          break;
      case EXTENT:
          pos = m_selection.extent();
          break;
      }
  
      LocalFrame* frame = pos.document()->frame();
      if (!frame)
          return x;
  
      if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
          VisiblePosition visiblePosition(pos, m_selection.affinity());
          // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
          // after the selection is created and before this function is called.
          x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
          m_xPosForVerticalArrowNavigation = x;
      } else
          x = m_xPosForVerticalArrowNavigation;
  
      return x;
  }
  
  void FrameSelection::clear()
  {
      m_granularity = CharacterGranularity;
      setSelection(VisibleSelection());
  }
  
  void FrameSelection::prepareForDestruction()
  {
      m_granularity = CharacterGranularity;
  
      m_caretBlinkTimer.stop();
  
      RenderView* view = m_frame->contentRenderer();
      if (view)
          view->clearSelection();
  
      setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
  }
  
  void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
  {
      if (m_selection.isBaseFirst())
          setBase(pos, trigger);
      else
          setExtent(pos, trigger);
  }
  
  void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
  {
      if (m_selection.isBaseFirst())
          setExtent(pos, trigger);
      else
          setBase(pos, trigger);
  }
  
  void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
  {
      const bool selectionHasDirection = true;
      setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
  }
  
  void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
  {
      const bool selectionHasDirection = true;
      setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
  }
  
  RenderBlock* FrameSelection::caretRenderer() const
  {
      return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
  }
  
  static bool isNonOrphanedCaret(const VisibleSelection& selection)
  {
      return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
  }
  
  IntRect FrameSelection::absoluteCaretBounds()
  {
      m_frame->document()->updateLayout();
      if (!isNonOrphanedCaret(m_selection)) {
          clearCaretRect();
      } else {
          updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity()));
      }
      return absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
  }
  
  void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
  {
      if (m_selection.isCaret() && m_shouldPaintCaret) {
          updateCaretRect(m_frame->document(), PositionWithAffinity(m_selection.start(), m_selection.affinity()));
          CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
      }
  }
  
  bool FrameSelection::contains(const LayoutPoint& point)
  {
      Document* document = m_frame->document();
  
      // Treat a collapsed selection like no selection.
      if (!isRange())
          return false;
      if (!document->renderView())
          return false;
  
      HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
      HitTestResult result(point);
      document->renderView()->hitTest(request, result);
      Node* innerNode = result.innerNode();
      if (!innerNode || !innerNode->renderer())
          return false;
  
      VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
      if (visiblePos.isNull())
          return false;
  
      if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
          return false;
  
      Position start(m_selection.visibleStart().deepEquivalent());
      Position end(m_selection.visibleEnd().deepEquivalent());
      Position p(visiblePos.deepEquivalent());
  
      return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
  }
  
  void FrameSelection::selectAll()
  {
      Document* document = m_frame->document();
  
      RefPtr<Node> root = nullptr;
      Node* selectStartTarget = 0;
      if (isContentEditable()) {
          root = highestEditableRoot(m_selection.start());
          if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
              selectStartTarget = shadowRoot->shadowHost();
          else
              selectStartTarget = root.get();
      } else {
          root = m_selection.nonBoundaryShadowTreeRootNode();
          if (root) {
              selectStartTarget = root->shadowHost();
          } else {
              root = document;
              selectStartTarget = document;
          }
      }
      if (!root)
          return;
  
      if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)))
          return;
  
      VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
      setSelection(newSelection);
      notifyRendererOfSelectionChange(UserTriggered);
  }
  
  bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, DirectoinalOption directional, SetSelectionOptions options)
  {
      if (!range || !range->startContainer() || !range->endContainer())
          return false;
      ASSERT(range->startContainer()->document() == range->endContainer()->document());
  
      // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
      // they start at the beginning of the next line instead
      m_logicalRange = nullptr;
      stopObservingVisibleSelectionChangeIfNecessary();
  
      VisibleSelection newSelection(range, affinity, directional == Directional);
      setSelection(newSelection, options);
  
      m_logicalRange = range->cloneRange();
      startObservingVisibleSelectionChange();
  
      return true;
  }
  
  PassRefPtr<Range> FrameSelection::firstRange() const
  {
      if (m_logicalRange)
          return m_logicalRange->cloneRange();
      return m_selection.firstRange();
  }
  
  bool FrameSelection::isInPasswordField() const
  {
      return false;
  }
  
  void FrameSelection::notifyAccessibilityForSelectionChange()
  {
  }
  
  void FrameSelection::focusedOrActiveStateChanged()
  {
      bool activeAndFocused = isFocusedAndActive();
  
      RefPtr<Document> document = m_frame->document();
      document->updateRenderTreeIfNeeded();
  
      // Caret appears in the active frame.
      if (activeAndFocused)
          setSelectionFromNone();
      else
          m_frame->spellChecker().spellCheckAfterBlur();
      setCaretVisibility(activeAndFocused ? Visible : Hidden);
  
      // Update for caps lock state
      m_frame->eventHandler().capsLockStateMayHaveChanged();
  
      // We may have lost active status even though the focusElement hasn't changed
      // give the element a chance to recalc style if its affected by focus.
      if (Element* element = document->focusedElement())
          element->focusStateChanged();
  }
  
  void FrameSelection::pageActivationChanged()
  {
      focusedOrActiveStateChanged();
  }
  
  void FrameSelection::setFocused(bool flag)
  {
      if (m_focused == flag)
          return;
      m_focused = flag;
  
      focusedOrActiveStateChanged();
  }
  
  bool FrameSelection::isFocusedAndActive() const
  {
      return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
  }
  
  inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame)
  {
      return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
  }
  
  void FrameSelection::updateAppearance(ResetCaretBlinkOption option)
  {
      // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
      // the FrameSelection will paint a blinking caret as usual).
      bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd());
  
      bool shouldBlink = !paintBlockCursor && shouldBlinkCaret();
  
      bool willNeedCaretRectUpdate = false;
  
      // If the caret moved, stop the blink timer so we can restart with a
      // black caret in the new location.
      if (option == ResetCaretBlink || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) {
          m_caretBlinkTimer.stop();
  
          m_shouldPaintCaret = false;
          willNeedCaretRectUpdate = true;
      }
  
      // Start blinking with a black caret. Be sure not to restart if we're
      // already blinking in the right location.
      if (shouldBlink && !m_caretBlinkTimer.isActive()) {
          if (double blinkInterval = RenderTheme::theme().caretBlinkInterval())
              m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE);
  
          m_shouldPaintCaret = true;
          willNeedCaretRectUpdate = true;
      }
  
      if (willNeedCaretRectUpdate)
          setCaretRectNeedsUpdate();
  
      RenderView* view = m_frame->contentRenderer();
      if (!view)
          return;
  
      // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
      // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
  
      VisibleSelection selection;
      VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : m_selection.visibleEnd();
      selection = VisibleSelection(m_selection.visibleStart(), endVisiblePosition);
  
      if (!selection.isRange()) {
          view->clearSelection();
          return;
      }
  
      m_frame->document()->updateLayout();
  
      // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
      // Example: foo <a>bar</a>.  Imagine that a line wrap occurs after 'foo', and that 'bar' is selected.   If we pass [foo, 3]
      // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
      // and will fill the gap before 'bar'.
      Position startPos = selection.start();
      Position candidate = startPos.downstream();
      if (candidate.isCandidate())
          startPos = candidate;
      Position endPos = selection.end();
      candidate = endPos.upstream();
      if (candidate.isCandidate())
          endPos = candidate;
  
      // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
      // because we don't yet notify the FrameSelection of text removal.
      if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
          RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
          RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
          if (startRenderer->view() == view && endRenderer->view() == view)
              view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
      }
  }
  
  void FrameSelection::setCaretVisibility(CaretVisibility visibility)
  {
      if (caretVisibility() == visibility)
          return;
  
      CaretBase::setCaretVisibility(visibility);
  
      updateAppearance();
  }
  
  bool FrameSelection::shouldBlinkCaret() const
  {
      if (!caretIsVisible() || !isCaret())
          return false;
  
      Element* root = rootEditableElement();
      if (!root)
          return false;
  
      Element* focusedElement = root->document().focusedElement();
      if (!focusedElement)
          return false;
  
      return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode());
  }
  
  void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
  {
      ASSERT(caretIsVisible());
      ASSERT(isCaret());
      if (isCaretBlinkingSuspended() && m_shouldPaintCaret)
          return;
      m_shouldPaintCaret = !m_shouldPaintCaret;
      setCaretRectNeedsUpdate();
  }
  
  void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
  {
  }
  
  // Helper function that tells whether a particular node is an element that has an entire
  // LocalFrame and FrameView, a <frame>, <iframe>, or <object>.
  static bool isFrameElement(const Node* n)
  {
      // FIXME(sky): Remove this.
      return false;
  }
  
  void FrameSelection::setFocusedNodeIfNeeded()
  {
      if (isNone() || !isFocused())
          return;
  
      if (Element* target = rootEditableElement()) {
          // Walk up the DOM tree to search for a node to focus.
          while (target) {
              // We don't want to set focus on a subframe when selecting in a parent frame,
              // so add the !isFrameElement check here. There's probably a better way to make this
              // work in the long term, but this is the safest fix at this time.
              if (target->isFocusable() && !isFrameElement(target)) {
                  m_frame->page()->focusController().setFocusedElement(target, m_frame);
                  return;
              }
              target = target->parentOrShadowHostElement();
          }
          m_frame->document()->setFocusedElement(nullptr);
      }
  }
  
  String FrameSelection::selectedText() const
  {
      // We remove '\0' characters because they are not visibly rendered to the user.
      return plainText(toNormalizedRange().get()).replace(0, "");
  }
  
  void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
  {
      LayoutRect rect;
  
      switch (selectionType()) {
      case NoSelection:
          return;
      case CaretSelection:
          rect = absoluteCaretBounds();
          break;
      case RangeSelection:
          Position position = revealExtentOption == RevealExtent ? extent() : base();
          rect = VisiblePosition(position).absoluteCaretBounds();
          break;
      }
  
      Position start = this->start();
      ASSERT(start.deprecatedNode());
      if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
          // FIXME: This code only handles scrolling the startContainer's layer, but
          // the selection rect could intersect more than just that.
          // See <rdar://problem/4799899>.
  
          // FIXME(sky): Figure out how to reveal the selection...
      }
  }
  
  void FrameSelection::setSelectionFromNone()
  {
      // Put a caret inside the body if the entire frame is editable (either the
      // entire WebView is editable or designMode is on for this document).
  
      // FIXME(sky): We have no body.
  }
  
  bool FrameSelection::dispatchSelectStart()
  {
      Node* selectStartTarget = m_selection.extent().containerNode();
      if (!selectStartTarget)
          return true;
  
      return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
  }
  
  void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
  {
      m_shouldShowBlockCursor = shouldShowBlockCursor;
  
      m_frame->document()->updateLayout();
  
      updateAppearance();
  }
  
  void FrameSelection::didChangeVisibleSelection()
  {
      ASSERT(m_observingVisibleSelection);
      // Invalidate the logical range when the underlying VisibleSelection has changed.
      m_logicalRange = nullptr;
      m_selection.clearChangeObserver();
      m_observingVisibleSelection = false;
  }
  
  VisibleSelection FrameSelection::validateSelection(const VisibleSelection& selection)
  {
      if (!m_frame || selection.isNone())
          return selection;
  
      Position base = selection.base();
      Position extent = selection.extent();
      bool isBaseValid = base.document() == m_frame->document();
      bool isExtentValid = extent.document() == m_frame->document();
  
      if (isBaseValid && isExtentValid)
          return selection;
  
      VisibleSelection newSelection;
      if (isBaseValid) {
          newSelection.setWithoutValidation(base, base);
      } else if (isExtentValid) {
          newSelection.setWithoutValidation(extent, extent);
      }
      return newSelection;
  }
  
  void FrameSelection::startObservingVisibleSelectionChange()
  {
      ASSERT(!m_observingVisibleSelection);
      m_selection.setChangeObserver(*this);
      m_observingVisibleSelection = true;
  }
  
  void FrameSelection::stopObservingVisibleSelectionChangeIfNecessary()
  {
      if (m_observingVisibleSelection) {
          m_selection.clearChangeObserver();
          m_observingVisibleSelection = false;
      }
  }
  
  #ifndef NDEBUG
  
  void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
  {
      m_selection.formatForDebugger(buffer, length);
  }
  
  void FrameSelection::showTreeForThis() const
  {
      m_selection.showTreeForThis();
  }
  
  #endif
  
  void FrameSelection::setCaretRectNeedsUpdate()
  {
      if (m_frame)
          m_frame->document()->scheduleVisualUpdate();
  }
  
  }
  
  #ifndef NDEBUG
  
  void showTree(const blink::FrameSelection& sel)
  {
      sel.showTreeForThis();
  }
  
  void showTree(const blink::FrameSelection* sel)
  {
      if (sel)
          sel->showTreeForThis();
  }
  
  #endif
  