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/extensions/extension_system.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/common/extensions/extension.h"
15 #include "chrome/common/extensions/extension_file_util.h"
16 #include "chrome/common/extensions/extension_l10n_util.h"
17 #include "content/public/browser/browser_thread.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 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
91 extensions::ExtensionSystem* extension_system =
92 extensions::ExtensionSystem::Get(profile);
93 ExtensionService* extension_service = extension_system->extension_service();
94 return extension_service->component_loader();
98 ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl()
99 : is_initialized_(false),
100 weak_ptr_factory_(this) {
103 ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() {
106 std::vector<ComponentExtensionIME> ComponentExtensionIMEManagerImpl::ListIME() {
107 DCHECK(thread_checker_.CalledOnValidThread());
108 return component_extension_list_;
111 bool ComponentExtensionIMEManagerImpl::Load(const std::string& extension_id,
112 const std::string& manifest,
113 const base::FilePath& file_path) {
114 DCHECK(thread_checker_.CalledOnValidThread());
115 if (loaded_extension_id_.find(extension_id) != loaded_extension_id_.end())
117 const std::string loaded_extension_id =
118 GetComponentLoader()->Add(manifest, file_path);
119 DCHECK_EQ(loaded_extension_id, extension_id);
120 loaded_extension_id_.insert(extension_id);
124 bool ComponentExtensionIMEManagerImpl::Unload(const std::string& extension_id,
125 const base::FilePath& file_path) {
126 DCHECK(thread_checker_.CalledOnValidThread());
127 if (loaded_extension_id_.find(extension_id) == loaded_extension_id_.end())
129 GetComponentLoader()->Remove(extension_id);
130 loaded_extension_id_.erase(extension_id);
134 scoped_ptr<DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest(
135 const base::FilePath& file_path) {
136 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
138 scoped_ptr<DictionaryValue> manifest(
139 extension_file_util::LoadManifest(file_path, &error));
141 LOG(ERROR) << "Failed at getting manifest";
142 if (!extension_l10n_util::LocalizeExtension(file_path,
145 LOG(ERROR) << "Localization failed";
147 return manifest.Pass();
150 void ComponentExtensionIMEManagerImpl::InitializeAsync(
151 const base::Closure& callback) {
152 DCHECK(!is_initialized_);
153 DCHECK(thread_checker_.CalledOnValidThread());
155 std::vector<ComponentExtensionIME>* component_extension_ime_list
156 = new std::vector<ComponentExtensionIME>;
157 content::BrowserThread::PostTaskAndReply(
158 content::BrowserThread::FILE,
160 base::Bind(&ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo,
161 base::Unretained(component_extension_ime_list)),
163 &ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo,
164 weak_ptr_factory_.GetWeakPtr(),
165 base::Owned(component_extension_ime_list),
169 bool ComponentExtensionIMEManagerImpl::IsInitialized() {
170 return is_initialized_;
174 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
175 const DictionaryValue& dict,
176 ComponentExtensionEngine* out) {
177 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
180 if (!dict.GetString(extensions::manifest_keys::kType, &type))
184 if (!dict.GetString(extensions::manifest_keys::kId, &out->engine_id))
186 if (!dict.GetString(extensions::manifest_keys::kName, &out->display_name))
189 std::set<std::string> languages;
190 const base::Value* language_value = NULL;
191 if (dict.Get(extensions::manifest_keys::kLanguage, &language_value)) {
192 if (language_value->GetType() == base::Value::TYPE_STRING) {
193 std::string language_str;
194 language_value->GetAsString(&language_str);
195 languages.insert(language_str);
196 } else if (language_value->GetType() == base::Value::TYPE_LIST) {
197 const base::ListValue* language_list = NULL;
198 language_value->GetAsList(&language_list);
199 for (size_t j = 0; j < language_list->GetSize(); ++j) {
200 std::string language_str;
201 if (language_list->GetString(j, &language_str))
202 languages.insert(language_str);
206 DCHECK(!languages.empty());
207 out->language_codes.assign(languages.begin(), languages.end());
209 const ListValue* layouts = NULL;
210 if (!dict.GetList(extensions::manifest_keys::kLayouts, &layouts))
213 for (size_t i = 0; i < layouts->GetSize(); ++i) {
215 if (layouts->GetString(i, &buffer))
216 out->layouts.push_back(buffer);
222 bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
223 const DictionaryValue& manifest,
224 const std::string& extension_id,
225 ComponentExtensionIME* out) {
226 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
227 if (!manifest.GetString(extensions::manifest_keys::kDescription,
230 std::string url_string;
231 if (!manifest.GetString(extensions::manifest_keys::kOptionsPage, &url_string))
232 return true; // It's okay to return true on no option page case.
234 GURL url = extensions::Extension::GetResourceURL(
235 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
239 out->options_page_url = url;
244 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
245 std::vector<ComponentExtensionIME>* out_imes) {
246 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
248 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
249 ComponentExtensionIME component_ime;
250 component_ime.path = base::FilePath(
251 whitelisted_component_extension[i].path);
253 const base::FilePath manifest_path =
254 component_ime.path.Append("manifest.json");
256 if (!base::PathExists(component_ime.path) ||
257 !base::PathExists(manifest_path))
260 if (!base::ReadFileToString(manifest_path, &component_ime.manifest))
263 scoped_ptr<DictionaryValue> manifest = GetManifest(component_ime.path);
267 if (!ReadExtensionInfo(*manifest.get(),
268 whitelisted_component_extension[i].id,
271 component_ime.id = whitelisted_component_extension[i].id;
273 const ListValue* component_list;
274 if (!manifest->GetList(extensions::manifest_keys::kInputComponents,
278 for (size_t i = 0; i < component_list->GetSize(); ++i) {
279 const DictionaryValue* dictionary;
280 if (!component_list->GetDictionary(i, &dictionary))
283 ComponentExtensionEngine engine;
284 ReadEngineComponent(*dictionary, &engine);
285 component_ime.engines.push_back(engine);
287 out_imes->push_back(component_ime);
291 void ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo(
292 std::vector<ComponentExtensionIME>* result,
293 const base::Closure& callback) {
294 DCHECK(thread_checker_.CalledOnValidThread());
296 component_extension_list_ = *result;
297 is_initialized_ = true;
301 } // namespace chromeos