Home | History | Annotate | Download | only in input_method
      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