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.
5 #include "base/bind_helpers.h"
6 #include "base/strings/stringprintf.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/extensions/extension_browsertest.h"
9 #include "content/public/test/browser_test_utils.h"
10 #include "content/public/test/test_utils.h"
11 #include "extensions/browser/process_manager.h"
12 #include "extensions/common/manifest_handlers/background_info.h"
13 #include "extensions/test/extension_test_message_listener.h"
14 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
15 #include "ui/base/ime/chromeos/composition_text_chromeos.h"
16 #include "ui/base/ime/chromeos/extension_ime_util.h"
17 #include "ui/base/ime/chromeos/ime_bridge.h"
18 #include "ui/base/ime/chromeos/input_method_descriptor.h"
19 #include "ui/base/ime/chromeos/input_method_manager.h"
20 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
21 #include "ui/base/ime/chromeos/mock_ime_input_context_handler.h"
22 #include "ui/base/ime/text_input_flags.h"
23 #include "ui/chromeos/ime/input_method_menu_item.h"
24 #include "ui/chromeos/ime/input_method_menu_manager.h"
25 #include "ui/events/event.h"
26 #include "ui/events/keycodes/dom/dom_code.h"
27 #include "ui/events/keycodes/dom/keycode_converter.h"
30 namespace input_method
{
33 const char kIdentityIMEID
[] =
34 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpIdentityIME";
35 const char kToUpperIMEID
[] =
36 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpToUpperIME";
37 const char kAPIArgumentIMEID
[] =
38 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpAPIArgumentIME";
39 const char kExtensionID
[] = "iafoklpfplgfnoimmaejoeondnjnlcfp";
41 // InputMethod extension should work on 1)normal extension, 2)normal extension
42 // in incognito mode 3)component extension.
45 kTestTypeIncognito
= 1,
46 kTestTypeComponent
= 2,
49 class InputMethodEngineBrowserTest
50 : public ExtensionBrowserTest
,
51 public ::testing::WithParamInterface
<TestType
> {
53 InputMethodEngineBrowserTest()
54 : ExtensionBrowserTest() {}
55 virtual ~InputMethodEngineBrowserTest() {}
57 void SetUpInProcessBrowserTestFixture() override
{
58 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
61 void TearDownInProcessBrowserTestFixture() override
{ extension_
= NULL
; }
64 void LoadTestInputMethod() {
65 // This will load "chrome/test/data/extensions/input_ime"
66 ExtensionTestMessageListener
ime_ready_listener("ReadyToUseImeEvent",
68 extension_
= LoadExtensionWithType("input_ime", GetParam());
69 ASSERT_TRUE(extension_
);
70 ASSERT_TRUE(ime_ready_listener
.WaitUntilSatisfied());
72 // Extension IMEs are not enabled by default.
73 std::vector
<std::string
> extension_ime_ids
;
74 extension_ime_ids
.push_back(kIdentityIMEID
);
75 extension_ime_ids
.push_back(kToUpperIMEID
);
76 extension_ime_ids
.push_back(kAPIArgumentIMEID
);
77 InputMethodManager::Get()->GetActiveIMEState()->SetEnabledExtensionImes(
80 InputMethodDescriptors extension_imes
;
81 InputMethodManager::Get()->GetActiveIMEState()->GetInputMethodExtensions(
84 // Test IME has two input methods, thus InputMethodManager should have two
86 // Note: Even extension is loaded by LoadExtensionAsComponent as above, the
87 // IME does not managed by ComponentExtensionIMEManager or it's id won't
88 // start with __comp__. The component extension IME is whitelisted and
89 // managed by ComponentExtensionIMEManager, but its framework is same as
90 // normal extension IME.
91 EXPECT_EQ(3U, extension_imes
.size());
94 const extensions::Extension
* LoadExtensionWithType(
95 const std::string
& extension_name
, TestType type
) {
98 return LoadExtension(test_data_dir_
.AppendASCII(extension_name
));
99 case kTestTypeIncognito
:
100 return LoadExtensionIncognito(
101 test_data_dir_
.AppendASCII(extension_name
));
102 case kTestTypeComponent
:
103 return LoadExtensionAsComponent(
104 test_data_dir_
.AppendASCII(extension_name
));
110 const extensions::Extension
* extension_
;
113 class KeyEventDoneCallback
{
115 explicit KeyEventDoneCallback(bool expected_argument
)
116 : expected_argument_(expected_argument
),
118 ~KeyEventDoneCallback() {}
120 void Run(bool consumed
) {
121 if (consumed
== expected_argument_
) {
122 base::MessageLoop::current()->Quit();
127 void WaitUntilCalled() {
129 content::RunMessageLoop();
133 bool expected_argument_
;
136 DISALLOW_COPY_AND_ASSIGN(KeyEventDoneCallback
);
139 INSTANTIATE_TEST_CASE_P(InputMethodEngineBrowserTest
,
140 InputMethodEngineBrowserTest
,
141 ::testing::Values(kTestTypeNormal
));
142 INSTANTIATE_TEST_CASE_P(InputMethodEngineIncognitoBrowserTest
,
143 InputMethodEngineBrowserTest
,
144 ::testing::Values(kTestTypeIncognito
));
145 INSTANTIATE_TEST_CASE_P(InputMethodEngineComponentExtensionBrowserTest
,
146 InputMethodEngineBrowserTest
,
147 ::testing::Values(kTestTypeComponent
));
149 IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest
,
151 LoadTestInputMethod();
153 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
154 kIdentityIMEID
, false /* show_message */);
156 scoped_ptr
<MockIMEInputContextHandler
> mock_input_context(
157 new MockIMEInputContextHandler());
158 scoped_ptr
<MockIMECandidateWindowHandler
> mock_candidate_window(
159 new MockIMECandidateWindowHandler());
161 IMEBridge::Get()->SetInputContextHandler(mock_input_context
.get());
162 IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window
.get());
164 IMEEngineHandlerInterface
* engine_handler
=
165 IMEBridge::Get()->GetCurrentEngineHandler();
166 ASSERT_TRUE(engine_handler
);
168 // onActivate event should be fired if Enable function is called.
169 ExtensionTestMessageListener
activated_listener("onActivate", false);
170 engine_handler
->Enable("IdentityIME");
171 ASSERT_TRUE(activated_listener
.WaitUntilSatisfied());
172 ASSERT_TRUE(activated_listener
.was_satisfied());
174 // onFocus event should be fired if FocusIn function is called.
175 ExtensionTestMessageListener
focus_listener("onFocus:text", false);
176 IMEEngineHandlerInterface::InputContext
context(ui::TEXT_INPUT_TYPE_TEXT
,
177 ui::TEXT_INPUT_MODE_DEFAULT
,
178 ui::TEXT_INPUT_FLAG_NONE
);
179 engine_handler
->FocusIn(context
);
180 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
181 ASSERT_TRUE(focus_listener
.was_satisfied());
183 // onKeyEvent should be fired if ProcessKeyEvent is called.
184 KeyEventDoneCallback
callback(false); // EchoBackIME doesn't consume keys.
185 ExtensionTestMessageListener
keyevent_listener("onKeyEvent", false);
186 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
, ui::VKEY_A
, ui::EF_NONE
);
187 engine_handler
->ProcessKeyEvent(key_event
,
188 base::Bind(&KeyEventDoneCallback::Run
,
189 base::Unretained(&callback
)));
190 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
191 ASSERT_TRUE(keyevent_listener
.was_satisfied());
192 callback
.WaitUntilCalled();
194 // onSurroundingTextChange should be fired if SetSurroundingText is called.
195 ExtensionTestMessageListener
surrounding_text_listener(
196 "onSurroundingTextChanged", false);
197 engine_handler
->SetSurroundingText("text", // Surrounding text.
198 0, // focused position.
199 1, // anchor position.
200 0); // offset position.
201 ASSERT_TRUE(surrounding_text_listener
.WaitUntilSatisfied());
202 ASSERT_TRUE(surrounding_text_listener
.was_satisfied());
204 // onMenuItemActivated should be fired if PropertyActivate is called.
205 ExtensionTestMessageListener
property_listener("onMenuItemActivated", false);
206 engine_handler
->PropertyActivate("property_name");
207 ASSERT_TRUE(property_listener
.WaitUntilSatisfied());
208 ASSERT_TRUE(property_listener
.was_satisfied());
210 // onReset should be fired if Reset is called.
211 ExtensionTestMessageListener
reset_listener("onReset", false);
212 engine_handler
->Reset();
213 ASSERT_TRUE(reset_listener
.WaitUntilSatisfied());
214 ASSERT_TRUE(reset_listener
.was_satisfied());
216 // onBlur should be fired if FocusOut is called.
217 ExtensionTestMessageListener
blur_listener("onBlur", false);
218 engine_handler
->FocusOut();
219 ASSERT_TRUE(blur_listener
.WaitUntilSatisfied());
220 ASSERT_TRUE(blur_listener
.was_satisfied());
222 // onDeactivated should be fired if Disable is called.
223 ExtensionTestMessageListener
disabled_listener("onDeactivated", false);
224 engine_handler
->Disable();
225 ASSERT_TRUE(disabled_listener
.WaitUntilSatisfied());
226 ASSERT_TRUE(disabled_listener
.was_satisfied());
228 IMEBridge::Get()->SetInputContextHandler(NULL
);
229 IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
232 IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest
,
234 LoadTestInputMethod();
236 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
237 kAPIArgumentIMEID
, false /* show_message */);
239 scoped_ptr
<MockIMEInputContextHandler
> mock_input_context(
240 new MockIMEInputContextHandler());
241 scoped_ptr
<MockIMECandidateWindowHandler
> mock_candidate_window(
242 new MockIMECandidateWindowHandler());
244 IMEBridge::Get()->SetInputContextHandler(mock_input_context
.get());
245 IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window
.get());
247 IMEEngineHandlerInterface
* engine_handler
=
248 IMEBridge::Get()->GetCurrentEngineHandler();
249 ASSERT_TRUE(engine_handler
);
251 extensions::ExtensionHost
* host
=
252 extensions::ProcessManager::Get(profile())
253 ->GetBackgroundHostForExtension(extension_
->id());
256 engine_handler
->Enable("APIArgumentIME");
257 IMEEngineHandlerInterface::InputContext
context(ui::TEXT_INPUT_TYPE_TEXT
,
258 ui::TEXT_INPUT_MODE_DEFAULT
,
259 ui::TEXT_INPUT_FLAG_NONE
);
260 engine_handler
->FocusIn(context
);
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);
269 ui::KeyEvent
key_event(
270 ui::ET_KEY_PRESSED
, ui::VKEY_A
, ui::DomCode::KEY_A
, ui::EF_NONE
);
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();
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);
285 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
288 ui::EF_CONTROL_DOWN
);
289 engine_handler
->ProcessKeyEvent(key_event
,
290 base::Bind(&KeyEventDoneCallback::Run
,
291 base::Unretained(&callback
)));
292 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
293 EXPECT_TRUE(keyevent_listener
.was_satisfied());
294 callback
.WaitUntilCalled();
297 SCOPED_TRACE("KeyDown, Ctrl:No, alt:Yes, Shift:No, Caps:No");
298 KeyEventDoneCallback
callback(false);
299 const std::string expected_value
=
300 "onKeyEvent::keydown:a:KeyA:false:true:false:false";
301 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
303 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
307 engine_handler
->ProcessKeyEvent(key_event
,
308 base::Bind(&KeyEventDoneCallback::Run
,
309 base::Unretained(&callback
)));
310 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
311 EXPECT_TRUE(keyevent_listener
.was_satisfied());
312 callback
.WaitUntilCalled();
315 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:No");
316 KeyEventDoneCallback
callback(false);
317 const std::string expected_value
=
318 "onKeyEvent::keydown:A:KeyA:false:false:true:false";
319 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
321 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
325 engine_handler
->ProcessKeyEvent(key_event
,
326 base::Bind(&KeyEventDoneCallback::Run
,
327 base::Unretained(&callback
)));
328 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
329 EXPECT_TRUE(keyevent_listener
.was_satisfied());
330 callback
.WaitUntilCalled();
333 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:Yes");
334 KeyEventDoneCallback
callback(false);
335 const std::string expected_value
=
336 "onKeyEvent::keydown:A:KeyA:false:false:false:true";
337 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
339 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
342 ui::EF_CAPS_LOCK_DOWN
);
343 engine_handler
->ProcessKeyEvent(key_event
,
344 base::Bind(&KeyEventDoneCallback::Run
,
345 base::Unretained(&callback
)));
346 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
347 EXPECT_TRUE(keyevent_listener
.was_satisfied());
348 callback
.WaitUntilCalled();
351 SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:Yes, Shift:No, Caps:No");
352 KeyEventDoneCallback
callback(false);
353 const std::string expected_value
=
354 "onKeyEvent::keydown:a:KeyA:true:true:false:false";
355 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
357 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
360 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
);
361 engine_handler
->ProcessKeyEvent(key_event
,
362 base::Bind(&KeyEventDoneCallback::Run
,
363 base::Unretained(&callback
)));
364 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
365 EXPECT_TRUE(keyevent_listener
.was_satisfied());
366 callback
.WaitUntilCalled();
369 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:Yes");
370 KeyEventDoneCallback
callback(false);
371 const std::string expected_value
=
372 "onKeyEvent::keydown:a:KeyA:false:false:true:true";
373 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
375 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
378 ui::EF_SHIFT_DOWN
| ui::EF_CAPS_LOCK_DOWN
);
379 engine_handler
->ProcessKeyEvent(key_event
,
380 base::Bind(&KeyEventDoneCallback::Run
,
381 base::Unretained(&callback
)));
382 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
383 EXPECT_TRUE(keyevent_listener
.was_satisfied());
384 callback
.WaitUntilCalled();
388 ui::KeyboardCode keycode
;
391 } kMediaKeyCases
[] = {
392 { ui::VKEY_BROWSER_BACK
, "BrowserBack", "HistoryBack" },
393 { ui::VKEY_BROWSER_FORWARD
, "BrowserForward", "HistoryForward" },
394 { ui::VKEY_BROWSER_REFRESH
, "BrowserRefresh", "BrowserRefresh" },
395 { ui::VKEY_MEDIA_LAUNCH_APP2
, "ChromeOSFullscreen", "ChromeOSFullscreen" },
396 { ui::VKEY_MEDIA_LAUNCH_APP1
,
397 "ChromeOSSwitchWindow", "ChromeOSSwitchWindow" },
398 { ui::VKEY_BRIGHTNESS_DOWN
, "BrightnessDown", "BrightnessDown" },
399 { ui::VKEY_BRIGHTNESS_UP
, "BrightnessUp", "BrightnessUp" },
400 { ui::VKEY_VOLUME_MUTE
, "VolumeMute", "AudioVolumeMute" },
401 { ui::VKEY_VOLUME_DOWN
, "VolumeDown", "AudioVolumeDown" },
402 { ui::VKEY_VOLUME_UP
, "VolumeUp", "AudioVolumeUp" },
403 { ui::VKEY_F1
, "F1", "HistoryBack" },
404 { ui::VKEY_F2
, "F2", "HistoryForward" },
405 { ui::VKEY_F3
, "F3", "BrowserRefresh" },
406 { ui::VKEY_F4
, "F4", "ChromeOSFullscreen" },
407 { ui::VKEY_F5
, "F5", "ChromeOSSwitchWindow" },
408 { ui::VKEY_F6
, "F6", "BrightnessDown" },
409 { ui::VKEY_F7
, "F7", "BrightnessUp" },
410 { ui::VKEY_F8
, "F8", "AudioVolumeMute" },
411 { ui::VKEY_F9
, "F9", "AudioVolumeDown" },
412 { ui::VKEY_F10
, "F10", "AudioVolumeUp" },
414 for (size_t i
= 0; i
< arraysize(kMediaKeyCases
); ++i
) {
415 SCOPED_TRACE(std::string("KeyDown, ") + kMediaKeyCases
[i
].code
);
416 KeyEventDoneCallback
callback(false);
417 const std::string expected_value
=
418 base::StringPrintf("onKeyEvent::keydown:%s:%s:false:false:false:false",
419 kMediaKeyCases
[i
].key
, kMediaKeyCases
[i
].code
);
420 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
422 ui::KeyEvent
key_event(
424 kMediaKeyCases
[i
].keycode
,
425 ui::KeycodeConverter::CodeStringToDomCode(kMediaKeyCases
[i
].code
),
427 engine_handler
->ProcessKeyEvent(key_event
,
428 base::Bind(&KeyEventDoneCallback::Run
,
429 base::Unretained(&callback
)));
430 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
431 EXPECT_TRUE(keyevent_listener
.was_satisfied());
432 callback
.WaitUntilCalled();
434 // TODO(nona): Add browser tests for other API as well.
436 SCOPED_TRACE("commitText test");
437 mock_input_context
->Reset();
438 mock_candidate_window
->Reset();
440 const char commit_text_test_script
[] =
441 "chrome.input.ime.commitText({"
442 " contextID: engineBridge.getFocusedContextID().contextID,"
443 " text:'COMMIT_TEXT'"
446 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
447 commit_text_test_script
));
448 EXPECT_EQ(1, mock_input_context
->commit_text_call_count());
449 EXPECT_EQ("COMMIT_TEXT", mock_input_context
->last_commit_text());
452 SCOPED_TRACE("sendKeyEvents test");
453 mock_input_context
->Reset();
454 mock_candidate_window
->Reset();
456 const char send_key_events_test_script
[] =
457 "chrome.input.ime.sendKeyEvents({"
458 " contextID: engineBridge.getFocusedContextID().contextID,"
472 ExtensionTestMessageListener
keyevent_listener_down(
473 std::string("onKeyEvent:") + kExtensionID
+
474 ":keydown:z:KeyZ:false:false:false:false",
476 ExtensionTestMessageListener
keyevent_listener_up(
477 std::string("onKeyEvent:") + kExtensionID
+
478 ":keyup:z:KeyZ:false:false:false:false",
481 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
482 send_key_events_test_script
));
484 ASSERT_TRUE(keyevent_listener_down
.WaitUntilSatisfied());
485 EXPECT_TRUE(keyevent_listener_down
.was_satisfied());
486 ASSERT_TRUE(keyevent_listener_up
.WaitUntilSatisfied());
487 EXPECT_TRUE(keyevent_listener_up
.was_satisfied());
490 SCOPED_TRACE("sendKeyEvents test with keyCode");
491 mock_input_context
->Reset();
492 mock_candidate_window
->Reset();
494 const char send_key_events_test_script
[] =
495 "chrome.input.ime.sendKeyEvents({"
496 " contextID: engineBridge.getFocusedContextID().contextID,"
512 ExtensionTestMessageListener
keyevent_listener_down(
513 std::string("onKeyEvent:") + kExtensionID
+
514 ":keydown:a:KeyQ:false:false:false:false",
516 ExtensionTestMessageListener
keyevent_listener_up(
517 std::string("onKeyEvent:") + kExtensionID
+
518 ":keyup:a:KeyQ:false:false:false:false",
521 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
522 send_key_events_test_script
));
524 ASSERT_TRUE(keyevent_listener_down
.WaitUntilSatisfied());
525 EXPECT_TRUE(keyevent_listener_down
.was_satisfied());
526 ASSERT_TRUE(keyevent_listener_up
.WaitUntilSatisfied());
527 EXPECT_TRUE(keyevent_listener_up
.was_satisfied());
530 SCOPED_TRACE("setComposition test");
531 mock_input_context
->Reset();
532 mock_candidate_window
->Reset();
534 const char set_composition_test_script
[] =
535 "chrome.input.ime.setComposition({"
536 " contextID: engineBridge.getFocusedContextID().contextID,"
537 " text:'COMPOSITION_TEXT',"
542 " style: 'underline'"
546 " style: 'doubleUnderline'"
550 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
551 set_composition_test_script
));
552 EXPECT_EQ(1, mock_input_context
->update_preedit_text_call_count());
555 mock_input_context
->last_update_composition_arg().cursor_pos
);
556 EXPECT_TRUE(mock_input_context
->last_update_composition_arg().is_visible
);
558 const CompositionText
& composition_text
=
559 mock_input_context
->last_update_composition_arg().composition_text
;
560 EXPECT_EQ(base::UTF8ToUTF16("COMPOSITION_TEXT"), composition_text
.text());
561 const std::vector
<CompositionText::UnderlineAttribute
>& underlines
=
562 composition_text
.underline_attributes();
564 ASSERT_EQ(2U, underlines
.size());
565 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE
,
567 EXPECT_EQ(0U, underlines
[0].start_index
);
568 EXPECT_EQ(5U, underlines
[0].end_index
);
570 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE
,
572 EXPECT_EQ(6U, underlines
[1].start_index
);
573 EXPECT_EQ(10U, underlines
[1].end_index
);
576 SCOPED_TRACE("clearComposition test");
577 mock_input_context
->Reset();
578 mock_candidate_window
->Reset();
580 const char commite_text_test_script
[] =
581 "chrome.input.ime.clearComposition({"
582 " contextID: engineBridge.getFocusedContextID().contextID,"
585 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
586 commite_text_test_script
));
587 EXPECT_EQ(1, mock_input_context
->update_preedit_text_call_count());
589 mock_input_context
->last_update_composition_arg().is_visible
);
590 const CompositionText
& composition_text
=
591 mock_input_context
->last_update_composition_arg().composition_text
;
592 EXPECT_TRUE(composition_text
.text().empty());
595 SCOPED_TRACE("setCandidateWindowProperties:visibility test");
596 mock_input_context
->Reset();
597 mock_candidate_window
->Reset();
599 const char set_candidate_window_properties_test_script
[] =
600 "chrome.input.ime.setCandidateWindowProperties({"
601 " engineID: engineBridge.getActiveEngineID(),"
606 ASSERT_TRUE(content::ExecuteScript(
607 host
->host_contents(),
608 set_candidate_window_properties_test_script
));
609 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
611 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
614 SCOPED_TRACE("setCandidateWindowProperties:cursor_visibility test");
615 mock_input_context
->Reset();
616 mock_candidate_window
->Reset();
618 const char set_candidate_window_properties_test_script
[] =
619 "chrome.input.ime.setCandidateWindowProperties({"
620 " engineID: engineBridge.getActiveEngineID(),"
622 " cursorVisible: true,"
625 ASSERT_TRUE(content::ExecuteScript(
626 host
->host_contents(),
627 set_candidate_window_properties_test_script
));
628 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
630 // window visibility is kept as before.
632 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
634 const ui::CandidateWindow
& table
=
635 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
636 EXPECT_TRUE(table
.is_cursor_visible());
639 SCOPED_TRACE("setCandidateWindowProperties:vertical test");
640 mock_input_context
->Reset();
641 mock_candidate_window
->Reset();
643 const char set_candidate_window_properties_test_script
[] =
644 "chrome.input.ime.setCandidateWindowProperties({"
645 " engineID: engineBridge.getActiveEngineID(),"
650 ASSERT_TRUE(content::ExecuteScript(
651 host
->host_contents(),
652 set_candidate_window_properties_test_script
));
653 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
655 // window visibility is kept as before.
657 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
659 const ui::CandidateWindow
& table
=
660 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
662 // cursor visibility is kept as before.
663 EXPECT_TRUE(table
.is_cursor_visible());
665 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
668 SCOPED_TRACE("setCandidateWindowProperties:pageSize test");
669 mock_input_context
->Reset();
670 mock_candidate_window
->Reset();
672 const char set_candidate_window_properties_test_script
[] =
673 "chrome.input.ime.setCandidateWindowProperties({"
674 " engineID: engineBridge.getActiveEngineID(),"
679 ASSERT_TRUE(content::ExecuteScript(
680 host
->host_contents(),
681 set_candidate_window_properties_test_script
));
682 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
684 // window visibility is kept as before.
686 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
688 const ui::CandidateWindow
& table
=
689 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
691 // cursor visibility is kept as before.
692 EXPECT_TRUE(table
.is_cursor_visible());
694 // oritantation is kept as before.
695 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
697 EXPECT_EQ(7U, table
.page_size());
700 SCOPED_TRACE("setCandidateWindowProperties:auxTextVisibility test");
701 mock_input_context
->Reset();
702 mock_candidate_window
->Reset();
704 const char set_candidate_window_properties_test_script
[] =
705 "chrome.input.ime.setCandidateWindowProperties({"
706 " engineID: engineBridge.getActiveEngineID(),"
708 " auxiliaryTextVisible: true"
711 ASSERT_TRUE(content::ExecuteScript(
712 host
->host_contents(),
713 set_candidate_window_properties_test_script
));
714 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
716 const ui::CandidateWindow
& table
=
717 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
718 EXPECT_TRUE(table
.is_auxiliary_text_visible());
721 SCOPED_TRACE("setCandidateWindowProperties:auxText test");
722 mock_input_context
->Reset();
723 mock_candidate_window
->Reset();
725 const char set_candidate_window_properties_test_script
[] =
726 "chrome.input.ime.setCandidateWindowProperties({"
727 " engineID: engineBridge.getActiveEngineID(),"
729 " auxiliaryText: 'AUXILIARY_TEXT'"
732 ASSERT_TRUE(content::ExecuteScript(
733 host
->host_contents(),
734 set_candidate_window_properties_test_script
));
735 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
737 // aux text visibility is kept as before.
738 const ui::CandidateWindow
& table
=
739 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
740 EXPECT_TRUE(table
.is_auxiliary_text_visible());
741 EXPECT_EQ("AUXILIARY_TEXT", table
.auxiliary_text());
744 SCOPED_TRACE("setCandidates test");
745 mock_input_context
->Reset();
746 mock_candidate_window
->Reset();
748 const char set_candidates_test_script
[] =
749 "chrome.input.ime.setCandidates({"
750 " contextID: engineBridge.getFocusedContextID().contextID,"
752 " candidate: 'CANDIDATE_1',"
755 " candidate: 'CANDIDATE_2',"
759 " candidate: 'CANDIDATE_3',"
762 " annotation: 'ANNOTACTION_3'"
764 " candidate: 'CANDIDATE_4',"
767 " annotation: 'ANNOTACTION_4',"
774 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
775 set_candidates_test_script
));
777 // window visibility is kept as before.
779 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
781 const ui::CandidateWindow
& table
=
782 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
784 // cursor visibility is kept as before.
785 EXPECT_TRUE(table
.is_cursor_visible());
787 // oritantation is kept as before.
788 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
790 // page size is kept as before.
791 EXPECT_EQ(7U, table
.page_size());
793 ASSERT_EQ(4U, table
.candidates().size());
795 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_1"),
796 table
.candidates().at(0).value
);
798 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_2"),
799 table
.candidates().at(1).value
);
800 EXPECT_EQ(base::UTF8ToUTF16("LABEL_2"), table
.candidates().at(1).label
);
802 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_3"),
803 table
.candidates().at(2).value
);
804 EXPECT_EQ(base::UTF8ToUTF16("LABEL_3"), table
.candidates().at(2).label
);
805 EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_3"),
806 table
.candidates().at(2).annotation
);
808 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_4"),
809 table
.candidates().at(3).value
);
810 EXPECT_EQ(base::UTF8ToUTF16("LABEL_4"), table
.candidates().at(3).label
);
811 EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_4"),
812 table
.candidates().at(3).annotation
);
813 EXPECT_EQ(base::UTF8ToUTF16("TITLE_4"),
814 table
.candidates().at(3).description_title
);
815 EXPECT_EQ(base::UTF8ToUTF16("BODY_4"),
816 table
.candidates().at(3).description_body
);
819 SCOPED_TRACE("setCursorPosition test");
820 mock_input_context
->Reset();
821 mock_candidate_window
->Reset();
823 const char set_cursor_position_test_script
[] =
824 "chrome.input.ime.setCursorPosition({"
825 " contextID: engineBridge.getFocusedContextID().contextID,"
828 ASSERT_TRUE(content::ExecuteScript(
829 host
->host_contents(), set_cursor_position_test_script
));
830 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
832 // window visibility is kept as before.
834 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
836 const ui::CandidateWindow
& table
=
837 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
839 // cursor visibility is kept as before.
840 EXPECT_TRUE(table
.is_cursor_visible());
842 // oritantation is kept as before.
843 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
845 // page size is kept as before.
846 EXPECT_EQ(7U, table
.page_size());
848 // candidates are same as before.
849 ASSERT_EQ(4U, table
.candidates().size());
851 // Candidate ID == 2 is 1 in index.
852 EXPECT_EQ(1U, table
.cursor_position());
855 SCOPED_TRACE("setMenuItem test");
856 mock_input_context
->Reset();
857 mock_candidate_window
->Reset();
859 const char set_menu_item_test_script
[] =
860 "chrome.input.ime.setMenuItems({"
861 " engineID: engineBridge.getActiveEngineID(),"
879 " style: 'separator',"
884 ASSERT_TRUE(content::ExecuteScript(
885 host
->host_contents(), set_menu_item_test_script
));
887 const ui::ime::InputMethodMenuItemList
& props
=
888 ui::ime::InputMethodMenuManager::GetInstance()->
889 GetCurrentInputMethodMenuItemList();
890 ASSERT_EQ(5U, props
.size());
892 EXPECT_EQ("ID0", props
[0].key
);
893 EXPECT_EQ("ID1", props
[1].key
);
894 EXPECT_EQ("ID2", props
[2].key
);
895 EXPECT_EQ("ID3", props
[3].key
);
896 EXPECT_EQ("ID4", props
[4].key
);
898 EXPECT_EQ("LABEL1", props
[1].label
);
899 EXPECT_EQ("LABEL2", props
[2].label
);
900 EXPECT_EQ("LABEL3", props
[3].label
);
901 EXPECT_EQ("LABEL4", props
[4].label
);
903 EXPECT_TRUE(props
[2].is_selection_item
);
904 // TODO(nona): Add tests for style: ["toggle" and "separator"]
905 // and visible:, when implement them.
907 EXPECT_TRUE(props
[4].is_selection_item_checked
);
910 SCOPED_TRACE("deleteSurroundingText test");
911 mock_input_context
->Reset();
912 mock_candidate_window
->Reset();
914 const char delete_surrounding_text_test_script
[] =
915 "chrome.input.ime.deleteSurroundingText({"
916 " engineID: engineBridge.getActiveEngineID(),"
917 " contextID: engineBridge.getFocusedContextID().contextID,"
921 ASSERT_TRUE(content::ExecuteScript(
922 host
->host_contents(), delete_surrounding_text_test_script
));
924 EXPECT_EQ(1, mock_input_context
->delete_surrounding_text_call_count());
925 EXPECT_EQ(5, mock_input_context
->last_delete_surrounding_text_arg().offset
);
927 mock_input_context
->last_delete_surrounding_text_arg().length
);
930 SCOPED_TRACE("onFocus test");
931 mock_input_context
->Reset();
932 mock_candidate_window
->Reset();
935 ExtensionTestMessageListener
focus_listener("onFocus:text", false);
936 IMEEngineHandlerInterface::InputContext
context(
937 ui::TEXT_INPUT_TYPE_TEXT
, ui::TEXT_INPUT_MODE_DEFAULT
,
938 ui::TEXT_INPUT_FLAG_NONE
);
939 engine_handler
->FocusIn(context
);
940 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
941 ASSERT_TRUE(focus_listener
.was_satisfied());
944 ExtensionTestMessageListener
focus_listener("onFocus:search", false);
945 IMEEngineHandlerInterface::InputContext
context(
946 ui::TEXT_INPUT_TYPE_SEARCH
, ui::TEXT_INPUT_MODE_DEFAULT
,
947 ui::TEXT_INPUT_FLAG_NONE
);
948 engine_handler
->FocusIn(context
);
949 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
950 ASSERT_TRUE(focus_listener
.was_satisfied());
953 ExtensionTestMessageListener
focus_listener("onFocus:tel", false);
954 IMEEngineHandlerInterface::InputContext
context(
955 ui::TEXT_INPUT_TYPE_TELEPHONE
, ui::TEXT_INPUT_MODE_DEFAULT
,
956 ui::TEXT_INPUT_FLAG_NONE
);
957 engine_handler
->FocusIn(context
);
958 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
959 ASSERT_TRUE(focus_listener
.was_satisfied());
962 ExtensionTestMessageListener
focus_listener("onFocus:url", false);
963 IMEEngineHandlerInterface::InputContext
context(
964 ui::TEXT_INPUT_TYPE_URL
, ui::TEXT_INPUT_MODE_DEFAULT
,
965 ui::TEXT_INPUT_FLAG_NONE
);
966 engine_handler
->FocusIn(context
);
967 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
968 ASSERT_TRUE(focus_listener
.was_satisfied());
971 ExtensionTestMessageListener
focus_listener("onFocus:email", false);
972 IMEEngineHandlerInterface::InputContext
context(
973 ui::TEXT_INPUT_TYPE_EMAIL
, ui::TEXT_INPUT_MODE_DEFAULT
,
974 ui::TEXT_INPUT_FLAG_NONE
);
975 engine_handler
->FocusIn(context
);
976 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
977 ASSERT_TRUE(focus_listener
.was_satisfied());
980 ExtensionTestMessageListener
focus_listener("onFocus:number", false);
981 IMEEngineHandlerInterface::InputContext
context(
982 ui::TEXT_INPUT_TYPE_NUMBER
, ui::TEXT_INPUT_MODE_DEFAULT
,
983 ui::TEXT_INPUT_FLAG_NONE
);
984 engine_handler
->FocusIn(context
);
985 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
986 ASSERT_TRUE(focus_listener
.was_satisfied());
990 SCOPED_TRACE("changeInputMethod with uncommited text test");
991 // For http://crbug.com/529999.
993 mock_input_context
->Reset();
994 mock_candidate_window
->Reset();
996 EXPECT_TRUE(mock_input_context
->last_commit_text().empty());
998 const char set_composition_test_script
[] =
999 "chrome.input.ime.setComposition({"
1000 " contextID: engineBridge.getFocusedContextID().contextID,"
1006 " style: 'underline'"
1010 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
1011 set_composition_test_script
));
1012 EXPECT_EQ(2U, mock_input_context
->last_update_composition_arg().cursor_pos
);
1013 EXPECT_TRUE(mock_input_context
->last_update_composition_arg().is_visible
);
1015 const CompositionText
& composition_text
=
1016 mock_input_context
->last_update_composition_arg().composition_text
;
1017 EXPECT_EQ(base::UTF8ToUTF16("us"), composition_text
.text());
1018 const std::vector
<CompositionText::UnderlineAttribute
>& underlines
=
1019 composition_text
.underline_attributes();
1021 ASSERT_EQ(1U, underlines
.size());
1022 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE
,
1023 underlines
[0].type
);
1024 EXPECT_EQ(0U, underlines
[0].start_index
);
1025 EXPECT_EQ(1U, underlines
[0].end_index
);
1026 EXPECT_TRUE(mock_input_context
->last_commit_text().empty());
1028 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
1029 kIdentityIMEID
, false /* show_message */);
1030 EXPECT_EQ("us", mock_input_context
->last_commit_text());
1032 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
1033 extension_ime_util::GetInputMethodIDByEngineID("zh-t-i0-pinyin"),
1034 false /* show_message */);
1035 EXPECT_EQ("", mock_input_context
->last_commit_text());
1037 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
1038 extension_ime_util::GetInputMethodIDByEngineID("xkb:us::eng"),
1039 false /* show_message */);
1040 EXPECT_EQ("", mock_input_context
->last_commit_text());
1043 IMEBridge::Get()->SetInputContextHandler(NULL
);
1044 IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
1048 } // namespace input_method
1049 } // namespace chromeos