component_extension_ime_manager_impl.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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/extensions/extension_system.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/profiles/profile_manager.h"
14#include "chrome/common/extensions/extension.h"
15#include "chrome/common/extensions/extension_file_util.h"
16#include "chrome/common/extensions/extension_l10n_util.h"
17#include "chrome/common/extensions/extension_manifest_constants.h"
18#include "content/public/browser/browser_thread.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#if defined(OFFICIAL_BUILD)
30  {
31    // Official Google Japanese Input.
32    "fpfbhcjppmaeaijcidgiibchfbnhbelj",
33    "/usr/share/chromeos-assets/input_methods/nacl_mozc",
34  },
35  {
36    // Google input tools.
37    "gjaehgfemfahhmlgpdfknkhdnemmolop",
38    "/usr/share/chromeos-assets/input_methods/input_tools",
39  },
40#else
41  {
42    // Open-sourced Mozc Japanese Input.
43    "bbaiamgfapehflhememkfglaehiobjnk",
44    "/usr/share/chromeos-assets/input_methods/nacl_mozc",
45  },
46#endif
47};
48
49extensions::ComponentLoader* GetComponentLoader() {
50  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
51  extensions::ExtensionSystem* extension_system =
52      extensions::ExtensionSystem::Get(profile);
53  ExtensionService* extension_service = extension_system->extension_service();
54  return extension_service->component_loader();
55}
56}  // namespace
57
58ComponentExtensionIMEManagerImpl::ComponentExtensionIMEManagerImpl()
59    : is_initialized_(false),
60      weak_ptr_factory_(this) {
61}
62
63ComponentExtensionIMEManagerImpl::~ComponentExtensionIMEManagerImpl() {
64}
65
66std::vector<ComponentExtensionIME> ComponentExtensionIMEManagerImpl::ListIME() {
67  DCHECK(thread_checker_.CalledOnValidThread());
68  return component_extension_list_;
69}
70
71bool ComponentExtensionIMEManagerImpl::Load(const std::string& extension_id,
72                                            const std::string& manifest,
73                                            const base::FilePath& file_path) {
74  DCHECK(thread_checker_.CalledOnValidThread());
75  if (loaded_extension_id_.find(extension_id) != loaded_extension_id_.end())
76    return false;
77  const std::string loaded_extension_id =
78      GetComponentLoader()->Add(manifest, file_path);
79  DCHECK_EQ(loaded_extension_id, extension_id);
80  loaded_extension_id_.insert(extension_id);
81  return true;
82}
83
84bool ComponentExtensionIMEManagerImpl::Unload(const std::string& extension_id,
85                                              const base::FilePath& file_path) {
86  DCHECK(thread_checker_.CalledOnValidThread());
87  if (loaded_extension_id_.find(extension_id) == loaded_extension_id_.end())
88    return false;
89  GetComponentLoader()->Remove(extension_id);
90  loaded_extension_id_.erase(extension_id);
91  return true;
92}
93
94scoped_ptr<DictionaryValue> ComponentExtensionIMEManagerImpl::GetManifest(
95    const base::FilePath& file_path) {
96  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
97  std::string error;
98  scoped_ptr<DictionaryValue> manifest(
99      extension_file_util::LoadManifest(file_path, &error));
100  if (!manifest.get())
101    LOG(ERROR) << "Failed at getting manifest";
102  if (!extension_l10n_util::LocalizeExtension(file_path,
103                                              manifest.get(),
104                                              &error))
105    LOG(ERROR) << "Localization failed";
106
107  return manifest.Pass();
108}
109
110void ComponentExtensionIMEManagerImpl::InitializeAsync(
111    const base::Closure& callback) {
112  DCHECK(!is_initialized_);
113  DCHECK(thread_checker_.CalledOnValidThread());
114
115  std::vector<ComponentExtensionIME>* component_extension_ime_list
116      = new std::vector<ComponentExtensionIME>;
117  BrowserThread::PostTaskAndReply(
118      BrowserThread::FILE,
119      FROM_HERE,
120      base::Bind(&ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo,
121                 base::Unretained(component_extension_ime_list)),
122      base::Bind(
123          &ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo,
124          weak_ptr_factory_.GetWeakPtr(),
125          base::Owned(component_extension_ime_list),
126          callback));
127}
128
129bool ComponentExtensionIMEManagerImpl::IsInitialized() {
130  return is_initialized_;
131}
132
133// static
134bool ComponentExtensionIMEManagerImpl::ReadEngineComponent(
135    const DictionaryValue& dict,
136    ComponentExtensionEngine* out) {
137  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
138  DCHECK(out);
139  std::string type;
140  if (!dict.GetString(extension_manifest_keys::kType, &type))
141    return false;
142  if (type != "ime")
143    return false;
144  if (!dict.GetString(extension_manifest_keys::kId, &out->engine_id))
145    return false;
146  if (!dict.GetString(extension_manifest_keys::kName, &out->display_name))
147    return false;
148  if (!dict.GetString(extension_manifest_keys::kLanguage, &out->language_code))
149    return false;
150
151  const ListValue* layouts = NULL;
152  if (!dict.GetList(extension_manifest_keys::kLayouts, &layouts))
153    return false;
154
155  for (size_t i = 0; i < layouts->GetSize(); ++i) {
156    std::string buffer;
157    if (layouts->GetString(i, &buffer))
158      out->layouts.push_back(buffer);
159  }
160  return true;
161}
162
163// static
164bool ComponentExtensionIMEManagerImpl::ReadExtensionInfo(
165    const DictionaryValue& manifest,
166    const std::string& extension_id,
167    ComponentExtensionIME* out) {
168  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
169  if (!manifest.GetString(extension_manifest_keys::kDescription,
170                          &out->description))
171    return false;
172  std::string url_string;
173  if (!manifest.GetString(extension_manifest_keys::kOptionsPage, &url_string))
174    return true;  // It's okay to return true on no option page case.
175
176  GURL url = extensions::Extension::GetResourceURL(
177      extensions::Extension::GetBaseURLFromExtensionId(extension_id),
178      url_string);
179  if (!url.is_valid())
180    return false;
181  out->options_page_url = url;
182  return true;
183}
184
185// static
186void ComponentExtensionIMEManagerImpl::ReadComponentExtensionsInfo(
187    std::vector<ComponentExtensionIME>* out_imes) {
188  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
189  DCHECK(out_imes);
190  for (size_t i = 0; i < arraysize(whitelisted_component_extension); ++i) {
191    ComponentExtensionIME component_ime;
192    component_ime.path = base::FilePath(
193        whitelisted_component_extension[i].path);
194
195    const base::FilePath manifest_path =
196        component_ime.path.Append("manifest.json");
197
198    if (!file_util::PathExists(component_ime.path) ||
199        !file_util::PathExists(manifest_path))
200      continue;
201
202    if (!file_util::ReadFileToString(manifest_path, &component_ime.manifest))
203      continue;
204
205    scoped_ptr<DictionaryValue> manifest = GetManifest(component_ime.path);
206    if (!manifest.get())
207      continue;
208
209    if (!ReadExtensionInfo(*manifest.get(),
210                           whitelisted_component_extension[i].id,
211                           &component_ime))
212      continue;
213    component_ime.id = whitelisted_component_extension[i].id;
214
215    const ListValue* component_list;
216    if (!manifest->GetList(extension_manifest_keys::kInputComponents,
217                           &component_list))
218      continue;
219
220    for (size_t i = 0; i < component_list->GetSize(); ++i) {
221      const DictionaryValue* dictionary;
222      if (!component_list->GetDictionary(i, &dictionary))
223        continue;
224
225      ComponentExtensionEngine engine;
226      ReadEngineComponent(*dictionary, &engine);
227      component_ime.engines.push_back(engine);
228    }
229    out_imes->push_back(component_ime);
230  }
231}
232
233void ComponentExtensionIMEManagerImpl::OnReadComponentExtensionsInfo(
234    std::vector<ComponentExtensionIME>* result,
235    const base::Closure& callback) {
236  DCHECK(thread_checker_.CalledOnValidThread());
237  DCHECK(result);
238  component_extension_list_ = *result;
239  is_initialized_ = true;
240  callback.Run();
241}
242
243}  // namespace chromeos
244