root/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SetUpInProcessBrowserTestFixture
  2. TearDownInProcessBrowserTestFixture
  3. LoadTestInputMethod
  4. LoadExtensionWithType
  5. is_called_
  6. Run
  7. WaitUntilCalled
  8. IN_PROC_BROWSER_TEST_P
  9. IN_PROC_BROWSER_TEST_P

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/ime/input_method_menu_item.h"
#include "ash/ime/input_method_menu_manager.h"
#include "base/bind_helpers.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "chromeos/ime/component_extension_ime_manager.h"
#include "chromeos/ime/composition_text.h"
#include "chromeos/ime/input_method_descriptor.h"
#include "chromeos/ime/input_method_manager.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "ui/base/ime/chromeos/ime_bridge.h"
#include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
#include "ui/base/ime/chromeos/mock_ime_input_context_handler.h"
#include "ui/events/event.h"

namespace chromeos {
namespace input_method {
namespace {

const char kIdentityIMEID[] =
    "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpIdentityIME";
const char kToUpperIMEID[] =
    "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpToUpperIME";
const char kAPIArgumentIMEID[] =
    "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpAPIArgumentIME";
const char kExtensionID[] = "iafoklpfplgfnoimmaejoeondnjnlcfp";

// InputMethod extension should work on 1)normal extension, 2)normal extension
// in incognito mode 3)component extension.
enum TestType {
  kTestTypeNormal = 0,
  kTestTypeIncognito = 1,
  kTestTypeComponent = 2,
};

class InputMethodEngineBrowserTest
    : public ExtensionBrowserTest,
      public ::testing::WithParamInterface<TestType> {
 public:
  InputMethodEngineBrowserTest()
      : ExtensionBrowserTest() {}
  virtual ~InputMethodEngineBrowserTest() {}

  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
  }

  virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
    extension_ = NULL;
  }

 protected:
  void LoadTestInputMethod() {
    // This will load "chrome/test/data/extensions/input_ime"
    ExtensionTestMessageListener ime_ready_listener("ReadyToUseImeEvent",
                                                    false);
    extension_ = LoadExtensionWithType("input_ime", GetParam());
    ASSERT_TRUE(extension_);
    ASSERT_TRUE(ime_ready_listener.WaitUntilSatisfied());

    // Make sure ComponentExtensionIMEManager is initialized.
    // ComponentExtensionIMEManagerImpl::InitializeAsync posts
    // ReadComponentExtensionsInfo to the FILE thread for the
    // initialization.  If it is never initialized for some reasons,
    // the test is timed out and failed.
    ComponentExtensionIMEManager* ceimm =
        InputMethodManager::Get()->GetComponentExtensionIMEManager();
    while (!ceimm->IsInitialized()) {
      content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
    }

    // Extension IMEs are not enabled by default.
    std::vector<std::string> extension_ime_ids;
    extension_ime_ids.push_back(kIdentityIMEID);
    extension_ime_ids.push_back(kToUpperIMEID);
    extension_ime_ids.push_back(kAPIArgumentIMEID);
    InputMethodManager::Get()->SetEnabledExtensionImes(&extension_ime_ids);

    InputMethodDescriptors extension_imes;
    InputMethodManager::Get()->GetInputMethodExtensions(&extension_imes);

    // Test IME has two input methods, thus InputMethodManager should have two
    // extension IME.
    // Note: Even extension is loaded by LoadExtensionAsComponent as above, the
    // IME does not managed by ComponentExtensionIMEManager or it's id won't
    // start with __comp__. The component extension IME is whitelisted and
    // managed by ComponentExtensionIMEManager, but its framework is same as
    // normal extension IME.
    EXPECT_EQ(3U, extension_imes.size());
  }

  const extensions::Extension* LoadExtensionWithType(
      const std::string& extension_name, TestType type) {
    switch (type) {
      case kTestTypeNormal:
        return LoadExtension(test_data_dir_.AppendASCII(extension_name));
      case kTestTypeIncognito:
        return LoadExtensionIncognito(
            test_data_dir_.AppendASCII(extension_name));
      case kTestTypeComponent:
        return LoadExtensionAsComponent(
            test_data_dir_.AppendASCII(extension_name));
    }
    NOTREACHED();
    return NULL;
  }

  const extensions::Extension* extension_;
};

class KeyEventDoneCallback {
 public:
  explicit KeyEventDoneCallback(bool expected_argument)
      : expected_argument_(expected_argument),
        is_called_(false) {}
  ~KeyEventDoneCallback() {}

  void Run(bool consumed) {
    if (consumed == expected_argument_) {
      base::MessageLoop::current()->Quit();
      is_called_ = true;
    }
  }

  void WaitUntilCalled() {
    while (!is_called_)
      content::RunMessageLoop();
  }

 private:
  bool expected_argument_;
  bool is_called_;

  DISALLOW_COPY_AND_ASSIGN(KeyEventDoneCallback);
};

INSTANTIATE_TEST_CASE_P(InputMethodEngineBrowserTest,
                        InputMethodEngineBrowserTest,
                        ::testing::Values(kTestTypeNormal));
INSTANTIATE_TEST_CASE_P(InputMethodEngineIncognitoBrowserTest,
                        InputMethodEngineBrowserTest,
                        ::testing::Values(kTestTypeIncognito));
INSTANTIATE_TEST_CASE_P(InputMethodEngineComponentExtensionBrowserTest,
                        InputMethodEngineBrowserTest,
                        ::testing::Values(kTestTypeComponent));

IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest,
                       BasicScenarioTest) {
  LoadTestInputMethod();

  InputMethodManager::Get()->ChangeInputMethod(kIdentityIMEID);

  scoped_ptr<MockIMEInputContextHandler> mock_input_context(
      new MockIMEInputContextHandler());
  scoped_ptr<MockIMECandidateWindowHandler> mock_candidate_window(
      new MockIMECandidateWindowHandler());

  IMEBridge::Get()->SetInputContextHandler(mock_input_context.get());
  IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window.get());

  IMEEngineHandlerInterface* engine_handler =
      IMEBridge::Get()->GetCurrentEngineHandler();
  ASSERT_TRUE(engine_handler);

  // onActivate event should be fired if Enable function is called.
  ExtensionTestMessageListener activated_listener("onActivate", false);
  engine_handler->Enable();
  ASSERT_TRUE(activated_listener.WaitUntilSatisfied());
  ASSERT_TRUE(activated_listener.was_satisfied());

  // onFocus event should be fired if FocusIn function is called.
  ExtensionTestMessageListener focus_listener("onFocus:text", false);
  IMEEngineHandlerInterface::InputContext context(ui::TEXT_INPUT_TYPE_TEXT,
                                                  ui::TEXT_INPUT_MODE_DEFAULT);
  engine_handler->FocusIn(context);
  ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
  ASSERT_TRUE(focus_listener.was_satisfied());

  // onKeyEvent should be fired if ProcessKeyEvent is called.
  KeyEventDoneCallback callback(false);  // EchoBackIME doesn't consume keys.
  ExtensionTestMessageListener keyevent_listener("onKeyEvent", false);
  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE, false);
  engine_handler->ProcessKeyEvent(key_event,
                                  base::Bind(&KeyEventDoneCallback::Run,
                                             base::Unretained(&callback)));
  ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
  ASSERT_TRUE(keyevent_listener.was_satisfied());
  callback.WaitUntilCalled();

  // onSurroundingTextChange should be fired if SetSurroundingText is called.
  ExtensionTestMessageListener surrounding_text_listener(
      "onSurroundingTextChanged", false);
  engine_handler->SetSurroundingText("text",  // Surrounding text.
                                     0,  // focused position.
                                     1);  // anchor position.
  ASSERT_TRUE(surrounding_text_listener.WaitUntilSatisfied());
  ASSERT_TRUE(surrounding_text_listener.was_satisfied());

  // onMenuItemActivated should be fired if PropertyActivate is called.
  ExtensionTestMessageListener property_listener("onMenuItemActivated", false);
  engine_handler->PropertyActivate("property_name");
  ASSERT_TRUE(property_listener.WaitUntilSatisfied());
  ASSERT_TRUE(property_listener.was_satisfied());

  // onReset should be fired if Reset is called.
  ExtensionTestMessageListener reset_listener("onReset", false);
  engine_handler->Reset();
  ASSERT_TRUE(reset_listener.WaitUntilSatisfied());
  ASSERT_TRUE(reset_listener.was_satisfied());

  // onBlur should be fired if FocusOut is called.
  ExtensionTestMessageListener blur_listener("onBlur", false);
  engine_handler->FocusOut();
  ASSERT_TRUE(blur_listener.WaitUntilSatisfied());
  ASSERT_TRUE(blur_listener.was_satisfied());

  // onDeactivated should be fired if Disable is called.
  ExtensionTestMessageListener disabled_listener("onDeactivated", false);
  engine_handler->Disable();
  ASSERT_TRUE(disabled_listener.WaitUntilSatisfied());
  ASSERT_TRUE(disabled_listener.was_satisfied());

  IMEBridge::Get()->SetInputContextHandler(NULL);
  IMEBridge::Get()->SetCandidateWindowHandler(NULL);
}

IN_PROC_BROWSER_TEST_P(InputMethodEngineBrowserTest,
                       APIArgumentTest) {
  LoadTestInputMethod();

  InputMethodManager::Get()->ChangeInputMethod(kAPIArgumentIMEID);

  scoped_ptr<MockIMEInputContextHandler> mock_input_context(
      new MockIMEInputContextHandler());
  scoped_ptr<MockIMECandidateWindowHandler> mock_candidate_window(
      new MockIMECandidateWindowHandler());

  IMEBridge::Get()->SetInputContextHandler(mock_input_context.get());
  IMEBridge::Get()->SetCandidateWindowHandler(mock_candidate_window.get());

  IMEEngineHandlerInterface* engine_handler =
      IMEBridge::Get()->GetCurrentEngineHandler();
  ASSERT_TRUE(engine_handler);

  extensions::ExtensionHost* host = FindHostWithPath(
      extensions::ExtensionSystem::Get(profile())->process_manager(),
      extensions::BackgroundInfo::GetBackgroundURL(extension_).path(),
      1);

  engine_handler->Enable();
  IMEEngineHandlerInterface::InputContext context(ui::TEXT_INPUT_TYPE_TEXT,
                                                  ui::TEXT_INPUT_MODE_DEFAULT);
  engine_handler->FocusIn(context);

  {
    SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:No");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:a:KeyA:false:false:false:false";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(
        ui::ET_KEY_PRESSED, ui::VKEY_A, "KeyA", ui::EF_NONE, false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  {
    SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:No, Shift:No, Caps:No");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:a:KeyA:true:false:false:false";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                           ui::VKEY_A,
                           "KeyA",
                           ui::EF_CONTROL_DOWN,
                           false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  {
    SCOPED_TRACE("KeyDown, Ctrl:No, alt:Yes, Shift:No, Caps:No");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:a:KeyA:false:true:false:false";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                           ui::VKEY_A,
                           "KeyA",
                           ui::EF_ALT_DOWN,
                           false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  {
    SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:No");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:A:KeyA:false:false:true:false";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                           ui::VKEY_A,
                           "KeyA",
                           ui::EF_SHIFT_DOWN,
                           false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  {
    SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:No, Caps:Yes");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:a:KeyA:false:false:false:true";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                           ui::VKEY_A,
                           "KeyA",
                           ui::EF_CAPS_LOCK_DOWN,
                           false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  {
    SCOPED_TRACE("KeyDown, Ctrl:Yes, alt:Yes, Shift:No, Caps:No");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:a:KeyA:true:true:false:false";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                           ui::VKEY_A,
                           "KeyA",
                           ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
                           false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  {
    SCOPED_TRACE("KeyDown, Ctrl:No, alt:No, Shift:Yes, Caps:Yes");
    KeyEventDoneCallback callback(false);
    const std::string expected_value =
        "onKeyEvent::keydown:A:KeyA:false:false:true:true";
    ExtensionTestMessageListener keyevent_listener(expected_value, false);

    ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                           ui::VKEY_A,
                           "KeyA",
                           ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN,
                           false);
    engine_handler->ProcessKeyEvent(key_event,
                                    base::Bind(&KeyEventDoneCallback::Run,
                                               base::Unretained(&callback)));
    ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener.was_satisfied());
    callback.WaitUntilCalled();
  }
  // TODO(nona): Add browser tests for other API as well.
  {
    SCOPED_TRACE("commitText test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char commit_text_test_script[] =
        "chrome.input.ime.commitText({"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "  text:'COMMIT_TEXT'"
        "});";

    ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                       commit_text_test_script));
    EXPECT_EQ(1, mock_input_context->commit_text_call_count());
    EXPECT_EQ("COMMIT_TEXT", mock_input_context->last_commit_text());
  }
  {
    SCOPED_TRACE("sendKeyEvents test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char send_key_events_test_script[] =
        "chrome.input.ime.sendKeyEvents({"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "  keyData : [{"
        "    type : 'keydown',"
        "    requestId : '0',"
        "    key : 'z',"
        "    code : 'KeyZ',"
        "  },{"
        "    type : 'keyup',"
        "    requestId : '1',"
        "    key : 'z',"
        "    code : 'KeyZ',"
        "  }]"
        "});";

    ExtensionTestMessageListener keyevent_listener_down(
        std::string("onKeyEvent:") + kExtensionID +
        ":keydown:z:KeyZ:false:false:false:false",
        false);
    ExtensionTestMessageListener keyevent_listener_up(
        std::string("onKeyEvent:") + kExtensionID +
        ":keyup:z:KeyZ:false:false:false:false",
        false);

    ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                       send_key_events_test_script));

    ASSERT_TRUE(keyevent_listener_down.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener_down.was_satisfied());
    ASSERT_TRUE(keyevent_listener_up.WaitUntilSatisfied());
    EXPECT_TRUE(keyevent_listener_up.was_satisfied());
  }
  {
    SCOPED_TRACE("setComposition test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_composition_test_script[] =
        "chrome.input.ime.setComposition({"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "  text:'COMPOSITION_TEXT',"
        "  cursor:4,"
        "  segments : [{"
        "    start: 0,"
        "    end: 5,"
        "    style: 'underline'"
        "  },{"
        "    start: 6,"
        "    end: 10,"
        "    style: 'doubleUnderline'"
        "  }]"
        "});";

    ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                       set_composition_test_script));
    EXPECT_EQ(1, mock_input_context->update_preedit_text_call_count());

    EXPECT_EQ(4U,
              mock_input_context->last_update_composition_arg().cursor_pos);
    EXPECT_TRUE(mock_input_context->last_update_composition_arg().is_visible);

    const CompositionText& composition_text =
        mock_input_context->last_update_composition_arg().composition_text;
    EXPECT_EQ(base::UTF8ToUTF16("COMPOSITION_TEXT"), composition_text.text());
    const std::vector<CompositionText::UnderlineAttribute>& underlines =
        composition_text.underline_attributes();

    ASSERT_EQ(2U, underlines.size());
    EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE,
              underlines[0].type);
    EXPECT_EQ(0U, underlines[0].start_index);
    EXPECT_EQ(5U, underlines[0].end_index);

    EXPECT_EQ(CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE,
              underlines[1].type);
    EXPECT_EQ(6U, underlines[1].start_index);
    EXPECT_EQ(10U, underlines[1].end_index);
  }
  {
    SCOPED_TRACE("clearComposition test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char commite_text_test_script[] =
        "chrome.input.ime.clearComposition({"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "});";

    ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                       commite_text_test_script));
    EXPECT_EQ(1, mock_input_context->update_preedit_text_call_count());
    EXPECT_FALSE(
        mock_input_context->last_update_composition_arg().is_visible);
    const CompositionText& composition_text =
        mock_input_context->last_update_composition_arg().composition_text;
    EXPECT_TRUE(composition_text.text().empty());
  }
  {
    SCOPED_TRACE("setCandidateWindowProperties:visibility test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidate_window_properties_test_script[] =
        "chrome.input.ime.setCandidateWindowProperties({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  properties: {"
        "    visible: true,"
        "  }"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(),
        set_candidate_window_properties_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());
    EXPECT_TRUE(
        mock_candidate_window->last_update_lookup_table_arg().is_visible);
  }
  {
    SCOPED_TRACE("setCandidateWindowProperties:cursor_visibility test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidate_window_properties_test_script[] =
        "chrome.input.ime.setCandidateWindowProperties({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  properties: {"
        "    cursorVisible: true,"
        "  }"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(),
        set_candidate_window_properties_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());

    // window visibility is kept as before.
    EXPECT_TRUE(
        mock_candidate_window->last_update_lookup_table_arg().is_visible);

    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;
    EXPECT_TRUE(table.is_cursor_visible());
  }
  {
    SCOPED_TRACE("setCandidateWindowProperties:vertical test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidate_window_properties_test_script[] =
        "chrome.input.ime.setCandidateWindowProperties({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  properties: {"
        "    vertical: true,"
        "  }"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(),
        set_candidate_window_properties_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());

    // window visibility is kept as before.
    EXPECT_TRUE(
        mock_candidate_window->last_update_lookup_table_arg().is_visible);

    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;

    // cursor visibility is kept as before.
    EXPECT_TRUE(table.is_cursor_visible());

    EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());
  }
  {
    SCOPED_TRACE("setCandidateWindowProperties:pageSize test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidate_window_properties_test_script[] =
        "chrome.input.ime.setCandidateWindowProperties({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  properties: {"
        "    pageSize: 7,"
        "  }"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(),
        set_candidate_window_properties_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());

    // window visibility is kept as before.
    EXPECT_TRUE(
        mock_candidate_window->last_update_lookup_table_arg().is_visible);

    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;

    // cursor visibility is kept as before.
    EXPECT_TRUE(table.is_cursor_visible());

    // oritantation is kept as before.
    EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());

    EXPECT_EQ(7U, table.page_size());
  }
  {
    SCOPED_TRACE("setCandidateWindowProperties:auxTextVisibility test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidate_window_properties_test_script[] =
        "chrome.input.ime.setCandidateWindowProperties({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  properties: {"
        "    auxiliaryTextVisible: true"
        "  }"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(),
        set_candidate_window_properties_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());

    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;
    EXPECT_TRUE(table.is_auxiliary_text_visible());
  }
  {
    SCOPED_TRACE("setCandidateWindowProperties:auxText test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidate_window_properties_test_script[] =
        "chrome.input.ime.setCandidateWindowProperties({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  properties: {"
        "    auxiliaryText: 'AUXILIARY_TEXT'"
        "  }"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(),
        set_candidate_window_properties_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());

    // aux text visibility is kept as before.
    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;
    EXPECT_TRUE(table.is_auxiliary_text_visible());
    EXPECT_EQ("AUXILIARY_TEXT", table.auxiliary_text());
  }
  {
    SCOPED_TRACE("setCandidates test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_candidates_test_script[] =
        "chrome.input.ime.setCandidates({"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "  candidates: [{"
        "    candidate: 'CANDIDATE_1',"
        "    id: 1,"
        "    },{"
        "    candidate: 'CANDIDATE_2',"
        "    id: 2,"
        "    label: 'LABEL_2',"
        "    },{"
        "    candidate: 'CANDIDATE_3',"
        "    id: 3,"
        "    label: 'LABEL_3',"
        "    annotation: 'ANNOTACTION_3'"
        "    },{"
        "    candidate: 'CANDIDATE_4',"
        "    id: 4,"
        "    label: 'LABEL_4',"
        "    annotation: 'ANNOTACTION_4',"
        "    usage: {"
        "      title: 'TITLE_4',"
        "      body: 'BODY_4'"
        "    }"
        "  }]"
        "});";
    ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                       set_candidates_test_script));

    // window visibility is kept as before.
    EXPECT_TRUE(
        mock_candidate_window->last_update_lookup_table_arg().is_visible);

    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;

    // cursor visibility is kept as before.
    EXPECT_TRUE(table.is_cursor_visible());

    // oritantation is kept as before.
    EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());

    // page size is kept as before.
    EXPECT_EQ(7U, table.page_size());

    ASSERT_EQ(4U, table.candidates().size());

    EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_1"),
              table.candidates().at(0).value);

    EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_2"),
              table.candidates().at(1).value);
    EXPECT_EQ(base::UTF8ToUTF16("LABEL_2"), table.candidates().at(1).label);

    EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_3"),
              table.candidates().at(2).value);
    EXPECT_EQ(base::UTF8ToUTF16("LABEL_3"), table.candidates().at(2).label);
    EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_3"),
              table.candidates().at(2).annotation);

    EXPECT_EQ(base::UTF8ToUTF16("CANDIDATE_4"),
              table.candidates().at(3).value);
    EXPECT_EQ(base::UTF8ToUTF16("LABEL_4"), table.candidates().at(3).label);
    EXPECT_EQ(base::UTF8ToUTF16("ANNOTACTION_4"),
              table.candidates().at(3).annotation);
    EXPECT_EQ(base::UTF8ToUTF16("TITLE_4"),
              table.candidates().at(3).description_title);
    EXPECT_EQ(base::UTF8ToUTF16("BODY_4"),
              table.candidates().at(3).description_body);
  }
  {
    SCOPED_TRACE("setCursorPosition test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_cursor_position_test_script[] =
        "chrome.input.ime.setCursorPosition({"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "  candidateID: 2"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(), set_cursor_position_test_script));
    EXPECT_EQ(1, mock_candidate_window->update_lookup_table_call_count());

    // window visibility is kept as before.
    EXPECT_TRUE(
        mock_candidate_window->last_update_lookup_table_arg().is_visible);

    const ui::CandidateWindow& table =
        mock_candidate_window->last_update_lookup_table_arg().lookup_table;

    // cursor visibility is kept as before.
    EXPECT_TRUE(table.is_cursor_visible());

    // oritantation is kept as before.
    EXPECT_EQ(ui::CandidateWindow::VERTICAL, table.orientation());

    // page size is kept as before.
    EXPECT_EQ(7U, table.page_size());

    // candidates are same as before.
    ASSERT_EQ(4U, table.candidates().size());

    // Candidate ID == 2 is 1 in index.
    EXPECT_EQ(1U, table.cursor_position());
  }
  {
    SCOPED_TRACE("setMenuItem test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char set_menu_item_test_script[] =
        "chrome.input.ime.setMenuItems({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  items: [{"
        "    id: 'ID0',"
        "  },{"
        "    id: 'ID1',"
        "    label: 'LABEL1',"
        "  },{"
        "    id: 'ID2',"
        "    label: 'LABEL2',"
        "    style: 'radio',"
        "  },{"
        "    id: 'ID3',"
        "    label: 'LABEL3',"
        "    style: 'check',"
        "    visible: true,"
        "  },{"
        "    id: 'ID4',"
        "    label: 'LABEL4',"
        "    style: 'separator',"
        "    visible: true,"
        "    checked: true"
        "  }]"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(), set_menu_item_test_script));

    const ash::ime::InputMethodMenuItemList& props =
        ash::ime::InputMethodMenuManager::GetInstance()->
        GetCurrentInputMethodMenuItemList();
    ASSERT_EQ(5U, props.size());

    EXPECT_EQ("ID0", props[0].key);
    EXPECT_EQ("ID1", props[1].key);
    EXPECT_EQ("ID2", props[2].key);
    EXPECT_EQ("ID3", props[3].key);
    EXPECT_EQ("ID4", props[4].key);

    EXPECT_EQ("LABEL1", props[1].label);
    EXPECT_EQ("LABEL2", props[2].label);
    EXPECT_EQ("LABEL3", props[3].label);
    EXPECT_EQ("LABEL4", props[4].label);

    EXPECT_TRUE(props[2].is_selection_item);
    // TODO(nona): Add tests for style: ["toggle" and "separator"]
    // and visible:, when implement them.

    EXPECT_TRUE(props[4].is_selection_item_checked);
  }
  {
    SCOPED_TRACE("deleteSurroundingText test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    const char delete_surrounding_text_test_script[] =
        "chrome.input.ime.deleteSurroundingText({"
        "  engineID: engineBridge.getActiveEngineID(),"
        "  contextID: engineBridge.getFocusedContextID().contextID,"
        "  offset: 5,"
        "  length: 3"
        "});";
    ASSERT_TRUE(content::ExecuteScript(
        host->host_contents(), delete_surrounding_text_test_script));

    EXPECT_EQ(1, mock_input_context->delete_surrounding_text_call_count());
    EXPECT_EQ(5, mock_input_context->last_delete_surrounding_text_arg().offset);
    EXPECT_EQ(3U,
              mock_input_context->last_delete_surrounding_text_arg().length);
  }
  {
    SCOPED_TRACE("onFocus test");
    mock_input_context->Reset();
    mock_candidate_window->Reset();

    {
      ExtensionTestMessageListener focus_listener("onFocus:text", false);
      IMEEngineHandlerInterface::InputContext context(
          ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT);
      engine_handler->FocusIn(context);
      ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
      ASSERT_TRUE(focus_listener.was_satisfied());
    }
    {
      ExtensionTestMessageListener focus_listener("onFocus:search", false);
      IMEEngineHandlerInterface::InputContext context(
          ui::TEXT_INPUT_TYPE_SEARCH, ui::TEXT_INPUT_MODE_DEFAULT);
      engine_handler->FocusIn(context);
      ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
      ASSERT_TRUE(focus_listener.was_satisfied());
    }
    {
      ExtensionTestMessageListener focus_listener("onFocus:tel", false);
      IMEEngineHandlerInterface::InputContext context(
          ui::TEXT_INPUT_TYPE_TELEPHONE, ui::TEXT_INPUT_MODE_DEFAULT);
      engine_handler->FocusIn(context);
      ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
      ASSERT_TRUE(focus_listener.was_satisfied());
    }
    {
      ExtensionTestMessageListener focus_listener("onFocus:url", false);
      IMEEngineHandlerInterface::InputContext context(
          ui::TEXT_INPUT_TYPE_URL, ui::TEXT_INPUT_MODE_DEFAULT);
      engine_handler->FocusIn(context);
      ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
      ASSERT_TRUE(focus_listener.was_satisfied());
    }
    {
      ExtensionTestMessageListener focus_listener("onFocus:email", false);
      IMEEngineHandlerInterface::InputContext context(
          ui::TEXT_INPUT_TYPE_EMAIL, ui::TEXT_INPUT_MODE_DEFAULT);
      engine_handler->FocusIn(context);
      ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
      ASSERT_TRUE(focus_listener.was_satisfied());
    }
    {
      ExtensionTestMessageListener focus_listener("onFocus:number", false);
      IMEEngineHandlerInterface::InputContext context(
          ui::TEXT_INPUT_TYPE_NUMBER, ui::TEXT_INPUT_MODE_DEFAULT);
      engine_handler->FocusIn(context);
      ASSERT_TRUE(focus_listener.WaitUntilSatisfied());
      ASSERT_TRUE(focus_listener.was_satisfied());
    }
  }

  IMEBridge::Get()->SetInputContextHandler(NULL);
  IMEBridge::Get()->SetCandidateWindowHandler(NULL);
}

}  // namespace
}  // namespace input_method
}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */