1// Copyright (c) 2011 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/extensions/extension_management_api.h"
6
7#include <map>
8#include <string>
9
10#include "base/basictypes.h"
11#include "base/json/json_writer.h"
12#include "base/metrics/histogram.h"
13#include "base/string_number_conversions.h"
14#include "base/string_util.h"
15#include "chrome/browser/extensions/extension_event_names.h"
16#include "chrome/browser/extensions/extension_event_router.h"
17#include "chrome/browser/extensions/extension_service.h"
18#include "chrome/browser/extensions/extension_updater.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/webui/extension_icon_source.h"
22#include "chrome/common/extensions/extension.h"
23#include "chrome/common/extensions/extension_constants.h"
24#include "chrome/common/extensions/extension_error_utils.h"
25#include "chrome/common/extensions/extension_icon_set.h"
26#include "chrome/common/extensions/url_pattern.h"
27#include "content/common/notification_service.h"
28#include "content/common/notification_type.h"
29
30using base::IntToString;
31namespace events = extension_event_names;
32
33namespace {
34
35const char kAppLaunchUrlKey[] = "appLaunchUrl";
36const char kDescriptionKey[] = "description";
37const char kEnabledKey[] = "enabled";
38const char kHomepageURLKey[] = "homepageUrl";
39const char kIconsKey[] = "icons";
40const char kIdKey[] = "id";
41const char kIsAppKey[] = "isApp";
42const char kNameKey[] = "name";
43const char kOptionsUrlKey[] = "optionsUrl";
44const char kPermissionsKey[] = "permissions";
45const char kMayDisableKey[] = "mayDisable";
46const char kSizeKey[] = "size";
47const char kUrlKey[] = "url";
48const char kVersionKey[] = "version";
49
50const char kNoExtensionError[] = "No extension with id *";
51const char kNotAnAppError[] = "Extension * is not an App";
52const char kUserCantDisableError[] = "Extension * can not be disabled by user";
53}
54
55ExtensionService* ExtensionManagementFunction::service() {
56  return profile()->GetExtensionService();
57}
58
59static DictionaryValue* CreateExtensionInfo(const Extension& extension,
60                                            bool enabled) {
61  DictionaryValue* info = new DictionaryValue();
62  info->SetString(kIdKey, extension.id());
63  info->SetBoolean(kIsAppKey, extension.is_app());
64  info->SetString(kNameKey, extension.name());
65  info->SetBoolean(kEnabledKey, enabled);
66  info->SetBoolean(kMayDisableKey,
67                   Extension::UserMayDisable(extension.location()));
68  info->SetString(kVersionKey, extension.VersionString());
69  info->SetString(kDescriptionKey, extension.description());
70  info->SetString(kOptionsUrlKey,
71                    extension.options_url().possibly_invalid_spec());
72  info->SetString(kHomepageURLKey,
73                    extension.GetHomepageURL().possibly_invalid_spec());
74  if (extension.is_app())
75    info->SetString(kAppLaunchUrlKey,
76                    extension.GetFullLaunchURL().possibly_invalid_spec());
77
78  const ExtensionIconSet::IconMap& icons = extension.icons().map();
79  if (!icons.empty()) {
80    ListValue* icon_list = new ListValue();
81    std::map<int, std::string>::const_iterator icon_iter;
82    for (icon_iter = icons.begin(); icon_iter != icons.end(); ++icon_iter) {
83      DictionaryValue* icon_info = new DictionaryValue();
84      Extension::Icons size = static_cast<Extension::Icons>(icon_iter->first);
85      GURL url = ExtensionIconSource::GetIconURL(
86          &extension, size, ExtensionIconSet::MATCH_EXACTLY, false);
87      icon_info->SetInteger(kSizeKey, icon_iter->first);
88      icon_info->SetString(kUrlKey, url.spec());
89      icon_list->Append(icon_info);
90    }
91    info->Set("icons", icon_list);
92  }
93
94  const std::set<std::string> perms = extension.api_permissions();
95  ListValue* permission_list = new ListValue();
96  if (!perms.empty()) {
97    std::set<std::string>::const_iterator perms_iter;
98    for (perms_iter = perms.begin(); perms_iter != perms.end(); ++perms_iter) {
99      StringValue* permission_name = new StringValue(*perms_iter);
100      permission_list->Append(permission_name);
101    }
102  }
103  info->Set("permissions", permission_list);
104
105  ListValue* host_permission_list = new ListValue();
106  if (!extension.is_hosted_app()) {
107    // Skip host permissions for hosted apps.
108    const URLPatternList host_perms = extension.host_permissions();
109    if (!host_perms.empty()) {
110      std::vector<URLPattern>::const_iterator host_perms_iter;
111      for (host_perms_iter = host_perms.begin();
112           host_perms_iter != host_perms.end();
113           ++host_perms_iter) {
114        StringValue* name = new StringValue(host_perms_iter->GetAsString());
115        host_permission_list->Append(name);
116      }
117    }
118  }
119  info->Set("hostPermissions", host_permission_list);
120
121  return info;
122}
123
124static void AddExtensionInfo(ListValue* list,
125                             const ExtensionList& extensions,
126                             bool enabled) {
127  for (ExtensionList::const_iterator i = extensions.begin();
128       i != extensions.end(); ++i) {
129    const Extension& extension = **i;
130
131    if (extension.location() == Extension::COMPONENT)
132      continue;  // Skip built-in extensions.
133
134    list->Append(CreateExtensionInfo(extension, enabled));
135  }
136}
137
138bool GetAllExtensionsFunction::RunImpl() {
139  ListValue* result = new ListValue();
140  result_.reset(result);
141
142  AddExtensionInfo(result, *service()->extensions(), true);
143  AddExtensionInfo(result, *service()->disabled_extensions(), false);
144
145  return true;
146}
147
148bool GetExtensionByIdFunction::RunImpl() {
149  std::string extension_id;
150  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id));
151  const Extension* extension = service()->GetExtensionById(extension_id, true);
152  if (!extension) {
153    error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError,
154                                                     extension_id);
155    return false;
156  }
157  bool enabled = service()->extension_prefs()->
158      GetExtensionState(extension_id) == Extension::ENABLED;
159
160  DictionaryValue* result = CreateExtensionInfo(*extension, enabled);
161  result_.reset(result);
162
163  return true;
164}
165
166bool LaunchAppFunction::RunImpl() {
167  std::string extension_id;
168  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id));
169  const Extension* extension = service()->GetExtensionById(extension_id, true);
170  if (!extension) {
171    error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError,
172                                                     extension_id);
173    return false;
174  }
175  if (!extension->is_app()) {
176    error_ = ExtensionErrorUtils::FormatErrorMessage(kNotAnAppError,
177                                                     extension_id);
178    return false;
179  }
180
181  // Look at prefs to find the right launch container.
182  // |default_pref_value| is set to LAUNCH_REGULAR so that if
183  // the user has not set a preference, we open the app in a tab.
184  extension_misc::LaunchContainer launch_container =
185      service()->extension_prefs()->GetLaunchContainer(
186          extension, ExtensionPrefs::LAUNCH_DEFAULT);
187  Browser::OpenApplication(profile(), extension, launch_container, NULL);
188  UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram,
189                            extension_misc::APP_LAUNCH_EXTENSION_API,
190                            extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
191
192  return true;
193}
194
195bool SetEnabledFunction::RunImpl() {
196  std::string extension_id;
197  bool enable;
198  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id));
199  EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &enable));
200
201  if (!service()->GetExtensionById(extension_id, true)) {
202    error_ = ExtensionErrorUtils::FormatErrorMessage(
203        kNoExtensionError, extension_id);
204    return false;
205  }
206
207  ExtensionPrefs* prefs = service()->extension_prefs();
208  Extension::State state = prefs->GetExtensionState(extension_id);
209
210  if (!Extension::UserMayDisable(
211      prefs->GetInstalledExtensionInfo(extension_id)->extension_location)) {
212    error_ = ExtensionErrorUtils::FormatErrorMessage(
213        kUserCantDisableError, extension_id);
214    return false;
215  }
216
217  if (state == Extension::DISABLED && enable) {
218    service()->EnableExtension(extension_id);
219  } else if (state == Extension::ENABLED && !enable) {
220    service()->DisableExtension(extension_id);
221  }
222
223  return true;
224}
225
226bool UninstallFunction::RunImpl() {
227  std::string extension_id;
228  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id));
229
230  if (!service()->GetExtensionById(extension_id, true)) {
231    error_ = ExtensionErrorUtils::FormatErrorMessage(
232        kNoExtensionError, extension_id);
233    return false;
234  }
235
236  ExtensionPrefs* prefs = service()->extension_prefs();
237
238  if (!Extension::UserMayDisable(
239      prefs->GetInstalledExtensionInfo(extension_id)->extension_location)) {
240    error_ = ExtensionErrorUtils::FormatErrorMessage(
241        kUserCantDisableError, extension_id);
242    return false;
243  }
244
245  service()->UninstallExtension(extension_id, false /* external_uninstall */,
246                                NULL);
247  return true;
248}
249
250// static
251ExtensionManagementEventRouter* ExtensionManagementEventRouter::GetInstance() {
252  return Singleton<ExtensionManagementEventRouter>::get();
253}
254
255ExtensionManagementEventRouter::ExtensionManagementEventRouter() {}
256
257ExtensionManagementEventRouter::~ExtensionManagementEventRouter() {}
258
259void ExtensionManagementEventRouter::Init() {
260  NotificationType::Type types[] = {
261    NotificationType::EXTENSION_INSTALLED,
262    NotificationType::EXTENSION_UNINSTALLED,
263    NotificationType::EXTENSION_LOADED,
264    NotificationType::EXTENSION_UNLOADED
265  };
266
267  // Don't re-init (eg in the case of multiple profiles).
268  if (registrar_.IsEmpty()) {
269    for (size_t i = 0; i < arraysize(types); i++) {
270      registrar_.Add(this,
271                     types[i],
272                     NotificationService::AllSources());
273    }
274  }
275}
276
277void ExtensionManagementEventRouter::Observe(
278    NotificationType type,
279    const NotificationSource& source,
280    const NotificationDetails& details) {
281  const char* event_name = NULL;
282  switch (type.value) {
283    case NotificationType::EXTENSION_INSTALLED:
284      event_name = events::kOnExtensionInstalled;
285      break;
286    case NotificationType::EXTENSION_UNINSTALLED:
287      event_name = events::kOnExtensionUninstalled;
288      break;
289    case NotificationType::EXTENSION_LOADED:
290      event_name = events::kOnExtensionEnabled;
291      break;
292    case NotificationType::EXTENSION_UNLOADED:
293      event_name = events::kOnExtensionDisabled;
294      break;
295    default:
296      NOTREACHED();
297      return;
298  }
299
300  Profile* profile = Source<Profile>(source).ptr();
301  CHECK(profile);
302
303  ListValue args;
304  if (event_name == events::kOnExtensionUninstalled) {
305    const std::string& extension_id =
306        Details<UninstalledExtensionInfo>(details).ptr()->extension_id;
307    args.Append(Value::CreateStringValue(extension_id));
308  } else {
309    const Extension* extension = NULL;
310    if (event_name == events::kOnExtensionDisabled) {
311      extension = Details<UnloadedExtensionInfo>(details)->extension;
312    } else {
313      extension = Details<const Extension>(details).ptr();
314    }
315    CHECK(extension);
316    ExtensionService* service = profile->GetExtensionService();
317    bool enabled = service->GetExtensionById(extension->id(), false) != NULL;
318    args.Append(CreateExtensionInfo(*extension, enabled));
319  }
320
321  std::string args_json;
322  base::JSONWriter::Write(&args, false /* pretty_print */, &args_json);
323
324  profile->GetExtensionEventRouter()->DispatchEventToRenderers(
325      event_name, args_json, NULL, GURL());
326}
327