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/ime_bridge.h"
17 #include "ui/base/ime/chromeos/input_method_descriptor.h"
18 #include "ui/base/ime/chromeos/input_method_manager.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/base/ime/text_input_flags.h"
22 #include "ui/chromeos/ime/input_method_menu_item.h"
23 #include "ui/chromeos/ime/input_method_menu_manager.h"
24 #include "ui/events/event.h"
25 #include "ui/events/keycodes/dom/dom_code.h"
26 #include "ui/events/keycodes/dom/keycode_converter.h"
29 namespace input_method
{
32 const char kIdentityIMEID
[] =
33 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpIdentityIME";
34 const char kToUpperIMEID
[] =
35 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpToUpperIME";
36 const char kAPIArgumentIMEID
[] =
37 "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpAPIArgumentIME";
38 const char kExtensionID
[] = "iafoklpfplgfnoimmaejoeondnjnlcfp";
40 // InputMethod extension should work on 1)normal extension, 2)normal extension
41 // in incognito mode 3)component extension.
44 kTestTypeIncognito
= 1,
45 kTestTypeComponent
= 2,
48 class InputMethodEngineBrowserTest
49 : public ExtensionBrowserTest
,
50 public ::testing::WithParamInterface
<TestType
> {
52 InputMethodEngineBrowserTest()
53 : ExtensionBrowserTest() {}
54 virtual ~InputMethodEngineBrowserTest() {}
56 void SetUpInProcessBrowserTestFixture() override
{
57 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
60 void TearDownInProcessBrowserTestFixture() override
{ extension_
= NULL
; }
63 void LoadTestInputMethod() {
64 // This will load "chrome/test/data/extensions/input_ime"
65 ExtensionTestMessageListener
ime_ready_listener("ReadyToUseImeEvent",
67 extension_
= LoadExtensionWithType("input_ime", GetParam());
68 ASSERT_TRUE(extension_
);
69 ASSERT_TRUE(ime_ready_listener
.WaitUntilSatisfied());
71 // Extension IMEs are not enabled by default.
72 std::vector
<std::string
> extension_ime_ids
;
73 extension_ime_ids
.push_back(kIdentityIMEID
);
74 extension_ime_ids
.push_back(kToUpperIMEID
);
75 extension_ime_ids
.push_back(kAPIArgumentIMEID
);
76 InputMethodManager::Get()->GetActiveIMEState()->SetEnabledExtensionImes(
79 InputMethodDescriptors extension_imes
;
80 InputMethodManager::Get()->GetActiveIMEState()->GetInputMethodExtensions(
83 // Test IME has two input methods, thus InputMethodManager should have two
85 // Note: Even extension is loaded by LoadExtensionAsComponent as above, the
86 // IME does not managed by ComponentExtensionIMEManager or it's id won't
87 // start with __comp__. The component extension IME is whitelisted and
88 // managed by ComponentExtensionIMEManager, but its framework is same as
89 // normal extension IME.
90 EXPECT_EQ(3U, extension_imes
.size());
93 const extensions::Extension
* LoadExtensionWithType(
94 const std::string
& extension_name
, TestType type
) {
97 return LoadExtension(test_data_dir_
.AppendASCII(extension_name
));
98 case kTestTypeIncognito
:
99 return LoadExtensionIncognito(
100 test_data_dir_
.AppendASCII(extension_name
));
101 case kTestTypeComponent
:
102 return LoadExtensionAsComponent(
103 test_data_dir_
.AppendASCII(extension_name
));
109 const extensions::Extension
* extension_
;
112 class KeyEventDoneCallback
{
114 explicit KeyEventDoneCallback(bool expected_argument
)
115 : expected_argument_(expected_argument
),
117 ~KeyEventDoneCallback() {}
119 void Run(bool consumed
) {
120 if (consumed
== expected_argument_
) {
121 base::MessageLoop::current()->Quit();
126 void WaitUntilCalled() {
128 content::RunMessageLoop();
132 bool expected_argument_
;
135 DISALLOW_COPY_AND_ASSIGN(KeyEventDoneCallback
);
138 INSTANTIATE_TEST_CASE_P(InputMethodEngineBrowserTest
,
139 InputMethodEngineBrowserTest
,
140 ::testing::Values(kTestTypeNormal
));
141 INSTANTIATE_TEST_CASE_P(InputMethodEngineIncognitoBrowserTest
,
142 InputMethodEngineBrowserTest
,
143 ::testing::Values(kTestTypeIncognito
));
144 INSTANTIATE_TEST_CASE_P(InputMethodEngineComponentExtensionBrowserTest
,
145 InputMethodEngineBrowserTest
,
146 ::testing::Values(kTestTypeComponent
));
148 IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest
,
150 LoadTestInputMethod();
152 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
153 kIdentityIMEID
, false /* show_message */);
155 scoped_ptr
<MockIMEInputContextHandler
> mock_input_context(
156 new MockIMEInputContextHandler());
157 scoped_ptr
<MockIMECandidateWindowHandler
> mock_candidate_window(
158 new MockIMECandidateWindowHandler());
160 IMEBridge::Get()->SetInputContextHandler(mock_input_context
.get());
161 IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window
.get());
163 IMEEngineHandlerInterface
* engine_handler
=
164 IMEBridge::Get()->GetCurrentEngineHandler();
165 ASSERT_TRUE(engine_handler
);
167 // onActivate event should be fired if Enable function is called.
168 ExtensionTestMessageListener
activated_listener("onActivate", false);
169 engine_handler
->Enable("IdentityIME");
170 ASSERT_TRUE(activated_listener
.WaitUntilSatisfied());
171 ASSERT_TRUE(activated_listener
.was_satisfied());
173 // onFocus event should be fired if FocusIn function is called.
174 ExtensionTestMessageListener
focus_listener("onFocus:text", false);
175 IMEEngineHandlerInterface::InputContext
context(ui::TEXT_INPUT_TYPE_TEXT
,
176 ui::TEXT_INPUT_MODE_DEFAULT
,
177 ui::TEXT_INPUT_FLAG_NONE
);
178 engine_handler
->FocusIn(context
);
179 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
180 ASSERT_TRUE(focus_listener
.was_satisfied());
182 // onKeyEvent should be fired if ProcessKeyEvent is called.
183 KeyEventDoneCallback
callback(false); // EchoBackIME doesn't consume keys.
184 ExtensionTestMessageListener
keyevent_listener("onKeyEvent", false);
185 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
, ui::VKEY_A
, ui::EF_NONE
);
186 engine_handler
->ProcessKeyEvent(key_event
,
187 base::Bind(&KeyEventDoneCallback::Run
,
188 base::Unretained(&callback
)));
189 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
190 ASSERT_TRUE(keyevent_listener
.was_satisfied());
191 callback
.WaitUntilCalled();
193 // onSurroundingTextChange should be fired if SetSurroundingText is called.
194 ExtensionTestMessageListener
surrounding_text_listener(
195 "onSurroundingTextChanged", false);
196 engine_handler
->SetSurroundingText("text", // Surrounding text.
197 0, // focused position.
198 1, // anchor position.
199 0); // offset position.
200 ASSERT_TRUE(surrounding_text_listener
.WaitUntilSatisfied());
201 ASSERT_TRUE(surrounding_text_listener
.was_satisfied());
203 // onMenuItemActivated should be fired if PropertyActivate is called.
204 ExtensionTestMessageListener
property_listener("onMenuItemActivated", false);
205 engine_handler
->PropertyActivate("property_name");
206 ASSERT_TRUE(property_listener
.WaitUntilSatisfied());
207 ASSERT_TRUE(property_listener
.was_satisfied());
209 // onReset should be fired if Reset is called.
210 ExtensionTestMessageListener
reset_listener("onReset", false);
211 engine_handler
->Reset();
212 ASSERT_TRUE(reset_listener
.WaitUntilSatisfied());
213 ASSERT_TRUE(reset_listener
.was_satisfied());
215 // onBlur should be fired if FocusOut is called.
216 ExtensionTestMessageListener
blur_listener("onBlur", false);
217 engine_handler
->FocusOut();
218 ASSERT_TRUE(blur_listener
.WaitUntilSatisfied());
219 ASSERT_TRUE(blur_listener
.was_satisfied());
221 // onDeactivated should be fired if Disable is called.
222 ExtensionTestMessageListener
disabled_listener("onDeactivated", false);
223 engine_handler
->Disable();
224 ASSERT_TRUE(disabled_listener
.WaitUntilSatisfied());
225 ASSERT_TRUE(disabled_listener
.was_satisfied());
227 IMEBridge::Get()->SetInputContextHandler(NULL
);
228 IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
231 IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest
,
233 LoadTestInputMethod();
235 InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
236 kAPIArgumentIMEID
, false /* show_message */);
238 scoped_ptr
<MockIMEInputContextHandler
> mock_input_context(
239 new MockIMEInputContextHandler());
240 scoped_ptr
<MockIMECandidateWindowHandler
> mock_candidate_window(
241 new MockIMECandidateWindowHandler());
243 IMEBridge::Get()->SetInputContextHandler(mock_input_context
.get());
244 IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window
.get());
246 IMEEngineHandlerInterface
* engine_handler
=
247 IMEBridge::Get()->GetCurrentEngineHandler();
248 ASSERT_TRUE(engine_handler
);
250 extensions::ExtensionHost
* host
=
251 extensions::ProcessManager::Get(profile())
252 ->GetBackgroundHostForExtension(extension_
->id());
255 engine_handler
->Enable("APIArgumentIME");
256 IMEEngineHandlerInterface::InputContext
context(ui::TEXT_INPUT_TYPE_TEXT
,
257 ui::TEXT_INPUT_MODE_DEFAULT
,
258 ui::TEXT_INPUT_FLAG_NONE
);
259 engine_handler
->FocusIn(context
);
262 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:No");
263 KeyEventDoneCallback
callback(false);
264 const std::string expected_value
=
265 "onKeyEvent::keydown:a:KeyA:false:false:false:false";
266 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
268 ui::KeyEvent
key_event(
269 ui::ET_KEY_PRESSED
, ui::VKEY_A
, ui::DomCode::KEY_A
, ui::EF_NONE
);
270 engine_handler
->ProcessKeyEvent(key_event
,
271 base::Bind(&KeyEventDoneCallback::Run
,
272 base::Unretained(&callback
)));
273 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
274 EXPECT_TRUE(keyevent_listener
.was_satisfied());
275 callback
.WaitUntilCalled();
278 SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:No, Shift:No, Caps:No");
279 KeyEventDoneCallback
callback(false);
280 const std::string expected_value
=
281 "onKeyEvent::keydown:a:KeyA:true:false:false:false";
282 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
284 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
287 ui::EF_CONTROL_DOWN
);
288 engine_handler
->ProcessKeyEvent(key_event
,
289 base::Bind(&KeyEventDoneCallback::Run
,
290 base::Unretained(&callback
)));
291 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
292 EXPECT_TRUE(keyevent_listener
.was_satisfied());
293 callback
.WaitUntilCalled();
296 SCOPED_TRACE("KeyDown, Ctrl:No, alt:Yes, Shift:No, Caps:No");
297 KeyEventDoneCallback
callback(false);
298 const std::string expected_value
=
299 "onKeyEvent::keydown:a:KeyA:false:true:false:false";
300 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
302 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
306 engine_handler
->ProcessKeyEvent(key_event
,
307 base::Bind(&KeyEventDoneCallback::Run
,
308 base::Unretained(&callback
)));
309 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
310 EXPECT_TRUE(keyevent_listener
.was_satisfied());
311 callback
.WaitUntilCalled();
314 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:No");
315 KeyEventDoneCallback
callback(false);
316 const std::string expected_value
=
317 "onKeyEvent::keydown:A:KeyA:false:false:true:false";
318 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
320 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
324 engine_handler
->ProcessKeyEvent(key_event
,
325 base::Bind(&KeyEventDoneCallback::Run
,
326 base::Unretained(&callback
)));
327 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
328 EXPECT_TRUE(keyevent_listener
.was_satisfied());
329 callback
.WaitUntilCalled();
332 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:Yes");
333 KeyEventDoneCallback
callback(false);
334 const std::string expected_value
=
335 "onKeyEvent::keydown:A:KeyA:false:false:false:true";
336 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
338 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
341 ui::EF_CAPS_LOCK_DOWN
);
342 engine_handler
->ProcessKeyEvent(key_event
,
343 base::Bind(&KeyEventDoneCallback::Run
,
344 base::Unretained(&callback
)));
345 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
346 EXPECT_TRUE(keyevent_listener
.was_satisfied());
347 callback
.WaitUntilCalled();
350 SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:Yes, Shift:No, Caps:No");
351 KeyEventDoneCallback
callback(false);
352 const std::string expected_value
=
353 "onKeyEvent::keydown:a:KeyA:true:true:false:false";
354 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
356 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
359 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
);
360 engine_handler
->ProcessKeyEvent(key_event
,
361 base::Bind(&KeyEventDoneCallback::Run
,
362 base::Unretained(&callback
)));
363 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
364 EXPECT_TRUE(keyevent_listener
.was_satisfied());
365 callback
.WaitUntilCalled();
368 SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:Yes");
369 KeyEventDoneCallback
callback(false);
370 const std::string expected_value
=
371 "onKeyEvent::keydown:a:KeyA:false:false:true:true";
372 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
374 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
,
377 ui::EF_SHIFT_DOWN
| ui::EF_CAPS_LOCK_DOWN
);
378 engine_handler
->ProcessKeyEvent(key_event
,
379 base::Bind(&KeyEventDoneCallback::Run
,
380 base::Unretained(&callback
)));
381 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
382 EXPECT_TRUE(keyevent_listener
.was_satisfied());
383 callback
.WaitUntilCalled();
387 ui::KeyboardCode keycode
;
390 } kMediaKeyCases
[] = {
391 { ui::VKEY_BROWSER_BACK
, "BrowserBack", "HistoryBack" },
392 { ui::VKEY_BROWSER_FORWARD
, "BrowserForward", "HistoryForward" },
393 { ui::VKEY_BROWSER_REFRESH
, "BrowserRefresh", "BrowserRefresh" },
394 { ui::VKEY_MEDIA_LAUNCH_APP2
, "ChromeOSFullscreen", "ChromeOSFullscreen" },
395 { ui::VKEY_MEDIA_LAUNCH_APP1
,
396 "ChromeOSSwitchWindow", "ChromeOSSwitchWindow" },
397 { ui::VKEY_BRIGHTNESS_DOWN
, "BrightnessDown", "BrightnessDown" },
398 { ui::VKEY_BRIGHTNESS_UP
, "BrightnessUp", "BrightnessUp" },
399 { ui::VKEY_VOLUME_MUTE
, "VolumeMute", "AudioVolumeMute" },
400 { ui::VKEY_VOLUME_DOWN
, "VolumeDown", "AudioVolumeDown" },
401 { ui::VKEY_VOLUME_UP
, "VolumeUp", "AudioVolumeUp" },
402 { ui::VKEY_F1
, "F1", "HistoryBack" },
403 { ui::VKEY_F2
, "F2", "HistoryForward" },
404 { ui::VKEY_F3
, "F3", "BrowserRefresh" },
405 { ui::VKEY_F4
, "F4", "ChromeOSFullscreen" },
406 { ui::VKEY_F5
, "F5", "ChromeOSSwitchWindow" },
407 { ui::VKEY_F6
, "F6", "BrightnessDown" },
408 { ui::VKEY_F7
, "F7", "BrightnessUp" },
409 { ui::VKEY_F8
, "F8", "AudioVolumeMute" },
410 { ui::VKEY_F9
, "F9", "AudioVolumeDown" },
411 { ui::VKEY_F10
, "F10", "AudioVolumeUp" },
413 for (size_t i
= 0; i
< arraysize(kMediaKeyCases
); ++i
) {
414 SCOPED_TRACE(std::string("KeyDown, ") + kMediaKeyCases
[i
].code
);
415 KeyEventDoneCallback
callback(false);
416 const std::string expected_value
=
417 base::StringPrintf("onKeyEvent::keydown:%s:%s:false:false:false:false",
418 kMediaKeyCases
[i
].key
, kMediaKeyCases
[i
].code
);
419 ExtensionTestMessageListener
keyevent_listener(expected_value
, false);
421 ui::KeyEvent
key_event(
423 kMediaKeyCases
[i
].keycode
,
424 ui::KeycodeConverter::CodeStringToDomCode(kMediaKeyCases
[i
].code
),
426 engine_handler
->ProcessKeyEvent(key_event
,
427 base::Bind(&KeyEventDoneCallback::Run
,
428 base::Unretained(&callback
)));
429 ASSERT_TRUE(keyevent_listener
.WaitUntilSatisfied());
430 EXPECT_TRUE(keyevent_listener
.was_satisfied());
431 callback
.WaitUntilCalled();
433 // TODO(nona): Add browser tests for other API as well.
435 SCOPED_TRACE("commitText test");
436 mock_input_context
->Reset();
437 mock_candidate_window
->Reset();
439 const char commit_text_test_script
[] =
440 "chrome.input.ime.commitText({"
441 " contextID: engineBridge.getFocusedContextID().contextID,"
442 " text:'COMMIT_TEXT'"
445 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
446 commit_text_test_script
));
447 EXPECT_EQ(1, mock_input_context
->commit_text_call_count());
448 EXPECT_EQ("COMMIT_TEXT", mock_input_context
->last_commit_text());
451 SCOPED_TRACE("sendKeyEvents test");
452 mock_input_context
->Reset();
453 mock_candidate_window
->Reset();
455 const char send_key_events_test_script
[] =
456 "chrome.input.ime.sendKeyEvents({"
457 " contextID: engineBridge.getFocusedContextID().contextID,"
471 ExtensionTestMessageListener
keyevent_listener_down(
472 std::string("onKeyEvent:") + kExtensionID
+
473 ":keydown:z:KeyZ:false:false:false:false",
475 ExtensionTestMessageListener
keyevent_listener_up(
476 std::string("onKeyEvent:") + kExtensionID
+
477 ":keyup:z:KeyZ:false:false:false:false",
480 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
481 send_key_events_test_script
));
483 ASSERT_TRUE(keyevent_listener_down
.WaitUntilSatisfied());
484 EXPECT_TRUE(keyevent_listener_down
.was_satisfied());
485 ASSERT_TRUE(keyevent_listener_up
.WaitUntilSatisfied());
486 EXPECT_TRUE(keyevent_listener_up
.was_satisfied());
489 SCOPED_TRACE("sendKeyEvents test with keyCode");
490 mock_input_context
->Reset();
491 mock_candidate_window
->Reset();
493 const char send_key_events_test_script
[] =
494 "chrome.input.ime.sendKeyEvents({"
495 " contextID: engineBridge.getFocusedContextID().contextID,"
511 ExtensionTestMessageListener
keyevent_listener_down(
512 std::string("onKeyEvent:") + kExtensionID
+
513 ":keydown:a:KeyQ:false:false:false:false",
515 ExtensionTestMessageListener
keyevent_listener_up(
516 std::string("onKeyEvent:") + kExtensionID
+
517 ":keyup:a:KeyQ:false:false:false:false",
520 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
521 send_key_events_test_script
));
523 ASSERT_TRUE(keyevent_listener_down
.WaitUntilSatisfied());
524 EXPECT_TRUE(keyevent_listener_down
.was_satisfied());
525 ASSERT_TRUE(keyevent_listener_up
.WaitUntilSatisfied());
526 EXPECT_TRUE(keyevent_listener_up
.was_satisfied());
529 SCOPED_TRACE("setComposition test");
530 mock_input_context
->Reset();
531 mock_candidate_window
->Reset();
533 const char set_composition_test_script
[] =
534 "chrome.input.ime.setComposition({"
535 " contextID: engineBridge.getFocusedContextID().contextID,"
536 " text:'COMPOSITION_TEXT',"
541 " style: 'underline'"
545 " style: 'doubleUnderline'"
549 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
550 set_composition_test_script
));
551 EXPECT_EQ(1, mock_input_context
->update_preedit_text_call_count());
554 mock_input_context
->last_update_composition_arg().cursor_pos
);
555 EXPECT_TRUE(mock_input_context
->last_update_composition_arg().is_visible
);
557 const CompositionText
& composition_text
=
558 mock_input_context
->last_update_composition_arg().composition_text
;
559 EXPECT_EQ(base::UTF8ToUTF16("COMPOSITION_TEXT"), composition_text
.text());
560 const std::vector
<CompositionText::UnderlineAttribute
>& underlines
=
561 composition_text
.underline_attributes();
563 ASSERT_EQ(2U, underlines
.size());
564 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE
,
566 EXPECT_EQ(0U, underlines
[0].start_index
);
567 EXPECT_EQ(5U, underlines
[0].end_index
);
569 EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE
,
571 EXPECT_EQ(6U, underlines
[1].start_index
);
572 EXPECT_EQ(10U, underlines
[1].end_index
);
575 SCOPED_TRACE("clearComposition test");
576 mock_input_context
->Reset();
577 mock_candidate_window
->Reset();
579 const char commite_text_test_script
[] =
580 "chrome.input.ime.clearComposition({"
581 " contextID: engineBridge.getFocusedContextID().contextID,"
584 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
585 commite_text_test_script
));
586 EXPECT_EQ(1, mock_input_context
->update_preedit_text_call_count());
588 mock_input_context
->last_update_composition_arg().is_visible
);
589 const CompositionText
& composition_text
=
590 mock_input_context
->last_update_composition_arg().composition_text
;
591 EXPECT_TRUE(composition_text
.text().empty());
594 SCOPED_TRACE("setCandidateWindowProperties:visibility test");
595 mock_input_context
->Reset();
596 mock_candidate_window
->Reset();
598 const char set_candidate_window_properties_test_script
[] =
599 "chrome.input.ime.setCandidateWindowProperties({"
600 " engineID: engineBridge.getActiveEngineID(),"
605 ASSERT_TRUE(content::ExecuteScript(
606 host
->host_contents(),
607 set_candidate_window_properties_test_script
));
608 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
610 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
613 SCOPED_TRACE("setCandidateWindowProperties:cursor_visibility test");
614 mock_input_context
->Reset();
615 mock_candidate_window
->Reset();
617 const char set_candidate_window_properties_test_script
[] =
618 "chrome.input.ime.setCandidateWindowProperties({"
619 " engineID: engineBridge.getActiveEngineID(),"
621 " cursorVisible: true,"
624 ASSERT_TRUE(content::ExecuteScript(
625 host
->host_contents(),
626 set_candidate_window_properties_test_script
));
627 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
629 // window visibility is kept as before.
631 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
633 const ui::CandidateWindow
& table
=
634 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
635 EXPECT_TRUE(table
.is_cursor_visible());
638 SCOPED_TRACE("setCandidateWindowProperties:vertical test");
639 mock_input_context
->Reset();
640 mock_candidate_window
->Reset();
642 const char set_candidate_window_properties_test_script
[] =
643 "chrome.input.ime.setCandidateWindowProperties({"
644 " engineID: engineBridge.getActiveEngineID(),"
649 ASSERT_TRUE(content::ExecuteScript(
650 host
->host_contents(),
651 set_candidate_window_properties_test_script
));
652 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
654 // window visibility is kept as before.
656 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
658 const ui::CandidateWindow
& table
=
659 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
661 // cursor visibility is kept as before.
662 EXPECT_TRUE(table
.is_cursor_visible());
664 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
667 SCOPED_TRACE("setCandidateWindowProperties:pageSize test");
668 mock_input_context
->Reset();
669 mock_candidate_window
->Reset();
671 const char set_candidate_window_properties_test_script
[] =
672 "chrome.input.ime.setCandidateWindowProperties({"
673 " engineID: engineBridge.getActiveEngineID(),"
678 ASSERT_TRUE(content::ExecuteScript(
679 host
->host_contents(),
680 set_candidate_window_properties_test_script
));
681 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
683 // window visibility is kept as before.
685 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
687 const ui::CandidateWindow
& table
=
688 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
690 // cursor visibility is kept as before.
691 EXPECT_TRUE(table
.is_cursor_visible());
693 // oritantation is kept as before.
694 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
696 EXPECT_EQ(7U, table
.page_size());
699 SCOPED_TRACE("setCandidateWindowProperties:auxTextVisibility test");
700 mock_input_context
->Reset();
701 mock_candidate_window
->Reset();
703 const char set_candidate_window_properties_test_script
[] =
704 "chrome.input.ime.setCandidateWindowProperties({"
705 " engineID: engineBridge.getActiveEngineID(),"
707 " auxiliaryTextVisible: true"
710 ASSERT_TRUE(content::ExecuteScript(
711 host
->host_contents(),
712 set_candidate_window_properties_test_script
));
713 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
715 const ui::CandidateWindow
& table
=
716 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
717 EXPECT_TRUE(table
.is_auxiliary_text_visible());
720 SCOPED_TRACE("setCandidateWindowProperties:auxText test");
721 mock_input_context
->Reset();
722 mock_candidate_window
->Reset();
724 const char set_candidate_window_properties_test_script
[] =
725 "chrome.input.ime.setCandidateWindowProperties({"
726 " engineID: engineBridge.getActiveEngineID(),"
728 " auxiliaryText: 'AUXILIARY_TEXT'"
731 ASSERT_TRUE(content::ExecuteScript(
732 host
->host_contents(),
733 set_candidate_window_properties_test_script
));
734 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
736 // aux text visibility is kept as before.
737 const ui::CandidateWindow
& table
=
738 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
739 EXPECT_TRUE(table
.is_auxiliary_text_visible());
740 EXPECT_EQ("AUXILIARY_TEXT", table
.auxiliary_text());
743 SCOPED_TRACE("setCandidates test");
744 mock_input_context
->Reset();
745 mock_candidate_window
->Reset();
747 const char set_candidates_test_script
[] =
748 "chrome.input.ime.setCandidates({"
749 " contextID: engineBridge.getFocusedContextID().contextID,"
751 " candidate: 'CANDIDATE_1',"
754 " candidate: 'CANDIDATE_2',"
758 " candidate: 'CANDIDATE_3',"
761 " annotation: 'ANNOTACTION_3'"
763 " candidate: 'CANDIDATE_4',"
766 " annotation: 'ANNOTACTION_4',"
773 ASSERT_TRUE(content::ExecuteScript(host
->host_contents(),
774 set_candidates_test_script
));
776 // window visibility is kept as before.
778 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
780 const ui::CandidateWindow
& table
=
781 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
783 // cursor visibility is kept as before.
784 EXPECT_TRUE(table
.is_cursor_visible());
786 // oritantation is kept as before.
787 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
789 // page size is kept as before.
790 EXPECT_EQ(7U, table
.page_size());
792 ASSERT_EQ(4U, table
.candidates().size());
794 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_1"),
795 table
.candidates().at(0).value
);
797 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_2"),
798 table
.candidates().at(1).value
);
799 EXPECT_EQ(base::UTF8ToUTF16("LABEL_2"), table
.candidates().at(1).label
);
801 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_3"),
802 table
.candidates().at(2).value
);
803 EXPECT_EQ(base::UTF8ToUTF16("LABEL_3"), table
.candidates().at(2).label
);
804 EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_3"),
805 table
.candidates().at(2).annotation
);
807 EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_4"),
808 table
.candidates().at(3).value
);
809 EXPECT_EQ(base::UTF8ToUTF16("LABEL_4"), table
.candidates().at(3).label
);
810 EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_4"),
811 table
.candidates().at(3).annotation
);
812 EXPECT_EQ(base::UTF8ToUTF16("TITLE_4"),
813 table
.candidates().at(3).description_title
);
814 EXPECT_EQ(base::UTF8ToUTF16("BODY_4"),
815 table
.candidates().at(3).description_body
);
818 SCOPED_TRACE("setCursorPosition test");
819 mock_input_context
->Reset();
820 mock_candidate_window
->Reset();
822 const char set_cursor_position_test_script
[] =
823 "chrome.input.ime.setCursorPosition({"
824 " contextID: engineBridge.getFocusedContextID().contextID,"
827 ASSERT_TRUE(content::ExecuteScript(
828 host
->host_contents(), set_cursor_position_test_script
));
829 EXPECT_EQ(1, mock_candidate_window
->update_lookup_table_call_count());
831 // window visibility is kept as before.
833 mock_candidate_window
->last_update_lookup_table_arg().is_visible
);
835 const ui::CandidateWindow
& table
=
836 mock_candidate_window
->last_update_lookup_table_arg().lookup_table
;
838 // cursor visibility is kept as before.
839 EXPECT_TRUE(table
.is_cursor_visible());
841 // oritantation is kept as before.
842 EXPECT_EQ(ui::CandidateWindow::VERTICAL
, table
.orientation());
844 // page size is kept as before.
845 EXPECT_EQ(7U, table
.page_size());
847 // candidates are same as before.
848 ASSERT_EQ(4U, table
.candidates().size());
850 // Candidate ID == 2 is 1 in index.
851 EXPECT_EQ(1U, table
.cursor_position());
854 SCOPED_TRACE("setMenuItem test");
855 mock_input_context
->Reset();
856 mock_candidate_window
->Reset();
858 const char set_menu_item_test_script
[] =
859 "chrome.input.ime.setMenuItems({"
860 " engineID: engineBridge.getActiveEngineID(),"
878 " style: 'separator',"
883 ASSERT_TRUE(content::ExecuteScript(
884 host
->host_contents(), set_menu_item_test_script
));
886 const ui::ime::InputMethodMenuItemList
& props
=
887 ui::ime::InputMethodMenuManager::GetInstance()->
888 GetCurrentInputMethodMenuItemList();
889 ASSERT_EQ(5U, props
.size());
891 EXPECT_EQ("ID0", props
[0].key
);
892 EXPECT_EQ("ID1", props
[1].key
);
893 EXPECT_EQ("ID2", props
[2].key
);
894 EXPECT_EQ("ID3", props
[3].key
);
895 EXPECT_EQ("ID4", props
[4].key
);
897 EXPECT_EQ("LABEL1", props
[1].label
);
898 EXPECT_EQ("LABEL2", props
[2].label
);
899 EXPECT_EQ("LABEL3", props
[3].label
);
900 EXPECT_EQ("LABEL4", props
[4].label
);
902 EXPECT_TRUE(props
[2].is_selection_item
);
903 // TODO(nona): Add tests for style: ["toggle" and "separator"]
904 // and visible:, when implement them.
906 EXPECT_TRUE(props
[4].is_selection_item_checked
);
909 SCOPED_TRACE("deleteSurroundingText test");
910 mock_input_context
->Reset();
911 mock_candidate_window
->Reset();
913 const char delete_surrounding_text_test_script
[] =
914 "chrome.input.ime.deleteSurroundingText({"
915 " engineID: engineBridge.getActiveEngineID(),"
916 " contextID: engineBridge.getFocusedContextID().contextID,"
920 ASSERT_TRUE(content::ExecuteScript(
921 host
->host_contents(), delete_surrounding_text_test_script
));
923 EXPECT_EQ(1, mock_input_context
->delete_surrounding_text_call_count());
924 EXPECT_EQ(5, mock_input_context
->last_delete_surrounding_text_arg().offset
);
926 mock_input_context
->last_delete_surrounding_text_arg().length
);
929 SCOPED_TRACE("onFocus test");
930 mock_input_context
->Reset();
931 mock_candidate_window
->Reset();
934 ExtensionTestMessageListener
focus_listener("onFocus:text", false);
935 IMEEngineHandlerInterface::InputContext
context(
936 ui::TEXT_INPUT_TYPE_TEXT
, ui::TEXT_INPUT_MODE_DEFAULT
,
937 ui::TEXT_INPUT_FLAG_NONE
);
938 engine_handler
->FocusIn(context
);
939 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
940 ASSERT_TRUE(focus_listener
.was_satisfied());
943 ExtensionTestMessageListener
focus_listener("onFocus:search", false);
944 IMEEngineHandlerInterface::InputContext
context(
945 ui::TEXT_INPUT_TYPE_SEARCH
, ui::TEXT_INPUT_MODE_DEFAULT
,
946 ui::TEXT_INPUT_FLAG_NONE
);
947 engine_handler
->FocusIn(context
);
948 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
949 ASSERT_TRUE(focus_listener
.was_satisfied());
952 ExtensionTestMessageListener
focus_listener("onFocus:tel", false);
953 IMEEngineHandlerInterface::InputContext
context(
954 ui::TEXT_INPUT_TYPE_TELEPHONE
, ui::TEXT_INPUT_MODE_DEFAULT
,
955 ui::TEXT_INPUT_FLAG_NONE
);
956 engine_handler
->FocusIn(context
);
957 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
958 ASSERT_TRUE(focus_listener
.was_satisfied());
961 ExtensionTestMessageListener
focus_listener("onFocus:url", false);
962 IMEEngineHandlerInterface::InputContext
context(
963 ui::TEXT_INPUT_TYPE_URL
, ui::TEXT_INPUT_MODE_DEFAULT
,
964 ui::TEXT_INPUT_FLAG_NONE
);
965 engine_handler
->FocusIn(context
);
966 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
967 ASSERT_TRUE(focus_listener
.was_satisfied());
970 ExtensionTestMessageListener
focus_listener("onFocus:email", false);
971 IMEEngineHandlerInterface::InputContext
context(
972 ui::TEXT_INPUT_TYPE_EMAIL
, ui::TEXT_INPUT_MODE_DEFAULT
,
973 ui::TEXT_INPUT_FLAG_NONE
);
974 engine_handler
->FocusIn(context
);
975 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
976 ASSERT_TRUE(focus_listener
.was_satisfied());
979 ExtensionTestMessageListener
focus_listener("onFocus:number", false);
980 IMEEngineHandlerInterface::InputContext
context(
981 ui::TEXT_INPUT_TYPE_NUMBER
, ui::TEXT_INPUT_MODE_DEFAULT
,
982 ui::TEXT_INPUT_FLAG_NONE
);
983 engine_handler
->FocusIn(context
);
984 ASSERT_TRUE(focus_listener
.WaitUntilSatisfied());
985 ASSERT_TRUE(focus_listener
.was_satisfied());
989 IMEBridge::Get()->SetInputContextHandler(NULL
);
990 IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
994 } // namespace input_method
995 } // namespace chromeos