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
30 using base::IntToString;
31 namespace events = extension_event_names;
32
33 namespace {
34
35 const char kAppLaunchUrlKey[] = "appLaunchUrl";
36 const char kDescriptionKey[] = "description";
37 const char kEnabledKey[] = "enabled";
38 const char kHomepageURLKey[] = "homepageUrl";
39 const char kIconsKey[] = "icons";
40 const char kIdKey[] = "id";
41 const char kIsAppKey[] = "isApp";
42 const char kNameKey[] = "name";
43 const char kOptionsUrlKey[] = "optionsUrl";
44 const char kPermissionsKey[] = "permissions";
45 const char kMayDisableKey[] = "mayDisable";
46 const char kSizeKey[] = "size";
47 const char kUrlKey[] = "url";
48 const char kVersionKey[] = "version";
49
50 const char kNoExtensionError[] = "No extension with id *";
51 const char kNotAnAppError[] = "Extension * is not an App";
52 const char kUserCantDisableError[] = "Extension * can not be disabled by user";
53 }
54
service()55 ExtensionService* ExtensionManagementFunction::service() {
56 return profile()->GetExtensionService();
57 }
58
CreateExtensionInfo(const Extension & extension,bool enabled)59 static 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
AddExtensionInfo(ListValue * list,const ExtensionList & extensions,bool enabled)124 static 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
RunImpl()138 bool 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
RunImpl()148 bool 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
RunImpl()166 bool 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
RunImpl()195 bool 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
RunImpl()226 bool 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
GetInstance()251 ExtensionManagementEventRouter* ExtensionManagementEventRouter::GetInstance() {
252 return Singleton<ExtensionManagementEventRouter>::get();
253 }
254
ExtensionManagementEventRouter()255 ExtensionManagementEventRouter::ExtensionManagementEventRouter() {}
256
~ExtensionManagementEventRouter()257 ExtensionManagementEventRouter::~ExtensionManagementEventRouter() {}
258
Init()259 void 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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)277 void 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