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 "chromeos/ime/component_extension_ime_manager.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "chromeos/ime/extension_ime_util.h"
10
11 namespace chromeos {
12
13 namespace {
14
15 // The whitelist for enabling extension based xkb keyboards at login session.
16 const char* kLoginLayoutWhitelist[] = {
17 "be",
18 "br",
19 "ca",
20 "ca(eng)",
21 "ca(multix)",
22 "ch",
23 "ch(fr)",
24 "cz",
25 "cz(qwerty)",
26 "de",
27 "de(neo)",
28 "dk",
29 "ee",
30 "es",
31 "es(cat)",
32 "fi",
33 "fr",
34 "gb(dvorak)",
35 "gb(extd)",
36 "hr",
37 "hu",
38 "is",
39 "it",
40 "jp",
41 "latam",
42 "lt",
43 "lv(apostrophe)",
44 "no",
45 "pl",
46 "pt",
47 "ro",
48 "se",
49 "si",
50 "tr",
51 "us",
52 "us(altgr-intl)",
53 "us(colemak)",
54 "us(dvorak)",
55 "us(intl)"
56 };
57
58 // Gets the input method category according to the given input method id.
59 // This is used for sorting a list of input methods.
GetInputMethodCategory(const std::string & id)60 int GetInputMethodCategory(const std::string& id) {
61 const std::string engine_id =
62 chromeos::extension_ime_util::GetComponentIDByInputMethodID(id);
63 if (StartsWithASCII(engine_id, "xkb:", true))
64 return 0;
65 if (StartsWithASCII(engine_id, "vkd_", true))
66 return 1;
67 if (engine_id.find("-t-i0-") != std::string::npos &&
68 !StartsWithASCII(engine_id, "zh-", true)) {
69 return 2;
70 }
71 return 3;
72 }
73
InputMethodCompare(const input_method::InputMethodDescriptor & im1,const input_method::InputMethodDescriptor & im2)74 bool InputMethodCompare(const input_method::InputMethodDescriptor& im1,
75 const input_method::InputMethodDescriptor& im2) {
76 return GetInputMethodCategory(im1.id()) < GetInputMethodCategory(im2.id());
77 }
78
79 } // namespace
80
ComponentExtensionEngine()81 ComponentExtensionEngine::ComponentExtensionEngine() {
82 }
83
~ComponentExtensionEngine()84 ComponentExtensionEngine::~ComponentExtensionEngine() {
85 }
86
ComponentExtensionIME()87 ComponentExtensionIME::ComponentExtensionIME() {
88 }
89
~ComponentExtensionIME()90 ComponentExtensionIME::~ComponentExtensionIME() {
91 }
92
ComponentExtensionIMEManagerDelegate()93 ComponentExtensionIMEManagerDelegate::ComponentExtensionIMEManagerDelegate() {
94 }
95
~ComponentExtensionIMEManagerDelegate()96 ComponentExtensionIMEManagerDelegate::~ComponentExtensionIMEManagerDelegate() {
97 }
98
ComponentExtensionIMEManager()99 ComponentExtensionIMEManager::ComponentExtensionIMEManager() {
100 for (size_t i = 0; i < arraysize(kLoginLayoutWhitelist); ++i) {
101 login_layout_set_.insert(kLoginLayoutWhitelist[i]);
102 }
103 }
104
~ComponentExtensionIMEManager()105 ComponentExtensionIMEManager::~ComponentExtensionIMEManager() {
106 }
107
Initialize(scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate)108 void ComponentExtensionIMEManager::Initialize(
109 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
110 delegate_ = delegate.Pass();
111 std::vector<ComponentExtensionIME> ext_list = delegate_->ListIME();
112 for (size_t i = 0; i < ext_list.size(); ++i) {
113 ComponentExtensionIME& ext = ext_list[i];
114 bool extension_exists = IsWhitelistedExtension(ext.id);
115 if (!extension_exists)
116 component_extension_imes_[ext.id] = ext;
117 for (size_t j = 0; j < ext.engines.size(); ++j) {
118 ComponentExtensionEngine& ime = ext.engines[j];
119 const std::string input_method_id =
120 extension_ime_util::GetComponentInputMethodID(ext.id, ime.engine_id);
121 if (extension_exists && !IsWhitelisted(input_method_id))
122 component_extension_imes_[ext.id].engines.push_back(ime);
123 input_method_id_set_.insert(input_method_id);
124 }
125 }
126 }
127
LoadComponentExtensionIME(Profile * profile,const std::string & input_method_id)128 bool ComponentExtensionIMEManager::LoadComponentExtensionIME(
129 Profile* profile,
130 const std::string& input_method_id) {
131 ComponentExtensionIME ime;
132 if (FindEngineEntry(input_method_id, &ime)) {
133 delegate_->Load(profile, ime.id, ime.manifest, ime.path);
134 return true;
135 }
136 return false;
137 }
138
UnloadComponentExtensionIME(Profile * profile,const std::string & input_method_id)139 bool ComponentExtensionIMEManager::UnloadComponentExtensionIME(
140 Profile* profile,
141 const std::string& input_method_id) {
142 ComponentExtensionIME ime;
143 if (!FindEngineEntry(input_method_id, &ime))
144 return false;
145 delegate_->Unload(profile, ime.id, ime.path);
146 return true;
147 }
148
IsWhitelisted(const std::string & input_method_id)149 bool ComponentExtensionIMEManager::IsWhitelisted(
150 const std::string& input_method_id) {
151 return input_method_id_set_.find(input_method_id) !=
152 input_method_id_set_.end();
153 }
154
IsWhitelistedExtension(const std::string & extension_id)155 bool ComponentExtensionIMEManager::IsWhitelistedExtension(
156 const std::string& extension_id) {
157 return component_extension_imes_.find(extension_id) !=
158 component_extension_imes_.end();
159 }
160
161 input_method::InputMethodDescriptors
GetAllIMEAsInputMethodDescriptor()162 ComponentExtensionIMEManager::GetAllIMEAsInputMethodDescriptor() {
163 input_method::InputMethodDescriptors result;
164 for (std::map<std::string, ComponentExtensionIME>::const_iterator it =
165 component_extension_imes_.begin();
166 it != component_extension_imes_.end(); ++it) {
167 const ComponentExtensionIME& ext = it->second;
168 for (size_t j = 0; j < ext.engines.size(); ++j) {
169 const ComponentExtensionEngine& ime = ext.engines[j];
170 const std::string input_method_id =
171 extension_ime_util::GetComponentInputMethodID(
172 ext.id, ime.engine_id);
173 const std::vector<std::string>& layouts = ime.layouts;
174 result.push_back(
175 input_method::InputMethodDescriptor(
176 input_method_id,
177 ime.display_name,
178 std::string(), // TODO(uekawa): Set short name.
179 layouts,
180 ime.language_codes,
181 // Enables extension based xkb keyboards on login screen.
182 extension_ime_util::IsKeyboardLayoutExtension(
183 input_method_id) && IsInLoginLayoutWhitelist(layouts),
184 ime.options_page_url,
185 ime.input_view_url));
186 }
187 }
188 std::stable_sort(result.begin(), result.end(), InputMethodCompare);
189 return result;
190 }
191
192 input_method::InputMethodDescriptors
GetXkbIMEAsInputMethodDescriptor()193 ComponentExtensionIMEManager::GetXkbIMEAsInputMethodDescriptor() {
194 input_method::InputMethodDescriptors result;
195 const input_method::InputMethodDescriptors& descriptors =
196 GetAllIMEAsInputMethodDescriptor();
197 for (size_t i = 0; i < descriptors.size(); ++i) {
198 if (extension_ime_util::IsKeyboardLayoutExtension(descriptors[i].id()))
199 result.push_back(descriptors[i]);
200 }
201 return result;
202 }
203
FindEngineEntry(const std::string & input_method_id,ComponentExtensionIME * out_extension)204 bool ComponentExtensionIMEManager::FindEngineEntry(
205 const std::string& input_method_id,
206 ComponentExtensionIME* out_extension) {
207 if (!IsWhitelisted(input_method_id))
208 return false;
209
210 std::string extension_id =
211 extension_ime_util::GetExtensionIDFromInputMethodID(input_method_id);
212 std::map<std::string, ComponentExtensionIME>::iterator it =
213 component_extension_imes_.find(extension_id);
214 if (it == component_extension_imes_.end())
215 return false;
216
217 if (out_extension)
218 *out_extension = it->second;
219 return true;
220 }
221
IsInLoginLayoutWhitelist(const std::vector<std::string> & layouts)222 bool ComponentExtensionIMEManager::IsInLoginLayoutWhitelist(
223 const std::vector<std::string>& layouts) {
224 for (size_t i = 0; i < layouts.size(); ++i) {
225 if (login_layout_set_.find(layouts[i]) != login_layout_set_.end())
226 return true;
227 }
228 return false;
229 }
230
231 } // namespace chromeos
232