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
16using namespace blink;
17
18namespace {
19
20class InputMethodControllerTest : public ::testing::Test {
21protected:
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
27private:
28    virtual void SetUp() OVERRIDE;
29
30    OwnPtr<DummyPageHolder> m_dummyPageHolder;
31    HTMLDocument* m_document;
32};
33
34void InputMethodControllerTest::SetUp()
35{
36    m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
37    m_document = toHTMLDocument(&m_dummyPageHolder->document());
38    ASSERT(m_document);
39}
40
41Element* 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
51TEST_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
93TEST_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
111TEST_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
131TEST_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