ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / component_extension_ime_manager_impl.cc
blob059573349176df30c2c5058a60a113085e829e29
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 <algorithm>
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_util.h"
14 #include "base/sys_info.h"
15 #include "chrome/browser/extensions/component_loader.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/extensions/extension_constants.h"
21 #include "chrome/grit/browser_resources.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_l10n_util.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "ui/base/ime/chromeos/extension_ime_util.h"
29 #include "ui/base/resource/resource_bundle.h"
31 namespace chromeos {
33 namespace {
35 struct WhitelistedComponentExtensionIME {
36 const char* id;
37 int manifest_resource_id;
38 } whitelisted_component_extension[] = {
39 #if defined(GOOGLE_CHROME_BUILD)
41 // Official Google XKB Input.
42 extension_ime_util::kXkbExtensionId, IDR_GOOGLE_XKB_MANIFEST,
45 // Google input tools.
46 extension_ime_util::kT13nExtensionId, IDR_GOOGLE_INPUT_TOOLS_MANIFEST,
48 #else
50 // Open-sourced ChromeOS xkb extension.
51 extension_ime_util::kXkbExtensionId, IDR_XKB_MANIFEST,
54 // Open-sourced ChromeOS Keyboards extension.
55 extension_ime_util::kM17nExtensionId, IDR_M17N_MANIFEST,
58 // Open-sourced Pinyin Chinese Input Method.
59 extension_ime_util::kChinesePinyinExtensionId, IDR_PINYIN_MANIFEST,
62 // Open-sourced Zhuyin Chinese Input Method.
63 extension_ime_util::kChineseZhuyinExtensionId, IDR_ZHUYIN_MANIFEST,
66 // Open-sourced Cangjie Chinese Input Method.
67 extension_ime_util::kChineseCangjieExtensionId, IDR_CANGJIE_MANIFEST,
70 // Open-sourced Japanese Mozc Input.
71 extension_ime_util::kMozcExtensionId, IDR_MOZC_MANIFEST,
74 // Open-sourced Hangul Input.
75 extension_ime_util::kHangulExtensionId, IDR_HANGUL_MANIFEST,
77 #endif
79 // Braille hardware keyboard IME that works together with ChromeVox.
80 extension_misc::kBrailleImeExtensionId, IDR_BRAILLE_MANIFEST,
84 const char kImePathKeyName[] = "ime_path";
86 extensions::ComponentLoader* GetComponentLoader(Profile* profile) {
87 extensions::ExtensionSystem* extension_system =
88 extensions::ExtensionSystem::Get(profile);
89 ExtensionService* extension_service = extension_system->extension_service();
90 return extension_service->component_loader();
93 void DoLoadExtension(Profile* profile,
94 const std::string& extension_id,
95 const std::string& manifest,
96 const base::FilePath& file_path) {
97 extensions::ExtensionSystem* extension_system =
98 extensions::ExtensionSystem::Get(profile);
99 ExtensionService* extension_service = extension_system->extension_service();
100 DCHECK(extension_service);
101 if (extension_service->GetExtensionById(extension_id, false))
102 return;
103 const std::string loaded_extension_id =
104 GetComponentLoader(profile)->Add(manifest, file_path);
105 DCHECK_EQ(loaded_extension_id, extension_id);
108 bool CheckFilePath(const base::FilePath* file_path) {
109 return base::PathExists(*file_path);
112 void OnFilePathChecked(Profile* profile,
113 const std::string* extension_id,
114 const std::string* manifest,
115 const base::FilePath* file_path,
116 bool result) {
117 if (result) {
118 DoLoadExtension(profile, *extension_id, *manifest, *file_path);
119 } else {
120 LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
121 << "IME extension file path does not exist: " << file_path->value();
125 } // namespace
127 ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl() {
128 ReadComponentExtensionsInfo(&component_extension_list_);
131 ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() {
134 std::vector<ComponentExtensionIME> ComponentExtensionIMEManagerImpl::ListIME() {
135 return component_extension_list_;
138 void ComponentExtensionIMEManagerImpl::Load(Profile* profile,
139 const std::string& extension_id,
140 const std::string& manifest,
141 const base::FilePath& file_path) {
142 // Check the existence of file path to avoid unnecessary extension loading
143 // and InputMethodEngine creation, so that the virtual keyboard web content
144 // url won't be override by IME component extensions.
145 base::FilePath* copied_file_path = new base::FilePath(file_path);
146 content::BrowserThread::PostTaskAndReplyWithResult(
147 content::BrowserThread::FILE,
148 FROM_HERE,
149 base::Bind(&CheckFilePath,
150 base::Unretained(copied_file_path)),
151 base::Bind(&OnFilePathChecked,
152 base::Unretained(profile),
153 base::Owned(new std::string(extension_id)),
154 base::Owned(new std::string(manifest)),
155 base::Owned(copied_file_path)));
158 void ComponentExtensionIMEManagerImpl::Unload(Profile* profile,
159 const std::string& extension_id,
160 const base::FilePath& file_path) {
161 // Remove(extension_id) does nothing when the extension has already been
162 // removed or not been registered.
163 GetComponentLoader(profile)->Remove(extension_id);
166 scoped_ptr<base::DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest(
167 const std::string& manifest_string) {
168 std::string error;
169 JSONStringValueDeserializer deserializer(manifest_string);
170 scoped_ptr<base::Value> manifest(deserializer.Deserialize(NULL, &error));
171 if (!manifest.get())
172 LOG(ERROR) << "Failed at getting manifest";
174 return scoped_ptr<base::DictionaryValue>(
175 static_cast<base::DictionaryValue*>(manifest.release())).Pass();
178 // static
179 bool ComponentExtensionIMEManagerImpl::IsIMEExtensionID(const std::string& id) {
180 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
181 if (base::LowerCaseEqualsASCII(id, whitelisted_component_extension[i].id))
182 return true;
184 return false;
187 // static
188 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
189 const ComponentExtensionIME& component_extension,
190 const base::DictionaryValue& dict,
191 ComponentExtensionEngine* out) {
192 DCHECK(out);
193 std::string type;
194 if (!dict.GetString(extensions::manifest_keys::kType, &type))
195 return false;
196 if (type != "ime")
197 return false;
198 if (!dict.GetString(extensions::manifest_keys::kId, &out->engine_id))
199 return false;
200 if (!dict.GetString(extensions::manifest_keys::kName, &out->display_name))
201 return false;
202 if (!dict.GetString(extensions::manifest_keys::kIndicator, &out->indicator))
203 out->indicator = "";
205 std::set<std::string> languages;
206 const base::Value* language_value = NULL;
207 if (dict.Get(extensions::manifest_keys::kLanguage, &language_value)) {
208 if (language_value->GetType() == base::Value::TYPE_STRING) {
209 std::string language_str;
210 language_value->GetAsString(&language_str);
211 languages.insert(language_str);
212 } else if (language_value->GetType() == base::Value::TYPE_LIST) {
213 const base::ListValue* language_list = NULL;
214 language_value->GetAsList(&language_list);
215 for (size_t j = 0; j < language_list->GetSize(); ++j) {
216 std::string language_str;
217 if (language_list->GetString(j, &language_str))
218 languages.insert(language_str);
222 DCHECK(!languages.empty());
223 out->language_codes.assign(languages.begin(), languages.end());
225 const base::ListValue* layouts = NULL;
226 if (!dict.GetList(extensions::manifest_keys::kLayouts, &layouts))
227 return false;
229 for (size_t i = 0; i < layouts->GetSize(); ++i) {
230 std::string buffer;
231 if (layouts->GetString(i, &buffer))
232 out->layouts.push_back(buffer);
235 std::string url_string;
236 if (dict.GetString(extensions::manifest_keys::kInputView,
237 &url_string)) {
238 GURL url = extensions::Extension::GetResourceURL(
239 extensions::Extension::GetBaseURLFromExtensionId(
240 component_extension.id),
241 url_string);
242 if (!url.is_valid())
243 return false;
244 out->input_view_url = url;
247 if (dict.GetString(extensions::manifest_keys::kOptionsPage,
248 &url_string)) {
249 GURL url = extensions::Extension::GetResourceURL(
250 extensions::Extension::GetBaseURLFromExtensionId(
251 component_extension.id),
252 url_string);
253 if (!url.is_valid())
254 return false;
255 out->options_page_url = url;
256 } else {
257 // Fallback to extension level options page.
258 out->options_page_url = component_extension.options_page_url;
261 return true;
264 // static
265 bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
266 const base::DictionaryValue& manifest,
267 const std::string& extension_id,
268 ComponentExtensionIME* out) {
269 if (!manifest.GetString(extensions::manifest_keys::kDescription,
270 &out->description))
271 return false;
272 std::string path;
273 if (manifest.GetString(kImePathKeyName, &path))
274 out->path = base::FilePath(path);
275 std::string url_string;
276 if (manifest.GetString(extensions::manifest_keys::kOptionsPage,
277 &url_string)) {
278 GURL url = extensions::Extension::GetResourceURL(
279 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
280 url_string);
281 if (!url.is_valid())
282 return false;
283 out->options_page_url = url;
285 // It's okay to return true on no option page and/or input view page case.
286 return true;
289 // static
290 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
291 std::vector<ComponentExtensionIME>* out_imes) {
292 DCHECK(out_imes);
293 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
294 ComponentExtensionIME component_ime;
295 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
296 component_ime.manifest =
297 rb.GetRawDataResource(
298 whitelisted_component_extension[i].manifest_resource_id)
299 .as_string();
300 if (component_ime.manifest.empty())
301 continue;
303 scoped_ptr<base::DictionaryValue> manifest =
304 GetManifest(component_ime.manifest);
305 if (!manifest.get())
306 continue;
308 if (!ReadExtensionInfo(*manifest.get(),
309 whitelisted_component_extension[i].id,
310 &component_ime))
311 continue;
312 component_ime.id = whitelisted_component_extension[i].id;
314 if (!component_ime.path.IsAbsolute()) {
315 base::FilePath resources_path;
316 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_path))
317 NOTREACHED();
318 component_ime.path = resources_path.Append(component_ime.path);
321 const base::ListValue* component_list;
322 if (!manifest->GetList(extensions::manifest_keys::kInputComponents,
323 &component_list))
324 continue;
326 for (size_t i = 0; i < component_list->GetSize(); ++i) {
327 const base::DictionaryValue* dictionary;
328 if (!component_list->GetDictionary(i, &dictionary))
329 continue;
331 ComponentExtensionEngine engine;
332 ReadEngineComponent(component_ime, *dictionary, &engine);
333 component_ime.engines.push_back(engine);
335 out_imes->push_back(component_ime);
339 } // namespace chromeos