void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Position &pos)
{
    // It is only important to set a style to apply later if we're at the boundaries of
    // a paragraph. Otherwise, content that is moved as part of the work of the command
    // will lend their styles to the new paragraph without any extra work needed.
    VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
    if (!isStartOfParagraph(visiblePos) && !isEndOfParagraph(visiblePos))
        return;

    ASSERT(pos.isNotNull());
    m_style = EditingStyle::create(pos);
    m_style->mergeTypingStyle(pos.document());
}
Esempio n. 2
0
static bool needInterchangeNewlineAfter(const VisiblePosition& v)
{
    return isEndOfParagraph(v) && isStartOfParagraph(v.next());
}
Esempio n. 3
0
void InsertTextCommand::input(const String& text, bool selectInsertedText, RebalanceType whitespaceRebalance)
{
    
    ASSERT(text.find('\n') == notFound);

    if (!endingSelection().isNonOrphanedCaretOrRange())
        return;

    // Delete the current selection.
    // FIXME: This delete operation blows away the typing style.
    if (endingSelection().isRange()) {
        if (performTrivialReplace(text, selectInsertedText))
            return;
        deleteSelection(false, true, true, false);
    }

    Position startPosition(endingSelection().start());
    
    Position placeholder;
    // We want to remove preserved newlines and brs that will collapse (and thus become unnecessary) when content 
    // is inserted just before them.
    // FIXME: We shouldn't really have to do this, but removing placeholders is a workaround for 9661.
    // If the caret is just before a placeholder, downstream will normalize the caret to it.
    Position downstream(startPosition.downstream());
    if (lineBreakExistsAtPosition(downstream)) {
        // FIXME: This doesn't handle placeholders at the end of anonymous blocks.
        VisiblePosition caret(startPosition);
        if (isEndOfBlock(caret) && isStartOfParagraph(caret))
            placeholder = downstream;
        // Don't remove the placeholder yet, otherwise the block we're inserting into would collapse before
        // we get a chance to insert into it.  We check for a placeholder now, though, because doing so requires
        // the creation of a VisiblePosition, and if we did that post-insertion it would force a layout.
    }
    
    // Insert the character at the leftmost candidate.
    startPosition = startPosition.upstream();
    
    // It is possible for the node that contains startPosition to contain only unrendered whitespace,
    // and so deleteInsignificantText could remove it.  Save the position before the node in case that happens.
    Position positionBeforeStartNode(positionInParentBeforeNode(startPosition.containerNode()));
    deleteInsignificantText(startPosition.upstream(), startPosition.downstream());
    if (!startPosition.anchorNode()->inDocument())
        startPosition = positionBeforeStartNode;
    if (!startPosition.isCandidate())
        startPosition = startPosition.downstream();
    
    startPosition = positionAvoidingSpecialElementBoundary(startPosition);
    
    Position endPosition;
    
    if (text == "\t") {
        endPosition = insertTab(startPosition);
        startPosition = endPosition.previous();
        if (placeholder.isNotNull())
            removePlaceholderAt(placeholder);
    } else {
        // Make sure the document is set up to receive text
        startPosition = positionInsideTextNode(startPosition);
        ASSERT(startPosition.anchorType() == Position::PositionIsOffsetInAnchor);
        ASSERT(startPosition.containerNode());
        ASSERT(startPosition.containerNode()->isTextNode());
        if (placeholder.isNotNull())
            removePlaceholderAt(placeholder);
        RefPtr<Text> textNode = static_cast<Text*>(startPosition.containerNode());
        const unsigned offset = startPosition.offsetInContainerNode();

        insertTextIntoNode(textNode, offset, text);
        endPosition = Position(textNode, offset + text.length());

        if (whitespaceRebalance == RebalanceLeadingAndTrailingWhitespaces) {
            // The insertion may require adjusting adjacent whitespace, if it is present.
            rebalanceWhitespaceAt(endPosition);
            // Rebalancing on both sides isn't necessary if we've inserted only spaces.
            if (!shouldRebalanceLeadingWhitespaceFor(text))
                rebalanceWhitespaceAt(startPosition);
        } else {
            ASSERT(whitespaceRebalance == RebalanceAllWhitespaces);
            if (canRebalance(startPosition) && canRebalance(endPosition))
                rebalanceWhitespaceOnTextSubstring(textNode, startPosition.offsetInContainerNode(), endPosition.offsetInContainerNode());
        }
    }

    // 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);
    setEndingSelection(forcedEndingSelection);

    // Handle the case where there is a typing style.
    if (RefPtr<EditingStyle> typingStyle = document()->frame()->selection()->typingStyle()) {
        typingStyle->prepareToApplyAt(endPosition, EditingStyle::PreserveWritingDirection);
        if (!typingStyle->isEmpty())
            applyStyle(typingStyle.get());
    }

    if (!selectInsertedText)
        setEndingSelection(VisibleSelection(endingSelection().end(), endingSelection().affinity()));
}
Esempio n. 4
0
void TypingCommand::deleteKeyPressed(TextGranularity granularity)
{
    Selection selectionToDelete;
    Selection selectionAfterUndo;
    
    switch (endingSelection().state()) {
        case Selection::RANGE:
            selectionToDelete = endingSelection();
            selectionAfterUndo = selectionToDelete;
            break;
        case Selection::CARET: {
            m_smartDelete = false;

            SelectionController selection;
            selection.setSelection(endingSelection());
            selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity);
            
            // When the caret is at the start of the editable area in an empty list item, break out of the list item.
            if (endingSelection().visibleStart().previous(true).isNull()) {
                if (breakOutOfEmptyListItem()) {
                    typingAddedToOpenCommand();
                    return;
                }
            }
            
            VisiblePosition visibleStart(endingSelection().visibleStart());
            // If the caret is at the start of a paragraph after a table, move content into the last table cell.
            if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) {
                // Unless the caret is just before a table.  We don't want to move a table into the last table cell.
                if (isLastPositionBeforeTable(visibleStart))
                    return;
                // Extend the selection backward into the last cell, then deletion will handle the move.
                selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity);
            // If the caret is just after a table, select the table and don't delete anything.
            } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
                setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM));
                typingAddedToOpenCommand();
                return;
            }

            selectionToDelete = selection.selection();
            if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
                selectionAfterUndo = selectionToDelete;
            else
                // It's a little tricky to compute what the starting selection would have been in the original document.
                // We can't let the Selection class's validation kick in or it'll adjust for us based on
                // the current state of the document and we'll get the wrong result.
                selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
            break;
        }
        case Selection::NONE:
            ASSERT_NOT_REACHED();
            break;
    }
    
    if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) {
        // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
        // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
        // more text than you insert.  In that case all of the text that was around originally should be selected.
        if (m_openedByBackwardDelete)
            setStartingSelection(selectionAfterUndo);
        CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
        setSmartDelete(false);
        typingAddedToOpenCommand();
    }
}
void InsertTextCommand::input(const String& text, bool selectInsertedText)
{
    
    ASSERT(text.find('\n') == notFound);

    if (!endingSelection().isNonOrphanedCaretOrRange())
        return;

    // Delete the current selection.
    // FIXME: This delete operation blows away the typing style.
    if (endingSelection().isRange()) {
        if (performTrivialReplace(text, selectInsertedText))
            return;
        deleteSelection(false, true, true, false);
    }

    Position startPosition(endingSelection().start());
    
    Position placeholder;
    // We want to remove preserved newlines and brs that will collapse (and thus become unnecessary) when content 
    // is inserted just before them.
    // FIXME: We shouldn't really have to do this, but removing placeholders is a workaround for 9661.
    // If the caret is just before a placeholder, downstream will normalize the caret to it.
    Position downstream(startPosition.downstream());
    if (lineBreakExistsAtPosition(downstream)) {
        // FIXME: This doesn't handle placeholders at the end of anonymous blocks.
        VisiblePosition caret(startPosition);
        if (isEndOfBlock(caret) && isStartOfParagraph(caret))
            placeholder = downstream;
        // Don't remove the placeholder yet, otherwise the block we're inserting into would collapse before
        // we get a chance to insert into it.  We check for a placeholder now, though, because doing so requires
        // the creation of a VisiblePosition, and if we did that post-insertion it would force a layout.
    }
    
    // Insert the character at the leftmost candidate.
    startPosition = startPosition.upstream();
    
    // It is possible for the node that contains startPosition to contain only unrendered whitespace,
    // and so deleteInsignificantText could remove it.  Save the position before the node in case that happens.
    Position positionBeforeStartNode(positionInParentBeforeNode(startPosition.node()));
    deleteInsignificantText(startPosition.upstream(), startPosition.downstream());
    if (!startPosition.node()->inDocument())
        startPosition = positionBeforeStartNode;
    if (!startPosition.isCandidate())
        startPosition = startPosition.downstream();
    
    startPosition = positionAvoidingSpecialElementBoundary(startPosition);
    
    Position endPosition;
    
    if (text == "\t") {
        endPosition = insertTab(startPosition);
        startPosition = endPosition.previous();
        if (placeholder.isNotNull())
            removePlaceholderAt(placeholder);
        m_charactersAdded += 1;
    } else {
        // Make sure the document is set up to receive text
        startPosition = prepareForTextInsertion(startPosition);
        if (placeholder.isNotNull())
            removePlaceholderAt(placeholder);
        Text *textNode = static_cast<Text *>(startPosition.node());
        int offset = startPosition.deprecatedEditingOffset();

        insertTextIntoNode(textNode, offset, text);
        endPosition = Position(textNode, offset + text.length());

        // The insertion may require adjusting adjacent whitespace, if it is present.
        rebalanceWhitespaceAt(endPosition);
        // Rebalancing on both sides isn't necessary if we've inserted a space.
        if (text != " ") 
            rebalanceWhitespaceAt(startPosition);
            
        m_charactersAdded += 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(startPosition, endPosition);
    setEndingSelection(forcedEndingSelection);

    // Handle the case where there is a typing style.
    CSSMutableStyleDeclaration* typingStyle = document()->frame()->selection()->typingStyle();
    RefPtr<CSSComputedStyleDeclaration> endingStyle = endPosition.computedStyle();
    RefPtr<CSSValue> unicodeBidi;
    RefPtr<CSSValue> direction;
    if (typingStyle) {
        unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
        direction = typingStyle->getPropertyCSSValue(CSSPropertyDirection);
    }
    endingStyle->diff(typingStyle);
    if (typingStyle && unicodeBidi) {
        ASSERT(unicodeBidi->isPrimitiveValue());
        typingStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
        if (direction) {
            ASSERT(direction->isPrimitiveValue());
            typingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
        }
    }

    if (typingStyle && typingStyle->length())
        applyStyle(typingStyle);

    if (!selectInsertedText)
        setEndingSelection(VisibleSelection(endingSelection().end(), endingSelection().affinity()));
}
Esempio n. 6
-1
void InsertTextCommand::doApply()
{
    ASSERT(m_text.find('\n') == kNotFound);

    if (!endingSelection().isNonOrphanedCaretOrRange())
        return;

    // Delete the current selection.
    // FIXME: This delete operation blows away the typing style.
    if (endingSelection().isRange()) {
        if (performTrivialReplace(m_text, m_selectInsertedText))
            return;
        bool endOfSelectionWasAtStartOfBlock = isStartOfBlock(endingSelection().visibleEnd());
        deleteSelection(false, true, false, false);
        // deleteSelection eventually makes a new endingSelection out of a Position. If that Position doesn't have
        // a renderer (e.g. it is on a <frameset> in the DOM), the VisibleSelection cannot be canonicalized to
        // anything other than NoSelection. The rest of this function requires a real endingSelection, so bail out.
        if (endingSelection().isNone())
            return;
        if (endOfSelectionWasAtStartOfBlock) {
            if (EditingStyle* typingStyle = document().frame()->selection().typingStyle())
                typingStyle->removeBlockProperties();
        }
    } else if (document().frame()->editor().isOverwriteModeEnabled()) {
        if (performOverwrite(m_text, m_selectInsertedText))
            return;
    }

    Position startPosition(endingSelection().start());

    Position placeholder;
    // We want to remove preserved newlines and brs that will collapse (and thus become unnecessary) when content
    // is inserted just before them.
    // FIXME: We shouldn't really have to do this, but removing placeholders is a workaround for 9661.
    // If the caret is just before a placeholder, downstream will normalize the caret to it.
    Position downstream(startPosition.downstream());
    if (lineBreakExistsAtPosition(downstream)) {
        // FIXME: This doesn't handle placeholders at the end of anonymous blocks.
        VisiblePosition caret(startPosition);
        if (isEndOfBlock(caret) && isStartOfParagraph(caret))
            placeholder = downstream;
        // Don't remove the placeholder yet, otherwise the block we're inserting into would collapse before
        // we get a chance to insert into it.  We check for a placeholder now, though, because doing so requires
        // the creation of a VisiblePosition, and if we did that post-insertion it would force a layout.
    }

    // Insert the character at the leftmost candidate.
    startPosition = startPosition.upstream();

    // It is possible for the node that contains startPosition to contain only unrendered whitespace,
    // and so deleteInsignificantText could remove it.  Save the position before the node in case that happens.
    ASSERT(startPosition.containerNode());
    Position positionBeforeStartNode(positionInParentBeforeNode(*startPosition.containerNode()));
    deleteInsignificantText(startPosition, startPosition.downstream());
    if (!startPosition.inDocument())
        startPosition = positionBeforeStartNode;
    if (!startPosition.isCandidate())
        startPosition = startPosition.downstream();

    startPosition = positionAvoidingSpecialElementBoundary(startPosition);

    Position endPosition;

    if (m_text == "\t") {
        endPosition = insertTab(startPosition);
        startPosition = endPosition.previous();
        if (placeholder.isNotNull())
            removePlaceholderAt(placeholder);
    } else {
        // Make sure the document is set up to receive m_text
        startPosition = positionInsideTextNode(startPosition);
        ASSERT(startPosition.anchorType() == Position::PositionIsOffsetInAnchor);
        ASSERT(startPosition.containerNode());
        ASSERT(startPosition.containerNode()->isTextNode());
        if (placeholder.isNotNull())
            removePlaceholderAt(placeholder);
        RefPtrWillBeRawPtr<Text> textNode = startPosition.containerText();
        const unsigned offset = startPosition.offsetInContainerNode();

        insertTextIntoNode(textNode, offset, m_text);
        endPosition = Position(textNode, offset + m_text.length());

        if (m_rebalanceType == RebalanceLeadingAndTrailingWhitespaces) {
            // The insertion may require adjusting adjacent whitespace, if it is present.
            rebalanceWhitespaceAt(endPosition);
            // Rebalancing on both sides isn't necessary if we've inserted only spaces.
            if (!shouldRebalanceLeadingWhitespaceFor(m_text))
                rebalanceWhitespaceAt(startPosition);
        } else {
            ASSERT(m_rebalanceType == RebalanceAllWhitespaces);
            if (canRebalance(startPosition) && canRebalance(endPosition))
                rebalanceWhitespaceOnTextSubstring(textNode, startPosition.offsetInContainerNode(), endPosition.offsetInContainerNode());
        }
    }

    setEndingSelectionWithoutValidation(startPosition, endPosition);

    // Handle the case where there is a typing style.
    if (RefPtrWillBeRawPtr<EditingStyle> typingStyle = document().frame()->selection().typingStyle()) {
        typingStyle->prepareToApplyAt(endPosition, EditingStyle::PreserveWritingDirection);
        if (!typingStyle->isEmpty())
            applyStyle(typingStyle.get());
    }

    if (!m_selectInsertedText)
        setEndingSelection(VisibleSelection(endingSelection().end(), endingSelection().affinity(), endingSelection().isDirectional()));
}