1// Copyright (c) 2012 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 "chrome/browser/chromeos/extensions/input_method_api.h"
6
7#include "base/lazy_instance.h"
8#include "base/values.h"
9#include "chrome/browser/chromeos/extensions/input_method_event_router.h"
10#include "chrome/browser/chromeos/input_method/input_method_util.h"
11#include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
12#include "chromeos/ime/extension_ime_util.h"
13#include "chromeos/ime/input_method_descriptor.h"
14#include "chromeos/ime/input_method_manager.h"
15#include "extensions/browser/extension_function_registry.h"
16#include "extensions/browser/extension_system.h"
17#include "extensions/common/value_builder.h"
18
19namespace {
20
21// Prefix, which is used by XKB.
22const char kXkbPrefix[] = "xkb:";
23
24}  // namespace
25
26namespace extensions {
27
28ExtensionFunction::ResponseAction GetCurrentInputMethodFunction::Run() {
29#if !defined(OS_CHROMEOS)
30  EXTENSION_FUNCTION_VALIDATE(false);
31#else
32  chromeos::input_method::InputMethodManager* manager =
33      chromeos::input_method::InputMethodManager::Get();
34  return RespondNow(OneArgument(new base::StringValue(
35      manager->GetActiveIMEState()->GetCurrentInputMethod().id())));
36#endif
37}
38
39ExtensionFunction::ResponseAction SetCurrentInputMethodFunction::Run() {
40#if !defined(OS_CHROMEOS)
41  EXTENSION_FUNCTION_VALIDATE(false);
42#else
43  std::string new_input_method;
44  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &new_input_method));
45  scoped_refptr<chromeos::input_method::InputMethodManager::State> ime_state =
46      chromeos::input_method::InputMethodManager::Get()->GetActiveIMEState();
47  const std::vector<std::string>& input_methods =
48      ime_state->GetActiveInputMethodIds();
49  for (size_t i = 0; i < input_methods.size(); ++i) {
50    const std::string& input_method = input_methods[i];
51    if (input_method == new_input_method) {
52      ime_state->ChangeInputMethod(new_input_method, false /* show_message */);
53      return RespondNow(NoArguments());
54    }
55  }
56  return RespondNow(Error("Invalid input method id."));
57#endif
58}
59
60ExtensionFunction::ResponseAction GetInputMethodsFunction::Run() {
61#if !defined(OS_CHROMEOS)
62  EXTENSION_FUNCTION_VALIDATE(false);
63#else
64  base::ListValue* output = new base::ListValue();
65  chromeos::input_method::InputMethodManager* manager =
66      chromeos::input_method::InputMethodManager::Get();
67  chromeos::input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
68  scoped_refptr<chromeos::input_method::InputMethodManager::State> ime_state =
69      manager->GetActiveIMEState();
70  scoped_ptr<chromeos::input_method::InputMethodDescriptors> input_methods =
71      ime_state->GetActiveInputMethods();
72  for (size_t i = 0; i < input_methods->size(); ++i) {
73    const chromeos::input_method::InputMethodDescriptor& input_method =
74        (*input_methods)[i];
75    base::DictionaryValue* val = new base::DictionaryValue();
76    val->SetString("id", input_method.id());
77    val->SetString("name", util->GetInputMethodLongName(input_method));
78    val->SetString("indicator", util->GetInputMethodShortName(input_method));
79    output->Append(val);
80  }
81  return RespondNow(OneArgument(output));
82#endif
83}
84
85// static
86const char InputMethodAPI::kOnInputMethodChanged[] =
87    "inputMethodPrivate.onChanged";
88
89InputMethodAPI::InputMethodAPI(content::BrowserContext* context)
90    : context_(context) {
91  EventRouter::Get(context_)->RegisterObserver(this, kOnInputMethodChanged);
92  ExtensionFunctionRegistry* registry =
93      ExtensionFunctionRegistry::GetInstance();
94  registry->RegisterFunction<GetCurrentInputMethodFunction>();
95  registry->RegisterFunction<SetCurrentInputMethodFunction>();
96  registry->RegisterFunction<GetInputMethodsFunction>();
97}
98
99InputMethodAPI::~InputMethodAPI() {
100}
101
102// static
103std::string InputMethodAPI::GetInputMethodForXkb(const std::string& xkb_id) {
104  std::string xkb_prefix =
105      chromeos::extension_ime_util::GetInputMethodIDByEngineID(kXkbPrefix);
106  size_t prefix_length = xkb_prefix.length();
107  DCHECK(xkb_id.substr(0, prefix_length) == xkb_prefix);
108  return xkb_id.substr(prefix_length);
109}
110
111void InputMethodAPI::Shutdown() {
112  // UnregisterObserver may have already been called in OnListenerAdded,
113  // but it is safe to call it more than once.
114  EventRouter::Get(context_)->UnregisterObserver(this);
115}
116
117void InputMethodAPI::OnListenerAdded(
118    const extensions::EventListenerInfo& details) {
119  DCHECK(!input_method_event_router_.get());
120  input_method_event_router_.reset(
121      new chromeos::ExtensionInputMethodEventRouter(context_));
122  EventRouter::Get(context_)->UnregisterObserver(this);
123}
124
125static base::LazyInstance<BrowserContextKeyedAPIFactory<InputMethodAPI> >
126    g_factory = LAZY_INSTANCE_INITIALIZER;
127
128// static
129BrowserContextKeyedAPIFactory<InputMethodAPI>*
130InputMethodAPI::GetFactoryInstance() {
131  return g_factory.Pointer();
132}
133
134}  // namespace extensions
135