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"
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"
35 struct WhitelistedComponentExtensionIME
{
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
,
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
,
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))
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
,
118 DoLoadExtension(profile
, *extension_id
, *manifest
, *file_path
);
120 LOG_IF(ERROR
, base::SysInfo::IsRunningOnChromeOS())
121 << "IME extension file path does not exist: " << file_path
->value();
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,
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
) {
169 JSONStringValueDeserializer
deserializer(manifest_string
);
170 scoped_ptr
<base::Value
> manifest(deserializer
.Deserialize(NULL
, &error
));
172 LOG(ERROR
) << "Failed at getting manifest";
174 return scoped_ptr
<base::DictionaryValue
>(
175 static_cast<base::DictionaryValue
*>(manifest
.release())).Pass();
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
))
188 bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
189 const ComponentExtensionIME
& component_extension
,
190 const base::DictionaryValue
& dict
,
191 ComponentExtensionEngine
* out
) {
194 if (!dict
.GetString(extensions::manifest_keys::kType
, &type
))
198 if (!dict
.GetString(extensions::manifest_keys::kId
, &out
->engine_id
))
200 if (!dict
.GetString(extensions::manifest_keys::kName
, &out
->display_name
))
202 if (!dict
.GetString(extensions::manifest_keys::kIndicator
, &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
))
229 for (size_t i
= 0; i
< layouts
->GetSize(); ++i
) {
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
,
238 GURL url
= extensions::Extension::GetResourceURL(
239 extensions::Extension::GetBaseURLFromExtensionId(
240 component_extension
.id
),
244 out
->input_view_url
= url
;
247 if (dict
.GetString(extensions::manifest_keys::kOptionsPage
,
249 GURL url
= extensions::Extension::GetResourceURL(
250 extensions::Extension::GetBaseURLFromExtensionId(
251 component_extension
.id
),
255 out
->options_page_url
= url
;
257 // Fallback to extension level options page.
258 out
->options_page_url
= component_extension
.options_page_url
;
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
,
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
,
278 GURL url
= extensions::Extension::GetResourceURL(
279 extensions::Extension::GetBaseURLFromExtensionId(extension_id
),
283 out
->options_page_url
= url
;
285 // It's okay to return true on no option page and/or input view page case.
290 void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
291 std::vector
<ComponentExtensionIME
>* 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
)
300 if (component_ime
.manifest
.empty())
303 scoped_ptr
<base::DictionaryValue
> manifest
=
304 GetManifest(component_ime
.manifest
);
308 if (!ReadExtensionInfo(*manifest
.get(),
309 whitelisted_component_extension
[i
].id
,
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
))
318 component_ime
.path
= resources_path
.Append(component_ime
.path
);
321 const base::ListValue
* component_list
;
322 if (!manifest
->GetList(extensions::manifest_keys::kInputComponents
,
326 for (size_t i
= 0; i
< component_list
->GetSize(); ++i
) {
327 const base::DictionaryValue
* dictionary
;
328 if (!component_list
->GetDictionary(i
, &dictionary
))
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