component_extension_ime_manager_impl.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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. 4 5#include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h" 6 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/profiles/profile.h" 12#include "chrome/browser/profiles/profile_manager.h" 13#include "chrome/common/extensions/extension_file_util.h" 14#include "content/public/browser/browser_thread.h" 15#include "extensions/browser/extension_system.h" 16#include "extensions/common/extension.h" 17#include "extensions/common/extension_l10n_util.h" 18#include "extensions/common/manifest_constants.h" 19#include "ui/base/l10n/l10n_util.h" 20 21namespace chromeos { 22 23namespace { 24 25struct WhitelistedComponentExtensionIME { 26 const char* id; 27 const char* path; 28} whitelisted_component_extension[] = { 29 { 30 // ChromeOS Hangul Input. 31 "bdgdidmhaijohebebipajioienkglgfo", 32 "/usr/share/chromeos-assets/input_methods/hangul", 33 }, 34#if defined(OFFICIAL_BUILD) 35 { 36 // Official Google XKB Input. 37 "jkghodnilhceideoidjikpgommlajknk", 38 "/usr/share/chromeos-assets/input_methods/google_xkb", 39 }, 40 { 41 // Official Google Keyboards Input. 42 "habcdindjejkmepknlhkkloncjcpcnbf", 43 "/usr/share/chromeos-assets/input_methods/google_keyboards", 44 }, 45 { 46 // Official Google Japanese Input. 47 "fpfbhcjppmaeaijcidgiibchfbnhbelj", 48 "/usr/share/chromeos-assets/input_methods/nacl_mozc", 49 }, 50 { 51 // Google input tools. 52 "gjaehgfemfahhmlgpdfknkhdnemmolop", 53 "/usr/share/chromeos-assets/input_methods/input_tools", 54 }, 55#else 56 { 57 // Open-sourced ChromeOS xkb extension. 58 "fgoepimhcoialccpbmpnnblemnepkkao", 59 "/usr/share/chromeos-assets/input_methods/xkb", 60 }, 61 { 62 // Open-sourced ChromeOS Keyboards extension. 63 "jhffeifommiaekmbkkjlpmilogcfdohp", 64 "/usr/share/chromeos-assets/input_methods/keyboard_layouts", 65 }, 66 { 67 // Open-sourced Pinyin Chinese Input Method. 68 "cpgalbafkoofkjmaeonnfijgpfennjjn", 69 "/usr/share/chromeos-assets/input_methods/pinyin", 70 }, 71 { 72 // Open-sourced Zhuyin Chinese Input Method. 73 "ekbifjdfhkmdeeajnolmgdlmkllopefi", 74 "/usr/share/chromeos-assets/input_methods/zhuyin", 75 }, 76 { 77 // Open-sourced Cangjie Chinese Input Method. 78 "aeebooiibjahgpgmhkeocbeekccfknbj", 79 "/usr/share/chromeos-assets/input_methods/cangjie", 80 }, 81 { 82 // Open-sourced Mozc Japanese Input. 83 "bbaiamgfapehflhememkfglaehiobjnk", 84 "/usr/share/chromeos-assets/input_methods/nacl_mozc", 85 }, 86#endif 87}; 88 89extensions::ComponentLoader* GetComponentLoader() { 90 // TODO(skuhne, nkostylev): At this time the only thing which makes sense here 91 // is to use the active profile. Nkostylev is working on getting IME settings 92 // to work for multi user by collecting all settings from all users. Once that 93 // is done we might have to re-visit this decision. 94 Profile* profile = ProfileManager::GetActiveUserProfile(); 95 extensions::ExtensionSystem* extension_system = 96 extensions::ExtensionSystem::Get(profile); 97 ExtensionService* extension_service = extension_system->extension_service(); 98 return extension_service->component_loader(); 99} 100} // namespace 101 102ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl() 103 : is_initialized_(false), 104 weak_ptr_factory_(this) { 105} 106 107ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() { 108} 109 110std::vector<ComponentExtensionIME> ComponentExtensionIMEManagerImpl::ListIME() { 111 DCHECK(thread_checker_.CalledOnValidThread()); 112 return component_extension_list_; 113} 114 115bool ComponentExtensionIMEManagerImpl::Load(const std::string& extension_id, 116 const std::string& manifest, 117 const base::FilePath& file_path) { 118 DCHECK(thread_checker_.CalledOnValidThread()); 119 Profile* profile = ProfileManager::GetActiveUserProfile(); 120 extensions::ExtensionSystem* extension_system = 121 extensions::ExtensionSystem::Get(profile); 122 ExtensionService* extension_service = extension_system->extension_service(); 123 if (extension_service->GetExtensionById(extension_id, false)) 124 return false; 125 const std::string loaded_extension_id = 126 GetComponentLoader()->Add(manifest, file_path); 127 DCHECK_EQ(loaded_extension_id, extension_id); 128 return true; 129} 130 131void ComponentExtensionIMEManagerImpl::Unload(const std::string& extension_id, 132 const base::FilePath& file_path) { 133 DCHECK(thread_checker_.CalledOnValidThread()); 134 // Remove(extension_id) does nothing when the extension has already been 135 // removed or not been registered. 136 GetComponentLoader()->Remove(extension_id); 137} 138 139scoped_ptr<base::DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest( 140 const base::FilePath& file_path) { 141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 142 std::string error; 143 scoped_ptr<base::DictionaryValue> manifest( 144 extension_file_util::LoadManifest(file_path, &error)); 145 if (!manifest.get()) 146 LOG(ERROR) << "Failed at getting manifest"; 147 if (!extension_l10n_util::LocalizeExtension(file_path, 148 manifest.get(), 149 &error)) 150 LOG(ERROR) << "Localization failed"; 151 152 return manifest.Pass(); 153} 154 155void ComponentExtensionIMEManagerImpl::InitializeAsync( 156 const base::Closure& callback) { 157 DCHECK(!is_initialized_); 158 DCHECK(thread_checker_.CalledOnValidThread()); 159 160 std::vector<ComponentExtensionIME>* component_extension_ime_list 161 = new std::vector<ComponentExtensionIME>; 162 content::BrowserThread::PostTaskAndReply( 163 content::BrowserThread::FILE, 164 FROM_HERE, 165 base::Bind(&ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo, 166 base::Unretained(component_extension_ime_list)), 167 base::Bind( 168 &ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo, 169 weak_ptr_factory_.GetWeakPtr(), 170 base::Owned(component_extension_ime_list), 171 callback)); 172} 173 174bool ComponentExtensionIMEManagerImpl::IsInitialized() { 175 return is_initialized_; 176} 177 178// static 179bool ComponentExtensionIMEManagerImpl::ReadEngineComponent( 180 const ComponentExtensionIME& component_extension, 181 const base::DictionaryValue& dict, 182 ComponentExtensionEngine* out) { 183 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 184 DCHECK(out); 185 std::string type; 186 if (!dict.GetString(extensions::manifest_keys::kType, &type)) 187 return false; 188 if (type != "ime") 189 return false; 190 if (!dict.GetString(extensions::manifest_keys::kId, &out->engine_id)) 191 return false; 192 if (!dict.GetString(extensions::manifest_keys::kName, &out->display_name)) 193 return false; 194 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); 209 } 210 } 211 } 212 DCHECK(!languages.empty()); 213 out->language_codes.assign(languages.begin(), languages.end()); 214 215 const base::ListValue* layouts = NULL; 216 if (!dict.GetList(extensions::manifest_keys::kLayouts, &layouts)) 217 return false; 218 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); 223 } 224 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; 235 } 236 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; 249 } 250 251 return true; 252} 253 254// static 255bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo( 256 const base::DictionaryValue& manifest, 257 const std::string& extension_id, 258 ComponentExtensionIME* out) { 259 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 260 if (!manifest.GetString(extensions::manifest_keys::kDescription, 261 &out->description)) 262 return false; 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; 272 } 273 // It's okay to return true on no option page and/or input view page case. 274 return true; 275} 276 277// static 278void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo( 279 std::vector<ComponentExtensionIME>* out_imes) { 280 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 281 DCHECK(out_imes); 282 for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) { 283 ComponentExtensionIME component_ime; 284 component_ime.path = base::FilePath( 285 whitelisted_component_extension[i].path); 286 287 const base::FilePath manifest_path = 288 component_ime.path.Append("manifest.json"); 289 290 if (!base::PathExists(component_ime.path) || 291 !base::PathExists(manifest_path)) 292 continue; 293 294 if (!base::ReadFileToString(manifest_path, &component_ime.manifest)) 295 continue; 296 297 scoped_ptr<base::DictionaryValue> manifest = 298 GetManifest(component_ime.path); 299 if (!manifest.get()) 300 continue; 301 302 if (!ReadExtensionInfo(*manifest.get(), 303 whitelisted_component_extension[i].id, 304 &component_ime)) 305 continue; 306 component_ime.id = whitelisted_component_extension[i].id; 307 308 const base::ListValue* component_list; 309 if (!manifest->GetList(extensions::manifest_keys::kInputComponents, 310 &component_list)) 311 continue; 312 313 for (size_t i = 0; i < component_list->GetSize(); ++i) { 314 const base::DictionaryValue* dictionary; 315 if (!component_list->GetDictionary(i, &dictionary)) 316 continue; 317 318 ComponentExtensionEngine engine; 319 ReadEngineComponent(component_ime, *dictionary, &engine); 320 component_ime.engines.push_back(engine); 321 } 322 out_imes->push_back(component_ime); 323 } 324} 325 326void ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo( 327 std::vector<ComponentExtensionIME>* result, 328 const base::Closure& callback) { 329 DCHECK(thread_checker_.CalledOnValidThread()); 330 DCHECK(result); 331 component_extension_list_ = *result; 332 is_initialized_ = true; 333 callback.Run(); 334} 335 336} // namespace chromeos 337