// FIXME: We should merge this function with ApplyBlockElementCommand::formatSelection
void IndentOutdentCommand::outdentRegion(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
{
    VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection);

    if (endOfParagraph(startOfSelection) == endOfLastParagraph) {
        outdentParagraph();
        return;
    }
    
    Position originalSelectionEnd = endingSelection().end();
    VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
    VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());

    while (endOfCurrentParagraph != endAfterSelection) {
        VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
        if (endOfCurrentParagraph == endOfLastParagraph)
            setEndingSelection(VisibleSelection(originalSelectionEnd, DOWNSTREAM));
        else
            setEndingSelection(endOfCurrentParagraph);
        
        outdentParagraph();
        
        // outdentParagraph could move more than one paragraph if the paragraph
        // is in a list item. As a result, endAfterSelection and endOfNextParagraph
        // could refer to positions no longer in the document.
        if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->inDocument())
            break;
            
        if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->inDocument()) {
            endOfCurrentParagraph = endingSelection().end();
            endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
        }
        endOfCurrentParagraph = endOfNextParagraph;
    }
}
void RenderTextControl::setSelectionRange(int start, int end)
{
    end = max(end, 0);
    start = min(max(start, 0), end);

    ASSERT(!document()->childNeedsAndNotInStyleRecalc());

    if (style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height()) {
        cacheSelection(start, end);
        return;
    }
    VisiblePosition startPosition = visiblePositionForIndex(start);
    VisiblePosition endPosition;
    if (start == end)
        endPosition = startPosition;
    else
        endPosition = visiblePositionForIndex(end);

    // startPosition and endPosition can be null position for example when
    // "-webkit-user-select: none" style attribute is specified.
    if (startPosition.isNotNull() && endPosition.isNotNull()) {
        ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node());
    }
    VisibleSelection newSelection = VisibleSelection(startPosition, endPosition);

    if (Frame* frame = document()->frame())
        frame->selection()->setSelection(newSelection);

    // FIXME: Granularity is stored separately on the frame, but also in the selection controller.
    // The granularity in the selection controller should be used, and then this line of code would not be needed.
    if (Frame* frame = document()->frame())
        frame->setSelectionGranularity(CharacterGranularity);
}
void IndentOutdentCommand::indentRegion(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
{
    // Special case empty unsplittable elements because there's nothing to split
    // and there's nothing to move.
    Position start = startOfSelection.deepEquivalent().downstream();
    if (isAtUnsplittableElement(start)) {
        RefPtr<Element> blockquote = createIndentBlockquoteElement(document());
        insertNodeAt(blockquote, start);
        RefPtr<Element> placeholder = createBreakElement(document());
        appendNode(placeholder, blockquote);
        setEndingSelection(VisibleSelection(Position(placeholder.get(), 0), DOWNSTREAM));
        return;
    }

    RefPtr<Element> blockquoteForNextIndent;
    VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
    VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
    int endOfCurrentParagraphIndex = indexForVisiblePosition(endOfCurrentParagraph);
    int endAfterSelectionIndex = indexForVisiblePosition(endAfterSelection);

    // When indenting within a <pre> tag, we need to split each paragraph into a separate node for moveParagraphWithClones to work.
    // However, splitting text nodes can cause endOfCurrentParagraph and endAfterSelection to point to an invalid position if we
    // changed the text node it was pointing at.  So we have to reset these positions.
    int numParagraphs = countParagraphs(endOfCurrentParagraph, endAfterSelection);
    if (splitTextNodes(startOfParagraph(startOfSelection), numParagraphs + 1)) {
        RefPtr<Range> endOfCurrentParagraphRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endOfCurrentParagraphIndex, 0, true);
        RefPtr<Range> endAfterSelectionRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endAfterSelectionIndex, 0, true);
        if (!endOfCurrentParagraphRange.get() || !endAfterSelectionRange.get()) {
            ASSERT_NOT_REACHED();
            return;
        }
        endOfCurrentParagraph = VisiblePosition(endOfCurrentParagraphRange->startPosition(), DOWNSTREAM);
        endAfterSelection = VisiblePosition(endAfterSelectionRange->startPosition(), DOWNSTREAM);
    }

    while (endOfCurrentParagraph != endAfterSelection) {
        // Iterate across the selected paragraphs...
        VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
        if (tryIndentingAsListItem(endOfCurrentParagraph))
            blockquoteForNextIndent = 0;
        else
            indentIntoBlockquote(endOfCurrentParagraph, endOfNextParagraph, blockquoteForNextIndent);

        // indentIntoBlockquote could move more than one paragraph if the paragraph
        // is in a list item or a table. As a result, endAfterSelection could refer to a position
        // no longer in the document.
        if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().node()->inDocument())
            break;
        // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().node()
        // If somehow we did, return to prevent crashes.
        if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) {
            ASSERT_NOT_REACHED();
            return;
        }
        endOfCurrentParagraph = endOfNextParagraph;
    }   
}
void ApplyBlockElementCommand::formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
{
    // Special case empty unsplittable elements because there's nothing to split
    // and there's nothing to move.
    Position start = startOfSelection.deepEquivalent().downstream();
    if (isAtUnsplittableElement(start)) {
        RefPtr<Element> blockquote = createBlockElement();
        insertNodeAt(blockquote, start);
        RefPtr<Element> placeholder = createBreakElement(document());
        appendNode(placeholder, blockquote);
        setEndingSelection(VisibleSelection(positionBeforeNode(placeholder.get()), DOWNSTREAM, endingSelection().isDirectional()));
        return;
    }

    RefPtr<Element> blockquoteForNextIndent;
    VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
    VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
    m_endOfLastParagraph = endOfParagraph(endOfSelection).deepEquivalent();

    bool atEnd = false;
    Position end;
    while (endOfCurrentParagraph != endAfterSelection && !atEnd) {
        if (endOfCurrentParagraph.deepEquivalent() == m_endOfLastParagraph)
            atEnd = true;

        rangeForParagraphSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end);
        endOfCurrentParagraph = end;

        Position afterEnd = end.next();
        Node* enclosingCell = enclosingNodeOfType(start, &isTableCell);
        VisiblePosition endOfNextParagraph = endOfNextParagrahSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end);

        formatRange(start, end, m_endOfLastParagraph, blockquoteForNextIndent);

        // Don't put the next paragraph in the blockquote we just created for this paragraph unless
        // the next paragraph is in the same cell.
        if (enclosingCell && enclosingCell != enclosingNodeOfType(endOfNextParagraph.deepEquivalent(), &isTableCell))
            blockquoteForNextIndent = 0;

        // indentIntoBlockquote could move more than one paragraph if the paragraph
        // is in a list item or a table. As a result, endAfterSelection could refer to a position
        // no longer in the document.
        if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->inDocument())
            break;
        // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().deprecatedNode()
        // If somehow we did, return to prevent crashes.
        if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->inDocument()) {
            ASSERT_NOT_REACHED();
            return;
        }
        endOfCurrentParagraph = endOfNextParagraph;
    }
}
VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
{
    if (visiblePos.isNull())
        return VisiblePositionRange();

    // make a caret selection for the position before marker position (to make sure
    // we move off of a line start)
    VisiblePosition prevVisiblePos = visiblePos.previous();
    if (prevVisiblePos.isNull())
        return VisiblePositionRange();

    VisiblePosition startPosition = startOfLine(prevVisiblePos);

    // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
    // always be a valid line range.  However, startOfLine will return null for position next to a floating object,
    // since floating object doesn't really belong to any line.
    // This check will reposition the marker before the floating object, to ensure we get a line start.
    if (startPosition.isNull()) {
        while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
            prevVisiblePos = prevVisiblePos.previous();
            startPosition = startOfLine(prevVisiblePos);
        }
    } else
        startPosition = updateAXLineStartForVisiblePosition(startPosition);

    VisiblePosition endPosition = endOfLine(prevVisiblePos);
    return VisiblePositionRange(startPosition, endPosition);
}
int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
{
    if (visiblePos.isNull() || !node())
        return -1;

    // If the position is not in the same editable region as this AX object, return -1.
    Node* containerNode = visiblePos.deepEquivalent().containerNode();
    if (!containerNode->containsIncludingShadowDOM(node()) && !node()->containsIncludingShadowDOM(containerNode))
        return -1;

    int lineCount = -1;
    VisiblePosition currentVisiblePos = visiblePos;
    VisiblePosition savedVisiblePos;

    // move up until we get to the top
    // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
    // top document.
    do {
        savedVisiblePos = currentVisiblePos;
        VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0, HasEditableAXRole);
        currentVisiblePos = prevVisiblePos;
        ++lineCount;
    }  while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos)));

    return lineCount;
}
bool FormatBlockCommand::modifyRange()
{
    ASSERT(endingSelection().isRange());
    VisiblePosition visibleStart = endingSelection().visibleStart();
    VisiblePosition visibleEnd = endingSelection().visibleEnd();
    VisiblePosition startOfLastParagraph = startOfParagraph(visibleEnd);
    
    if (startOfParagraph(visibleStart) == startOfLastParagraph)
        return false;

    setEndingSelection(visibleStart);
    doApply();
    visibleStart = endingSelection().visibleStart();
    VisiblePosition nextParagraph = endOfParagraph(visibleStart).next();
    while (nextParagraph.isNotNull() && nextParagraph != startOfLastParagraph) {
        setEndingSelection(nextParagraph);
        doApply();
        nextParagraph = endOfParagraph(endingSelection().visibleStart()).next();
    }
    setEndingSelection(visibleEnd);
    doApply();
    visibleEnd = endingSelection().visibleEnd();
    setEndingSelection(Selection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM));

    return true;
}
示例#8
0
RefPtr<Range> Frame::rangeForPoint(const IntPoint& framePoint)
{
    VisiblePosition position = visiblePositionForPoint(framePoint);
    if (position.isNull())
        return nullptr;

    Position deepPosition = position.deepEquivalent();
    Text* containerText = deepPosition.containerText();
    if (!containerText || !containerText->renderer() || containerText->renderer()->style().userSelect() == SELECT_NONE)
        return nullptr;

    VisiblePosition previous = position.previous();
    if (previous.isNotNull()) {
        RefPtr<Range> previousCharacterRange = makeRange(previous, position);
        LayoutRect rect = editor().firstRectForRange(previousCharacterRange.get());
        if (rect.contains(framePoint))
            return previousCharacterRange;
    }

    VisiblePosition next = position.next();
    if (RefPtr<Range> nextCharacterRange = makeRange(position, next)) {
        LayoutRect rect = editor().firstRectForRange(nextCharacterRange.get());
        if (rect.contains(framePoint))
            return nextCharacterRange;
    }

    return nullptr;
}
示例#9
0
VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
{
    // find first previous DOM position that is visible
    Position pos = previousVisiblePosition(m_deepPosition);
    
    // return null visible position if there is no previous visible position
    if (pos.atStart())
        return VisiblePosition();
        
    VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
    ASSERT(prev != *this);
    
#ifndef NDEBUG
    // we should always be able to make the affinity DOWNSTREAM, because going previous from an
    // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!).
    if (prev.isNotNull() && m_affinity == UPSTREAM) {
        VisiblePosition temp = prev;
        temp.setAffinity(UPSTREAM);
        ASSERT(inSameLine(temp, prev));
    }
#endif

    if (!stayInEditableContent || prev.isNull())
        return prev;
    
    Node* highestRoot = highestEditableRoot(deepEquivalent());
    
    if (!prev.deepEquivalent().node()->isAncestor(highestRoot))
        return VisiblePosition();
        
    if (highestEditableRoot(prev.deepEquivalent()) == highestRoot)
        return prev;

    return lastEditablePositionBeforePositionInRoot(prev.deepEquivalent(), highestRoot);
}
示例#10
0
VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) const
{
    // FIXME: Support CanSkipEditingBoundary
    ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
    // find first previous DOM position that is visible
    Position pos = previousVisuallyDistinctCandidate(m_deepPosition);
    
    // return null visible position if there is no previous visible position
    if (pos.atStartOfTree())
        return VisiblePosition();
        
    VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
    ASSERT(prev != *this);
    
#ifndef NDEBUG
    // we should always be able to make the affinity DOWNSTREAM, because going previous from an
    // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!).
    if (prev.isNotNull() && m_affinity == UPSTREAM) {
        VisiblePosition temp = prev;
        temp.setAffinity(UPSTREAM);
        ASSERT(inSameLine(temp, prev));
    }
#endif

    if (rule == CanCrossEditingBoundary)
        return prev;
    
    return honorEditingBoundaryAtOrBefore(prev);
}
示例#11
0
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
    LocalFrame* frame = document().frame();
    if (!frame)
        return;

    if (!frame->spellChecker().isContinuousSpellCheckingEnabled())
        return;

    frame->spellChecker().cancelCheck();

    // Take a look at the selection that results after typing and determine whether we need to spellcheck.
    // Since the word containing the current selection is never marked, this does a check to
    // see if typing made a new word that is not in the current selection. Basically, you
    // get this by being at the end of a word and typing a space.
    VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    VisiblePosition previous = start.previous();

    VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);

    if (commandType == InsertParagraphSeparator) {
        VisiblePosition p2 = nextWordPosition(start);
        VisibleSelection words(p1, endOfWord(p2));
        frame->spellChecker().markMisspellingsAfterLineBreak(words);
    } else if (previous.isNotNull()) {
        VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
        if (p1 != p2)
            frame->spellChecker().markMisspellingsAfterTypingToWord(p1, endingSelection());
    }
}
示例#12
0
VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
{
    if (visiblePos.isNull())
        return VisiblePositionRange();

    // make sure we move off of a line end
    VisiblePosition nextVisiblePos = visiblePos.next();
    if (nextVisiblePos.isNull())
        return VisiblePositionRange();

    VisiblePosition startPosition = startOfLine(nextVisiblePos);

    // fetch for a valid line start position
    if (startPosition.isNull() ) {
        startPosition = visiblePos;
        nextVisiblePos = nextVisiblePos.next();
    } else
        startPosition = updateAXLineStartForVisiblePosition(startPosition);

    VisiblePosition endPosition = endOfLine(nextVisiblePos);

    // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
    // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
    // return null for position by a floating object, since floating object doesn't really belong to any line.
    // This check will reposition the marker after the floating object, to ensure we get a line end.
    while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
        nextVisiblePos = nextVisiblePos.next();
        endPosition = endOfLine(nextVisiblePos);
    }

    return VisiblePositionRange(startPosition, endPosition);
}
示例#13
0
VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) const
{
    Position pos = previousVisuallyDistinctCandidate(m_deepPosition);

    // return null visible position if there is no previous visible position
    if (pos.atStartOfTree())
        return VisiblePosition();

    VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
    ASSERT(prev != *this);

#if ENABLE(ASSERT)
    // we should always be able to make the affinity DOWNSTREAM, because going previous from an
    // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!).
    if (prev.isNotNull() && m_affinity == UPSTREAM) {
        VisiblePosition temp = prev;
        temp.setAffinity(UPSTREAM);
        ASSERT(inSameLine(temp, prev));
    }
#endif

    switch (rule) {
    case CanCrossEditingBoundary:
        return prev;
    case CannotCrossEditingBoundary:
        return honorEditingBoundaryAtOrBefore(prev);
    case CanSkipOverEditingBoundary:
        return skipToStartOfEditingBoundary(prev);
    }

    ASSERT_NOT_REACHED();
    return honorEditingBoundaryAtOrBefore(prev);
}
示例#14
0
TextDirection SelectionModifier::directionOfSelection() const {
  InlineBox* startBox = nullptr;
  InlineBox* endBox = nullptr;
  // 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())
    startBox = computeInlineBoxPosition(startPosition).inlineBox;
  if (endPosition.isNotNull())
    endBox = computeInlineBoxPosition(endPosition).inlineBox;
  if (startBox && endBox && startBox->direction() == endBox->direction())
    return startBox->direction();

  return directionOfEnclosingBlock();
}
示例#15
0
VisiblePosition SelectionModifier::nextWordPositionForPlatform(
    const VisiblePosition& originalPosition) {
  VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);

  if (frame() &&
      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.deepEquivalent() !=
            positionAfterCurrentWord.deepEquivalent())
      positionAfterCurrentWord =
          previousWordPosition(positionAfterSpacingAndFollowingWord);

    bool movingBackwardsMovedPositionToStartOfCurrentWord =
        positionAfterCurrentWord.deepEquivalent() ==
        previousWordPosition(nextWordPosition(originalPosition))
            .deepEquivalent();
    if (movingBackwardsMovedPositionToStartOfCurrentWord)
      positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
  }
  return positionAfterCurrentWord;
}
示例#16
0
EphemeralRange LocalFrame::rangeForPoint(const IntPoint& framePoint) {
  const PositionWithAffinity positionWithAffinity =
      positionForPoint(framePoint);
  if (positionWithAffinity.isNull())
    return EphemeralRange();

  VisiblePosition position = createVisiblePosition(positionWithAffinity);
  VisiblePosition previous = previousPositionOf(position);
  if (previous.isNotNull()) {
    const EphemeralRange previousCharacterRange = makeRange(previous, position);
    IntRect rect = editor().firstRectForRange(previousCharacterRange);
    if (rect.contains(framePoint))
      return EphemeralRange(previousCharacterRange);
  }

  VisiblePosition next = nextPositionOf(position);
  const EphemeralRange nextCharacterRange = makeRange(position, next);
  if (nextCharacterRange.isNotNull()) {
    IntRect rect = editor().firstRectForRange(nextCharacterRange);
    if (rect.contains(framePoint))
      return EphemeralRange(nextCharacterRange);
  }

  return EphemeralRange();
}
示例#17
0
bool InsertListCommand::modifyRange()
{
    ASSERT(endingSelection().isRange());
    VisiblePosition visibleStart = endingSelection().visibleStart();
    VisiblePosition visibleEnd = endingSelection().visibleEnd();
    VisiblePosition startOfLastParagraph = startOfParagraph(visibleEnd);
    
    if (startOfParagraph(visibleStart) == startOfLastParagraph)
        return false;
    
    Node* startList = enclosingList(visibleStart.deepEquivalent().node());
    Node* endList = enclosingList(visibleEnd.deepEquivalent().node());
    if (!startList || startList != endList)
        m_forceCreateList = true;

    setEndingSelection(visibleStart);
    doApply();
    visibleStart = endingSelection().visibleStart();
    VisiblePosition nextParagraph = endOfParagraph(visibleStart).next();
    while (nextParagraph.isNotNull() && nextParagraph != startOfLastParagraph) {
        setEndingSelection(nextParagraph);
        doApply();
        nextParagraph = endOfParagraph(endingSelection().visibleStart()).next();
    }
    setEndingSelection(visibleEnd);
    doApply();
    visibleEnd = endingSelection().visibleEnd();
    setEndingSelection(Selection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM));
    m_forceCreateList = false;
    
    return true;
}
示例#18
0
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled()
     && !document()->frame()->editor()->isAutomaticQuoteSubstitutionEnabled()
     && !document()->frame()->editor()->isAutomaticLinkDetectionEnabled()
     && !document()->frame()->editor()->isAutomaticDashSubstitutionEnabled()
     && !document()->frame()->editor()->isAutomaticTextReplacementEnabled())
        return;
#else
    if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled())
        return;
#endif
    // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
    // Since the word containing the current selection is never marked, this does a check to
    // see if typing made a new word that is not in the current selection. Basically, you
    // get this by being at the end of a word and typing a space.    
    VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    VisiblePosition previous = start.previous();
    if (previous.isNotNull()) {
        VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
        VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
        if (p1 != p2) {
            RefPtr<Range> range = makeRange(p1, p2);
            String strippedPreviousWord;
            if (range && (commandType == TypingCommand::InsertText || commandType == TypingCommand::InsertLineBreak || commandType == TypingCommand::InsertParagraphSeparator || commandType == TypingCommand::InsertParagraphSeparatorInQuotedContent))
                strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
			document()->frame()->lspellcheck();//FIX ME: Opensource issue.
        } else if (commandType == TypingCommand::InsertText)
            document()->frame()->editor()->startCorrectionPanelTimer();
    }
}
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled()
     && !document()->frame()->editor()->isAutomaticQuoteSubstitutionEnabled()
     && !document()->frame()->editor()->isAutomaticLinkDetectionEnabled()
     && !document()->frame()->editor()->isAutomaticDashSubstitutionEnabled()
     && !document()->frame()->editor()->isAutomaticTextReplacementEnabled())
        return;
#else
    if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled())
        return;
#endif
    // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
    // Since the word containing the current selection is never marked, this does a check to
    // see if typing made a new word that is not in the current selection. Basically, you
    // get this by being at the end of a word and typing a space.    
    VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    VisiblePosition previous = start.previous();
    if (previous.isNotNull()) {
        VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
        VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
        if (p1 != p2)
            document()->frame()->editor()->markMisspellingsAfterTypingToWord(p1, endingSelection());
#if SUPPORT_AUTOCORRECTION_PANEL
        else if (commandType == TypingCommand::InsertText)
            document()->frame()->editor()->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
#else
    UNUSED_PARAM(commandType);
#endif
    }
}
VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) const
{
    Position pos = previousVisuallyDistinctCandidate(m_deepPosition);

    // return null visible position if there is no previous visible position
    if (pos.atStartOfTree())
        return VisiblePosition();

    VisiblePosition prev = VisiblePosition(pos);
    ASSERT(prev.deepEquivalent() != m_deepPosition);

#if ENABLE(ASSERT)
    // we should always be able to make the affinity |TextAffinity::Downstream|,
    // because going previous from an |TextAffinity::Upstream| position can
    // never yield another |TextAffinity::Upstream position| (unless line wrap
    // length is 0!).
    if (prev.isNotNull() && m_affinity == TextAffinity::Upstream) {
        ASSERT(inSameLine(PositionWithAffinity(prev.deepEquivalent()), PositionWithAffinity(prev.deepEquivalent(), TextAffinity::Upstream)));
    }
#endif

    switch (rule) {
    case CanCrossEditingBoundary:
        return prev;
    case CannotCrossEditingBoundary:
        return honorEditingBoundaryAtOrBefore(prev);
    case CanSkipOverEditingBoundary:
        return skipToStartOfEditingBoundary(prev);
    }

    ASSERT_NOT_REACHED();
    return honorEditingBoundaryAtOrBefore(prev);
}
void IndentOutdentCommand::outdentParagraph()
{
    VisiblePosition visibleStartOfParagraph = startOfParagraph(endingSelection().visibleStart());
    VisiblePosition visibleEndOfParagraph = endOfParagraph(visibleStartOfParagraph);

    Node* enclosingNode = enclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), &isListOrIndentBlockquote);
    if (!enclosingNode)
        return;

    // Use InsertListCommand to remove the selection from the list
    if (enclosingNode->hasTagName(olTag)) {
        applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::OrderedList, ""));
        return;        
    }
    if (enclosingNode->hasTagName(ulTag)) {
        applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::UnorderedList, ""));
        return;
    }
    
    // The selection is inside a blockquote
    VisiblePosition positionInEnclosingBlock = VisiblePosition(Position(enclosingNode, 0));
    VisiblePosition startOfEnclosingBlock = startOfBlock(positionInEnclosingBlock);
    VisiblePosition endOfEnclosingBlock = endOfBlock(positionInEnclosingBlock);
    if (visibleStartOfParagraph == startOfEnclosingBlock &&
        visibleEndOfParagraph == endOfEnclosingBlock) {
        // The blockquote doesn't contain anything outside the paragraph, so it can be totally removed.
        removeNodePreservingChildren(enclosingNode);
        updateLayout();
        visibleStartOfParagraph = VisiblePosition(visibleStartOfParagraph.deepEquivalent());
        visibleEndOfParagraph = VisiblePosition(visibleEndOfParagraph.deepEquivalent());
        if (visibleStartOfParagraph.isNotNull() && !isStartOfParagraph(visibleStartOfParagraph))
            insertNodeAt(createBreakElement(document()), visibleStartOfParagraph.deepEquivalent());
        if (visibleEndOfParagraph.isNotNull() && !isEndOfParagraph(visibleEndOfParagraph))
            insertNodeAt(createBreakElement(document()), visibleEndOfParagraph.deepEquivalent());
        return;
    }
    Node* enclosingBlockFlow = enclosingBlockFlowElement(visibleStartOfParagraph);
    RefPtr<Node> splitBlockquoteNode = enclosingNode;
    if (enclosingBlockFlow != enclosingNode)
        splitBlockquoteNode = splitTreeToNode(enclosingBlockFlowElement(visibleStartOfParagraph), enclosingNode, true);
    RefPtr<Node> placeholder = createBreakElement(document());
    insertNodeBefore(placeholder, splitBlockquoteNode);
    moveParagraph(startOfParagraph(visibleStartOfParagraph), endOfParagraph(visibleEndOfParagraph), VisiblePosition(Position(placeholder.get(), 0)), true);
}
void setAffinityUsingLinePosition(VisiblePosition &pos)
{
    // When not moving across line wrap, make sure to end up with DOWNSTREAM affinity.
    if (pos.isNotNull() && pos.affinity() == UPSTREAM) {
        VisiblePosition temp(pos);
        temp.setAffinity(DOWNSTREAM);
        if (!visiblePositionsOnDifferentLines(temp, pos))
            pos.setAffinity(DOWNSTREAM);
    }
}
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
    Frame& frame = this->frame();

#if PLATFORM(MAC)
    if (!frame.editor().isContinuousSpellCheckingEnabled()
        && !frame.editor().isAutomaticQuoteSubstitutionEnabled()
        && !frame.editor().isAutomaticLinkDetectionEnabled()
        && !frame.editor().isAutomaticDashSubstitutionEnabled()
        && !frame.editor().isAutomaticTextReplacementEnabled())
            return;
    if (frame.editor().isHandlingAcceptedCandidate())
        return;
#else
    if (!frame.editor().isContinuousSpellCheckingEnabled())
        return;
#endif
    // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
    // Since the word containing the current selection is never marked, this does a check to
    // see if typing made a new word that is not in the current selection. Basically, you
    // get this by being at the end of a word and typing a space.    
    VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    VisiblePosition previous = start.previous();
    if (previous.isNotNull()) {
#if !PLATFORM(IOS)
        VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
        VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
        if (p1 != p2) {
            RefPtr<Range> range = makeRange(p1, p2);
            String strippedPreviousWord;
            if (range && (commandType == TypingCommand::InsertText || commandType == TypingCommand::InsertLineBreak || commandType == TypingCommand::InsertParagraphSeparator || commandType == TypingCommand::InsertParagraphSeparatorInQuotedContent))
                strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
            frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), !strippedPreviousWord.isEmpty());
        } else if (commandType == TypingCommand::InsertText)
            frame.editor().startAlternativeTextUITimer();
#else
        UNUSED_PARAM(commandType);
        // If this bug gets fixed, this PLATFORM(IOS) code could be removed:
        // <rdar://problem/7259611> Word boundary code on iPhone gives different results than desktop
        EWordSide startWordSide = LeftWordIfOnBoundary;
        UChar32 c = previous.characterAfter();
        // FIXME: VisiblePosition::characterAfter() and characterBefore() do not emit newlines the same
        // way as TextIterator, so we do an isEndOfParagraph check here.
        if (isSpaceOrNewline(c) || c == 0xA0 || isEndOfParagraph(previous)) {
            startWordSide = RightWordIfOnBoundary;
        }
        VisiblePosition p1 = startOfWord(previous, startWordSide);
        VisiblePosition p2 = startOfWord(start, startWordSide);
        if (p1 != p2)
            frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), false);
#endif // !PLATFORM(IOS)
    }
}
void IndentOutdentCommand::indentRegion(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
{
    // Special case empty unsplittable elements because there's nothing to split
    // and there's nothing to move.
    Position start = startOfSelection.deepEquivalent().downstream();
    if (isAtUnsplittableElement(start)) {
        RefPtr<Element> blockquote = createIndentBlockquoteElement(document());
        insertNodeAt(blockquote, start);
        RefPtr<Element> placeholder = createBreakElement(document());
        appendNode(placeholder, blockquote);
        setEndingSelection(VisibleSelection(Position(placeholder.get(), 0), DOWNSTREAM));
        return;
    }

    RefPtr<Element> blockquoteForNextIndent;
    VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
    VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
    while (endOfCurrentParagraph != endAfterSelection) {
        // Iterate across the selected paragraphs...
        VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
        if (tryIndentingAsListItem(endOfCurrentParagraph))
            blockquoteForNextIndent = 0;
        else
            indentIntoBlockquote(endOfCurrentParagraph, endOfNextParagraph, blockquoteForNextIndent);

        // indentIntoBlockquote could move more than one paragraph if the paragraph
        // is in a list item or a table. As a result, endAfterSelection could refer to a position
        // no longer in the document.
        if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().node()->inDocument())
            break;
        // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().node()
        // If somehow we did, return to prevent crashes.
        if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) {
            ASSERT_NOT_REACHED();
            return;
        }
        endOfCurrentParagraph = endOfNextParagraph;
    }   
}
void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction)
{
    document().updateLayoutIgnorePendingStylesheets();

    if (!renderer() || !renderer()->isTextControl())
        return;

    end = std::max(end, 0);
    start = std::min(std::max(start, 0), end);

    if (!hasVisibleTextArea(*renderer(), innerTextElement())) {
        cacheSelection(start, end, direction);
        return;
    }
    VisiblePosition startPosition = visiblePositionForIndex(start);
    VisiblePosition endPosition;
    if (start == end)
        endPosition = startPosition;
    else
        endPosition = visiblePositionForIndex(end);

#if !PLATFORM(IOS)
    // startPosition and endPosition can be null position for example when
    // "-webkit-user-select: none" style attribute is specified.
    if (startPosition.isNotNull() && endPosition.isNotNull()) {
        ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowHost() == this
            && endPosition.deepEquivalent().deprecatedNode()->shadowHost() == this);
    }
#endif
    VisibleSelection newSelection;
    if (direction == SelectionHasBackwardDirection)
        newSelection = VisibleSelection(endPosition, startPosition);
    else
        newSelection = VisibleSelection(startPosition, endPosition);
    newSelection.setIsDirectional(direction != SelectionHasNoDirection);

    if (Frame* frame = document().frame())
        frame->selection().setSelection(newSelection);
}
示例#26
0
// FIXME: We should merge this function with ApplyBlockElementCommand::formatSelection
void IndentOutdentCommand::outdentRegion(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection, EditingState* editingState)
{
    VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
    VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection);

    if (endOfCurrentParagraph.deepEquivalent() == endOfLastParagraph.deepEquivalent()) {
        outdentParagraph(editingState);
        return;
    }

    Position originalSelectionEnd = endingSelection().end();
    VisiblePosition endAfterSelection = endOfParagraph(nextPositionOf(endOfLastParagraph));

    while (endOfCurrentParagraph.deepEquivalent() != endAfterSelection.deepEquivalent()) {
        VisiblePosition endOfNextParagraph = endOfParagraph(nextPositionOf(endOfCurrentParagraph));
        if (endOfCurrentParagraph.deepEquivalent() == endOfLastParagraph.deepEquivalent())
            setEndingSelection(VisibleSelection(originalSelectionEnd, TextAffinity::Downstream));
        else
            setEndingSelection(endOfCurrentParagraph);

        outdentParagraph(editingState);
        if (editingState->isAborted())
            return;

        // outdentParagraph could move more than one paragraph if the paragraph
        // is in a list item. As a result, endAfterSelection and endOfNextParagraph
        // could refer to positions no longer in the document.
        if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().inShadowIncludingDocument())
            break;

        if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().inShadowIncludingDocument()) {
            endOfCurrentParagraph = createVisiblePosition(endingSelection().end());
            endOfNextParagraph = endOfParagraph(nextPositionOf(endOfCurrentParagraph));
        }
        endOfCurrentParagraph = endOfNextParagraph;
    }
}
void IndentOutdentCommand::indentRegion()
{
    VisibleSelection selection = selectionForParagraphIteration(endingSelection());
    VisiblePosition startOfSelection = selection.visibleStart();
    VisiblePosition endOfSelection = selection.visibleEnd();
    int startIndex = indexForVisiblePosition(startOfSelection);
    int endIndex = indexForVisiblePosition(endOfSelection);

    ASSERT(!startOfSelection.isNull());
    ASSERT(!endOfSelection.isNull());

    // Special case empty unsplittable elements because there's nothing to split
    // and there's nothing to move.
    Position start = startOfSelection.deepEquivalent().downstream();
    if (isAtUnsplittableElement(start)) {
        RefPtr<Element> blockquote = createIndentBlockquoteElement(document());
        insertNodeAt(blockquote, start);
        RefPtr<Element> placeholder = createBreakElement(document());
        appendNode(placeholder, blockquote);
        setEndingSelection(VisibleSelection(Position(placeholder.get(), 0), DOWNSTREAM));
        return;
    }

    RefPtr<Element> blockquoteForNextIndent;
    VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
    VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
    while (endOfCurrentParagraph != endAfterSelection) {
        // Iterate across the selected paragraphs...
        VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
        if (tryIndentingAsListItem(endOfCurrentParagraph))
            blockquoteForNextIndent = 0;
        else
            indentIntoBlockquote(endOfCurrentParagraph, endOfNextParagraph, blockquoteForNextIndent);
        // blockquoteForNextIndent maybe updated
        // this is due to the way prepareBlockquoteLevelForInsertion was designed.
        // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().node()
        // If somehow we did, return to prevent crashes.
        if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) {
            ASSERT_NOT_REACHED();
            return;
        }
        endOfCurrentParagraph = endOfNextParagraph;
    }

    RefPtr<Range> startRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, 0, true);
    RefPtr<Range> endRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endIndex, 0, true);
    if (startRange && endRange)
        setEndingSelection(VisibleSelection(startRange->startPosition(), endRange->startPosition(), DOWNSTREAM));
}
VisiblePosition VisiblePosition::previous() const
{
    VisiblePosition result =  VisiblePosition(previousVisiblePosition(m_deepPosition), DOWNSTREAM);

#ifndef NDEBUG
    // we should always be able to make the affinity DOWNSTREAM, because going previous from an
    // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!).
    if (result.isNotNull() && m_affinity == UPSTREAM) {
        VisiblePosition temp = result;
        temp.setAffinity(UPSTREAM);
        ASSERT(!visiblePositionsOnDifferentLines(temp, result));
    }
#endif
    return result;
}
示例#29
0
bool InsertListCommand::selectionHasListOfType(const VisibleSelection& selection, const HTMLQualifiedName& listTag)
{
    VisiblePosition start = selection.visibleStart();

    if (!enclosingList(start.deepEquivalent().deprecatedNode()))
        return false;

    VisiblePosition end = startOfParagraph(selection.visibleEnd());
    while (start.isNotNull() && start != end) {
        HTMLElement* listElement = enclosingList(start.deepEquivalent().deprecatedNode());
        if (!listElement || !listElement->hasTagName(listTag))
            return false;
        start = startOfNextParagraph(start);
    }

    return true;
}
示例#30
0
void TypingCommand::markMisspellingsAfterTyping()
{
    if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled())
        return;
    // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
    // Since the word containing the current selection is never marked, this does a check to
    // see if typing made a new word that is not in the current selection. Basically, you
    // get this by being at the end of a word and typing a space.    
    VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    VisiblePosition previous = start.previous();
    if (previous.isNotNull()) {
        VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
        VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
        if (p1 != p2)
            document()->frame()->editor()->markMisspellingsAfterTypingToPosition(p1);
    }
}