Пример #1
0
void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd)
{
    Element* editable = frame().selection().rootEditableElement();
    if (!editable)
        return;

    const EphemeralRange range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable);
    if (range.isNull())
        return;

    const Position start = range.startPosition();
    if (editableRootForPosition(start) != editable)
        return;

    const Position end = range.endPosition();
    if (editableRootForPosition(end) != editable)
        return;

    clear();

    for (const auto& underline : underlines) {
        unsigned underlineStart = compositionStart + underline.startOffset;
        unsigned underlineEnd = compositionStart + underline.endOffset;
        EphemeralRange ephemeralLineRange = PlainTextRange(underlineStart, underlineEnd).createRange(*editable);
        if (ephemeralLineRange.isNull())
            continue;
        frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thick, underline.backgroundColor);
    }

    m_hasComposition = true;
    if (!m_compositionRange)
        m_compositionRange = Range::create(range.document());
    m_compositionRange->setStart(range.startPosition());
    m_compositionRange->setEnd(range.endPosition());
}
void SurroundingText::initialize(const Position& startPosition, const Position& endPosition, unsigned maxLength)
{
    ASSERT(startPosition.document() == endPosition.document());

    const unsigned halfMaxLength = maxLength / 2;

    Document* document = startPosition.document();
    // The position will have no document if it is null (as in no position).
    if (!document || !document->documentElement())
        return;

    // The forward range starts at the selection end and ends at the document's
    // end. It will then be updated to only contain the text in the text in the
    // right range around the selection.
    CharacterIterator forwardIterator(endPosition, lastPositionInNode(document->documentElement()).parentAnchoredEquivalent(), TextIteratorStopsOnFormControls);
    // FIXME: why do we stop going trough the text if we were not able to select something on the right?
    if (!forwardIterator.atEnd())
        forwardIterator.advance(maxLength - halfMaxLength);

    EphemeralRange forwardRange = forwardIterator.range();
    if (forwardRange.isNull() || !Range::create(*document, endPosition, forwardRange.startPosition())->text().length())
        return;

    // Same as with the forward range but with the backward range. The range
    // starts at the document's start and ends at the selection start and will
    // be updated.
    BackwardsCharacterIterator backwardsIterator(firstPositionInNode(document->documentElement()).parentAnchoredEquivalent(), startPosition, TextIteratorStopsOnFormControls);
    if (!backwardsIterator.atEnd())
        backwardsIterator.advance(halfMaxLength);

    m_startOffsetInContent = Range::create(*document, backwardsIterator.endPosition(), startPosition)->text().length();
    m_endOffsetInContent = Range::create(*document, backwardsIterator.endPosition(), endPosition)->text().length();
    m_contentRange = Range::create(*document, backwardsIterator.endPosition(), forwardRange.startPosition());
    ASSERT(m_contentRange);
}
// static
PassRefPtrWillBeRawPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, const EphemeralRange& checkingRange, const EphemeralRange& paragraphRange, int requestNumber)
{
    if (checkingRange.isNull())
        return nullptr;
    if (!checkingRange.startPosition().computeContainerNode()->rootEditableElement())
        return nullptr;

    String text = plainText(checkingRange, TextIteratorEmitsObjectReplacementCharacter);
    if (text.isEmpty())
        return nullptr;

    RefPtrWillBeRawPtr<Range> checkingRangeObject = createRange(checkingRange);
    RefPtrWillBeRawPtr<Range> paragraphRangeObject = nullptr;
    // Share identical Range objects.
    if (checkingRange == paragraphRange)
        paragraphRangeObject = checkingRangeObject;
    else
        paragraphRangeObject = createRange(paragraphRange);

    const DocumentMarkerVector& markers = checkingRangeObject->ownerDocument().markers().markersInRange(checkingRange, DocumentMarker::SpellCheckClientMarkers());
    Vector<uint32_t> hashes(markers.size());
    Vector<unsigned> offsets(markers.size());
    for (size_t i = 0; i < markers.size(); ++i) {
        hashes[i] = markers[i]->hash();
        offsets[i] = markers[i]->startOffset();
    }

    return adoptRefWillBeNoop(new SpellCheckRequest(checkingRangeObject, paragraphRangeObject, text, textCheckingOptions, processType, hashes, offsets, requestNumber));
}
Пример #4
0
PlainTextRange InputMethodController::getSelectionOffsets() const
{
    EphemeralRange range = firstEphemeralRangeOf(frame().selection().selection());
    if (range.isNull())
        return PlainTextRange();
    ContainerNode* editable = frame().selection().rootEditableElementOrTreeScopeRootNode();
    ASSERT(editable);
    return PlainTextRange::create(*editable, range);
}
Пример #5
0
// static
WebRange WebRange::fromDocumentRange(WebLocalFrame* frame, int start, int length)
{
    LocalFrame* webFrame = toWebLocalFrameImpl(frame)->frame();
    Element* selectionRoot = webFrame->selection().rootEditableElement();
    ContainerNode* scope = selectionRoot ? selectionRoot : webFrame->document()->documentElement();
    const EphemeralRange range = PlainTextRange(start, start + length).createRange(*scope);
    if (range.isNull())
        return WebRange();
    return Range::create(range.document(), range.startPosition(), range.endPosition());
}
Пример #6
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);
}
Пример #7
0
bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionOffsets)
{
    if (selectionOffsets.isNull())
        return false;
    Element* rootEditableElement = frame().selection().rootEditableElement();
    if (!rootEditableElement)
        return false;

    const EphemeralRange range = selectionOffsets.createRange(*rootEditableElement);
    if (range.isNull())
        return false;

    return frame().selection().setSelectedRange(range, VP_DEFAULT_AFFINITY, SelectionDirectionalMode::NonDirectional, FrameSelection::CloseTyping);
}
Пример #8
0
bool SelectionEditor::setSelectedRange(const EphemeralRange& range, TextAffinity affinity, SelectionDirectionalMode directional, FrameSelection::SetSelectionOptions options)
{
    if (range.isNull())
        return false;

    // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
    // they start at the beginning of the next line instead
    m_logicalRange = nullptr;
    stopObservingVisibleSelectionChangeIfNecessary();

    // Since |FrameSeleciton::setSelection()| dispatches events and DOM tree
    // can be modified by event handlers, we should create |Range| object before
    // calling it.
    m_logicalRange = createRange(range);

    VisibleSelection newSelection(range.startPosition(), range.endPosition(), affinity, directional == SelectionDirectionalMode::Directional);
    m_frameSelection->setSelection(newSelection, options);
    startObservingVisibleSelectionChange();
    return true;
}
PlainTextRange PlainTextRange::create(const ContainerNode& scope, const EphemeralRange& range)
{
    if (range.isNull())
        return PlainTextRange();

    // The critical assumption is that this only gets called with ranges that
    // concentrate on a given area containing the selection root. This is done
    // because of text fields and textareas. The DOM for those is not
    // directly in the document DOM, so ensure that the range does not cross a
    // boundary of one of those.
    Node* startContainer = range.startPosition().computeContainerNode();
    if (startContainer != &scope && !startContainer->isDescendantOf(&scope))
        return PlainTextRange();
    Node* endContainer = range.endPosition().computeContainerNode();
    if (endContainer != scope && !endContainer->isDescendantOf(&scope))
        return PlainTextRange();

    size_t start = TextIterator::rangeLength(Position(&const_cast<ContainerNode&>(scope), 0), range.startPosition());
    size_t end = TextIterator::rangeLength(Position(&const_cast<ContainerNode&>(scope), 0), range.endPosition());

    return PlainTextRange(start, end);
}
Пример #10
0
void InputMethodController::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
{
    Editor::RevealSelectionScope revealSelectionScope(&editor());

    // Updates styles before setting selection for composition to prevent
    // inserting the previous composition text into text nodes oddly.
    // See https://bugs.webkit.org/show_bug.cgi?id=46868
    frame().document()->updateLayoutTreeIfNeeded();

    selectComposition();

    if (frame().selection().isNone())
        return;

    if (Element* target = frame().document()->focusedElement()) {
        // Dispatch an appropriate composition event to the focused node.
        // We check the composition status and choose an appropriate composition event since this
        // function is used for three purposes:
        // 1. Starting a new composition.
        //    Send a compositionstart and a compositionupdate event when this function creates
        //    a new composition node, i.e.
        //    !hasComposition() && !text.isEmpty().
        //    Sending a compositionupdate event at this time ensures that at least one
        //    compositionupdate event is dispatched.
        // 2. Updating the existing composition node.
        //    Send a compositionupdate event when this function updates the existing composition
        //    node, i.e. hasComposition() && !text.isEmpty().
        // 3. Canceling the ongoing composition.
        //    Send a compositionend event when function deletes the existing composition node, i.e.
        //    !hasComposition() && test.isEmpty().
        RefPtrWillBeRawPtr<CompositionEvent> event = nullptr;
        if (!hasComposition()) {
            // We should send a compositionstart event only when the given text is not empty because this
            // function doesn't create a composition node when the text is empty.
            if (!text.isEmpty()) {
                target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositionstart, frame().domWindow(), frame().selectedText()));
                event = CompositionEvent::create(EventTypeNames::compositionupdate, frame().domWindow(), text);
            }
        } else {
            if (!text.isEmpty())
                event = CompositionEvent::create(EventTypeNames::compositionupdate, frame().domWindow(), text);
            else
                event = CompositionEvent::create(EventTypeNames::compositionend, frame().domWindow(), text);
        }
        if (event.get())
            target->dispatchEvent(event);
    }

    // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
    // will delete the old composition with an optimized replace operation.
    if (text.isEmpty()) {
        ASSERT(frame().document());
        TypingCommand::deleteSelection(*frame().document(), TypingCommand::PreventSpellChecking);
    }

    clear();

    if (text.isEmpty())
        return;
    ASSERT(frame().document());
    TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);

    // Find out what node has the composition now.
    Position base = mostForwardCaretPosition(frame().selection().base());
    Node* baseNode = base.anchorNode();
    if (!baseNode || !baseNode->isTextNode())
        return;

    Position extent = frame().selection().extent();
    Node* extentNode = extent.anchorNode();
    if (baseNode != extentNode)
        return;

    unsigned extentOffset = extent.computeOffsetInContainerNode();
    unsigned baseOffset = base.computeOffsetInContainerNode();
    if (baseOffset + text.length() != extentOffset)
        return;

    m_isDirty = true;
    m_hasComposition = true;
    if (!m_compositionRange)
        m_compositionRange = Range::create(baseNode->document());
    m_compositionRange->setStart(baseNode, baseOffset);
    m_compositionRange->setEnd(baseNode, extentOffset);

    if (baseNode->layoutObject())
        baseNode->layoutObject()->setShouldDoFullPaintInvalidation();

    unsigned start = std::min(baseOffset + selectionStart, extentOffset);
    unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset);
    RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
    frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Downstream, SelectionDirectionalMode::NonDirectional, NotUserTriggered);

    if (underlines.isEmpty()) {
        frame().document()->markers().addCompositionMarker(m_compositionRange->startPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTheme::theme().platformDefaultCompositionBackgroundColor());
        return;
    }
    for (const auto& underline : underlines) {
        unsigned underlineStart = baseOffset + underline.startOffset;
        unsigned underlineEnd = baseOffset + underline.endOffset;
        EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, underlineStart), Position(baseNode, underlineEnd));
        if (ephemeralLineRange.isNull())
            continue;
        frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thick, underline.backgroundColor);
    }
}