18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/*
28e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Redistribution and use in source and binary forms, with or without
58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * modification, are permitted provided that the following conditions
68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * are met:
78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 1. Redistributions of source code must retain the above copyright
88e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *    notice, this list of conditions and the following disclaimer.
98e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 2. Redistributions in binary form must reproduce the above copyright
108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *    notice, this list of conditions and the following disclaimer in the
118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *    documentation and/or other materials provided with the distribution.
128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */
258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "config.h"
278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "InsertTextCommand.h"
288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Document.h"
308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Element.h"
318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "EditingText.h"
328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Editor.h"
338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Frame.h"
348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "HTMLInterchange.h"
358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "htmlediting.h"
368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "visible_units.h"
372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include <wtf/unicode/CharacterNames.h>
388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectnamespace WebCore {
408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectInsertTextCommand::InsertTextCommand(Document *document)
42cad810f21b803229eb11403f9209855525a25d57Steve Block    : CompositeEditCommand(document)
438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid InsertTextCommand::doApply()
478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen MurdochPosition InsertTextCommand::positionInsideTextNode(const Position& p)
518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    Position pos = p;
5381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if (isTabSpanTextNode(pos.anchorNode())) {
548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        RefPtr<Node> textNode = document()->createEditingTextNode("");
5581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        insertNodeAtTabSpanPosition(textNode.get(), pos);
562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return firstPositionInNode(textNode.get());
578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // Prepare for text input by looking at the specified position.
6081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // It may be necessary to insert a text node to receive characters.
6181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if (!pos.containerNode()->isTextNode()) {
628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        RefPtr<Node> textNode = document()->createEditingTextNode("");
6381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        insertNodeAt(textNode.get(), pos);
642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return firstPositionInNode(textNode.get());
658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return pos;
688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// This avoids the expense of a full fledged delete operation, and avoids a layout that typically results
718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// from text removal.
728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectbool InsertTextCommand::performTrivialReplace(const String& text, bool selectInsertedText)
738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!endingSelection().isRange())
758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return false;
768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (text.contains('\t') || text.contains(' ') || text.contains('\n'))
788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return false;
798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    Position start = endingSelection().start().parentAnchoredEquivalent();
812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    Position end = endingSelection().end().parentAnchoredEquivalent();
822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    ASSERT(start.anchorType() == Position::PositionIsOffsetInAnchor);
832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor);
842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (start.containerNode() != end.containerNode() || !start.containerNode()->isTextNode() || isTabSpanTextNode(start.containerNode()))
868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return false;
872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    replaceTextInNode(static_cast<Text*>(start.containerNode()), start.offsetInContainerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), text);
892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    Position endPosition(start.containerNode(), start.offsetInContainerNode() + text.length());
912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // We could have inserted a part of composed character sequence,
938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // so we are basically treating ending selection as a range to avoid validation.
948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // <http://bugs.webkit.org/show_bug.cgi?id=15781>
958f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    VisibleSelection forcedEndingSelection;
968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    forcedEndingSelection.setWithoutValidation(start, endPosition);
978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    setEndingSelection(forcedEndingSelection);
982fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!selectInsertedText)
1008f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        setEndingSelection(VisibleSelection(endingSelection().visibleEnd()));
1018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return true;
1038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1052fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid InsertTextCommand::input(const String& text, bool selectInsertedText, RebalanceType whitespaceRebalance)
1068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
108f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick    ASSERT(text.find('\n') == notFound);
1098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
110e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block    if (!endingSelection().isNonOrphanedCaretOrRange())
1118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
1120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Delete the current selection.
1148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // FIXME: This delete operation blows away the typing style.
1158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (endingSelection().isRange()) {
1168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (performTrivialReplace(text, selectInsertedText))
1178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            return;
1188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        deleteSelection(false, true, true, false);
1198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
1200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1215f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    Position startPosition(endingSelection().start());
1225f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian
1235f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    Position placeholder;
1245f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    // We want to remove preserved newlines and brs that will collapse (and thus become unnecessary) when content
1255f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    // is inserted just before them.
1265f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    // FIXME: We shouldn't really have to do this, but removing placeholders is a workaround for 9661.
1275f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    // If the caret is just before a placeholder, downstream will normalize the caret to it.
1285f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    Position downstream(startPosition.downstream());
1295f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    if (lineBreakExistsAtPosition(downstream)) {
1305f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        // FIXME: This doesn't handle placeholders at the end of anonymous blocks.
1315f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        VisiblePosition caret(startPosition);
1325f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        if (isEndOfBlock(caret) && isStartOfParagraph(caret))
1335f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian            placeholder = downstream;
1345f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        // Don't remove the placeholder yet, otherwise the block we're inserting into would collapse before
1355f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        // we get a chance to insert into it.  We check for a placeholder now, though, because doing so requires
1365f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        // the creation of a VisiblePosition, and if we did that post-insertion it would force a layout.
1375f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    }
1385f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian
1398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Insert the character at the leftmost candidate.
1405f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    startPosition = startPosition.upstream();
1415f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian
1428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // It is possible for the node that contains startPosition to contain only unrendered whitespace,
1438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // and so deleteInsignificantText could remove it.  Save the position before the node in case that happens.
14481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    Position positionBeforeStartNode(positionInParentBeforeNode(startPosition.containerNode()));
1458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    deleteInsignificantText(startPosition.upstream(), startPosition.downstream());
14681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if (!startPosition.anchorNode()->inDocument())
1478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        startPosition = positionBeforeStartNode;
1488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!startPosition.isCandidate())
1498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        startPosition = startPosition.downstream();
1508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
151635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    startPosition = positionAvoidingSpecialElementBoundary(startPosition);
1528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    Position endPosition;
1548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (text == "\t") {
1568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        endPosition = insertTab(startPosition);
1578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        startPosition = endPosition.previous();
1585f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        if (placeholder.isNotNull())
1595f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian            removePlaceholderAt(placeholder);
1608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    } else {
1618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Make sure the document is set up to receive text
16281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        startPosition = positionInsideTextNode(startPosition);
16381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        ASSERT(startPosition.anchorType() == Position::PositionIsOffsetInAnchor);
16481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        ASSERT(startPosition.containerNode());
16581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        ASSERT(startPosition.containerNode()->isTextNode());
1665f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian        if (placeholder.isNotNull())
1675f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian            removePlaceholderAt(placeholder);
16881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        Text* textNode = static_cast<Text*>(startPosition.containerNode());
16981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        const unsigned offset = startPosition.offsetInContainerNode();
1708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertTextIntoNode(textNode, offset, text);
1722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        endPosition = Position(textNode, offset + text.length(), Position::PositionIsOffsetInAnchor);
1732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (whitespaceRebalance == RebalanceLeadingAndTrailingWhitespaces) {
1752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // The insertion may require adjusting adjacent whitespace, if it is present.
1762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            rebalanceWhitespaceAt(endPosition);
1772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // Rebalancing on both sides isn't necessary if we've inserted only spaces.
1782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            if (!shouldRebalanceLeadingWhitespaceFor(text))
1792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                rebalanceWhitespaceAt(startPosition);
1802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        } else {
1812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            ASSERT(whitespaceRebalance == RebalanceAllWhitespaces);
1822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            if (canRebalance(startPosition) && canRebalance(endPosition))
18381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch                rebalanceWhitespaceOnTextSubstring(textNode, startPosition.offsetInContainerNode(), endPosition.offsetInContainerNode());
1842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        }
1858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
1868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // We could have inserted a part of composed character sequence,
1888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // so we are basically treating ending selection as a range to avoid validation.
1898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // <http://bugs.webkit.org/show_bug.cgi?id=15781>
1908f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    VisibleSelection forcedEndingSelection;
1918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    forcedEndingSelection.setWithoutValidation(startPosition, endPosition);
1928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    setEndingSelection(forcedEndingSelection);
1938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Handle the case where there is a typing style.
1954576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang    if (RefPtr<EditingStyle> typingStyle = document()->frame()->selection()->typingStyle()) {
1964576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        typingStyle->prepareToApplyAt(endPosition, EditingStyle::PreserveWritingDirection);
1974576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        if (!typingStyle->isEmpty())
198f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch            applyStyle(typingStyle.get());
199635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
2008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!selectInsertedText)
2028f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        setEndingSelection(VisibleSelection(endingSelection().end(), endingSelection().affinity()));
2038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectPosition InsertTextCommand::insertTab(const Position& pos)
2068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
2078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    Position insertPos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent();
2088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
20981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    Node* node = insertPos.deprecatedNode();
2105f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    unsigned int offset = insertPos.deprecatedEditingOffset();
2118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // keep tabs coalesced in tab span
2138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (isTabSpanTextNode(node)) {
2148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertTextIntoNode(static_cast<Text *>(node), offset, "\t");
2152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return Position(node, offset + 1, Position::PositionIsOffsetInAnchor);
2168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // create new tab span
2198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    RefPtr<Element> spanNode = createTabSpanElement(document());
2208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // place it
2228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!node->isTextNode()) {
2238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        insertNodeAt(spanNode.get(), insertPos);
2248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    } else {
2258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        Text *textNode = static_cast<Text *>(node);
2268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (offset >= textNode->length()) {
2278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            insertNodeAfter(spanNode.get(), textNode);
2288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        } else {
2298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // split node to make room for the span
2308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // NOTE: splitTextNode uses textNode for the
2318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // second node in the split, so we need to
2328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // insert the span before it.
2338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            if (offset > 0)
2348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                splitTextNode(textNode, offset);
235635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project            insertNodeBefore(spanNode, textNode);
2368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
2378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // return the position following the new tab
2402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return lastPositionInNode(spanNode.get());
2418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectbool InsertTextCommand::isInsertTextCommand() const
2448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
2458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return true;
2468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
249