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