VisibleSelection SurroundingTextTest::select(int start, int end)
{
    Element* element = document().getElementById("selection");
    VisibleSelection selection;
    selection.setBase(Position(toText(element->firstChild()), start));
    selection.setExtent(Position(toText(element->firstChild()), end));
    return selection;
}
Exemple #2
0
int Frame::checkOverflowScroll(OverflowScrollAction action)
{
    Position extent = selection().selection().extent();
    if (extent.isNull())
        return OverflowScrollNone;

    RenderObject* renderer = extent.deprecatedNode()->renderer();
    if (!renderer)
        return OverflowScrollNone;

    FrameView* view = this->view();
    if (!view)
        return OverflowScrollNone;

    RenderBlock* containingBlock = renderer->containingBlock();
    if (!containingBlock || !containingBlock->hasOverflowClip())
        return OverflowScrollNone;
    RenderLayer* layer = containingBlock->layer();
    ASSERT(layer);

    IntRect visibleRect = IntRect(view->scrollX(), view->scrollY(), view->visibleWidth(), view->visibleHeight());
    IntPoint position = m_overflowAutoScrollPos;
    if (visibleRect.contains(position.x(), position.y()))
        return OverflowScrollNone;

    int scrollType = 0;
    int deltaX = 0;
    int deltaY = 0;
    IntPoint selectionPosition;

    // This constant will make the selection draw a little bit beyond the edge of the visible area.
    // This prevents a visual glitch, in that you can fail to select a portion of a character that
    // is being rendered right at the edge of the visible rectangle.
    // FIXME: This probably needs improvement, and may need to take the font size into account.
    static const int scrollBoundsAdjustment = 3;

    // FIXME: Make a small buffer at the end of a visible rectangle so that autoscrolling works 
    // even if the visible extends to the limits of the screen.
    if (position.x() < visibleRect.x()) {
        scrollType |= OverflowScrollLeft;
        if (action == PerformOverflowScroll) {
            deltaX -= static_cast<int>(m_overflowAutoScrollDelta);
            selectionPosition.setX(view->scrollX() - scrollBoundsAdjustment);
        }
    } else if (position.x() > visibleRect.maxX()) {
        scrollType |= OverflowScrollRight;
        if (action == PerformOverflowScroll) {
            deltaX += static_cast<int>(m_overflowAutoScrollDelta);
            selectionPosition.setX(view->scrollX() + view->visibleWidth() + scrollBoundsAdjustment);
        }
    }

    if (position.y() < visibleRect.y()) {
        scrollType |= OverflowScrollUp;
        if (action == PerformOverflowScroll) {
            deltaY -= static_cast<int>(m_overflowAutoScrollDelta);
            selectionPosition.setY(view->scrollY() - scrollBoundsAdjustment);
        }
    } else if (position.y() > visibleRect.maxY()) {
        scrollType |= OverflowScrollDown;
        if (action == PerformOverflowScroll) {
            deltaY += static_cast<int>(m_overflowAutoScrollDelta);
            selectionPosition.setY(view->scrollY() + view->visibleHeight() + scrollBoundsAdjustment);
        }
    }

    Ref<Frame> protectedThis(*this);

    if (action == PerformOverflowScroll && (deltaX || deltaY)) {
        layer->scrollToOffset(layer->scrollOffset() + IntSize(deltaX, deltaY));

        // Handle making selection.
        VisiblePosition visiblePosition(renderer->positionForPoint(selectionPosition, nullptr));
        if (visiblePosition.isNotNull()) {
            VisibleSelection visibleSelection = selection().selection();
            visibleSelection.setExtent(visiblePosition);
            if (selection().granularity() != CharacterGranularity)
                visibleSelection.expandUsingGranularity(selection().granularity());
            if (selection().shouldChangeSelection(visibleSelection))
                selection().setSelection(visibleSelection);
        }

        m_overflowAutoScrollDelta *= 1.02f; // Accelerate the scroll
    }
    return scrollType;
}
VisibleSelection DirectionGranularityStrategy::updateExtent(
    const IntPoint& extentPoint,
    LocalFrame* frame) {
  const VisibleSelection& selection = frame->selection().selection();

  if (m_state == StrategyState::Cleared)
    m_state = StrategyState::Expanding;

  VisiblePosition oldOffsetExtentPosition = selection.visibleExtent();
  IntPoint oldExtentLocation = positionLocation(oldOffsetExtentPosition);

  IntPoint oldOffsetExtentPoint =
      oldExtentLocation + m_diffExtentPointFromExtentPosition;
  IntPoint oldExtentPoint =
      IntPoint(oldOffsetExtentPoint.x() - m_offset, oldOffsetExtentPoint.y());

  // Apply the offset.
  IntPoint newOffsetExtentPoint = extentPoint;
  int dx = extentPoint.x() - oldExtentPoint.x();
  if (m_offset != 0) {
    if (m_offset > 0 && dx > 0)
      m_offset = std::max(0, m_offset - dx);
    else if (m_offset < 0 && dx < 0)
      m_offset = std::min(0, m_offset - dx);
    newOffsetExtentPoint.move(m_offset, 0);
  }

  VisiblePosition newOffsetExtentPosition =
      visiblePositionForContentsPoint(newOffsetExtentPoint, frame);
  IntPoint newOffsetLocation = positionLocation(newOffsetExtentPosition);

  // Reset the offset in case of a vertical change in the location (could be
  // due to a line change or due to an unusual layout, e.g. rotated text).
  bool verticalChange = newOffsetLocation.y() != oldExtentLocation.y();
  if (verticalChange) {
    m_offset = 0;
    m_granularity = CharacterGranularity;
    newOffsetExtentPoint = extentPoint;
    newOffsetExtentPosition =
        visiblePositionForContentsPoint(extentPoint, frame);
  }

  const VisiblePosition base = selection.visibleBase();

  // Do not allow empty selection.
  if (newOffsetExtentPosition.deepEquivalent() == base.deepEquivalent())
    return selection;

  // The direction granularity strategy, particularly the "offset" feature
  // doesn't work with non-horizontal text (e.g. when the text is rotated).
  // So revert to the behavior equivalent to the character granularity
  // strategy if we detect that the text's baseline coordinate changed
  // without a line change.
  if (verticalChange &&
      inSameLine(newOffsetExtentPosition, oldOffsetExtentPosition)) {
    return createVisibleSelection(selection.visibleBase(),
                                  newOffsetExtentPosition);
  }

  int oldExtentBaseOrder = selection.isBaseFirst() ? 1 : -1;

  int newExtentBaseOrder;
  bool thisMoveShrunkSelection;
  if (newOffsetExtentPosition.deepEquivalent() ==
      oldOffsetExtentPosition.deepEquivalent()) {
    if (m_granularity == CharacterGranularity)
      return selection;

    // If we are in Word granularity, we cannot exit here, since we may pass
    // the middle of the word without changing the position (in which case
    // the selection needs to expand).
    thisMoveShrunkSelection = false;
    newExtentBaseOrder = oldExtentBaseOrder;
  } else {
    bool selectionExpanded = arePositionsInSpecifiedOrder(
        newOffsetExtentPosition, oldOffsetExtentPosition, oldExtentBaseOrder);
    bool extentBaseOrderSwitched =
        selectionExpanded
            ? false
            : !arePositionsInSpecifiedOrder(newOffsetExtentPosition, base,
                                            oldExtentBaseOrder);
    newExtentBaseOrder =
        extentBaseOrderSwitched ? -oldExtentBaseOrder : oldExtentBaseOrder;

    // Determine the word boundary, i.e. the boundary extending beyond which
    // should change the granularity to WordGranularity.
    VisiblePosition wordBoundary;
    if (extentBaseOrderSwitched) {
      // Special case.
      // If the extent-base order was switched, then the selection is now
      // expanding in a different direction than before. Therefore we
      // calculate the word boundary in this new direction and based on
      // the |base| position.
      wordBoundary = nextWordBound(base, newExtentBaseOrder > 0
                                             ? SearchDirection::SearchForward
                                             : SearchDirection::SearchBackwards,
                                   BoundAdjust::NextBoundIfOnBound);
      m_granularity = CharacterGranularity;
    } else {
      // Calculate the word boundary based on |oldExtentWithGranularity|.
      // If selection was shrunk in the last update and the extent is now
      // exactly on the word boundary - we need to take the next bound as
      // the bound of the current word.
      wordBoundary = nextWordBound(oldOffsetExtentPosition,
                                   oldExtentBaseOrder > 0
                                       ? SearchDirection::SearchForward
                                       : SearchDirection::SearchBackwards,
                                   m_state == StrategyState::Shrinking
                                       ? BoundAdjust::NextBoundIfOnBound
                                       : BoundAdjust::CurrentPosIfOnBound);
    }

    bool expandedBeyondWordBoundary;
    if (selectionExpanded)
      expandedBeyondWordBoundary = arePositionsInSpecifiedOrder(
          newOffsetExtentPosition, wordBoundary, newExtentBaseOrder);
    else if (extentBaseOrderSwitched)
      expandedBeyondWordBoundary = arePositionsInSpecifiedOrder(
          newOffsetExtentPosition, wordBoundary, newExtentBaseOrder);
    else
      expandedBeyondWordBoundary = false;

    // The selection is shrunk if the extent changes position to be closer to
    // the base, and the extent/base order wasn't switched.
    thisMoveShrunkSelection = !extentBaseOrderSwitched && !selectionExpanded;

    if (expandedBeyondWordBoundary)
      m_granularity = WordGranularity;
    else if (thisMoveShrunkSelection)
      m_granularity = CharacterGranularity;
  }

  VisiblePosition newSelectionExtent = newOffsetExtentPosition;
  if (m_granularity == WordGranularity) {
    // Determine the bounds of the word where the extent is located.
    // Set the selection extent to one of the two bounds depending on
    // whether the extent is passed the middle of the word.
    VisiblePosition boundBeforeExtent =
        nextWordBound(newOffsetExtentPosition, SearchDirection::SearchBackwards,
                      BoundAdjust::CurrentPosIfOnBound);
    VisiblePosition boundAfterExtent =
        nextWordBound(newOffsetExtentPosition, SearchDirection::SearchForward,
                      BoundAdjust::CurrentPosIfOnBound);
    int xMiddleBetweenBounds = (positionLocation(boundAfterExtent).x() +
                                positionLocation(boundBeforeExtent).x()) /
                               2;
    bool offsetExtentBeforeMiddle =
        newOffsetExtentPoint.x() < xMiddleBetweenBounds;
    newSelectionExtent =
        offsetExtentBeforeMiddle ? boundBeforeExtent : boundAfterExtent;
    // Update the offset if selection expanded in word granularity.
    if (newSelectionExtent.deepEquivalent() !=
            selection.visibleExtent().deepEquivalent() &&
        ((newExtentBaseOrder > 0 && !offsetExtentBeforeMiddle) ||
         (newExtentBaseOrder < 0 && offsetExtentBeforeMiddle))) {
      m_offset = positionLocation(newSelectionExtent).x() - extentPoint.x();
    }
  }

  // Only update the state if the selection actually changed as a result of
  // this move.
  if (newSelectionExtent.deepEquivalent() !=
      selection.visibleExtent().deepEquivalent())
    m_state = thisMoveShrunkSelection ? StrategyState::Shrinking
                                      : StrategyState::Expanding;

  m_diffExtentPointFromExtentPosition =
      extentPoint + IntSize(m_offset, 0) - positionLocation(newSelectionExtent);
  VisibleSelection newSelection = selection;
  newSelection.setExtent(newSelectionExtent);
  return newSelection;
}
bool SelectionModifier::modify(EAlteration alter,
                               SelectionDirection direction,
                               TextGranularity granularity) {
  DCHECK(!frame()->document()->needsLayoutTreeUpdate());
  DocumentLifecycle::DisallowTransitionScope disallowTransition(
      frame()->document()->lifecycle());

  willBeModified(alter, direction);

  bool wasRange = m_selection.isRange();
  VisiblePosition originalStartPosition = m_selection.visibleStart();
  VisiblePosition position;
  switch (direction) {
    case DirectionRight:
      if (alter == FrameSelection::AlterationMove)
        position = modifyMovingRight(granularity);
      else
        position = modifyExtendingRight(granularity);
      break;
    case DirectionForward:
      if (alter == FrameSelection::AlterationExtend)
        position = modifyExtendingForward(granularity);
      else
        position = modifyMovingForward(granularity);
      break;
    case DirectionLeft:
      if (alter == FrameSelection::AlterationMove)
        position = modifyMovingLeft(granularity);
      else
        position = modifyExtendingLeft(granularity);
      break;
    case DirectionBackward:
      if (alter == FrameSelection::AlterationExtend)
        position = modifyExtendingBackward(granularity);
      else
        position = modifyMovingBackward(granularity);
      break;
  }

  if (position.isNull())
    return false;

  if (isSpatialNavigationEnabled(frame())) {
    if (!wasRange && alter == FrameSelection::AlterationMove &&
        position.deepEquivalent() == originalStartPosition.deepEquivalent())
      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(frame()) ||
                               alter == FrameSelection::AlterationExtend);

  switch (alter) {
    case FrameSelection::AlterationMove:
      m_selection = createVisibleSelection(
          SelectionInDOMTree::Builder()
              .collapse(position.toPositionWithAffinity())
              .setIsDirectional(m_selection.isDirectional())
              .build());
      break;
    case FrameSelection::AlterationExtend:

      if (!m_selection.isCaret() && (granularity == WordGranularity ||
                                     granularity == ParagraphGranularity ||
                                     granularity == LineGranularity) &&
          frame() &&
          !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 (!frame() ||
          !frame()
               ->editor()
               .behavior()
               .shouldAlwaysGrowSelectionWhenExtendingToBoundary() ||
          m_selection.isCaret() || !isBoundary(granularity)) {
        m_selection.setExtent(position);
      } else {
        TextDirection textDirection = directionOfEnclosingBlock();
        if (direction == DirectionForward ||
            (textDirection == LTR && direction == DirectionRight) ||
            (textDirection == RTL && direction == DirectionLeft))
          setSelectionEnd(&m_selection, position);
        else
          setSelectionStart(&m_selection, position);
      }
      break;
  }

  if (granularity == LineGranularity || granularity == ParagraphGranularity)
    m_xPosForVerticalArrowNavigation = x;

  return true;
}
Exemple #5
0
bool SelectionEditor::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
{
    if (userTriggered == UserTriggered) {
        OwnPtrWillBeRawPtr<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);

    bool wasRange = m_selection.isRange();
    VisiblePosition originalStartPosition = m_selection.visibleStart();
    VisiblePosition position;
    switch (direction) {
    case DirectionRight:
        if (alter == FrameSelection::AlterationMove)
            position = modifyMovingRight(granularity);
        else
            position = modifyExtendingRight(granularity);
        break;
    case DirectionForward:
        if (alter == FrameSelection::AlterationExtend)
            position = modifyExtendingForward(granularity);
        else
            position = modifyMovingForward(granularity);
        break;
    case DirectionLeft:
        if (alter == FrameSelection::AlterationMove)
            position = modifyMovingLeft(granularity);
        else
            position = modifyExtendingLeft(granularity);
        break;
    case DirectionBackward:
        if (alter == FrameSelection::AlterationExtend)
            position = modifyExtendingBackward(granularity);
        else
            position = modifyMovingBackward(granularity);
        break;
    }

    if (position.isNull())
        return false;

    if (isSpatialNavigationEnabled(frame())) {
        if (!wasRange && alter == FrameSelection::AlterationMove && position.deepEquivalent() == originalStartPosition.deepEquivalent())
            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(frame()) || alter == FrameSelection::AlterationExtend);

    switch (alter) {
    case FrameSelection::AlterationMove:
        m_frameSelection->moveTo(position, userTriggered);
        break;
    case FrameSelection::AlterationExtend:

        if (!m_selection.isCaret()
            && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
            && frame() && !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 (!frame() || !frame()->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary()
            || m_selection.isCaret()
            || !isBoundary(granularity)) {
            m_frameSelection->setExtent(position, userTriggered);
        } else {
            TextDirection textDirection = directionOfEnclosingBlock();
            if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
                m_frameSelection->setEnd(position, userTriggered);
            else
                m_frameSelection->setStart(position, userTriggered);
        }
        break;
    }

    if (granularity == LineGranularity || granularity == ParagraphGranularity)
        m_xPosForVerticalArrowNavigation = x;

    return true;
}