Example #1
0
bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
{
    if (!coreObject || !coreObject->isAccessibilityRenderObject())
        return false;

    if (selection.isNone())
        return false;

    RefPtr<Range> range = selection.toNormalizedRange();
    if (!range)
        return false;

    // We want to check that both the selection intersects the node
    // AND that the selection is not just "touching" one of the
    // boundaries for the selected node. We want to check whether the
    // node is actually inside the region, at least partially.
    auto& node = *coreObject->node();
    auto* lastDescendant = node.lastDescendant();
    unsigned lastOffset = lastOffsetInNode(lastDescendant);
    auto intersectsResult = range->intersectsNode(node);
    return !intersectsResult.hasException()
           && intersectsResult.releaseReturnValue()
           && (&range->endContainer() != &node || range->endOffset())
           && (&range->startContainer() != lastDescendant || range->startOffset() != lastOffset);
}
Example #2
0
RefPtr<TextIndicator> TextIndicator::createWithRange(const Range& range, TextIndicatorPresentationTransition presentationTransition, unsigned margin)
{
    Frame* frame = range.startContainer()->document().frame();

    if (!frame)
        return nullptr;

#if PLATFORM(IOS)
    frame->editor().setIgnoreCompositionSelectionChange(true);
    frame->selection().setUpdateAppearanceEnabled(true);
#endif

    VisibleSelection oldSelection = frame->selection().selection();
    frame->selection().setSelection(range);

    RefPtr<TextIndicator> indicator = TextIndicator::createWithSelectionInFrame(*frame, presentationTransition, margin);

    frame->selection().setSelection(oldSelection);

    if (indicator)
        indicator->setWantsMargin(!areRangesEqual(&range, oldSelection.toNormalizedRange().get()));

#if PLATFORM(IOS)
    frame->editor().setIgnoreCompositionSelectionChange(false, Editor::RevealSelection::No);
    frame->selection().setUpdateAppearanceEnabled(false);
#endif

    return indicator.release();
}
Example #3
0
bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent)
{
    if (text.isEmpty())
        return false;

    VisibleSelection selection = selectionForCommand(triggeringEvent);
    if (!selection.isContentEditable())
        return false;

    spellChecker().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));

    // Get the selection to use for the event that triggered this insertText.
    // If the event handler changed the selection, we may want to use a different selection
    // that is contained in the event target.
    selection = selectionForCommand(triggeringEvent);
    if (selection.isContentEditable()) {
        if (Node* selectionStart = selection.start().deprecatedNode()) {
            RefPtr<Document> document(selectionStart->document());

            // Insert the text
            TypingCommand::Options options = 0;
            if (selectInsertedText)
                options |= TypingCommand::SelectInsertedText;
            TypingCommand::insertText(*document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);

            // Reveal the current selection
            if (LocalFrame* editedFrame = document->frame()) {
                if (Page* page = editedFrame->page())
                    page->focusController().focusedOrMainFrame()->selection().revealSelection(ScrollAlignment::alignCenterIfNeeded);
            }
        }
    }

    return true;
}
bool static selectionIsContainedByAnchorNode(const VisibleSelection& selection)
{
    // Check whether the start or end of the selection is outside of the selections
    // anchor node.
    return (selection.start().anchorType() == WebCore::Position::PositionIsOffsetInAnchor
            && selection.end().anchorType() == WebCore::Position::PositionIsOffsetInAnchor);
}
Example #5
0
// Updates |selectionInFlatTree| to match with |selection|.
void SelectionAdjuster::adjustSelectionInFlatTree(
    VisibleSelectionInFlatTree* selectionInFlatTree,
    const VisibleSelection& selection) {
  if (selection.isNone()) {
    *selectionInFlatTree = VisibleSelectionInFlatTree();
    return;
  }

  const PositionInFlatTree& base = toPositionInFlatTree(selection.base());
  const PositionInFlatTree& extent = toPositionInFlatTree(selection.extent());
  const PositionInFlatTree& position1 = toPositionInFlatTree(selection.start());
  const PositionInFlatTree& position2 = toPositionInFlatTree(selection.end());
  position1.anchorNode()->updateDistribution();
  position2.anchorNode()->updateDistribution();
  selectionInFlatTree->m_base = base;
  selectionInFlatTree->m_extent = extent;
  selectionInFlatTree->m_affinity = selection.m_affinity;
  selectionInFlatTree->m_isDirectional = selection.m_isDirectional;
  selectionInFlatTree->m_granularity = selection.m_granularity;
  selectionInFlatTree->m_hasTrailingWhitespace =
      selection.m_hasTrailingWhitespace;
  selectionInFlatTree->m_baseIsFirst =
      base.isNull() || base.compareTo(extent) <= 0;
  if (position1.compareTo(position2) <= 0) {
    selectionInFlatTree->m_start = position1;
    selectionInFlatTree->m_end = position2;
  } else {
    selectionInFlatTree->m_start = position2;
    selectionInFlatTree->m_end = position1;
  }
  selectionInFlatTree->updateSelectionType();
}
static String selectMisspellingAsync(Frame* selectedFrame, DocumentMarker& marker)
{
    VisibleSelection selection = selectedFrame->selection()->selection();
    if (!selection.isCaretOrRange())
        return String();

    // Caret and range selections always return valid normalized ranges.
    RefPtr<Range> selectionRange = selection.toNormalizedRange();
    Vector<DocumentMarker*> markers = selectedFrame->document()->markers()->markersInRange(selectionRange.get(), DocumentMarker::Spelling | DocumentMarker::Grammar);
    if (markers.size() != 1)
        return String();
    marker = *markers[0];

    // Cloning a range fails only for invalid ranges.
    RefPtr<Range> markerRange = selectionRange->cloneRange(ASSERT_NO_EXCEPTION);
    markerRange->setStart(markerRange->startContainer(), marker.startOffset());
    markerRange->setEnd(markerRange->endContainer(), marker.endOffset());
    if (selection.isCaret()) {
        selection = VisibleSelection(markerRange.get());
        selectedFrame->selection()->setSelection(selection, WordGranularity);
        selectionRange = selection.toNormalizedRange();
    }

    if (markerRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation) != selectionRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation))
        return String();

    return markerRange->text();
}
static void expandSelectionToGranularity(Frame* frame, int x, int y, TextGranularity granularity, bool isInputMode)
{
    ASSERT(frame);
    ASSERT(frame->selection());

    VisibleSelection selection;
    if (x < 0 || y < 0) {
        if (!isInputMode)
            return; // Invalid request

        // Input mode based selection, use the current selection as the selection point.
        ASSERT(frame->selection()->selectionType() != VisibleSelection::NoSelection);
        selection = frame->selection()->selection();
    } else {
        VisiblePosition pointLocation(frame->visiblePositionForPoint(WebCore::IntPoint(x, y)));
        selection = VisibleSelection(pointLocation, pointLocation);
    }

    if (!(selection.start().anchorNode() && selection.start().anchorNode()->isTextNode()))
        return;

    selection.expandUsingGranularity(granularity);
    RefPtr<Range> newRange = selection.toNormalizedRange();
    if (!newRange)
        return;
    ExceptionCode ec = 0;
    if (newRange->collapsed(ec))
        return;
    RefPtr<Range> oldRange = frame->selection()->selection().toNormalizedRange();
    EAffinity affinity = frame->selection()->affinity();
    if (isInputMode && !frame->editor()->client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false))
        return;
    frame->selection()->setSelectedRange(newRange.get(), affinity, true);
}
Example #8
0
static bool isTextWithCaret(AccessibilityObject* coreObject)
{
    if (!coreObject || !coreObject->isAccessibilityRenderObject())
        return false;

    Document* document = coreObject->document();
    if (!document)
        return false;

    Frame* frame = document->frame();
    if (!frame)
        return false;

    if (!frame->settings().caretBrowsingEnabled())
        return false;

    // Check text objects and paragraphs only.
    AtkObject* axObject = coreObject->wrapper();
    AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
    if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
        return false;

    // Finally, check whether the caret is set in the current object.
    VisibleSelection selection = coreObject->selection();
    if (!selection.isCaret())
        return false;

    return selectionBelongsToObject(coreObject, selection);
}
VisibleSelection PendingSelection::calcVisibleSelectionAlgorithm() const
{
    using PositionType = typename Strategy::PositionType;

    PositionType start = Strategy::selectionStart(m_selection);
    PositionType end = Strategy::selectionEnd(m_selection);
    SelectionType selectionType = VisibleSelection::selectionType(start, end);
    TextAffinity affinity = m_selection.affinity();

    bool paintBlockCursor = m_shouldShowBlockCursor && selectionType == SelectionType::CaretSelection && !isLogicalEndOfLine(VisiblePosition(end, affinity));
    VisibleSelection selection;
    if (enclosingTextFormControl(start.computeContainerNode())) {
        // TODO(yosin) We should use |PositionMoveType::Character| to avoid
        // ending paint at middle of character.
        PositionType endPosition = paintBlockCursor ? nextPositionOf(Strategy::selectionExtent(m_selection), PositionMoveType::CodePoint) : end;
        selection.setWithoutValidation(start, endPosition);
        return selection;
    }

    VisiblePosition visibleStart = VisiblePosition(start, selectionType == SelectionType::RangeSelection ? TextAffinity::Downstream : affinity);
    if (paintBlockCursor) {
        VisiblePosition visibleExtent(end, affinity);
        visibleExtent = visibleExtent.next(CanSkipOverEditingBoundary);
        return VisibleSelection(visibleStart, visibleExtent);
    }
    VisiblePosition visibleEnd(end, selectionType == SelectionType::RangeSelection ? TextAffinity::Upstream : affinity);
    return VisibleSelection(visibleStart, visibleEnd);
}
// This avoids the expense of a full fledged delete operation, and avoids a layout that typically results
// from text removal.
bool InsertTextCommand::performTrivialReplace(const String& text, bool selectInsertedText)
{
    if (!endingSelection().isRange())
        return false;
    
    if (text.contains('\t') || text.contains(' ') || text.contains('\n'))
        return false;
    
    Position start = endingSelection().start();
    Position end = endingSelection().end();
    
    if (start.node() != end.node() || !start.node()->isTextNode() || isTabSpanTextNode(start.node()))
        return false;
        
    replaceTextInNode(static_cast<Text*>(start.node()), start.deprecatedEditingOffset(), end.deprecatedEditingOffset() - start.deprecatedEditingOffset(), text);
    
    Position endPosition(start.node(), start.deprecatedEditingOffset() + text.length());
    
    // We could have inserted a part of composed character sequence,
    // so we are basically treating ending selection as a range to avoid validation.
    // <http://bugs.webkit.org/show_bug.cgi?id=15781>
    VisibleSelection forcedEndingSelection;
    forcedEndingSelection.setWithoutValidation(start, endPosition);
    setEndingSelection(forcedEndingSelection);
    
    if (!selectInsertedText)
        setEndingSelection(VisibleSelection(endingSelection().visibleEnd()));
    
    return true;
}
Example #11
0
// This avoids the expense of a full fledged delete operation, and avoids a layout that typically results
// from text removal.
bool InsertTextCommand::performTrivialReplace(const String& text, bool selectInsertedText)
{
    if (!endingSelection().isRange())
        return false;
    
    if (text.contains('\t') || text.contains(' ') || text.contains('\n'))
        return false;

    Position start = endingSelection().start();
    Position endPosition = replaceSelectedTextInNode(text);
    if (endPosition.isNull())
        return false;

    // We could have inserted a part of composed character sequence,
    // so we are basically treating ending selection as a range to avoid validation.
    // <http://bugs.webkit.org/show_bug.cgi?id=15781>
    VisibleSelection forcedEndingSelection;
    forcedEndingSelection.setWithoutValidation(start, endPosition);
    forcedEndingSelection.setIsDirectional(endingSelection().isDirectional());
    setEndingSelection(forcedEndingSelection);

    if (!selectInsertedText)
        setEndingSelection(VisibleSelection(endingSelection().visibleEnd(), endingSelection().isDirectional()));
    
    return true;
}
Example #12
0
void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping)
{
    if (unifiedTextCheckerEnabled()) {
        TextCheckingTypeMask textCheckingOptions = 0;

        if (isContinuousSpellCheckingEnabled())
            textCheckingOptions |= TextCheckingTypeSpelling;

        if (!(textCheckingOptions & TextCheckingTypeSpelling))
            return;

        if (isGrammarCheckingEnabled())
            textCheckingOptions |= TextCheckingTypeGrammar;

        VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
        if (textCheckingOptions & TextCheckingTypeGrammar) {
            VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
            markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
        } else {
            markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
        }
        return;
    }

    if (!isContinuousSpellCheckingEnabled())
        return;

    // Check spelling of one word
    RefPtr<Range> misspellingRange = nullptr;
    markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);

    // Autocorrect the misspelled word.
    if (!misspellingRange)
        return;

    // Get the misspelled word.
    const String misspelledWord = plainText(misspellingRange.get());
    String autocorrectedString = textChecker().getAutoCorrectSuggestionForMisspelledWord(misspelledWord);

    // If autocorrected word is non empty, replace the misspelled word by this word.
    if (!autocorrectedString.isEmpty()) {
        VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
        if (newSelection != m_frame.selection().selection()) {
            m_frame.selection().setSelection(newSelection);
        }

        m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false);

        // Reset the charet one character further.
        m_frame.selection().moveTo(m_frame.selection().selection().visibleEnd());
        m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
    }

    if (!isGrammarCheckingEnabled())
        return;

    // Check grammar of entire sentence
    markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
}
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;
}
Example #14
0
void visibleTextQuads(const VisibleSelection& selection, Vector<FloatQuad>& quads)
{
    if (!selection.isRange())
        return;
    ASSERT(selection.firstRange());

    visibleTextQuads(*(selection.firstRange()), quads, true /* useSelectionHeight */);
}
void InsertTextCommand::setEndingSelectionWithoutValidation(const Position& startPosition, const Position& endPosition)
{
    // We could have inserted a part of composed character sequence,
    // so we are basically treating ending selection as a range to avoid validation.
    // <http://bugs.webkit.org/show_bug.cgi?id=15781>
    VisibleSelection forcedEndingSelection;
    forcedEndingSelection.setWithoutValidation(startPosition, endPosition);
    forcedEndingSelection.setIsDirectional(endingSelection().isDirectional());
    setEndingSelection(forcedEndingSelection);
}
void TypingCommand::postTextStateChangeNotificationForDeletion(const VisibleSelection& selection)
{
    if (!AXObjectCache::accessibilityEnabled())
        return;
    postTextStateChangeNotification(AXTextEditTypeDelete, AccessibilityObject::stringForVisiblePositionRange(selection), selection.start());
    VisiblePositionIndexRange range;
    range.startIndex.value = indexForVisiblePosition(selection.start(), range.startIndex.scope);
    range.endIndex.value = indexForVisiblePosition(selection.end(), range.endIndex.scope);
    composition()->setTextInsertedByUnapplyRange(range);
}
Example #17
0
static char* webkitAccessibleTextGetWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
{
    AccessibilityObject* coreObject = core(text);
    Document* document = coreObject->document();
    if (!document)
        return emptyTextSelectionAtOffset(0, startOffset, endOffset);

    Node* node = getNodeForAccessibilityObject(coreObject);
    if (!node)
        return emptyTextSelectionAtOffset(0, startOffset, endOffset);

    int actualOffset = atkOffsetToWebCoreOffset(text, offset);

    // Besides of the usual conversion from ATK offsets to WebCore offsets,
    // we need to consider the potential embedded objects that might have been
    // inserted in the text exposed through AtkText when calculating the offset.
    actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);

    VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
    VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);

    // Take into account other relative positions, if needed, by
    // calculating the new position that we would need to consider.
    VisiblePosition newPosition = caretPosition;
    switch (textPosition) {
    case GetTextPositionAt:
        break;

    case GetTextPositionBefore:
        // Early return if asking for the previous word while already at the beginning.
        if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node))
            return emptyTextSelectionAtOffset(0, startOffset, endOffset);

        if (isStartOfLine(currentWord.end()))
            newPosition = currentWord.visibleStart().previous();
        else
            newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary);
        break;

    case GetTextPositionAfter:
        // Early return if asking for the following word while already at the end.
        if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node))
            return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);

        if (isEndOfLine(currentWord.end()))
            newPosition = currentWord.visibleEnd().next();
        else
            newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary);
        break;

    default:
        ASSERT_NOT_REACHED();
    }

    // Determine the relevant word we are actually interested in
    // and calculate the ATK offsets for it, then return everything.
    VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord;
    getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset);
    return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
}
void InputMethodController::selectComposition() const
{
    RefPtr<Range> range = compositionRange();
    if (!range)
        return;

    // The composition can start inside a composed character sequence, so we have to override checks.
    // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
    VisibleSelection selection;
    selection.setWithoutValidation(range->startPosition(), range->endPosition());
    m_frame->selection().setSelection(selection, 0);
}
Example #19
0
void InputMethodController::selectComposition() const
{
    const EphemeralRange range = compositionEphemeralRange();
    if (range.isNull())
        return;

    // The composition can start inside a composed character sequence, so we have to override checks.
    // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
    VisibleSelection selection;
    selection.setWithoutValidation(range.startPosition(), range.endPosition());
    frame().selection().setSelection(selection, 0);
}
Example #20
0
void visibleTextQuads(const VisibleSelection& selection, Vector<FloatQuad>& quads)
{
    if (!selection.isRange())
        return;

    // Make sure that both start and end have valid nodes associated otherwise
    // this can crash. See PR 220628.
    if (!selection.start().anchorNode() || !selection.end().anchorNode())
        return;

    visibleTextQuads(*(selection.firstRange()), quads, true /* useSelectionHeight */);
}
Example #21
0
bool Editor::findString(const String& target, FindOptions options)
{
    VisibleSelection selection = m_frame.selection().selection();

    RefPtr<Range> resultRange = rangeOfString(target, selection.firstRange().get(), options);

    if (!resultRange)
        return false;

    m_frame.selection().setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM));
    m_frame.selection().revealSelection();
    return true;
}
static gint webkit_accessible_text_get_n_selections(AtkText* text)
{
    AccessibilityObject* coreObject = core(text);
    VisibleSelection selection = coreObject->selection();

    // We don't support multiple selections for now, so there's only
    // two possibilities
    // Also, we don't want to do anything if the selection does not
    // belong to the currently selected object. We have to check since
    // there's no way to get the selection for a given object, only
    // the global one (the API is a bit confusing)
    return !selectionBelongsToObject(coreObject, selection) || selection.isNone() ? 0 : 1;
}
TEST_F(SurroundingTextTest, TreeCaretSelection)
{
    setHTML(String("<div>This is outside of <p id='selection'>foo bar</p> the selected node</div>"));

    {
        VisibleSelection selection = select(0);
        SurroundingText surroundingText(selection.start(), 1);

        EXPECT_EQ("f", surroundingText.content());
        EXPECT_EQ(0u, surroundingText.startOffsetInContent());
        EXPECT_EQ(0u, surroundingText.endOffsetInContent());
    }

    {
        VisibleSelection selection = select(0);
        SurroundingText surroundingText(selection.start(), 5);

        EXPECT_EQ("foo", surroundingText.content().simplifyWhiteSpace());
        EXPECT_EQ(1u, surroundingText.startOffsetInContent());
        EXPECT_EQ(1u, surroundingText.endOffsetInContent());
    }

    {
        VisibleSelection selection = select(0);
        SurroundingText surroundingText(selection.start(), 1337);

        EXPECT_EQ("This is outside of foo bar the selected node", surroundingText.content().simplifyWhiteSpace());
        EXPECT_EQ(20u, surroundingText.startOffsetInContent());
        EXPECT_EQ(20u, surroundingText.endOffsetInContent());
    }

    {
        VisibleSelection selection = select(6);
        SurroundingText surroundingText(selection.start(), 2);

        EXPECT_EQ("ar", surroundingText.content());
        EXPECT_EQ(1u, surroundingText.startOffsetInContent());
        EXPECT_EQ(1u, surroundingText.endOffsetInContent());
    }

    {
        VisibleSelection selection = select(6);
        SurroundingText surroundingText(selection.start(), 1337);

        EXPECT_EQ("This is outside of foo bar the selected node", surroundingText.content().simplifyWhiteSpace());
        EXPECT_EQ(26u, surroundingText.startOffsetInContent());
        EXPECT_EQ(26u, surroundingText.endOffsetInContent());
    }
}
void ApplyBlockElementCommand::doApply()
{
    if (!endingSelection().rootEditableElement())
        return;

    VisiblePosition visibleEnd = endingSelection().visibleEnd();
    VisiblePosition visibleStart = endingSelection().visibleStart();
    if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan())
        return;

    // When a selection ends at the start of a paragraph, we rarely paint
    // the selection gap before that paragraph, because there often is no gap.
    // In a case like this, it's not obvious to the user that the selection
    // ends "inside" that paragraph, so it would be confusing if Indent/Outdent
    // operated on that paragraph.
    // FIXME: We paint the gap before some paragraphs that are indented with left
    // margin/padding, but not others.  We should make the gap painting more consistent and
    // then use a left margin/padding rule here.
    if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) {
        VisibleSelection newSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary), endingSelection().isDirectional());
        if (newSelection.isNone())
            return;
        setEndingSelection(newSelection);
    }

    VisibleSelection selection = selectionForParagraphIteration(endingSelection());
    VisiblePosition startOfSelection = selection.visibleStart();
    VisiblePosition endOfSelection = selection.visibleEnd();
    ASSERT(!startOfSelection.isNull());
    ASSERT(!endOfSelection.isNull());
    RefPtrWillBeRawPtr<ContainerNode> startScope = nullptr;
    int startIndex = indexForVisiblePosition(startOfSelection, startScope);
    RefPtrWillBeRawPtr<ContainerNode> endScope = nullptr;
    int endIndex = indexForVisiblePosition(endOfSelection, endScope);

    formatSelection(startOfSelection, endOfSelection);

    document().updateLayoutIgnorePendingStylesheets();

    ASSERT(startScope == endScope);
    ASSERT(startIndex >= 0);
    ASSERT(startIndex <= endIndex);
    if (startScope == endScope && startIndex >= 0 && startIndex <= endIndex) {
        VisiblePosition start(visiblePositionForIndex(startIndex, startScope.get()));
        VisiblePosition end(visiblePositionForIndex(endIndex, endScope.get()));
        if (start.isNotNull() && end.isNotNull())
            setEndingSelection(VisibleSelection(start, end, endingSelection().isDirectional()));
    }
}
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));
}
Example #26
0
void SpellChecker::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
{
    // This function is called with a selection already expanded to word boundaries.
    // Might be nice to assert that here.

    // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
    // grammar checking can only be on if spell checking is also on.
    if (!isContinuousSpellCheckingEnabled())
        return;

    RefPtr<Range> searchRange(selection.toNormalizedRange());
    if (!searchRange)
        return;

    // If we're not in an editable node, bail.
    Node* editableNode = searchRange->startContainer();
    if (!editableNode || !editableNode->hasEditableStyle())
        return;

    if (!isSpellCheckingEnabledFor(editableNode))
        return;

    TextCheckingHelper checker(spellCheckerClient(), searchRange);
    if (checkSpelling)
        checker.markAllMisspellings(firstMisspellingRange);
    else if (isGrammarCheckingEnabled())
        checker.markAllBadGrammar();
}
Example #27
0
void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction, NeedToDispatchSelectEvent eventBehaviour, SelectionOption selectionOption)
{
    if (openShadowRoot() || !isTextFormControl())
        return;
    const int editorValueLength = static_cast<int>(innerEditorValue().length());
    ASSERT(editorValueLength >= 0);
    end = std::max(std::min(end, editorValueLength), 0);
    start = std::min(std::max(start, 0), end);
    cacheSelection(start, end, direction);

    if (selectionOption == NotChangeSelection || (selectionOption == ChangeSelectionIfFocused && document().focusedElement() != this) || !inShadowIncludingDocument()) {
        if (eventBehaviour == DispatchSelectEvent)
            scheduleSelectEvent();
        return;
    }

    LocalFrame* frame = document().frame();
    HTMLElement* innerEditor = innerEditorElement();
    if (!frame || !innerEditor)
        return;

    Position startPosition = positionForIndex(innerEditor, start);
    Position endPosition = start == end ? startPosition : positionForIndex(innerEditor, end);

    ASSERT(start == indexForPosition(innerEditor, startPosition));
    ASSERT(end == indexForPosition(innerEditor, endPosition));

#if ENABLE(ASSERT)
    // 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.anchorNode()->shadowHost() == this
            && endPosition.anchorNode()->shadowHost() == this);
    }
#endif // ENABLE(ASSERT)
    VisibleSelection newSelection;
    if (direction == SelectionHasBackwardDirection)
        newSelection.setWithoutValidation(endPosition, startPosition);
    else
        newSelection.setWithoutValidation(startPosition, endPosition);
    newSelection.setIsDirectional(direction != SelectionHasNoDirection);

    frame->selection().setSelection(newSelection, FrameSelection::DoNotAdjustInFlatTree | FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | (selectionOption == ChangeSelectionAndFocus ? 0 : FrameSelection::DoNotSetFocus));
    if (eventBehaviour == DispatchSelectEvent)
        scheduleSelectEvent();
}
Example #28
0
void EditCommand::setEndingSelection(const VisibleSelection &s)
{
    Element* root = s.rootEditableElement();
    for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
        cmd->m_endingSelection = s;
        cmd->m_endingRootEditableElement = root;
    }
}
Example #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;
}
static gint webkitAccessibleTextGetNSelections(AtkText* text)
{
    AccessibilityObject* coreObject = core(text);
    VisibleSelection selection = coreObject->selection();

    // Only range selections are needed for the purpose of this method
    if (!selection.isRange())
        return 0;

    // We don't support multiple selections for now, so there's only
    // two possibilities
    // Also, we don't want to do anything if the selection does not
    // belong to the currently selected object. We have to check since
    // there's no way to get the selection for a given object, only
    // the global one (the API is a bit confusing)
    return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
}