1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "config.h" 6 #include "core/editing/InputMethodController.h" 7 8 #include "core/dom/Element.h" 9 #include "core/dom/Range.h" 10 #include "core/frame/LocalFrame.h" 11 #include "core/html/HTMLDocument.h" 12 #include "core/html/HTMLInputElement.h" 13 #include "core/testing/DummyPageHolder.h" 14 #include <gtest/gtest.h> 15 16 using namespace blink; 17 18 namespace { 19 20 class InputMethodControllerTest : public ::testing::Test { 21 protected: 22 InputMethodController& controller() { return frame().inputMethodController(); } 23 HTMLDocument& document() const { return *m_document; } 24 LocalFrame& frame() const { return m_dummyPageHolder->frame(); } 25 Element* insertHTMLElement(const char* elementCode, const char* elementId); 26 27 private: 28 virtual void SetUp() OVERRIDE; 29 30 OwnPtr<DummyPageHolder> m_dummyPageHolder; 31 HTMLDocument* m_document; 32 }; 33 34 void InputMethodControllerTest::SetUp() 35 { 36 m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600)); 37 m_document = toHTMLDocument(&m_dummyPageHolder->document()); 38 ASSERT(m_document); 39 } 40 41 Element* InputMethodControllerTest::insertHTMLElement( 42 const char* elementCode, const char* elementId) 43 { 44 document().write(elementCode); 45 document().updateLayout(); 46 Element* element = document().getElementById(elementId); 47 element->focus(); 48 return element; 49 } 50 51 TEST_F(InputMethodControllerTest, BackspaceFromEndOfInput) 52 { 53 HTMLInputElement* input = toHTMLInputElement( 54 insertHTMLElement("<input id='sample'>", "sample")); 55 56 input->setValue("fooX"); 57 controller().setEditableSelectionOffsets(PlainTextRange(4, 4)); 58 EXPECT_STREQ("fooX", input->value().utf8().data()); 59 controller().extendSelectionAndDelete(0, 0); 60 EXPECT_STREQ("fooX", input->value().utf8().data()); 61 62 input->setValue("fooX"); 63 controller().setEditableSelectionOffsets(PlainTextRange(4, 4)); 64 EXPECT_STREQ("fooX", input->value().utf8().data()); 65 controller().extendSelectionAndDelete(1, 0); 66 EXPECT_STREQ("foo", input->value().utf8().data()); 67 68 input->setValue(String::fromUTF8("foo\xE2\x98\x85")); // U+2605 == "black star" 69 controller().setEditableSelectionOffsets(PlainTextRange(4, 4)); 70 EXPECT_STREQ("foo\xE2\x98\x85", input->value().utf8().data()); 71 controller().extendSelectionAndDelete(1, 0); 72 EXPECT_STREQ("foo", input->value().utf8().data()); 73 74 input->setValue(String::fromUTF8("foo\xF0\x9F\x8F\x86")); // U+1F3C6 == "trophy" 75 controller().setEditableSelectionOffsets(PlainTextRange(4, 4)); 76 EXPECT_STREQ("foo\xF0\x9F\x8F\x86", input->value().utf8().data()); 77 controller().extendSelectionAndDelete(1, 0); 78 EXPECT_STREQ("foo", input->value().utf8().data()); 79 80 input->setValue(String::fromUTF8("foo\xE0\xB8\x81\xE0\xB9\x89")); // composed U+0E01 "ka kai" + U+0E49 "mai tho" 81 controller().setEditableSelectionOffsets(PlainTextRange(4, 4)); 82 EXPECT_STREQ("foo\xE0\xB8\x81\xE0\xB9\x89", input->value().utf8().data()); 83 controller().extendSelectionAndDelete(1, 0); 84 EXPECT_STREQ("foo", input->value().utf8().data()); 85 86 input->setValue("fooX"); 87 controller().setEditableSelectionOffsets(PlainTextRange(4, 4)); 88 EXPECT_STREQ("fooX", input->value().utf8().data()); 89 controller().extendSelectionAndDelete(0, 1); 90 EXPECT_STREQ("fooX", input->value().utf8().data()); 91 } 92 93 TEST_F(InputMethodControllerTest, SetCompositionFromExistingText) 94 { 95 Element* div = insertHTMLElement( 96 "<div id='sample' contenteditable='true'>hello world</div>", "sample"); 97 98 Vector<CompositionUnderline> underlines; 99 underlines.append(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0)); 100 controller().setCompositionFromExistingText(underlines, 0, 5); 101 102 RefPtrWillBeRawPtr<Range> range = controller().compositionRange(); 103 EXPECT_EQ(0, range->startOffset()); 104 EXPECT_EQ(5, range->endOffset()); 105 106 PlainTextRange plainTextRange(PlainTextRange::create(*div, *range.get())); 107 EXPECT_EQ(0u, plainTextRange.start()); 108 EXPECT_EQ(5u, plainTextRange.end()); 109 } 110 111 TEST_F(InputMethodControllerTest, SetCompositionFromExistingTextWithCollapsedWhiteSpace) 112 { 113 // Creates a div with one leading new line char. The new line char is hidden 114 // from the user and IME, but is visible to InputMethodController. 115 Element* div = insertHTMLElement( 116 "<div id='sample' contenteditable='true'>\nhello world</div>", "sample"); 117 118 Vector<CompositionUnderline> underlines; 119 underlines.append(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0)); 120 controller().setCompositionFromExistingText(underlines, 0, 5); 121 122 RefPtrWillBeRawPtr<Range> range = controller().compositionRange(); 123 EXPECT_EQ(1, range->startOffset()); 124 EXPECT_EQ(6, range->endOffset()); 125 126 PlainTextRange plainTextRange(PlainTextRange::create(*div, *range.get())); 127 EXPECT_EQ(0u, plainTextRange.start()); 128 EXPECT_EQ(5u, plainTextRange.end()); 129 } 130 131 TEST_F(InputMethodControllerTest, SetCompositionFromExistingTextWithInvalidOffsets) 132 { 133 insertHTMLElement("<div id='sample' contenteditable='true'>test</div>", "sample"); 134 135 Vector<CompositionUnderline> underlines; 136 underlines.append(CompositionUnderline(7, 8, Color(255, 0, 0), false, 0)); 137 controller().setCompositionFromExistingText(underlines, 7, 8); 138 139 EXPECT_FALSE(controller().compositionRange()); 140 } 141 142 } // namespace 143