Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / component_extension_ime_manager_impl.cc
blobd423c327ce3095caeea54dd3881151e7f7c60137
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(ERROR) << "IME extension file path not exists: " << file_path->value();
113 } // namespace
115 ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl() {
116 ReadComponentExtensionsInfo(&component_extension_list_);
119 ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() {
122 std::vector<ComponentExtensionIME> ComponentExtensionIMEManagerImpl::ListIME() {
123 return component_extension_list_;
126 void ComponentExtensionIMEManagerImpl::Load(Profile* profile,
127 const std::string& extension_id,
128 const std::string& manifest,
129 const base::FilePath& file_path) {
130 // Check the existence of file path to avoid unnecessary extension loading
131 // and InputMethodEngine creation, so that the virtual keyboard web content
132 // url won't be override by IME component extensions.
133 base::FilePath* copied_file_path = new base::FilePath(file_path);
134 content::BrowserThread::PostTaskAndReplyWithResult(
135 content::BrowserThread::FILE,
136 FROM_HERE,
137 base::Bind(&CheckFilePath,
138 base::Unretained(copied_file_path)),
139 base::Bind(&OnFilePathChecked,
140 base::Unretained(profile),
141 base::Owned(new std::string(extension_id)),
142 base::Owned(new std::string(manifest)),
143 base::Owned(copied_file_path)));
146 void ComponentExtensionIMEManagerImpl::Unload(Profile* profile,
147 const std::string& extension_id,
148 const base::FilePath& file_path) {
149 // Remove(extension_id) does nothing when the extension has already been
150 // removed or not been registered.
151 GetComponentLoader(profile)->Remove(extension_id);
154 scoped_ptr<base::DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest(
155 const std::string& manifest_string) {
156 std::string error;
157 JSONStringValueDeserializer deserializer(manifest_string);
158 scoped_ptr<base::Value> manifest(deserializer.Deserialize(NULL, &error));
159 if (!manifest.get())
160 LOG(ERROR) << "Failed at getting manifest";
162 return scoped_ptr<base::DictionaryValue>(
163 static_cast<base::DictionaryValue*>(manifest.release())).Pass();
166 // static
167 bool ComponentExtensionIMEManagerImpl::IsIMEExtensionID(const std::string& id) {
168 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
169 if (base::LowerCaseEqualsASCII(id, whitelisted_component_extension[i].id))
170 return true;
172 return false;
175 // static
176 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
177 const ComponentExtensionIME& component_extension,
178 const base::DictionaryValue& dict,
179 ComponentExtensionEngine* out) {
180 DCHECK(out);
181 std::string type;
182 if (!dict.GetString(extensions::manifest_keys::kType, &type))
183 return false;
184 if (type != "ime")
185 return false;
186 if (!dict.GetString(extensions::manifest_keys::kId, &out->engine_id))
187 return false;
188 if (!dict.GetString(extensions::manifest_keys::kName, &out->display_name))
189 return false;
190 if (!dict.GetString(extensions::manifest_keys::kIndicator, &out->indicator))
191 out->indicator = "";
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))
215 return false;
217 for (size_t i = 0; i < layouts->GetSize(); ++i) {
218 std::string buffer;
219 if (layouts->GetString(i, &buffer))
220 out->layouts.push_back(buffer);
223 std::string url_string;
224 if (dict.GetString(extensions::manifest_keys::kInputView,
225 &url_string)) {
226 GURL url = extensions::Extension::GetResourceURL(
227 extensions::Extension::GetBaseURLFromExtensionId(
228 component_extension.id),
229 url_string);
230 if (!url.is_valid())
231 return false;
232 out->input_view_url = url;
235 if (dict.GetString(extensions::manifest_keys::kOptionsPage,
236 &url_string)) {
237 GURL url = extensions::Extension::GetResourceURL(
238 extensions::Extension::GetBaseURLFromExtensionId(
239 component_extension.id),
240 url_string);
241 if (!url.is_valid())
242 return false;
243 out->options_page_url = url;
244 } else {
245 // Fallback to extension level options page.
246 out->options_page_url = component_extension.options_page_url;
249 return true;
252 // static
253 bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
254 const base::DictionaryValue& manifest,
255 const std::string& extension_id,
256 ComponentExtensionIME* out) {
257 if (!manifest.GetString(extensions::manifest_keys::kDescription,
258 &out->description))
259 return false;
260 std::string path;
261 if (manifest.GetString(kImePathKeyName, &path))
262 out->path = base::FilePath(path);
263 std::string url_string;
264 if (manifest.GetString(extensions::manifest_keys::kOptionsPage,
265 &url_string)) {
266 GURL url = extensions::Extension::GetResourceURL(
267 extensions::Extension::GetBaseURLFromExtensionId(extension_id),
268 url_string);
269 if (!url.is_valid())
270 return false;
271 out->options_page_url = url;
273 // It's okay to return true on no option page and/or input view page case.
274 return true;
277 // static
278 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
279 std::vector<ComponentExtensionIME>* out_imes) {
280 DCHECK(out_imes);
281 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
282 ComponentExtensionIME component_ime;
283 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
284 component_ime.manifest =
285 rb.GetRawDataResource(
286 whitelisted_component_extension[i].manifest_resource_id)
287 .as_string();
288 if (component_ime.manifest.empty())
289 continue;
291 scoped_ptr<base::DictionaryValue> manifest =
292 GetManifest(component_ime.manifest);
293 if (!manifest.get())
294 continue;
296 if (!ReadExtensionInfo(*manifest.get(),
297 whitelisted_component_extension[i].id,
298 &component_ime))
299 continue;
300 component_ime.id = whitelisted_component_extension[i].id;
302 if (!component_ime.path.IsAbsolute()) {
303 base::FilePath resources_path;
304 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_path))
305 NOTREACHED();
306 component_ime.path = resources_path.Append(component_ime.path);
309 const base::ListValue* component_list;
310 if (!manifest->GetList(extensions::manifest_keys::kInputComponents,
311 &component_list))
312 continue;
314 for (size_t i = 0; i < component_list->GetSize(); ++i) {
315 const base::DictionaryValue* dictionary;
316 if (!component_list->GetDictionary(i, &dictionary))
317 continue;
319 ComponentExtensionEngine engine;
320 ReadEngineComponent(component_ime, *dictionary, &engine);
321 component_ime.engines.push_back(engine);
323 out_imes->push_back(component_ime);
327 } // namespace chromeos