2011-01-19 MORITA Hajime <morrita@google.com>
authormorrita@google.com <morrita@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 24 Jan 2011 06:53:34 +0000 (06:53 +0000)
committermorrita@google.com <morrita@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 24 Jan 2011 06:53:34 +0000 (06:53 +0000)
        Reviewed by Ryosuke Niwa.

        Space and tab characters "sent" by an input method give totally different results than typing them directly.
        https://bugs.webkit.org/show_bug.cgi?id=5241

        Added a test that exercises continous whitespace characters and tabs inserted by IME.

        * editing/inserting/insert-composition-whitespace-expected.txt: Added.
        * editing/inserting/insert-composition-whitespace.html: Added.
2011-01-19  MORITA Hajime  <morrita@google.com>

        Reviewed by Ryosuke Niwa.

        Space and tab characters "sent" by an input method give totally different results than typing them directly
        https://bugs.webkit.org/show_bug.cgi?id=5241

        * Introduced TextEvent::InputTypeComposition and TypingCommand::TextCompositionType to
          distinguish text input which is originated by composition.
        * Generalized rebalanceWhitespaceAt() to rebalanceWhitespaceOnTextSubstring() to rebalancing
          range of string on text node, instead of surrounding part of that.

        Test: editing/inserting/insert-composition-whitespace.html

        * dom/TextEvent.h:
        (WebCore::TextEvent::isComposition):
        * dom/TextEventInputType.h: Added TextEventInputComposition as a member of TextEvent::InputType
        * editing/CompositeEditCommand.cpp:
        (WebCore::containsOnlyWhitespace):
        (WebCore::CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor):
        (WebCore::CompositeEditCommand::canRebalance):
        (WebCore::CompositeEditCommand::rebalanceWhitespaceAt):
        (WebCore::CompositeEditCommand::rebalanceWhitespaceOnTextSubstring): Added: A generalized version of rebalanceWhitespaceAt(), which takes a range inside Text string.
        * editing/CompositeEditCommand.h:
        * editing/Editor.cpp:
        (WebCore::Editor::insertTextForConfirmedComposition): Added.
        (WebCore::Editor::insertTextWithoutSendingTextEvent):
        (WebCore::Editor::confirmComposition): Now uses insertTextForConfirmedComposition().
        (WebCore::Editor::setComposition):
        * editing/Editor.h:
        * editing/InsertTextCommand.cpp:
        (WebCore::InsertTextCommand::input):
        * editing/InsertTextCommand.h:
        * editing/TypingCommand.cpp:
        (WebCore::TypingCommand::TypingCommand):
        (WebCore::TypingCommand::insertText):
        (WebCore::TypingCommand::insertTextRunWithoutNewlines):
        * editing/TypingCommand.h: Added TypingCommand::m_compositionType and TypingCommand::TextCompositionType
        (WebCore::TypingCommand::setCompositionType): Added.
        (WebCore::TypingCommand::create):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@76482 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/inserting/insert-composition-whitespace-expected.txt [new file with mode: 0644]
LayoutTests/editing/inserting/insert-composition-whitespace.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/TextEvent.h
Source/WebCore/dom/TextEventInputType.h
Source/WebCore/editing/CompositeEditCommand.cpp
Source/WebCore/editing/CompositeEditCommand.h
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/Editor.h
Source/WebCore/editing/InsertTextCommand.cpp
Source/WebCore/editing/InsertTextCommand.h
Source/WebCore/editing/TypingCommand.cpp
Source/WebCore/editing/TypingCommand.h

index d1ec90f..951c77f 100644 (file)
@@ -1,3 +1,15 @@
+2011-01-19  MORITA Hajime  <morrita@google.com>
+
+        Reviewed by Ryosuke Niwa.
+
+        Space and tab characters "sent" by an input method give totally different results than typing them directly.
+        https://bugs.webkit.org/show_bug.cgi?id=5241
+        
+        Added a test that exercises continous whitespace characters and tabs inserted by IME.
+
+        * editing/inserting/insert-composition-whitespace-expected.txt: Added.
+        * editing/inserting/insert-composition-whitespace.html: Added.
+
 2011-01-23  Yuzo Fujishima  <yuzo@google.com>
 
         Unreviewed Chromium test expectation change.
diff --git a/LayoutTests/editing/inserting/insert-composition-whitespace-expected.txt b/LayoutTests/editing/inserting/insert-composition-whitespace-expected.txt
new file mode 100644 (file)
index 0000000..ade0725
--- /dev/null
@@ -0,0 +1,94 @@
+PASS compositingText is 'AB'
+PASS confirmedText is 'AB'
+PASS compositingText is 'A B'
+PASS confirmedText is 'A B'
+PASS compositingText is 'A  B'
+PASS confirmedText is 'A  B'
+PASS compositingText is 'A   B'
+PASS confirmedText is 'A   B'
+PASS compositingText is 'A    B'
+PASS confirmedText is 'A    B'
+PASS compositingText is ' AB'
+PASS confirmedText is ' AB'
+PASS compositingText is '  AB'
+PASS confirmedText is '  AB'
+PASS compositingText is '   AB'
+PASS confirmedText is '   AB'
+PASS compositingText is '    AB'
+PASS confirmedText is '    AB'
+PASS compositingText is '     AB'
+PASS confirmedText is '     AB'
+PASS compositingText is '      AB'
+PASS confirmedText is '      AB'
+PASS compositingText is '       AB'
+PASS confirmedText is '       AB'
+PASS compositingText is 'AB  '
+PASS confirmedText is 'AB  '
+PASS compositingText is 'AB   '
+PASS confirmedText is 'AB   '
+PASS compositingText is 'AB    '
+PASS confirmedText is 'AB    '
+PASS compositingText is 'AB     '
+PASS confirmedText is 'AB     '
+PASS compositingText is 'AB      '
+PASS confirmedText is 'AB      '
+PASS compositingText is 'AB       '
+PASS confirmedText is 'AB       '
+PASS compositingText is '  A  B  '
+PASS confirmedText is '  A  B  '
+PASS compositingText is '  A  B  '
+PASS confirmedText is '  A  B  '
+PASS compositingText is ' '
+PASS confirmedText is ' '
+PASS compositingText is '  '
+PASS confirmedText is '  '
+PASS compositingText is '   '
+PASS confirmedText is '   '
+PASS compositingText is 'AB'
+PASS confirmedText is 'AB'
+PASS compositingText is 'A B'
+PASS confirmedText is 'A B'
+PASS compositingText is 'A  B'
+PASS confirmedText is 'A  B'
+PASS compositingText is 'A   B'
+PASS confirmedText is 'A   B'
+PASS compositingText is 'A    B'
+PASS confirmedText is 'A    B'
+PASS compositingText is 'AB  '
+PASS confirmedText is 'AB  '
+PASS compositingText is 'AB   '
+PASS confirmedText is 'AB   '
+PASS compositingText is 'AB    '
+PASS confirmedText is 'AB    '
+PASS compositingText is 'AB     '
+PASS confirmedText is 'AB     '
+PASS compositingText is 'AB      '
+PASS confirmedText is 'AB      '
+PASS compositingText is 'AB       '
+PASS confirmedText is 'AB       '
+PASS compositingText is '  AB'
+PASS confirmedText is '  AB'
+PASS compositingText is '   AB'
+PASS confirmedText is '   AB'
+PASS compositingText is '    AB'
+PASS confirmedText is '    AB'
+PASS compositingText is '     AB'
+PASS confirmedText is '     AB'
+PASS compositingText is '      AB'
+PASS confirmedText is '      AB'
+PASS compositingText is '       AB'
+PASS confirmedText is '       AB'
+PASS compositingText is '  A  B  '
+PASS confirmedText is '  A  B  '
+PASS compositingText is '              A               B               '
+PASS confirmedText is '                A               B               '
+PASS compositingText is ' '
+PASS confirmedText is ' '
+PASS compositingText is '  '
+PASS confirmedText is '  '
+PASS compositingText is '   '
+PASS confirmedText is '   '
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/inserting/insert-composition-whitespace.html b/LayoutTests/editing/inserting/insert-composition-whitespace.html
new file mode 100644 (file)
index 0000000..81215dd
--- /dev/null
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+var sel = document.getSelection();
+var root = document.createElement("root");
+document.body.appendChild(root);
+
+
+function createEditable(tagName, text) {
+    var node = document.createElement(tagName);
+    node.contentEditable = true;
+    node.innerHTML = text;
+    return node;
+}
+
+function test(tagName, compositionText, expected) {
+    var node = createEditable(tagName, "X");
+    root.appendChild(node);
+
+    var textNode = node.firstChild;
+    sel.setBaseAndExtent(textNode, 0, textNode, textNode.data.length);
+    document.execCommand("Delete", false);
+
+    textInputController.setMarkedText(compositionText, 0, compositionText.length);
+    compositingText = node.innerText;
+    textInputController.unmarkText();
+    confirmedText = node.innerText;
+
+    shouldBe("compositingText", "'" + expected + "'");
+    shouldBe("confirmedText", "'" + expected + "'");
+}
+
+test("div", "AB", "AB");
+test("div", "A B", "A B");
+test("div", "A  B", "A \xA0B");
+test("div", "A   B", "A \xA0 B");
+test("div", "A    B", "A \xA0 \xA0B"); 
+
+test("div", " AB", "\xA0AB");
+test("div", "  AB", "\xA0\xA0AB");
+test("div", "   AB", "\xA0\xA0 AB");
+test("div", "    AB", "\xA0\xA0 \xA0AB");
+test("div", "     AB", "\xA0\xA0 \xA0 AB");
+test("div", "      AB", "\xA0\xA0 \xA0 \xA0AB");
+test("div", "       AB", "\xA0\xA0 \xA0 \xA0 AB");
+test("div", "AB  ", "AB \xA0");
+test("div", "AB   ", "AB \xA0\xA0");
+test("div", "AB    ", "AB \xA0 \xA0");
+test("div", "AB     ", "AB \xA0 \xA0\xA0");
+test("div", "AB      ", "AB \xA0 \xA0 \xA0");
+test("div", "AB       ", "AB \xA0 \xA0 \xA0\xA0");
+
+test("div", "  A  B  ", "\xA0\xA0A \xA0B \xA0");
+test("div", "\t\tA\t\tB\t\t", "\xA0\xA0A \xA0B \xA0");
+
+test("div", " ", "\xA0");
+test("div", "  ", "\xA0\xA0");
+// This is wrong. This should insert interleaved nbsp and whitespace. See https://bugs.webkit.org/show_bug.cgi?id=52781.
+test("div", "   ", "\xA0\xA0\xA0");
+
+test("pre", "AB", "AB");
+test("pre", "A B", "A B");
+test("pre", "A  B", "A  B");
+test("pre", "A   B", "A   B");
+test("pre", "A    B", "A    B");
+test("pre", "AB  ", "AB  ");
+test("pre", "AB   ", "AB   ");
+test("pre", "AB    ", "AB    ");
+test("pre", "AB     ", "AB     ");
+test("pre", "AB      ", "AB      ");
+test("pre", "AB       ", "AB       ");
+test("pre", "  AB", "  AB");
+test("pre", "   AB", "   AB");
+test("pre", "    AB", "    AB");
+test("pre", "     AB", "     AB");
+test("pre", "      AB", "      AB");
+test("pre", "       AB", "       AB");
+
+test("pre", "  A  B  ", "  A  B  ");
+test("pre", "\t\tA\t\tB\t\t", "\t\tA\t\tB\t\t");
+test("pre", " ", " ");
+test("pre", "  ", "  ");
+test("pre", "   ", "   ");
+
+root.style.display = "none";
+successfullyParsed = true;
+
+</script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 3f9e5ed..5a1bb1b 100644 (file)
@@ -1,3 +1,44 @@
+2011-01-19  MORITA Hajime  <morrita@google.com>
+
+        Reviewed by Ryosuke Niwa.
+
+        Space and tab characters "sent" by an input method give totally different results than typing them directly
+        https://bugs.webkit.org/show_bug.cgi?id=5241
+        
+        * Introduced TextEvent::InputTypeComposition and TypingCommand::TextCompositionType to 
+          distinguish text input which is originated by composition.
+        * Generalized rebalanceWhitespaceAt() to rebalanceWhitespaceOnTextSubstring() to rebalancing 
+          range of string on text node, instead of surrounding part of that.
+
+        Test: editing/inserting/insert-composition-whitespace.html
+
+        * dom/TextEvent.h:
+        (WebCore::TextEvent::isComposition):
+        * dom/TextEventInputType.h: Added TextEventInputComposition as a member of TextEvent::InputType
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::containsOnlyWhitespace):
+        (WebCore::CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor):
+        (WebCore::CompositeEditCommand::canRebalance):
+        (WebCore::CompositeEditCommand::rebalanceWhitespaceAt):
+        (WebCore::CompositeEditCommand::rebalanceWhitespaceOnTextSubstring): Added: A generalized version of rebalanceWhitespaceAt(), which takes a range inside Text string.
+        * editing/CompositeEditCommand.h:
+        * editing/Editor.cpp:
+        (WebCore::Editor::insertTextForConfirmedComposition): Added.
+        (WebCore::Editor::insertTextWithoutSendingTextEvent):
+        (WebCore::Editor::confirmComposition): Now uses insertTextForConfirmedComposition().
+        (WebCore::Editor::setComposition):
+        * editing/Editor.h:
+        * editing/InsertTextCommand.cpp:
+        (WebCore::InsertTextCommand::input):
+        * editing/InsertTextCommand.h:
+        * editing/TypingCommand.cpp:
+        (WebCore::TypingCommand::TypingCommand):
+        (WebCore::TypingCommand::insertText):
+        (WebCore::TypingCommand::insertTextRunWithoutNewlines):
+        * editing/TypingCommand.h: Added TypingCommand::m_compositionType and TypingCommand::TextCompositionType
+        (WebCore::TypingCommand::setCompositionType): Added.
+        (WebCore::TypingCommand::create):
+
 2011-01-23  Mark Rowe  <mrowe@apple.com>
 
         Follow-up to r76477.
index d770d38..26c8b93 100644 (file)
@@ -51,6 +51,7 @@ namespace WebCore {
         virtual bool isTextEvent() const;
 
         bool isLineBreak() const { return m_inputType == TextEventInputLineBreak; }
+        bool isComposition() const { return m_inputType == TextEventInputComposition; }
         bool isBackTab() const { return m_inputType == TextEventInputBackTab; }
         bool isPaste() const { return m_inputType == TextEventInputPaste; }
         bool isDrop() const { return m_inputType == TextEventInputDrop; }
index 2522ec4..f5a05eb 100644 (file)
@@ -31,6 +31,7 @@ namespace WebCore {
 enum TextEventInputType {
     TextEventInputKeyboard, // any newline characters in the text are line breaks only, not paragraph separators.
     TextEventInputLineBreak, // any tab characters in the text are backtabs.
+    TextEventInputComposition,
     TextEventInputBackTab,
     TextEventInputPaste,
     TextEventInputDrop,
index 552ed79..39280d4 100644 (file)
@@ -395,52 +395,84 @@ static inline bool isWhitespace(UChar c)
     return c == noBreakSpace || c == ' ' || c == '\n' || c == '\t';
 }
 
-// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
-void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
+static inline bool containsOnlyWhitespace(const String& text)
+{
+    for (unsigned i = 0; i < text.length(); ++i) {
+        if (!isWhitespace(text.characters()[i]))
+            return false;
+    }
+    
+    return true;
+}
+
+bool CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor(const String& text) const
+{
+    return containsOnlyWhitespace(text);
+}
+
+bool CompositeEditCommand::canRebalance(const Position& position) const
 {
     Node* node = position.containerNode();
     if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode())
-        return;
-    Text* textNode = static_cast<Text*>(node);
+        return false;
 
+    Text* textNode = static_cast<Text*>(node);
     if (textNode->length() == 0)
-        return;
+        return false;
+
     RenderObject* renderer = textNode->renderer();
     if (renderer && !renderer->style()->collapseWhiteSpace())
-        return;
+        return false;
 
-    String text = textNode->data();
-    ASSERT(!text.isEmpty());
+    return true;
+}
+
+// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
+void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
+{
+    Node* node = position.containerNode();
+    if (!canRebalance(position))
+        return;
 
+    // If the rebalance is for the single offset, and neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
     int offset = position.deprecatedEditingOffset();
-    // If neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
+    String text = static_cast<Text*>(node)->data();
     if (!isWhitespace(text[offset])) {
         offset--;
         if (offset < 0 || !isWhitespace(text[offset]))
             return;
     }
-    
+
+    rebalanceWhitespaceOnTextSubstring(static_cast<Text*>(node), position.offsetInContainerNode(), position.offsetInContainerNode());
+}
+
+void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(RefPtr<Text> textNode, int startOffset, int endOffset)
+{
+    String text = textNode->data();
+    ASSERT(!text.isEmpty());
+
     // Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
-    int upstream = offset;
+    int upstream = startOffset;
     while (upstream > 0 && isWhitespace(text[upstream - 1]))
         upstream--;
     
-    int downstream = offset;
-    while ((unsigned)downstream + 1 < text.length() && isWhitespace(text[downstream + 1]))
+    int downstream = endOffset;
+    while ((unsigned)downstream < text.length() && isWhitespace(text[downstream]))
         downstream++;
     
-    int length = downstream - upstream + 1;
-    ASSERT(length > 0);
-    
-    VisiblePosition visibleUpstreamPos(Position(position.containerNode(), upstream, Position::PositionIsOffsetInAnchor));
-    VisiblePosition visibleDownstreamPos(Position(position.containerNode(), downstream + 1, Position::PositionIsOffsetInAnchor));
+    int length = downstream - upstream;
+    if (!length)
+        return;
+
+    VisiblePosition visibleUpstreamPos(Position(textNode, upstream, Position::PositionIsOffsetInAnchor));
+    VisiblePosition visibleDownstreamPos(Position(textNode, downstream, Position::PositionIsOffsetInAnchor));
     
     String string = text.substring(upstream, length);
     String rebalancedString = stringWithRebalancedWhitespace(string,
     // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because
     // this function doesn't get all surrounding whitespace, just the whitespace in the current text node.
                                                              isStartOfParagraph(visibleUpstreamPos) || upstream == 0, 
-                                                             isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length() - 1);
+                                                             isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length());
     
     if (string != rebalancedString)
         replaceTextInNode(textNode, upstream, length, rebalancedString);
index 6db4eb1..9066b65 100644 (file)
@@ -71,7 +71,10 @@ protected:
     void mergeIdenticalElements(PassRefPtr<Element>, PassRefPtr<Element>);
     void rebalanceWhitespace();
     void rebalanceWhitespaceAt(const Position&);
+    void rebalanceWhitespaceOnTextSubstring(RefPtr<Text>, int startOffset, int endOffset);
     void prepareWhitespaceAtPositionForSplit(Position&);
+    bool canRebalance(const Position&) const;
+    bool shouldRebalanceLeadingWhitespaceFor(const String&) const;
     void removeCSSProperty(PassRefPtr<StyledElement>, CSSPropertyID);
     void removeNodeAttribute(PassRefPtr<Element>, const QualifiedName& attribute);
     void removeChildrenInRange(PassRefPtr<Node>, unsigned from, unsigned to);
index 23b41ce..3acec27 100644 (file)
@@ -1173,7 +1173,12 @@ bool Editor::insertText(const String& text, Event* triggeringEvent)
     return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
 }
 
-bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent)
+bool Editor::insertTextForConfirmedComposition(const String& text)
+{
+    return m_frame->eventHandler()->handleTextInputEvent(text, 0, TextEventInputComposition);
+}
+
+bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent)
 {
     if (text.isEmpty())
         return false;
@@ -1195,7 +1200,8 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
             RefPtr<Document> document = selectionStart->document();
             
             // Insert the text
-            TypingCommand::insertText(document.get(), text, selection, selectInsertedText);
+            TypingCommand::insertText(document.get(), text, selection, selectInsertedText, 
+                                      triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);
 
             // Reveal the current selection 
             if (Frame* editedFrame = document->frame())
@@ -1618,7 +1624,7 @@ void Editor::confirmComposition(const String& text, bool preserveSelection)
     m_compositionNode = 0;
     m_customCompositionUnderlines.clear();
 
-    insertText(text, 0);
+    insertTextForConfirmedComposition(text);
 
     if (preserveSelection) {
         m_frame->selection()->setSelection(oldSelection, false, false);
@@ -1687,7 +1693,7 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin
     m_customCompositionUnderlines.clear();
 
     if (!text.isEmpty()) {
-        TypingCommand::insertText(m_frame->document(), text, true, true);
+        TypingCommand::insertText(m_frame->document(), text, true, TypingCommand::TextCompositionUpdate);
 
         // Find out what node has the composition now.
         Position base = m_frame->selection()->base().downstream();
index 2e61ce6..1e6e387 100644 (file)
@@ -198,7 +198,8 @@ public:
     static bool commandIsSupportedFromMenuOrKeyBinding(const String& commandName); // Works without a frame.
 
     bool insertText(const String&, Event* triggeringEvent);
-    bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, Event* triggeringEvent);
+    bool insertTextForConfirmedComposition(const String& text);
+    bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, TextEvent* triggeringEvent);
     bool insertLineBreak();
     bool insertParagraphSeparator();
     
index fc18e91..74a111a 100644 (file)
@@ -106,7 +106,7 @@ bool InsertTextCommand::performTrivialReplace(const String& text, bool selectIns
     return true;
 }
 
-void InsertTextCommand::input(const String& text, bool selectInsertedText)
+void InsertTextCommand::input(const String& text, bool selectInsertedText, RebalanceType whitespaceRebalance)
 {
     
     ASSERT(text.find('\n') == notFound);
@@ -172,11 +172,17 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText)
         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);
+        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.deprecatedEditingOffset(), endPosition.deprecatedEditingOffset());
+        }
     }
 
     // We could have inserted a part of composed character sequence,
index 77ae016..672e576 100644 (file)
@@ -32,14 +32,20 @@ namespace WebCore {
 
 class InsertTextCommand : public CompositeEditCommand {
 public:
+    enum RebalanceType {
+        RebalanceLeadingAndTrailingWhitespaces,
+        RebalanceAllWhitespaces
+    };
+
     static PassRefPtr<InsertTextCommand> create(Document* document)
     {
         return adoptRef(new InsertTextCommand(document));
     }
 
-    void input(const String& text, bool selectInsertedText = false);
+    void input(const String& text, bool selectInsertedText = false, RebalanceType = RebalanceLeadingAndTrailingWhitespaces);
 
 private:
+
     InsertTextCommand(Document*);
 
     void deleteCharacter();
index d54b388..21d4314 100644 (file)
@@ -47,7 +47,8 @@ namespace WebCore {
 
 using namespace HTMLNames;
 
-TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity, bool killRing)
+TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity, TextCompositionType compositionType,
+                             bool killRing)
     : CompositeEditCommand(document), 
       m_commandType(commandType), 
       m_textToInsert(textToInsert), 
@@ -55,6 +56,7 @@ TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, con
       m_selectInsertedText(selectInsertedText),
       m_smartDelete(false),
       m_granularity(granularity),
+      m_compositionType(compositionType),
       m_killRing(killRing),
       m_openedByBackwardDelete(false)
 {
@@ -133,18 +135,18 @@ void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand
 }
     
 
-void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, bool insertedTextIsComposition)
+void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, TextCompositionType composition)
 {
     ASSERT(document);
     
     Frame* frame = document->frame();
     ASSERT(frame);
 
-    insertText(document, text, frame->selection()->selection(), selectInsertedText, insertedTextIsComposition);
+    insertText(document, text, frame->selection()->selection(), selectInsertedText, composition);
 }
 
 // FIXME: We shouldn't need to take selectionForInsertion. It should be identical to SelectionController's current selection.
-void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, bool selectInsertedText, bool insertedTextIsComposition)
+void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, bool selectInsertedText, TextCompositionType compositionType)
 {
 #if REMOVE_MARKERS_UPON_EDITING
     if (!text.isEmpty())
@@ -161,7 +163,7 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis
     String newText = text;
     Node* startNode = selectionForInsertion.start().node();
     
-    if (startNode && startNode->rootEditableElement() && !insertedTextIsComposition) {        
+    if (startNode && startNode->rootEditableElement() && compositionType != TextCompositionUpdate) {
         // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
         ExceptionCode ec = 0;
         RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
@@ -182,11 +184,13 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis
             lastTypingCommand->setStartingSelection(selectionForInsertion);
             lastTypingCommand->setEndingSelection(selectionForInsertion);
         }
+        
+        lastTypingCommand->setCompositionType(compositionType);
         lastTypingCommand->insertText(newText, selectInsertedText);
         return;
     }
 
-    RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText);
+    RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText, compositionType);
     if (changeSelection)  {
         cmd->setStartingSelection(selectionForInsertion);
         cmd->setEndingSelection(selectionForInsertion);
@@ -386,7 +390,8 @@ void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool select
         command->setStartingSelection(endingSelection());
         command->setEndingSelection(endingSelection());
     }
-    command->input(text, selectInsertedText);
+    command->input(text, selectInsertedText, 
+                   m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
     typingAddedToOpenCommand(InsertText);
 }
 
index 284ebc0..b34bdc1 100644 (file)
@@ -42,11 +42,17 @@ public:
         InsertParagraphSeparatorInQuotedContent
     };
 
+    enum TextCompositionType {
+        TextCompositionNone,
+        TextCompositionUpdate,
+        TextCompositionConfirm
+    };
+
     static void deleteSelection(Document*, bool smartDelete = false);
     static void deleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity, bool killRing = false);
     static void forwardDeleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity, bool killRing = false);
-    static void insertText(Document*, const String&, bool selectInsertedText = false, bool insertedTextIsComposition = false);
-    static void insertText(Document*, const String&, const VisibleSelection&, bool selectInsertedText = false, bool insertedTextIsComposition = false);
+    static void insertText(Document*, const String&, bool selectInsertedText = false, TextCompositionType = TextCompositionNone);
+    static void insertText(Document*, const String&, const VisibleSelection&, bool selectInsertedText = false, TextCompositionType = TextCompositionNone);
     static void insertLineBreak(Document*);
     static void insertParagraphSeparator(Document*);
     static void insertParagraphSeparatorInQuotedContent(Document*);
@@ -64,14 +70,20 @@ public:
     void deleteKeyPressed(TextGranularity, bool killRing);
     void forwardDeleteKeyPressed(TextGranularity, bool killRing);
     void deleteSelection(bool smartDelete);
+    void setCompositionType(TextCompositionType type) { m_compositionType = type; }
 
 private:
     static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text = "", bool selectInsertedText = false, TextGranularity granularity = CharacterGranularity, bool killRing = false)
     {
-        return adoptRef(new TypingCommand(document, command, text, selectInsertedText, granularity, killRing));
+        return adoptRef(new TypingCommand(document, command, text, selectInsertedText, granularity, TextCompositionNone, killRing));
+    }
+
+    static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text, bool selectInsertedText, TextCompositionType compositionType)
+    {
+        return adoptRef(new TypingCommand(document, command, text, selectInsertedText, CharacterGranularity, compositionType, false));
     }
 
-    TypingCommand(Document*, ETypingCommand, const String& text, bool selectInsertedText, TextGranularity, bool killRing);
+    TypingCommand(Document*, ETypingCommand, const String& text, bool selectInsertedText, TextGranularity, TextCompositionType, bool killRing);
 
     bool smartDelete() const { return m_smartDelete; }
     void setSmartDelete(bool smartDelete) { m_smartDelete = smartDelete; }
@@ -94,6 +106,7 @@ private:
     bool m_selectInsertedText;
     bool m_smartDelete;
     TextGranularity m_granularity;
+    TextCompositionType m_compositionType;
     bool m_killRing;
     bool m_preservesTypingStyle;