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.
5 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "chrome/browser/extensions/component_loader.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/profiles/profile_manager.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "extensions/browser/extension_system.h"
15 #include "extensions/common/extension.h"
16 #include "extensions/common/extension_l10n_util.h"
17 #include "extensions/common/file_util.h"
18 #include "extensions/common/manifest_constants.h"
19 #include "ui/base/l10n/l10n_util.h"
25 struct WhitelistedComponentExtensionIME
{
28 } whitelisted_component_extension
[] = {
30 // ChromeOS Hangul Input.
31 "bdgdidmhaijohebebipajioienkglgfo",
32 "/usr/share/chromeos-assets/input_methods/hangul",
34 #if defined(OFFICIAL_BUILD)
36 // Official Google XKB Input.
37 "jkghodnilhceideoidjikpgommlajknk",
38 "/usr/share/chromeos-assets/input_methods/google_xkb",
41 // Official Google Keyboards Input.
42 "habcdindjejkmepknlhkkloncjcpcnbf",
43 "/usr/share/chromeos-assets/input_methods/google_keyboards",
46 // Official Google Japanese Input.
47 "fpfbhcjppmaeaijcidgiibchfbnhbelj",
48 "/usr/share/chromeos-assets/input_methods/nacl_mozc",
51 // Google input tools.
52 "gjaehgfemfahhmlgpdfknkhdnemmolop",
53 "/usr/share/chromeos-assets/input_methods/input_tools",
57 // Open-sourced ChromeOS xkb extension.
58 "fgoepimhcoialccpbmpnnblemnepkkao",
59 "/usr/share/chromeos-assets/input_methods/xkb",
62 // Open-sourced ChromeOS Keyboards extension.
63 "jhffeifommiaekmbkkjlpmilogcfdohp",
64 "/usr/share/chromeos-assets/input_methods/keyboard_layouts",
67 // Open-sourced Pinyin Chinese Input Method.
68 "cpgalbafkoofkjmaeonnfijgpfennjjn",
69 "/usr/share/chromeos-assets/input_methods/pinyin",
72 // Open-sourced Zhuyin Chinese Input Method.
73 "ekbifjdfhkmdeeajnolmgdlmkllopefi",
74 "/usr/share/chromeos-assets/input_methods/zhuyin",
77 // Open-sourced Cangjie Chinese Input Method.
78 "aeebooiibjahgpgmhkeocbeekccfknbj",
79 "/usr/share/chromeos-assets/input_methods/cangjie",
82 // Open-sourced Mozc Japanese Input.
83 "bbaiamgfapehflhememkfglaehiobjnk",
84 "/usr/share/chromeos-assets/input_methods/nacl_mozc",
89 extensions::ComponentLoader
* GetComponentLoader() {
90 // TODO(skuhne, nkostylev): At this time the only thing which makes sense here
91 // is to use the active profile. Nkostylev is working on getting IME settings
92 // to work for multi user by collecting all settings from all users. Once that
93 // is done we might have to re-visit this decision.
94 Profile
* profile
= ProfileManager::GetActiveUserProfile();
95 extensions::ExtensionSystem
* extension_system
=
96 extensions::ExtensionSystem::Get(profile
);
97 ExtensionService
* extension_service
= extension_system
->extension_service();
98 return extension_service
->component_loader();
102 ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl()
103 : is_initialized_(false),
104 weak_ptr_factory_(this) {
107 ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() {
110 std::vector
<ComponentExtensionIME
> ComponentExtensionIMEManagerImpl::ListIME() {
111 DCHECK(thread_checker_
.CalledOnValidThread());
112 return component_extension_list_
;
115 bool ComponentExtensionIMEManagerImpl::Load(const std::string
& extension_id
,
116 const std::string
& manifest
,
117 const base::FilePath
& file_path
) {
118 DCHECK(thread_checker_
.CalledOnValidThread());
119 Profile
* profile
= ProfileManager::GetActiveUserProfile();
120 extensions::ExtensionSystem
* extension_system
=
121 extensions::ExtensionSystem::Get(profile
);
122 ExtensionService
* extension_service
= extension_system
->extension_service();
123 if (extension_service
->GetExtensionById(extension_id
, false))
125 const std::string loaded_extension_id
=
126 GetComponentLoader()->Add(manifest
, file_path
);
127 DCHECK_EQ(loaded_extension_id
, extension_id
);
131 void ComponentExtensionIMEManagerImpl::Unload(const std::string
& extension_id
,
132 const base::FilePath
& file_path
) {
133 DCHECK(thread_checker_
.CalledOnValidThread());
134 // Remove(extension_id) does nothing when the extension has already been
135 // removed or not been registered.
136 GetComponentLoader()->Remove(extension_id
);
139 scoped_ptr
<base::DictionaryValue
> ComponentExtensionIMEManagerImpl::GetManifest(
140 const base::FilePath
& file_path
) {
141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
143 scoped_ptr
<base::DictionaryValue
> manifest(
144 extensions::file_util::LoadManifest(file_path
, &error
));
146 LOG(ERROR
) << "Failed at getting manifest";
147 if (!extension_l10n_util::LocalizeExtension(file_path
,
150 LOG(ERROR
) << "Localization failed";
152 return manifest
.Pass();
155 void ComponentExtensionIMEManagerImpl::InitializeAsync(
156 const base::Closure
& callback
) {
157 DCHECK(!is_initialized_
);
158 DCHECK(thread_checker_
.CalledOnValidThread());
160 std::vector
<ComponentExtensionIME
>* component_extension_ime_list
161 = new std::vector
<ComponentExtensionIME
>;
162 content::BrowserThread::PostTaskAndReply(
163 content::BrowserThread::FILE,
165 base::Bind(&ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo
,
166 base::Unretained(component_extension_ime_list
)),
168 &ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo
,
169 weak_ptr_factory_
.GetWeakPtr(),
170 base::Owned(component_extension_ime_list
),
174 bool ComponentExtensionIMEManagerImpl::IsInitialized() {
175 return is_initialized_
;
179 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
180 const ComponentExtensionIME
& component_extension
,
181 const base::DictionaryValue
& dict
,
182 ComponentExtensionEngine
* out
) {
183 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
186 if (!dict
.GetString(extensions::manifest_keys::kType
, &type
))
190 if (!dict
.GetString(extensions::manifest_keys::kId
, &out
->engine_id
))
192 if (!dict
.GetString(extensions::manifest_keys::kName
, &out
->display_name
))
195 std::set
<std::string
> languages
;
196 const base::Value
* language_value
= NULL
;
197 if (dict
.Get(extensions::manifest_keys::kLanguage
, &language_value
)) {
198 if (language_value
->GetType() == base::Value::TYPE_STRING
) {
199 std::string language_str
;
200 language_value
->GetAsString(&language_str
);
201 languages
.insert(language_str
);
202 } else if (language_value
->GetType() == base::Value::TYPE_LIST
) {
203 const base::ListValue
* language_list
= NULL
;
204 language_value
->GetAsList(&language_list
);
205 for (size_t j
= 0; j
< language_list
->GetSize(); ++j
) {
206 std::string language_str
;
207 if (language_list
->GetString(j
, &language_str
))
208 languages
.insert(language_str
);
212 DCHECK(!languages
.empty());
213 out
->language_codes
.assign(languages
.begin(), languages
.end());
215 const base::ListValue
* layouts
= NULL
;
216 if (!dict
.GetList(extensions::manifest_keys::kLayouts
, &layouts
))
219 for (size_t i
= 0; i
< layouts
->GetSize(); ++i
) {
221 if (layouts
->GetString(i
, &buffer
))
222 out
->layouts
.push_back(buffer
);
225 std::string url_string
;
226 if (dict
.GetString(extensions::manifest_keys::kInputView
,
228 GURL url
= extensions::Extension::GetResourceURL(
229 extensions::Extension::GetBaseURLFromExtensionId(
230 component_extension
.id
),
234 out
->input_view_url
= url
;
237 if (dict
.GetString(extensions::manifest_keys::kOptionsPage
,
239 GURL url
= extensions::Extension::GetResourceURL(
240 extensions::Extension::GetBaseURLFromExtensionId(
241 component_extension
.id
),
245 out
->options_page_url
= url
;
247 // Fallback to extension level options page.
248 out
->options_page_url
= component_extension
.options_page_url
;
255 bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
256 const base::DictionaryValue
& manifest
,
257 const std::string
& extension_id
,
258 ComponentExtensionIME
* out
) {
259 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
260 if (!manifest
.GetString(extensions::manifest_keys::kDescription
,
263 std::string url_string
;
264 if (manifest
.GetString(extensions::manifest_keys::kOptionsPage
,
266 GURL url
= extensions::Extension::GetResourceURL(
267 extensions::Extension::GetBaseURLFromExtensionId(extension_id
),
271 out
->options_page_url
= url
;
273 // It's okay to return true on no option page and/or input view page case.
278 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
279 std::vector
<ComponentExtensionIME
>* out_imes
) {
280 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
282 for (size_t i
= 0; i
< arraysize(whitelisted_component_extension
); ++i
) {
283 ComponentExtensionIME component_ime
;
284 component_ime
.path
= base::FilePath(
285 whitelisted_component_extension
[i
].path
);
287 const base::FilePath manifest_path
=
288 component_ime
.path
.Append("manifest.json");
290 if (!base::PathExists(component_ime
.path
) ||
291 !base::PathExists(manifest_path
))
294 if (!base::ReadFileToString(manifest_path
, &component_ime
.manifest
))
297 scoped_ptr
<base::DictionaryValue
> manifest
=
298 GetManifest(component_ime
.path
);
302 if (!ReadExtensionInfo(*manifest
.get(),
303 whitelisted_component_extension
[i
].id
,
306 component_ime
.id
= whitelisted_component_extension
[i
].id
;
308 const base::ListValue
* component_list
;
309 if (!manifest
->GetList(extensions::manifest_keys::kInputComponents
,
313 for (size_t i
= 0; i
< component_list
->GetSize(); ++i
) {
314 const base::DictionaryValue
* dictionary
;
315 if (!component_list
->GetDictionary(i
, &dictionary
))
318 ComponentExtensionEngine engine
;
319 ReadEngineComponent(component_ime
, *dictionary
, &engine
);
320 component_ime
.engines
.push_back(engine
);
322 out_imes
->push_back(component_ime
);
326 void ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo(
327 std::vector
<ComponentExtensionIME
>* result
,
328 const base::Closure
& callback
) {
329 DCHECK(thread_checker_
.CalledOnValidThread());
331 component_extension_list_
= *result
;
332 is_initialized_
= true;
336 } // namespace chromeos