Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / component_extension_ime_manager_impl.cc
blob54eec6027912fdba4cb449856c2bc348858e45eb
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 {// ChromeOS Hangul Input.
40 extension_ime_util::kHangulExtensionId, IDR_HANGUL_MANIFEST,
42 #if defined(GOOGLE_CHROME_BUILD)
43 {// Official Google XKB Input.
44 extension_ime_util::kXkbExtensionId, IDR_GOOGLE_XKB_MANIFEST,
46 {// Google input tools.
47 extension_ime_util::kT13nExtensionId, IDR_GOOGLE_INPUT_TOOLS_MANIFEST,
49 #else
50 {// Open-sourced ChromeOS xkb extension.
51 extension_ime_util::kXkbExtensionId, IDR_XKB_MANIFEST,
53 {// Open-sourced ChromeOS Keyboards extension.
54 extension_ime_util::kM17nExtensionId, IDR_M17N_MANIFEST,
56 {// Open-sourced Pinyin Chinese Input Method.
57 extension_ime_util::kChinesePinyinExtensionId, IDR_PINYIN_MANIFEST,
59 {// Open-sourced Zhuyin Chinese Input Method.
60 extension_ime_util::kChineseZhuyinExtensionId, IDR_ZHUYIN_MANIFEST,
62 {// Open-sourced Cangjie Chinese Input Method.
63 extension_ime_util::kChineseCangjieExtensionId, IDR_CANGJIE_MANIFEST,
65 {// Japanese Mozc Input.
66 extension_ime_util::kMozcExtensionId, IDR_MOZC_MANIFEST,
68 #endif
69 {// Braille hardware keyboard IME that works together with ChromeVox.
70 extension_misc::kBrailleImeExtensionId, IDR_BRAILLE_MANIFEST,
74 const char kImePathKeyName[] = "ime_path";
76 extensions::ComponentLoader* GetComponentLoader(Profile* profile) {
77 extensions::ExtensionSystem* extension_system =
78 extensions::ExtensionSystem::Get(profile);
79 ExtensionService* extension_service = extension_system->extension_service();
80 return extension_service->component_loader();
83 void DoLoadExtension(Profile* profile,
84 const std::string& extension_id,
85 const std::string& manifest,
86 const base::FilePath& file_path) {
87 extensions::ExtensionSystem* extension_system =
88 extensions::ExtensionSystem::Get(profile);
89 ExtensionService* extension_service = extension_system->extension_service();
90 DCHECK(extension_service);
91 if (extension_service->GetExtensionById(extension_id, false))
92 return;
93 const std::string loaded_extension_id =
94 GetComponentLoader(profile)->Add(manifest, file_path);
95 DCHECK_EQ(loaded_extension_id, extension_id);
98 bool CheckFilePath(const base::FilePath* file_path) {
99 return base::PathExists(*file_path);
102 void OnFilePathChecked(Profile* profile,
103 const std::string* extension_id,
104 const std::string* manifest,
105 const base::FilePath* file_path,
106 bool result) {
107 if (result) {
108 DoLoadExtension(profile, *extension_id, *manifest, *file_path);
109 } else {
110 LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
111 << "IME extension file path does not exist: " << file_path->value();
115 } // namespace
117 ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl() {
118 ReadComponentExtensionsInfo(&component_extension_list_);
121 ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() {
124 std::vector<ComponentExtensionIME> ComponentExtensionIMEManagerImpl::ListIME() {
125 return component_extension_list_;
128 void ComponentExtensionIMEManagerImpl::Load(Profile* profile,
129 const std::string& extension_id,
130 const std::string& manifest,
131 const base::FilePath& file_path) {
132 // Check the existence of file path to avoid unnecessary extension loading
133 // and InputMethodEngine creation, so that the virtual keyboard web content
134 // url won't be override by IME component extensions.
135 base::FilePath* copied_file_path = new base::FilePath(file_path);
136 content::BrowserThread::PostTaskAndReplyWithResult(
137 content::BrowserThread::FILE,
138 FROM_HERE,
139 base::Bind(&CheckFilePath,
140 base::Unretained(copied_file_path)),
141 base::Bind(&OnFilePathChecked,
142 base::Unretained(profile),
143 base::Owned(new std::string(extension_id)),
144 base::Owned(new std::string(manifest)),
145 base::Owned(copied_file_path)));
148 void ComponentExtensionIMEManagerImpl::Unload(Profile* profile,
149 const std::string& extension_id,
150 const base::FilePath& file_path) {
151 // Remove(extension_id) does nothing when the extension has already been
152 // removed or not been registered.
153 GetComponentLoader(profile)->Remove(extension_id);
156 scoped_ptr<base::DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest(
157 const std::string& manifest_string) {
158 std::string error;
159 JSONStringValueDeserializer deserializer(manifest_string);
160 scoped_ptr<base::Value> manifest(deserializer.Deserialize(NULL, &error));
161 if (!manifest.get())
162 LOG(ERROR) << "Failed at getting manifest";
164 return scoped_ptr<base::DictionaryValue>(
165 static_cast<base::DictionaryValue*>(manifest.release())).Pass();
168 // static
169 bool ComponentExtensionIMEManagerImpl::IsIMEExtensionID(const std::string& id) {
170 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
171 if (base::LowerCaseEqualsASCII(id, whitelisted_component_extension[i].id))
172 return true;
174 return false;
177 // static
178 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
179 const ComponentExtensionIME& component_extension,
180 const base::DictionaryValue& dict,
181 ComponentExtensionEngine* out) {
182 DCHECK(out);
183 std::string type;
184 if (!dict.GetString(extensions::manifest_keys::kType, &type))
185 return false;
186 if (type != "ime")
187 return false;
188 if (!dict.GetString(extensions::manifest_keys::kId, &out->engine_id))
189 return false;
190 if (!dict.GetString(extensions::manifest_keys::kName, &out->display_name))
191 return false;
192 if (!dict.GetString(extensions::manifest_keys::kIndicator, &out->indicator))
193 out->indicator = "";
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))
217 return false;
219 for (size_t i = 0; i < layouts->GetSize(); ++i) {
220 std::string buffer;
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,
227 &url_string)) {
228 GURL url = extensions::Extension::GetResourceURL(
229 extensions::Extension::GetBaseURLFromExtensionId(
230 component_extension.id),
231 url_string);
232 if (!url.is_valid())
233 return false;
234 out->input_view_url = url;
237 if (dict.GetString(extensions::manifest_keys::kOptionsPage,
238 &url_string)) {
239 GURL url = extensions::Extension::GetResourceURL(
240 extensions::Extension::GetBaseURLFromExtensionId(
241 component_extension.id),
242 url_string);
243 if (!url.is_valid())
244 return false;
245 out->options_page_url = url;
246 } else {
247 // Fallback to extension level options page.
248 out->options_page_url = component_extension.options_page_url;
251 return true;
254 // static
255 bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
256 const base::DictionaryValue& manifest,
257 const std::string& extension_id,
258 ComponentExtensionIME* out) {
259 if (!manifest.GetString(extensions::manifest_keys::kDescription,
260 &out->description))
261 return false;
262 std::string path;
263 if (manifest.GetString(kImePathKeyName, &path))
264 out->path = base::FilePath(path);
265 std::string url_string;
266 if (manifest.GetString(extensions::manifest_keys::kOptionsPage,
267 &url_string)) {
268 GURL url = extensions::Extension::GetResourceURL(
269 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
270 url_string);
271 if (!url.is_valid())
272 return false;
273 out->options_page_url = url;
275 // It's okay to return true on no option page and/or input view page case.
276 return true;
279 // static
280 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
281 std::vector<ComponentExtensionIME>* out_imes) {
282 DCHECK(out_imes);
283 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
284 ComponentExtensionIME component_ime;
285 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
286 component_ime.manifest =
287 rb.GetRawDataResource(
288 whitelisted_component_extension[i].manifest_resource_id)
289 .as_string();
290 if (component_ime.manifest.empty())
291 continue;
293 scoped_ptr<base::DictionaryValue> manifest =
294 GetManifest(component_ime.manifest);
295 if (!manifest.get())
296 continue;
298 if (!ReadExtensionInfo(*manifest.get(),
299 whitelisted_component_extension[i].id,
300 &component_ime))
301 continue;
302 component_ime.id = whitelisted_component_extension[i].id;
304 if (!component_ime.path.IsAbsolute()) {
305 base::FilePath resources_path;
306 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_path))
307 NOTREACHED();
308 component_ime.path = resources_path.Append(component_ime.path);
311 const base::ListValue* component_list;
312 if (!manifest->GetList(extensions::manifest_keys::kInputComponents,
313 &component_list))
314 continue;
316 for (size_t i = 0; i < component_list->GetSize(); ++i) {
317 const base::DictionaryValue* dictionary;
318 if (!component_list->GetDictionary(i, &dictionary))
319 continue;
321 ComponentExtensionEngine engine;
322 ReadEngineComponent(component_ime, *dictionary, &engine);
323 component_ime.engines.push_back(engine);
325 out_imes->push_back(component_ime);
329 } // namespace chromeos