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 "chrome/common/extensions/extension_file_util.h"
14 #include "chrome/common/extensions/extension_l10n_util.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "extensions/browser/extension_system.h"
17 #include "extensions/common/extension.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 Keyboards extension.
31 "jhffeifommiaekmbkkjlpmilogcfdohp",
32 "/usr/share/chromeos-assets/input_methods/keyboard_layouts",
35 // ChromeOS Hangul Input.
36 "bdgdidmhaijohebebipajioienkglgfo",
37 "/usr/share/chromeos-assets/input_methods/hangul",
39 #if defined(OFFICIAL_BUILD)
41 // Official Google Japanese Input.
42 "fpfbhcjppmaeaijcidgiibchfbnhbelj",
43 "/usr/share/chromeos-assets/input_methods/nacl_mozc",
46 // Google Chinese Input (zhuyin)
47 "goedamlknlnjaengojinmfgpmdjmkooo",
48 "/usr/share/chromeos-assets/input_methods/zhuyin",
51 // Google Chinese Input (pinyin)
52 "nmblnjkfdkabgdofidlkienfnnbjhnab",
53 "/usr/share/chromeos-assets/input_methods/pinyin",
56 // Google Chinese Input (cangjie)
57 "gjhclobljhjhgoebiipblnmdodbmpdgd",
58 "/usr/share/chromeos-assets/input_methods/cangjie",
61 // Google input tools.
62 "gjaehgfemfahhmlgpdfknkhdnemmolop",
63 "/usr/share/chromeos-assets/input_methods/input_tools",
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 if (loaded_extension_id_.find(extension_id) != loaded_extension_id_.end())
121 const std::string loaded_extension_id =
122 GetComponentLoader()->Add(manifest, file_path);
123 DCHECK_EQ(loaded_extension_id, extension_id);
124 loaded_extension_id_.insert(extension_id);
128 bool ComponentExtensionIMEManagerImpl::Unload(const std::string& extension_id,
129 const base::FilePath& file_path) {
130 DCHECK(thread_checker_.CalledOnValidThread());
131 if (loaded_extension_id_.find(extension_id) == loaded_extension_id_.end())
133 GetComponentLoader()->Remove(extension_id);
134 loaded_extension_id_.erase(extension_id);
138 scoped_ptr<base::DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest(
139 const base::FilePath& file_path) {
140 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
142 scoped_ptr<base::DictionaryValue> manifest(
143 extension_file_util::LoadManifest(file_path, &error));
145 LOG(ERROR) << "Failed at getting manifest";
146 if (!extension_l10n_util::LocalizeExtension(file_path,
149 LOG(ERROR) << "Localization failed";
151 return manifest.Pass();
154 void ComponentExtensionIMEManagerImpl::InitializeAsync(
155 const base::Closure& callback) {
156 DCHECK(!is_initialized_);
157 DCHECK(thread_checker_.CalledOnValidThread());
159 std::vector<ComponentExtensionIME>* component_extension_ime_list
160 = new std::vector<ComponentExtensionIME>;
161 content::BrowserThread::PostTaskAndReply(
162 content::BrowserThread::FILE,
164 base::Bind(&ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo,
165 base::Unretained(component_extension_ime_list)),
167 &ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo,
168 weak_ptr_factory_.GetWeakPtr(),
169 base::Owned(component_extension_ime_list),
173 bool ComponentExtensionIMEManagerImpl::IsInitialized() {
174 return is_initialized_;
178 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
179 const base::DictionaryValue& dict,
180 ComponentExtensionEngine* out) {
181 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
184 if (!dict.GetString(extensions::manifest_keys::kType, &type))
188 if (!dict.GetString(extensions::manifest_keys::kId, &out->engine_id))
190 if (!dict.GetString(extensions::manifest_keys::kName, &out->display_name))
193 std::set<std::string> languages;
194 const base::Value* language_value = NULL;
195 if (dict.Get(extensions::manifest_keys::kLanguage, &language_value)) {
196 if (language_value->GetType() == base::Value::TYPE_STRING) {
197 std::string language_str;
198 language_value->GetAsString(&language_str);
199 languages.insert(language_str);
200 } else if (language_value->GetType() == base::Value::TYPE_LIST) {
201 const base::ListValue* language_list = NULL;
202 language_value->GetAsList(&language_list);
203 for (size_t j = 0; j < language_list->GetSize(); ++j) {
204 std::string language_str;
205 if (language_list->GetString(j, &language_str))
206 languages.insert(language_str);
210 DCHECK(!languages.empty());
211 out->language_codes.assign(languages.begin(), languages.end());
213 const base::ListValue* layouts = NULL;
214 if (!dict.GetList(extensions::manifest_keys::kLayouts, &layouts))
217 for (size_t i = 0; i < layouts->GetSize(); ++i) {
219 if (layouts->GetString(i, &buffer))
220 out->layouts.push_back(buffer);
226 bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
227 const base::DictionaryValue& manifest,
228 const std::string& extension_id,
229 ComponentExtensionIME* out) {
230 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
231 if (!manifest.GetString(extensions::manifest_keys::kDescription,
234 std::string url_string;
235 if (manifest.GetString(extensions::manifest_keys::kOptionsPage,
237 GURL url = extensions::Extension::GetResourceURL(
238 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
242 out->options_page_url = url;
244 if (manifest.GetString(extensions::manifest_keys::kInputView,
246 GURL url = extensions::Extension::GetResourceURL(
247 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
251 out->input_view_url = url;
253 // It's okay to return true on no option page and/or input view page case.
258 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
259 std::vector<ComponentExtensionIME>* out_imes) {
260 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
262 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
263 ComponentExtensionIME component_ime;
264 component_ime.path = base::FilePath(
265 whitelisted_component_extension[i].path);
267 const base::FilePath manifest_path =
268 component_ime.path.Append("manifest.json");
270 if (!base::PathExists(component_ime.path) ||
271 !base::PathExists(manifest_path))
274 if (!base::ReadFileToString(manifest_path, &component_ime.manifest))
277 scoped_ptr<base::DictionaryValue> manifest =
278 GetManifest(component_ime.path);
282 if (!ReadExtensionInfo(*manifest.get(),
283 whitelisted_component_extension[i].id,
286 component_ime.id = whitelisted_component_extension[i].id;
288 const base::ListValue* component_list;
289 if (!manifest->GetList(extensions::manifest_keys::kInputComponents,
293 for (size_t i = 0; i < component_list->GetSize(); ++i) {
294 const base::DictionaryValue* dictionary;
295 if (!component_list->GetDictionary(i, &dictionary))
298 ComponentExtensionEngine engine;
299 ReadEngineComponent(*dictionary, &engine);
300 component_ime.engines.push_back(engine);
302 out_imes->push_back(component_ime);
306 void ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo(
307 std::vector<ComponentExtensionIME>* result,
308 const base::Closure& callback) {
309 DCHECK(thread_checker_.CalledOnValidThread());
311 component_extension_list_ = *result;
312 is_initialized_ = true;
316 } // namespace chromeos