1 // Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "include/base/cef_callback.h"
6 #include "include/cef_pack_strings.h"
7 #include "include/views/cef_textfield.h"
8 #include "include/views/cef_textfield_delegate.h"
9 #include "include/wrapper/cef_closure_task.h"
10 #include "tests/ceftests/thread_helper.h"
11 #include "tests/ceftests/views/test_window_delegate.h"
12 #include "tests/gtest/include/gtest/gtest.h"
13
14 // See ui/events/keycodes/keyboard_codes.h
15 #define VKEY_UNKNOWN 0
16 #if defined(OS_WIN)
17 #define VKEY_A 'A'
18 #define VKEY_SPACE VK_SPACE
19 #define VKEY_RETURN VK_RETURN
20 #elif defined(OS_POSIX)
21 #define VKEY_A 0x41
22 #define VKEY_SPACE 0x20
23 #define VKEY_RETURN 0x0D
24 #else
25 #error "Unsupported platform"
26 #endif
27
28 #define TEXTFIELD_TEST(name) UI_THREAD_TEST(ViewsTextfieldTest, name)
29 #define TEXTFIELD_TEST_ASYNC(name) \
30 UI_THREAD_TEST_ASYNC(ViewsTextfieldTest, name)
31
32 namespace {
33
RunTextfieldContents(CefRefPtr<CefWindow> window)34 void RunTextfieldContents(CefRefPtr<CefWindow> window) {
35 CefRefPtr<CefTextfield> textfield = CefTextfield::CreateTextfield(nullptr);
36 EXPECT_TRUE(textfield.get());
37 EXPECT_TRUE(textfield->AsTextfield().get());
38
39 // Must be added to a parent window before retrieving the style to avoid
40 // a CHECK() in View::GetNativeTheme(). See https://crbug.com/1056756.
41 window->AddChildView(textfield);
42 window->Layout();
43
44 // Test defaults.
45 EXPECT_TRUE(textfield->GetText().empty());
46 EXPECT_FALSE(textfield->HasSelection());
47 EXPECT_EQ(CefRange(0, 0), textfield->GetSelectedRange());
48 EXPECT_EQ(0U, textfield->GetCursorPosition());
49
50 // Test set/get text.
51 const char kText[] = "My test message!";
52 textfield->SetText(kText);
53 EXPECT_STREQ(kText, textfield->GetText().ToString().c_str());
54
55 size_t cursor_pos = sizeof(kText) - 1;
56 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
57
58 // Test append text.
59 const char kAppendText[] = " And more.";
60 textfield->AppendText(kAppendText);
61 EXPECT_STREQ((std::string(kText) + kAppendText).c_str(),
62 textfield->GetText().ToString().c_str());
63 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
64
65 // Test select range.
66 EXPECT_FALSE(textfield->HasSelection());
67 EXPECT_EQ(
68 CefRange(static_cast<int>(cursor_pos), static_cast<int>(cursor_pos)),
69 textfield->GetSelectedRange());
70 textfield->SelectRange(CefRange(0, static_cast<int>(cursor_pos)));
71 EXPECT_TRUE(textfield->HasSelection());
72 EXPECT_EQ(CefRange(0, static_cast<int>(cursor_pos)),
73 textfield->GetSelectedRange());
74 EXPECT_STREQ(kText, textfield->GetSelectedText().ToString().c_str());
75 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
76
77 // Test insert or replace.
78 const char kReplaceText[] = "Other text.";
79 textfield->InsertOrReplaceText(kReplaceText);
80 EXPECT_STREQ((std::string(kReplaceText) + kAppendText).c_str(),
81 textfield->GetText().ToString().c_str());
82
83 cursor_pos = sizeof(kReplaceText) - 1;
84 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
85
86 // Test select all.
87 EXPECT_FALSE(textfield->HasSelection());
88 textfield->SelectAll(false);
89 EXPECT_TRUE(textfield->HasSelection());
90
91 cursor_pos = sizeof(kReplaceText) + sizeof(kAppendText) - 2;
92 EXPECT_EQ(CefRange(0, static_cast<int>(cursor_pos)),
93 textfield->GetSelectedRange());
94 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
95
96 // Test clear selection.
97 textfield->ClearSelection();
98 EXPECT_FALSE(textfield->HasSelection());
99 EXPECT_EQ(
100 CefRange(static_cast<int>(cursor_pos), static_cast<int>(cursor_pos)),
101 textfield->GetSelectedRange());
102 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
103
104 // Test selection with command.
105 EXPECT_TRUE(textfield->IsCommandEnabled(CEF_TFC_SELECT_ALL));
106 textfield->ExecuteCommand(CEF_TFC_SELECT_ALL);
107 EXPECT_TRUE(textfield->HasSelection());
108 EXPECT_EQ(CefRange(0, static_cast<int>(cursor_pos)),
109 textfield->GetSelectedRange());
110 EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
111
112 textfield->ClearEditHistory();
113 }
114
TextfieldContentsImpl(CefRefPtr<CefWaitableEvent> event)115 void TextfieldContentsImpl(CefRefPtr<CefWaitableEvent> event) {
116 auto config = std::make_unique<TestWindowDelegate::Config>();
117 config->on_window_created = base::BindOnce(RunTextfieldContents);
118 TestWindowDelegate::RunTest(event, std::move(config));
119 }
120
RunTextfieldStyle(CefRefPtr<CefWindow> window)121 void RunTextfieldStyle(CefRefPtr<CefWindow> window) {
122 CefRefPtr<CefTextfield> textfield = CefTextfield::CreateTextfield(nullptr);
123 EXPECT_TRUE(textfield.get());
124
125 // Must be added to a parent window before retrieving the style to avoid
126 // a CHECK() in View::GetNativeTheme(). See https://crbug.com/1056756.
127 window->AddChildView(textfield);
128 window->Layout();
129
130 // Test defaults.
131 EXPECT_FALSE(textfield->IsPasswordInput());
132 EXPECT_FALSE(textfield->IsReadOnly());
133
134 // Test password input.
135 textfield->SetPasswordInput(true);
136 EXPECT_TRUE(textfield->IsPasswordInput());
137 textfield->SetPasswordInput(false);
138 EXPECT_FALSE(textfield->IsPasswordInput());
139
140 // Test read only.
141 textfield->SetReadOnly(true);
142 EXPECT_TRUE(textfield->IsReadOnly());
143 textfield->SetReadOnly(false);
144 EXPECT_FALSE(textfield->IsReadOnly());
145
146 // Test colors.
147 const cef_color_t color = CefColorSetARGB(255, 255, 0, 255);
148
149 EXPECT_NE(color, textfield->GetTextColor());
150 textfield->SetTextColor(color);
151 EXPECT_EQ(color, textfield->GetTextColor());
152
153 EXPECT_NE(color, textfield->GetSelectionTextColor());
154 textfield->SetSelectionTextColor(color);
155 EXPECT_EQ(color, textfield->GetSelectionTextColor());
156
157 EXPECT_NE(color, textfield->GetSelectionBackgroundColor());
158 textfield->SetSelectionBackgroundColor(color);
159 EXPECT_EQ(color, textfield->GetSelectionBackgroundColor());
160
161 textfield->SetPlaceholderTextColor(color);
162
163 // Test fonts.
164 textfield->SetFontList("Arial, 14px");
165
166 // Test format ranges.
167 const char kText[] = "test text";
168 textfield->SetText(kText);
169 textfield->ApplyTextColor(color, CefRange(0, 5));
170 textfield->ApplyTextStyle(CEF_TEXT_STYLE_BOLD, true, CefRange(0, 5));
171
172 // Test placeholder text.
173 textfield->SetPlaceholderText(kText);
174 EXPECT_STREQ(kText, textfield->GetPlaceholderText().ToString().c_str());
175
176 textfield->SetAccessibleName("MyTextfield");
177 }
178
TextfieldStyleImpl(CefRefPtr<CefWaitableEvent> event)179 void TextfieldStyleImpl(CefRefPtr<CefWaitableEvent> event) {
180 auto config = std::make_unique<TestWindowDelegate::Config>();
181 config->on_window_created = base::BindOnce(RunTextfieldStyle);
182 TestWindowDelegate::RunTest(event, std::move(config));
183 }
184
185 } // namespace
186
187 // Test Textfield getters/setters.
188 TEXTFIELD_TEST_ASYNC(TextfieldContents)
189 TEXTFIELD_TEST_ASYNC(TextfieldStyle)
190
191 namespace {
192
193 const int kTextfieldID = 1;
194
195 // Contents need to be supported by the TranslateKey function.
196 const char kTestInputMessage[] = "Test Message";
197
TranslateKey(int c,int * keycode,uint32 * modifiers)198 void TranslateKey(int c, int* keycode, uint32* modifiers) {
199 *keycode = VKEY_UNKNOWN;
200 *modifiers = 0;
201
202 if (c >= 'a' && c <= 'z') {
203 *keycode = VKEY_A + (c - 'a');
204 } else if (c >= 'A' && c <= 'Z') {
205 *keycode = VKEY_A + (c - 'A');
206 *modifiers = EVENTFLAG_SHIFT_DOWN;
207 } else if (c == ' ') {
208 *keycode = VKEY_SPACE;
209 }
210 }
211
212 class TestTextfieldDelegate : public CefTextfieldDelegate {
213 public:
TestTextfieldDelegate()214 TestTextfieldDelegate() {}
215
OnKeyEvent(CefRefPtr<CefTextfield> textfield,const CefKeyEvent & event)216 bool OnKeyEvent(CefRefPtr<CefTextfield> textfield,
217 const CefKeyEvent& event) override {
218 EXPECT_TRUE(textfield.get());
219 EXPECT_EQ(textfield->GetID(), kTextfieldID);
220
221 if (event.type == KEYEVENT_RAWKEYDOWN &&
222 event.windows_key_code == VKEY_RETURN) {
223 // Got the whole string. Finish the test asynchronously.
224 CefPostTask(TID_UI, base::BindOnce(&TestTextfieldDelegate::FinishTest,
225 this, textfield));
226 return true;
227 }
228
229 if (event.type == KEYEVENT_CHAR) {
230 int keycode;
231 uint32 modifiers;
232 TranslateKey(kTestInputMessage[index_++], &keycode, &modifiers);
233
234 EXPECT_EQ(keycode, event.windows_key_code);
235 EXPECT_EQ(modifiers, event.modifiers);
236 }
237
238 return false;
239 }
240
OnAfterUserAction(CefRefPtr<CefTextfield> textfield)241 void OnAfterUserAction(CefRefPtr<CefTextfield> textfield) override {
242 after_user_action_ct_++;
243 }
244
245 private:
FinishTest(CefRefPtr<CefTextfield> textfield)246 void FinishTest(CefRefPtr<CefTextfield> textfield) {
247 // OnAfterUserAction() should be called for each unhandled character.
248 EXPECT_EQ(sizeof(kTestInputMessage) - 1, after_user_action_ct_);
249
250 // Verify the completed contents.
251 EXPECT_STREQ(kTestInputMessage, textfield->GetText().ToString().c_str());
252
253 // Close the window to end the test.
254 textfield->GetWindow()->Close();
255 }
256
257 int index_ = 0;
258 size_t after_user_action_ct_ = 0;
259
260 IMPLEMENT_REFCOUNTING(TestTextfieldDelegate);
261 DISALLOW_COPY_AND_ASSIGN(TestTextfieldDelegate);
262 };
263
RunTextfieldKeyEvent(CefRefPtr<CefWindow> window)264 void RunTextfieldKeyEvent(CefRefPtr<CefWindow> window) {
265 CefRefPtr<CefTextfield> textfield =
266 CefTextfield::CreateTextfield(new TestTextfieldDelegate());
267 textfield->SetID(kTextfieldID);
268
269 EXPECT_TRUE(textfield->AsTextfield());
270 EXPECT_EQ(kTextfieldID, textfield->GetID());
271 EXPECT_TRUE(textfield->IsVisible());
272 EXPECT_FALSE(textfield->IsDrawn());
273
274 window->AddChildView(textfield);
275 window->Layout();
276
277 EXPECT_TRUE(window->IsSame(textfield->GetWindow()));
278 EXPECT_TRUE(window->IsSame(textfield->GetParentView()));
279 EXPECT_TRUE(textfield->IsSame(window->GetViewForID(kTextfieldID)));
280 EXPECT_TRUE(textfield->IsVisible());
281 EXPECT_TRUE(textfield->IsDrawn());
282
283 window->Show();
284
285 // Give input focus to the textfield.
286 textfield->RequestFocus();
287
288 // Send the contents of |kTestInputMessage| to the textfield.
289 for (size_t i = 0; i < sizeof(kTestInputMessage) - 1; ++i) {
290 int keycode;
291 uint32 modifiers;
292 TranslateKey(kTestInputMessage[i], &keycode, &modifiers);
293 window->SendKeyPress(keycode, modifiers);
294 }
295
296 // Send return to end the text input.
297 window->SendKeyPress(VKEY_RETURN, 0);
298 }
299
TextfieldKeyEventImpl(CefRefPtr<CefWaitableEvent> event)300 void TextfieldKeyEventImpl(CefRefPtr<CefWaitableEvent> event) {
301 auto config = std::make_unique<TestWindowDelegate::Config>();
302 config->on_window_created = base::BindOnce(RunTextfieldKeyEvent);
303 config->close_window = false;
304 TestWindowDelegate::RunTest(event, std::move(config));
305 }
306
307 } // namespace
308
309 // Test Textfield input and events. This is primarily to exercise exposed CEF
310 // APIs and is not intended to comprehensively test Textfield-related behavior
311 // (which we presume that Chromium is testing).
312 TEXTFIELD_TEST_ASYNC(TextfieldKeyEvent)
313