1 // Copyright 2013 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 "ash/ime/input_method_menu_item.h"
6 #include "ash/ime/input_method_menu_manager.h"
7 #include "base/bind_helpers.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_browsertest.h"
10 #include "chrome/browser/extensions/extension_test_message_listener.h"
11 #include "chromeos/ime/component_extension_ime_manager.h"
12 #include "chromeos/ime/composition_text.h"
13 #include "chromeos/ime/input_method_descriptor.h"
14 #include "chromeos/ime/input_method_manager.h"
15 #include "content/public/test/browser_test_utils.h"
16 #include "content/public/test/test_utils.h"
17 #include "extensions/common/manifest_handlers/background_info.h"
18 #include "ui/base/ime/chromeos/ime_bridge.h"
19 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
20 #include "ui/base/ime/chromeos/mock_ime_input_context_handler.h"
21 #include "ui/events/event.h"
22
23 namespace chromeos {
24 namespace input_method {
25 namespace {
26
27 const char kIdentityIMEID[] =
28 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpIdentityIME";
29 const char kToUpperIMEID[] =
30 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpToUpperIME";
31 const char kAPIArgumentIMEID[] =
32 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpAPIArgumentIME";
33 const char kExtensionID[] = "iafoklpfplgfnoimmaejoeondnjnlcfp";
34
35 // InputMethod extension should work on 1)normal extension, 2)normal extension
36 // in incognito mode 3)component extension.
37 enum TestType {
38 kTestTypeNormal = 0,
39 kTestTypeIncognito = 1,
40 kTestTypeComponent = 2,
41 };
42
43 class InputMethodEngineBrowserTest
44 : public ExtensionBrowserTest,
45 public ::testing::WithParamInterface<TestType> {
46 public:
InputMethodEngineBrowserTest()47 InputMethodEngineBrowserTest()
48 : ExtensionBrowserTest() {}
~InputMethodEngineBrowserTest()49 virtual ~InputMethodEngineBrowserTest() {}
50
SetUpInProcessBrowserTestFixture()51 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
52 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
53 }
54
TearDownInProcessBrowserTestFixture()55 virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
56 extension_ = NULL;
57 }
58
59 protected:
LoadTestInputMethod()60 void LoadTestInputMethod() {
61 // This will load "chrome/test/data/extensions/input_ime"
62 ExtensionTestMessageListener ime_ready_listener("ReadyToUseImeEvent",
63 false);
64 extension_ = LoadExtensionWithType("input_ime", GetParam());
65 ASSERT_TRUE(extension_);
66 ASSERT_TRUE(ime_ready_listener.WaitUntilSatisfied());
67
68 // Make sure ComponentExtensionIMEManager is initialized.
69 // ComponentExtensionIMEManagerImpl::InitializeAsync posts
70 // ReadComponentExtensionsInfo to the FILE thread for the
71 // initialization. If it is never initialized for some reasons,
72 // the test is timed out and failed.
73 ComponentExtensionIMEManager* ceimm =
74 InputMethodManager::Get()->GetComponentExtensionIMEManager();
75 while (!ceimm->IsInitialized()) {
76 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
77 }
78
79 // Extension IMEs are not enabled by default.
80 std::vector<std::string> extension_ime_ids;
81 extension_ime_ids.push_back(kIdentityIMEID);
82 extension_ime_ids.push_back(kToUpperIMEID);
83 extension_ime_ids.push_back(kAPIArgumentIMEID);
84 InputMethodManager::Get()->SetEnabledExtensionImes(&extension_ime_ids);
85
86 InputMethodDescriptors extension_imes;
87 InputMethodManager::Get()->GetInputMethodExtensions(&extension_imes);
88
89 // Test IME has two input methods, thus InputMethodManager should have two
90 // extension IME.
91 // Note: Even extension is loaded by LoadExtensionAsComponent as above, the
92 // IME does not managed by ComponentExtensionIMEManager or it's id won't
93 // start with __comp__. The component extension IME is whitelisted and
94 // managed by ComponentExtensionIMEManager, but its framework is same as
95 // normal extension IME.
96 EXPECT_EQ(3U, extension_imes.size());
97 }
98
LoadExtensionWithType(const std::string & extension_name,TestType type)99 const extensions::Extension* LoadExtensionWithType(
100 const std::string& extension_name, TestType type) {
101 switch (type) {
102 case kTestTypeNormal:
103 return LoadExtension(test_data_dir_.AppendASCII(extension_name));
104 case kTestTypeIncognito:
105 return LoadExtensionIncognito(
106 test_data_dir_.AppendASCII(extension_name));
107 case kTestTypeComponent:
108 return LoadExtensionAsComponent(
109 test_data_dir_.AppendASCII(extension_name));
110 }
111 NOTREACHED();
112 return NULL;
113 }
114
115 const extensions::Extension* extension_;
116 };
117
118 class KeyEventDoneCallback {
119 public:
KeyEventDoneCallback(bool expected_argument)120 explicit KeyEventDoneCallback(bool expected_argument)
121 : expected_argument_(expected_argument),
122 is_called_(false) {}
~KeyEventDoneCallback()123 ~KeyEventDoneCallback() {}
124
Run(bool consumed)125 void Run(bool consumed) {
126 if (consumed == expected_argument_) {
127 base::MessageLoop::current()->Quit();
128 is_called_ = true;
129 }
130 }
131
WaitUntilCalled()132 void WaitUntilCalled() {
133 while (!is_called_)
134 content::RunMessageLoop();
135 }
136
137 private:
138 bool expected_argument_;
139 bool is_called_;
140
141 DISALLOW_COPY_AND_ASSIGN(KeyEventDoneCallback);
142 };
143
144 INSTANTIATE_TEST_CASE_P(InputMethodEngineBrowserTest,
145 InputMethodEngineBrowserTest,
146 ::testing::Values(kTestTypeNormal));
147 INSTANTIATE_TEST_CASE_P(InputMethodEngineIncognitoBrowserTest,
148 InputMethodEngineBrowserTest,
149 ::testing::Values(kTestTypeIncognito));
150 INSTANTIATE_TEST_CASE_P(InputMethodEngineComponentExtensionBrowserTest,
151 InputMethodEngineBrowserTest,
152 ::testing::Values(kTestTypeComponent));
153
IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest,BasicScenarioTest)154 IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest,
155 BasicScenarioTest) {
156 LoadTestInputMethod();
157
158 InputMethodManager::Get()->ChangeInputMethod(kIdentityIMEID);
159
160 scoped_ptr<MockIMEInputContextHandler> mock_input_context(
161 new MockIMEInputContextHandler());
162 scoped_ptr<MockIMECandidateWindowHandler> mock_candidate_window(
163 new MockIMECandidateWindowHandler());
164
165 IMEBridge::Get()->SetInputContextHandler(mock_input_context.get());
166 IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window.get());
167
168 IMEEngineHandlerInterface* engine_handler =
169 IMEBridge::Get()->GetCurrentEngineHandler();
170 ASSERT_TRUE(engine_handler);
171
172 // onActivate event should be fired if Enable function is called.
173 ExtensionTestMessageListener activated_listener("onActivate", false);
174 engine_handler->Enable();
175 ASSERT_TRUE(activated_listener.WaitUntilSatisfied());
176 ASSERT_TRUE(activated_listener.was_satisfied());
177
178 // onFocus event should be fired if FocusIn function is called.
179 ExtensionTestMessageListener focus_listener("onFocus:text", false);
180 IMEEngineHandlerInterface::InputContext context(ui::TEXT_INPUT_TYPE_TEXT,
181 ui::TEXT_INPUT_MODE_DEFAULT);
182 engine_handler->FocusIn(context);
183 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
184 ASSERT_TRUE(focus_listener.was_satisfied());
185
186 // onKeyEvent should be fired if ProcessKeyEvent is called.
187 KeyEventDoneCallback callback(false); // EchoBackIME doesn't consume keys.
188 ExtensionTestMessageListener keyevent_listener("onKeyEvent", false);
189 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE, false);
190 engine_handler->ProcessKeyEvent(key_event,
191 base::Bind(&KeyEventDoneCallback::Run,
192 base::Unretained(&callback)));
193 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
194 ASSERT_TRUE(keyevent_listener.was_satisfied());
195 callback.WaitUntilCalled();
196
197 // onSurroundingTextChange should be fired if SetSurroundingText is called.
198 ExtensionTestMessageListener surrounding_text_listener(
199 "onSurroundingTextChanged", false);
200 engine_handler->SetSurroundingText("text", // Surrounding text.
201 0, // focused position.
202 1); // anchor position.
203 ASSERT_TRUE(surrounding_text_listener.WaitUntilSatisfied());
204 ASSERT_TRUE(surrounding_text_listener.was_satisfied());
205
206 // onMenuItemActivated should be fired if PropertyActivate is called.
207 ExtensionTestMessageListener property_listener("onMenuItemActivated", false);
208 engine_handler->PropertyActivate("property_name");
209 ASSERT_TRUE(property_listener.WaitUntilSatisfied());
210 ASSERT_TRUE(property_listener.was_satisfied());
211
212 // onReset should be fired if Reset is called.
213 ExtensionTestMessageListener reset_listener("onReset", false);
214 engine_handler->Reset();
215 ASSERT_TRUE(reset_listener.WaitUntilSatisfied());
216 ASSERT_TRUE(reset_listener.was_satisfied());
217
218 // onBlur should be fired if FocusOut is called.
219 ExtensionTestMessageListener blur_listener("onBlur", false);
220 engine_handler->FocusOut();
221 ASSERT_TRUE(blur_listener.WaitUntilSatisfied());
222 ASSERT_TRUE(blur_listener.was_satisfied());
223
224 // onDeactivated should be fired if Disable is called.
225 ExtensionTestMessageListener disabled_listener("onDeactivated", false);
226 engine_handler->Disable();
227 ASSERT_TRUE(disabled_listener.WaitUntilSatisfied());
228 ASSERT_TRUE(disabled_listener.was_satisfied());
229
230 IMEBridge::Get()->SetInputContextHandler(NULL);
231 IMEBridge::Get()->SetCandidateWindowHandler(NULL);
232 }
233
IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest,APIArgumentTest)234 IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest,
235 APIArgumentTest) {
236 LoadTestInputMethod();
237
238 InputMethodManager::Get()->ChangeInputMethod(kAPIArgumentIMEID);
239
240 scoped_ptr<MockIMEInputContextHandler> mock_input_context(
241 new MockIMEInputContextHandler());
242 scoped_ptr<MockIMECandidateWindowHandler> mock_candidate_window(
243 new MockIMECandidateWindowHandler());
244
245 IMEBridge::Get()->SetInputContextHandler(mock_input_context.get());
246 IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window.get());
247
248 IMEEngineHandlerInterface* engine_handler =
249 IMEBridge::Get()->GetCurrentEngineHandler();
250 ASSERT_TRUE(engine_handler);
251
252 extensions::ExtensionHost* host = FindHostWithPath(
253 extensions::ExtensionSystem::Get(profile())->process_manager(),
254 extensions::BackgroundInfo::GetBackgroundURL(extension_).path(),
255 1);
256
257 engine_handler->Enable();
258 IMEEngineHandlerInterface::InputContext context(ui::TEXT_INPUT_TYPE_TEXT,
259 ui::TEXT_INPUT_MODE_DEFAULT);
260 engine_handler->FocusIn(context);
261
262 {
263 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:No");
264 KeyEventDoneCallback callback(false);
265 const std::string expected_value =
266 "onKeyEvent::keydown:a:KeyA:false:false:false:false";
267 ExtensionTestMessageListener keyevent_listener(expected_value, false);
268
269 ui::KeyEvent key_event(
270 ui::ET_KEY_PRESSED, ui::VKEY_A, "KeyA", ui::EF_NONE, false);
271 engine_handler->ProcessKeyEvent(key_event,
272 base::Bind(&KeyEventDoneCallback::Run,
273 base::Unretained(&callback)));
274 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
275 EXPECT_TRUE(keyevent_listener.was_satisfied());
276 callback.WaitUntilCalled();
277 }
278 {
279 SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:No, Shift:No, Caps:No");
280 KeyEventDoneCallback callback(false);
281 const std::string expected_value =
282 "onKeyEvent::keydown:a:KeyA:true:false:false:false";
283 ExtensionTestMessageListener keyevent_listener(expected_value, false);
284
285 ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
286 ui::VKEY_A,
287 "KeyA",
288 ui::EF_CONTROL_DOWN,
289 false);
290 engine_handler->ProcessKeyEvent(key_event,
291 base::Bind(&KeyEventDoneCallback::Run,
292 base::Unretained(&callback)));
293 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
294 EXPECT_TRUE(keyevent_listener.was_satisfied());
295 callback.WaitUntilCalled();
296 }
297 {
298 SCOPED_TRACE("KeyDown, Ctrl:No, alt:Yes, Shift:No, Caps:No");
299 KeyEventDoneCallback callback(false);
300 const std::string expected_value =
301 "onKeyEvent::keydown:a:KeyA:false:true:false:false";
302 ExtensionTestMessageListener keyevent_listener(expected_value, false);
303
304 ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
305 ui::VKEY_A,
306 "KeyA",
307 ui::EF_ALT_DOWN,
308 false);
309 engine_handler->ProcessKeyEvent(key_event,
310 base::Bind(&KeyEventDoneCallback::Run,
311 base::Unretained(&callback)));
312 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
313 EXPECT_TRUE(keyevent_listener.was_satisfied());
314 callback.WaitUntilCalled();
315 }
316 {
317 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:No");
318 KeyEventDoneCallback callback(false);
319 const std::string expected_value =
320 "onKeyEvent::keydown:A:KeyA:false:false:true:false";
321 ExtensionTestMessageListener keyevent_listener(expected_value, false);
322
323 ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
324 ui::VKEY_A,
325 "KeyA",
326 ui::EF_SHIFT_DOWN,
327 false);
328 engine_handler->ProcessKeyEvent(key_event,
329 base::Bind(&KeyEventDoneCallback::Run,
330 base::Unretained(&callback)));
331 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
332 EXPECT_TRUE(keyevent_listener.was_satisfied());
333 callback.WaitUntilCalled();
334 }
335 {
336 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:Yes");
337 KeyEventDoneCallback callback(false);
338 const std::string expected_value =
339 "onKeyEvent::keydown:A:KeyA:false:false:false:true";
340 ExtensionTestMessageListener keyevent_listener(expected_value, false);
341
342 ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
343 ui::VKEY_A,
344 "KeyA",
345 ui::EF_CAPS_LOCK_DOWN,
346 false);
347 engine_handler->ProcessKeyEvent(key_event,
348 base::Bind(&KeyEventDoneCallback::Run,
349 base::Unretained(&callback)));
350 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
351 EXPECT_TRUE(keyevent_listener.was_satisfied());
352 callback.WaitUntilCalled();
353 }
354 {
355 SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:Yes, Shift:No, Caps:No");
356 KeyEventDoneCallback callback(false);
357 const std::string expected_value =
358 "onKeyEvent::keydown:a:KeyA:true:true:false:false";
359 ExtensionTestMessageListener keyevent_listener(expected_value, false);
360
361 ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
362 ui::VKEY_A,
363 "KeyA",
364 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
365 false);
366 engine_handler->ProcessKeyEvent(key_event,
367 base::Bind(&KeyEventDoneCallback::Run,
368 base::Unretained(&callback)));
369 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
370 EXPECT_TRUE(keyevent_listener.was_satisfied());
371 callback.WaitUntilCalled();
372 }
373 {
374 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:Yes");
375 KeyEventDoneCallback callback(false);
376 const std::string expected_value =
377 "onKeyEvent::keydown:a:KeyA:false:false:true:true";
378 ExtensionTestMessageListener keyevent_listener(expected_value, false);
379
380 ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
381 ui::VKEY_A,
382 "KeyA",
383 ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN,
384 false);
385 engine_handler->ProcessKeyEvent(key_event,
386 base::Bind(&KeyEventDoneCallback::Run,
387 base::Unretained(&callback)));
388 ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
389 EXPECT_TRUE(keyevent_listener.was_satisfied());
390 callback.WaitUntilCalled();
391 }
392 // TODO(nona): Add browser tests for other API as well.
393 {
394 SCOPED_TRACE("commitText test");
395 mock_input_context->Reset();
396 mock_candidate_window->Reset();
397
398 const char commit_text_test_script[] =
399 "chrome.input.ime.commitText({"
400 " contextID: engineBridge.getFocusedContextID().contextID,"
401 " text:'COMMIT_TEXT'"
402 "});";
403
404 ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
405 commit_text_test_script));
406 EXPECT_EQ(1, mock_input_context->commit_text_call_count());
407 EXPECT_EQ("COMMIT_TEXT", mock_input_context->last_commit_text());
408 }
409 {
410 SCOPED_TRACE("sendKeyEvents test");
411 mock_input_context->Reset();
412 mock_candidate_window->Reset();
413
414 const char send_key_events_test_script[] =
415 "chrome.input.ime.sendKeyEvents({"
416 " contextID: engineBridge.getFocusedContextID().contextID,"
417 " keyData : [{"
418 " type : 'keydown',"
419 " requestId : '0',"
420 " key : 'z',"
421 " code : 'KeyZ',"
422 " },{"
423 " type : 'keyup',"
424 " requestId : '1',"
425 " key : 'z',"
426 " code : 'KeyZ',"
427 " }]"
428 "});";
429
430 ExtensionTestMessageListener keyevent_listener_down(
431 std::string("onKeyEvent:") + kExtensionID +
432 ":keydown:z:KeyZ:false:false:false:false",
433 false);
434 ExtensionTestMessageListener keyevent_listener_up(
435 std::string("onKeyEvent:") + kExtensionID +
436 ":keyup:z:KeyZ:false:false:false:false",
437 false);
438
439 ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
440 send_key_events_test_script));
441
442 ASSERT_TRUE(keyevent_listener_down.WaitUntilSatisfied());
443 EXPECT_TRUE(keyevent_listener_down.was_satisfied());
444 ASSERT_TRUE(keyevent_listener_up.WaitUntilSatisfied());
445 EXPECT_TRUE(keyevent_listener_up.was_satisfied());
446 }
447 {
448 SCOPED_TRACE("sendKeyEvents test with keyCode");
449 mock_input_context->Reset();
450 mock_candidate_window->Reset();
451
452 const char send_key_events_test_script[] =
453 "chrome.input.ime.sendKeyEvents({"
454 " contextID: engineBridge.getFocusedContextID().contextID,"
455 " keyData : [{"
456 " type : 'keydown',"
457 " requestId : '2',"
458 " key : 'a',"
459 " code : 'KeyQ',"
460 " keyCode : 0x41,"
461 " },{"
462 " type : 'keyup',"
463 " requestId : '3',"
464 " key : 'a',"
465 " code : 'KeyQ',"
466 " keyCode : 0x41,"
467 " }]"
468 "});";
469
470 ExtensionTestMessageListener keyevent_listener_down(
471 std::string("onKeyEvent:") + kExtensionID +
472 ":keydown:a:KeyQ:false:false:false:false",
473 false);
474 ExtensionTestMessageListener keyevent_listener_up(
475 std::string("onKeyEvent:") + kExtensionID +
476 ":keyup:a:KeyQ:false:false:false:false",
477 false);
478
479 ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
480 send_key_events_test_script));
481
482 ASSERT_TRUE(keyevent_listener_down.WaitUntilSatisfied());
483 EXPECT_TRUE(keyevent_listener_down.was_satisfied());
484 ASSERT_TRUE(keyevent_listener_up.WaitUntilSatisfied());
485 EXPECT_TRUE(keyevent_listener_up.was_satisfied());
486 }
487 {
488 SCOPED_TRACE("setComposition test");
489 mock_input_context->Reset();
490 mock_candidate_window->Reset();
491
492 const char set_composition_test_script[] =
493 "chrome.input.ime.setComposition({"
494 " contextID: engineBridge.getFocusedContextID().contextID,"
495 " text:'COMPOSITION_TEXT',"
496 " cursor:4,"
497 " segments : [{"
498 " start: 0,"
499 " end: 5,"
500 " style: 'underline'"
501 " },{"
502 " start: 6,"
503 " end: 10,"
504 " style: 'doubleUnderline'"
505 " }]"
506 "});";
507
508 ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
509 set_composition_test_script));
510 EXPECT_EQ(1, mock_input_context->update_preedit_text_call_count());
511
512 EXPECT_EQ(4U,
513 mock_input_context->last_update_composition_arg().cursor_pos);
514 EXPECT_TRUE(mock_input_context->last_update_composition_arg().is_visible);
515
516 const CompositionText& composition_text =
517 mock_input_context->last_update_composition_arg().composition_text;
518 EXPECT_EQ(base::UTF8ToUTF16("COMPOSITION_TEXT"), composition_text.text());
519 const std::vector<CompositionText::UnderlineAttribute>& underlines =
520 composition_text.underline_attributes();
521
522 ASSERT_EQ(2U, underlines.size());
523 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE,
524 underlines[0].type);
525 EXPECT_EQ(0U, underlines[0].start_index);
526 EXPECT_EQ(5U, underlines[0].end_index);
527
528 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE,
529 underlines[1].type);
530 EXPECT_EQ(6U, underlines[1].start_index);
531 EXPECT_EQ(10U, underlines[1].end_index);
532 }
533 {
534 SCOPED_TRACE("clearComposition test");
535 mock_input_context->Reset();
536 mock_candidate_window->Reset();
537
538 const char commite_text_test_script[] =
539 "chrome.input.ime.clearComposition({"
540 " contextID: engineBridge.getFocusedContextID().contextID,"
541 "});";
542
543 ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
544 commite_text_test_script));
545 EXPECT_EQ(1, mock_input_context->update_preedit_text_call_count());
546 EXPECT_FALSE(
547 mock_input_context->last_update_composition_arg().is_visible);
548 const CompositionText& composition_text =
549 mock_input_context->last_update_composition_arg().composition_text;
550 EXPECT_TRUE(composition_text.text().empty());
551 }
552 {
553 SCOPED_TRACE("setCandidateWindowProperties:visibility test");
554 mock_input_context->Reset();
555 mock_candidate_window->Reset();
556
557 const char set_candidate_window_properties_test_script[] =
558 "chrome.input.ime.setCandidateWindowProperties({"
559 " engineID: engineBridge.getActiveEngineID(),"
560 " properties: {"
561 " visible: true,"
562 " }"
563 "});";
564 ASSERT_TRUE(content::ExecuteScript(
565 host->host_contents(),
566 set_candidate_window_properties_test_script));
567 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
568 EXPECT_TRUE(
569 mock_candidate_window->last_update_lookup_table_arg().is_visible);
570 }
571 {
572 SCOPED_TRACE("setCandidateWindowProperties:cursor_visibility test");
573 mock_input_context->Reset();
574 mock_candidate_window->Reset();
575
576 const char set_candidate_window_properties_test_script[] =
577 "chrome.input.ime.setCandidateWindowProperties({"
578 " engineID: engineBridge.getActiveEngineID(),"
579 " properties: {"
580 " cursorVisible: true,"
581 " }"
582 "});";
583 ASSERT_TRUE(content::ExecuteScript(
584 host->host_contents(),
585 set_candidate_window_properties_test_script));
586 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
587
588 // window visibility is kept as before.
589 EXPECT_TRUE(
590 mock_candidate_window->last_update_lookup_table_arg().is_visible);
591
592 const ui::CandidateWindow& table =
593 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
594 EXPECT_TRUE(table.is_cursor_visible());
595 }
596 {
597 SCOPED_TRACE("setCandidateWindowProperties:vertical test");
598 mock_input_context->Reset();
599 mock_candidate_window->Reset();
600
601 const char set_candidate_window_properties_test_script[] =
602 "chrome.input.ime.setCandidateWindowProperties({"
603 " engineID: engineBridge.getActiveEngineID(),"
604 " properties: {"
605 " vertical: true,"
606 " }"
607 "});";
608 ASSERT_TRUE(content::ExecuteScript(
609 host->host_contents(),
610 set_candidate_window_properties_test_script));
611 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
612
613 // window visibility is kept as before.
614 EXPECT_TRUE(
615 mock_candidate_window->last_update_lookup_table_arg().is_visible);
616
617 const ui::CandidateWindow& table =
618 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
619
620 // cursor visibility is kept as before.
621 EXPECT_TRUE(table.is_cursor_visible());
622
623 EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());
624 }
625 {
626 SCOPED_TRACE("setCandidateWindowProperties:pageSize test");
627 mock_input_context->Reset();
628 mock_candidate_window->Reset();
629
630 const char set_candidate_window_properties_test_script[] =
631 "chrome.input.ime.setCandidateWindowProperties({"
632 " engineID: engineBridge.getActiveEngineID(),"
633 " properties: {"
634 " pageSize: 7,"
635 " }"
636 "});";
637 ASSERT_TRUE(content::ExecuteScript(
638 host->host_contents(),
639 set_candidate_window_properties_test_script));
640 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
641
642 // window visibility is kept as before.
643 EXPECT_TRUE(
644 mock_candidate_window->last_update_lookup_table_arg().is_visible);
645
646 const ui::CandidateWindow& table =
647 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
648
649 // cursor visibility is kept as before.
650 EXPECT_TRUE(table.is_cursor_visible());
651
652 // oritantation is kept as before.
653 EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());
654
655 EXPECT_EQ(7U, table.page_size());
656 }
657 {
658 SCOPED_TRACE("setCandidateWindowProperties:auxTextVisibility test");
659 mock_input_context->Reset();
660 mock_candidate_window->Reset();
661
662 const char set_candidate_window_properties_test_script[] =
663 "chrome.input.ime.setCandidateWindowProperties({"
664 " engineID: engineBridge.getActiveEngineID(),"
665 " properties: {"
666 " auxiliaryTextVisible: true"
667 " }"
668 "});";
669 ASSERT_TRUE(content::ExecuteScript(
670 host->host_contents(),
671 set_candidate_window_properties_test_script));
672 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
673
674 const ui::CandidateWindow& table =
675 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
676 EXPECT_TRUE(table.is_auxiliary_text_visible());
677 }
678 {
679 SCOPED_TRACE("setCandidateWindowProperties:auxText test");
680 mock_input_context->Reset();
681 mock_candidate_window->Reset();
682
683 const char set_candidate_window_properties_test_script[] =
684 "chrome.input.ime.setCandidateWindowProperties({"
685 " engineID: engineBridge.getActiveEngineID(),"
686 " properties: {"
687 " auxiliaryText: 'AUXILIARY_TEXT'"
688 " }"
689 "});";
690 ASSERT_TRUE(content::ExecuteScript(
691 host->host_contents(),
692 set_candidate_window_properties_test_script));
693 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
694
695 // aux text visibility is kept as before.
696 const ui::CandidateWindow& table =
697 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
698 EXPECT_TRUE(table.is_auxiliary_text_visible());
699 EXPECT_EQ("AUXILIARY_TEXT", table.auxiliary_text());
700 }
701 {
702 SCOPED_TRACE("setCandidates test");
703 mock_input_context->Reset();
704 mock_candidate_window->Reset();
705
706 const char set_candidates_test_script[] =
707 "chrome.input.ime.setCandidates({"
708 " contextID: engineBridge.getFocusedContextID().contextID,"
709 " candidates: [{"
710 " candidate: 'CANDIDATE_1',"
711 " id: 1,"
712 " },{"
713 " candidate: 'CANDIDATE_2',"
714 " id: 2,"
715 " label: 'LABEL_2',"
716 " },{"
717 " candidate: 'CANDIDATE_3',"
718 " id: 3,"
719 " label: 'LABEL_3',"
720 " annotation: 'ANNOTACTION_3'"
721 " },{"
722 " candidate: 'CANDIDATE_4',"
723 " id: 4,"
724 " label: 'LABEL_4',"
725 " annotation: 'ANNOTACTION_4',"
726 " usage: {"
727 " title: 'TITLE_4',"
728 " body: 'BODY_4'"
729 " }"
730 " }]"
731 "});";
732 ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
733 set_candidates_test_script));
734
735 // window visibility is kept as before.
736 EXPECT_TRUE(
737 mock_candidate_window->last_update_lookup_table_arg().is_visible);
738
739 const ui::CandidateWindow& table =
740 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
741
742 // cursor visibility is kept as before.
743 EXPECT_TRUE(table.is_cursor_visible());
744
745 // oritantation is kept as before.
746 EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());
747
748 // page size is kept as before.
749 EXPECT_EQ(7U, table.page_size());
750
751 ASSERT_EQ(4U, table.candidates().size());
752
753 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_1"),
754 table.candidates().at(0).value);
755
756 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_2"),
757 table.candidates().at(1).value);
758 EXPECT_EQ(base::UTF8ToUTF16("LABEL_2"), table.candidates().at(1).label);
759
760 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_3"),
761 table.candidates().at(2).value);
762 EXPECT_EQ(base::UTF8ToUTF16("LABEL_3"), table.candidates().at(2).label);
763 EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_3"),
764 table.candidates().at(2).annotation);
765
766 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_4"),
767 table.candidates().at(3).value);
768 EXPECT_EQ(base::UTF8ToUTF16("LABEL_4"), table.candidates().at(3).label);
769 EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_4"),
770 table.candidates().at(3).annotation);
771 EXPECT_EQ(base::UTF8ToUTF16("TITLE_4"),
772 table.candidates().at(3).description_title);
773 EXPECT_EQ(base::UTF8ToUTF16("BODY_4"),
774 table.candidates().at(3).description_body);
775 }
776 {
777 SCOPED_TRACE("setCursorPosition test");
778 mock_input_context->Reset();
779 mock_candidate_window->Reset();
780
781 const char set_cursor_position_test_script[] =
782 "chrome.input.ime.setCursorPosition({"
783 " contextID: engineBridge.getFocusedContextID().contextID,"
784 " candidateID: 2"
785 "});";
786 ASSERT_TRUE(content::ExecuteScript(
787 host->host_contents(), set_cursor_position_test_script));
788 EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
789
790 // window visibility is kept as before.
791 EXPECT_TRUE(
792 mock_candidate_window->last_update_lookup_table_arg().is_visible);
793
794 const ui::CandidateWindow& table =
795 mock_candidate_window->last_update_lookup_table_arg().lookup_table;
796
797 // cursor visibility is kept as before.
798 EXPECT_TRUE(table.is_cursor_visible());
799
800 // oritantation is kept as before.
801 EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());
802
803 // page size is kept as before.
804 EXPECT_EQ(7U, table.page_size());
805
806 // candidates are same as before.
807 ASSERT_EQ(4U, table.candidates().size());
808
809 // Candidate ID == 2 is 1 in index.
810 EXPECT_EQ(1U, table.cursor_position());
811 }
812 {
813 SCOPED_TRACE("setMenuItem test");
814 mock_input_context->Reset();
815 mock_candidate_window->Reset();
816
817 const char set_menu_item_test_script[] =
818 "chrome.input.ime.setMenuItems({"
819 " engineID: engineBridge.getActiveEngineID(),"
820 " items: [{"
821 " id: 'ID0',"
822 " },{"
823 " id: 'ID1',"
824 " label: 'LABEL1',"
825 " },{"
826 " id: 'ID2',"
827 " label: 'LABEL2',"
828 " style: 'radio',"
829 " },{"
830 " id: 'ID3',"
831 " label: 'LABEL3',"
832 " style: 'check',"
833 " visible: true,"
834 " },{"
835 " id: 'ID4',"
836 " label: 'LABEL4',"
837 " style: 'separator',"
838 " visible: true,"
839 " checked: true"
840 " }]"
841 "});";
842 ASSERT_TRUE(content::ExecuteScript(
843 host->host_contents(), set_menu_item_test_script));
844
845 const ash::ime::InputMethodMenuItemList& props =
846 ash::ime::InputMethodMenuManager::GetInstance()->
847 GetCurrentInputMethodMenuItemList();
848 ASSERT_EQ(5U, props.size());
849
850 EXPECT_EQ("ID0", props[0].key);
851 EXPECT_EQ("ID1", props[1].key);
852 EXPECT_EQ("ID2", props[2].key);
853 EXPECT_EQ("ID3", props[3].key);
854 EXPECT_EQ("ID4", props[4].key);
855
856 EXPECT_EQ("LABEL1", props[1].label);
857 EXPECT_EQ("LABEL2", props[2].label);
858 EXPECT_EQ("LABEL3", props[3].label);
859 EXPECT_EQ("LABEL4", props[4].label);
860
861 EXPECT_TRUE(props[2].is_selection_item);
862 // TODO(nona): Add tests for style: ["toggle" and "separator"]
863 // and visible:, when implement them.
864
865 EXPECT_TRUE(props[4].is_selection_item_checked);
866 }
867 {
868 SCOPED_TRACE("deleteSurroundingText test");
869 mock_input_context->Reset();
870 mock_candidate_window->Reset();
871
872 const char delete_surrounding_text_test_script[] =
873 "chrome.input.ime.deleteSurroundingText({"
874 " engineID: engineBridge.getActiveEngineID(),"
875 " contextID: engineBridge.getFocusedContextID().contextID,"
876 " offset: 5,"
877 " length: 3"
878 "});";
879 ASSERT_TRUE(content::ExecuteScript(
880 host->host_contents(), delete_surrounding_text_test_script));
881
882 EXPECT_EQ(1, mock_input_context->delete_surrounding_text_call_count());
883 EXPECT_EQ(5, mock_input_context->last_delete_surrounding_text_arg().offset);
884 EXPECT_EQ(3U,
885 mock_input_context->last_delete_surrounding_text_arg().length);
886 }
887 {
888 SCOPED_TRACE("onFocus test");
889 mock_input_context->Reset();
890 mock_candidate_window->Reset();
891
892 {
893 ExtensionTestMessageListener focus_listener("onFocus:text", false);
894 IMEEngineHandlerInterface::InputContext context(
895 ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT);
896 engine_handler->FocusIn(context);
897 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
898 ASSERT_TRUE(focus_listener.was_satisfied());
899 }
900 {
901 ExtensionTestMessageListener focus_listener("onFocus:search", false);
902 IMEEngineHandlerInterface::InputContext context(
903 ui::TEXT_INPUT_TYPE_SEARCH, ui::TEXT_INPUT_MODE_DEFAULT);
904 engine_handler->FocusIn(context);
905 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
906 ASSERT_TRUE(focus_listener.was_satisfied());
907 }
908 {
909 ExtensionTestMessageListener focus_listener("onFocus:tel", false);
910 IMEEngineHandlerInterface::InputContext context(
911 ui::TEXT_INPUT_TYPE_TELEPHONE, ui::TEXT_INPUT_MODE_DEFAULT);
912 engine_handler->FocusIn(context);
913 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
914 ASSERT_TRUE(focus_listener.was_satisfied());
915 }
916 {
917 ExtensionTestMessageListener focus_listener("onFocus:url", false);
918 IMEEngineHandlerInterface::InputContext context(
919 ui::TEXT_INPUT_TYPE_URL, ui::TEXT_INPUT_MODE_DEFAULT);
920 engine_handler->FocusIn(context);
921 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
922 ASSERT_TRUE(focus_listener.was_satisfied());
923 }
924 {
925 ExtensionTestMessageListener focus_listener("onFocus:email", false);
926 IMEEngineHandlerInterface::InputContext context(
927 ui::TEXT_INPUT_TYPE_EMAIL, ui::TEXT_INPUT_MODE_DEFAULT);
928 engine_handler->FocusIn(context);
929 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
930 ASSERT_TRUE(focus_listener.was_satisfied());
931 }
932 {
933 ExtensionTestMessageListener focus_listener("onFocus:number", false);
934 IMEEngineHandlerInterface::InputContext context(
935 ui::TEXT_INPUT_TYPE_NUMBER, ui::TEXT_INPUT_MODE_DEFAULT);
936 engine_handler->FocusIn(context);
937 ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
938 ASSERT_TRUE(focus_listener.was_satisfied());
939 }
940 }
941
942 IMEBridge::Get()->SetInputContextHandler(NULL);
943 IMEBridge::Get()->SetCandidateWindowHandler(NULL);
944 }
945
946 } // namespace
947 } // namespace input_method
948 } // namespace chromeos
949