Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: chrome/browser/extensions/extension_management_api.cc

Issue 10750010: Add an installType property to the management API (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Added location param to InstallExtension, rewrote test Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/bind.h"
12 #include "base/json/json_writer.h"
13 #include "base/metrics/histogram.h"
14 #include "base/string_number_conversions.h"
15 #include "base/string_util.h"
16 #include "chrome/browser/extensions/event_names.h"
17 #include "chrome/browser/extensions/event_router.h"
18 #include "chrome/browser/extensions/extension_management_api_constants.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/extensions/extension_system.h"
21 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
22 #include "chrome/browser/extensions/management_policy.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/extensions/application_launch.h"
25 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
26 #include "chrome/common/chrome_notification_types.h"
27 #include "chrome/common/chrome_utility_messages.h"
28 #include "chrome/common/extensions/extension.h"
29 #include "chrome/common/extensions/extension_constants.h"
30 #include "chrome/common/extensions/extension_error_utils.h"
31 #include "chrome/common/extensions/extension_icon_set.h"
32 #include "chrome/common/extensions/url_pattern.h"
33 #include "content/public/browser/notification_details.h"
34 #include "content/public/browser/notification_source.h"
35 #include "content/public/browser/utility_process_host.h"
36 #include "content/public/browser/utility_process_host_client.h"
37
38 #if !defined(OS_ANDROID)
39 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
40 #endif
41
42 using base::IntToString;
43 using content::BrowserThread;
44 using content::UtilityProcessHost;
45 using content::UtilityProcessHostClient;
46 using extensions::Extension;
47 using extensions::PermissionMessages;
48
49 namespace events = extensions::event_names;
50 namespace keys = extension_management_api_constants;
51
52 namespace {
53
54 enum AutoConfirmForTest {
55 DO_NOT_SKIP = 0,
56 PROCEED,
57 ABORT
58 };
59
60 AutoConfirmForTest auto_confirm_for_test = DO_NOT_SKIP;
61
62 } // namespace
63
64 ExtensionService* ExtensionManagementFunction::service() {
65 return profile()->GetExtensionService();
66 }
67
68 ExtensionService* AsyncExtensionManagementFunction::service() {
69 return profile()->GetExtensionService();
70 }
71
72 static DictionaryValue* CreateExtensionInfo(const Extension& extension,
73 ExtensionService* service) {
74 DictionaryValue* info = new DictionaryValue();
75 bool enabled = service->IsExtensionEnabled(extension.id());
76 extension.GetBasicInfo(enabled, info);
77
78 const extensions::ManagementPolicy* policy = extensions::ExtensionSystem::Get(
79 service->profile())->management_policy();
80 info->SetBoolean(keys::kMayDisableKey,
81 policy->UserMayModifySettings(&extension, NULL));
82
83 info->SetBoolean(keys::kIsAppKey, extension.is_app());
84
85 if (!enabled) {
86 extensions::ExtensionPrefs* prefs = service->extension_prefs();
87 bool permissions_escalated =
88 prefs->DidExtensionEscalatePermissions(extension.id());
89 const char* reason = permissions_escalated ?
90 keys::kDisabledReasonPermissionsIncrease : keys::kDisabledReasonUnknown;
91 info->SetString(keys::kDisabledReasonKey, reason);
92 }
93
94 if (!extension.update_url().is_empty())
95 info->SetString(keys::kUpdateUrlKey,
96 extension.update_url().possibly_invalid_spec());
97 if (extension.is_app())
98 info->SetString(keys::kAppLaunchUrlKey,
99 extension.GetFullLaunchURL().possibly_invalid_spec());
100
101 const ExtensionIconSet::IconMap& icons = extension.icons().map();
102 if (!icons.empty()) {
103 ListValue* icon_list = new ListValue();
104 std::map<ExtensionIconSet::Icons, std::string>::const_iterator icon_iter;
105 for (icon_iter = icons.begin(); icon_iter != icons.end(); ++icon_iter) {
106 DictionaryValue* icon_info = new DictionaryValue();
107 ExtensionIconSet::Icons size = icon_iter->first;
108 GURL url = ExtensionIconSource::GetIconURL(
109 &extension, size, ExtensionIconSet::MATCH_EXACTLY, false, NULL);
110 icon_info->SetInteger(keys::kSizeKey, icon_iter->first);
111 icon_info->SetString(keys::kUrlKey, url.spec());
112 icon_list->Append(icon_info);
113 }
114 info->Set(keys::kIconsKey, icon_list);
115 }
116
117 const std::set<std::string> perms =
118 extension.GetActivePermissions()->GetAPIsAsStrings();
119 ListValue* permission_list = new ListValue();
120 if (!perms.empty()) {
121 std::set<std::string>::const_iterator perms_iter;
122 for (perms_iter = perms.begin(); perms_iter != perms.end(); ++perms_iter) {
123 StringValue* permission_name = new StringValue(*perms_iter);
124 permission_list->Append(permission_name);
125 }
126 }
127 info->Set(keys::kPermissionsKey, permission_list);
128
129 ListValue* host_permission_list = new ListValue();
130 if (!extension.is_hosted_app()) {
131 // Skip host permissions for hosted apps.
132 const URLPatternSet host_perms =
133 extension.GetActivePermissions()->explicit_hosts();
134 if (!host_perms.is_empty()) {
135 URLPatternSet::const_iterator host_perms_iter;
136 for (host_perms_iter = host_perms.begin();
137 host_perms_iter != host_perms.end();
138 ++host_perms_iter) {
139 StringValue* name = new StringValue(host_perms_iter->GetAsString());
140 host_permission_list->Append(name);
141 }
142 }
143 }
144 info->Set(keys::kHostPermissionsKey, host_permission_list);
145
146 return info;
147 }
148
149 static void AddExtensionInfo(ListValue* list,
150 const ExtensionSet& extensions,
151 ExtensionService* service) {
152 for (ExtensionSet::const_iterator i = extensions.begin();
153 i != extensions.end(); ++i) {
154 const Extension& extension = **i;
155
156 if (extension.location() == Extension::COMPONENT)
157 continue; // Skip built-in extensions.
158
159 list->Append(CreateExtensionInfo(extension, service));
160 }
161 }
162
163 bool GetAllExtensionsFunction::RunImpl() {
164 ListValue* result = new ListValue();
165 SetResult(result);
166
167 AddExtensionInfo(result, *service()->extensions(), service());
168 AddExtensionInfo(result, *service()->disabled_extensions(), service());
169
170 return true;
171 }
172
173 bool GetExtensionByIdFunction::RunImpl() {
174 std::string extension_id;
175 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id));
176 const Extension* extension = service()->GetExtensionById(extension_id, true);
177 if (!extension) {
178 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
179 extension_id);
180 return false;
181 }
182 DictionaryValue* result = CreateExtensionInfo(*extension, service());
183 SetResult(result);
184
185 return true;
186 }
187
188 bool GetPermissionWarningsByIdFunction::RunImpl() {
189 std::string ext_id;
190 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &ext_id));
191
192 const Extension* extension = service()->GetExtensionById(ext_id, true);
193 if (!extension) {
194 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
195 ext_id);
196 return false;
197 }
198
199 PermissionMessages warnings = extension->GetPermissionMessages();
200 ListValue* result = new ListValue();
201 for (PermissionMessages::const_iterator i = warnings.begin();
202 i < warnings.end(); ++i)
203 result->Append(Value::CreateStringValue(i->message()));
204 SetResult(result);
205 return true;
206 }
207
208 namespace {
209
210 // This class helps GetPermissionWarningsByManifestFunction manage
211 // sending manifest JSON strings to the utility process for parsing.
212 class SafeManifestJSONParser : public UtilityProcessHostClient {
213 public:
214 SafeManifestJSONParser(GetPermissionWarningsByManifestFunction* client,
215 const std::string& manifest)
216 : client_(client),
217 manifest_(manifest) {}
218
219 void Start() {
220 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 BrowserThread::PostTask(
222 BrowserThread::IO,
223 FROM_HERE,
224 base::Bind(&SafeManifestJSONParser::StartWorkOnIOThread, this));
225 }
226
227 void StartWorkOnIOThread() {
228 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
229 UtilityProcessHost* host =
230 UtilityProcessHost::Create(this, BrowserThread::IO);
231 host->EnableZygote();
232 host->Send(new ChromeUtilityMsg_ParseJSON(manifest_));
233 }
234
235 virtual bool OnMessageReceived(const IPC::Message& message) {
236 bool handled = true;
237 IPC_BEGIN_MESSAGE_MAP(SafeManifestJSONParser, message)
238 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded,
239 OnJSONParseSucceeded)
240 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed,
241 OnJSONParseFailed)
242 IPC_MESSAGE_UNHANDLED(handled = false)
243 IPC_END_MESSAGE_MAP()
244 return handled;
245 }
246
247 void OnJSONParseSucceeded(const ListValue& wrapper) {
248 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
249 Value* value = NULL;
250 CHECK(wrapper.Get(0, &value));
251 if (value->IsType(Value::TYPE_DICTIONARY))
252 parsed_manifest_.reset(static_cast<DictionaryValue*>(value)->DeepCopy());
253 else
254 error_ = keys::kManifestParseError;
255
256 BrowserThread::PostTask(
257 BrowserThread::UI,
258 FROM_HERE,
259 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this));
260 }
261
262 void OnJSONParseFailed(const std::string& error) {
263 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
264 error_ = error;
265 BrowserThread::PostTask(
266 BrowserThread::UI,
267 FROM_HERE,
268 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this));
269 }
270
271 void ReportResultFromUIThread() {
272 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 if (error_.empty() && parsed_manifest_.get())
274 client_->OnParseSuccess(parsed_manifest_.release());
275 else
276 client_->OnParseFailure(error_);
277 }
278
279 private:
280 ~SafeManifestJSONParser() {}
281
282 // The client who we'll report results back to.
283 GetPermissionWarningsByManifestFunction* client_;
284
285 // Data to parse.
286 std::string manifest_;
287
288 // Results of parsing.
289 scoped_ptr<DictionaryValue> parsed_manifest_;
290
291 std::string error_;
292 };
293
294 } // namespace
295
296 bool GetPermissionWarningsByManifestFunction::RunImpl() {
297 std::string manifest_str;
298 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &manifest_str));
299
300 scoped_refptr<SafeManifestJSONParser> parser =
301 new SafeManifestJSONParser(this, manifest_str);
302 parser->Start();
303
304 // Matched with a Release() in OnParseSuccess/Failure().
305 AddRef();
306
307 // Response is sent async in OnParseSuccess/Failure().
308 return true;
309 }
310
311 void GetPermissionWarningsByManifestFunction::OnParseSuccess(
312 DictionaryValue* parsed_manifest) {
313 CHECK(parsed_manifest);
314
315 scoped_refptr<Extension> extension = Extension::Create(
316 FilePath(), Extension::INVALID, *parsed_manifest, Extension::NO_FLAGS,
317 &error_);
318 if (!extension.get()) {
319 OnParseFailure(keys::kExtensionCreateError);
320 return;
321 }
322
323 PermissionMessages warnings = extension->GetPermissionMessages();
324 ListValue* result = new ListValue();
325 for (PermissionMessages::const_iterator i = warnings.begin();
326 i < warnings.end(); ++i)
327 result->Append(Value::CreateStringValue(i->message()));
328 SetResult(result);
329 SendResponse(true);
330
331 // Matched with AddRef() in RunImpl().
332 Release();
333 }
334
335 void GetPermissionWarningsByManifestFunction::OnParseFailure(
336 const std::string& error) {
337 error_ = error;
338 SendResponse(false);
339
340 // Matched with AddRef() in RunImpl().
341 Release();
342 }
343
344 bool LaunchAppFunction::RunImpl() {
345 std::string extension_id;
346 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id));
347 const Extension* extension = service()->GetExtensionById(extension_id, true);
348 if (!extension) {
349 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
350 extension_id);
351 return false;
352 }
353 if (!extension->is_app()) {
354 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNotAnAppError,
355 extension_id);
356 return false;
357 }
358
359 // Look at prefs to find the right launch container.
360 // |default_pref_value| is set to LAUNCH_REGULAR so that if
361 // the user has not set a preference, we open the app in a tab.
362 extension_misc::LaunchContainer launch_container =
363 service()->extension_prefs()->GetLaunchContainer(
364 extension, extensions::ExtensionPrefs::LAUNCH_DEFAULT);
365 application_launch::OpenApplication(application_launch::LaunchParams(
366 profile(), extension, launch_container, NEW_FOREGROUND_TAB));
367 #if !defined(OS_ANDROID)
368 AppLauncherHandler::RecordAppLaunchType(
369 extension_misc::APP_LAUNCH_EXTENSION_API);
370 #endif
371
372 return true;
373 }
374
375 SetEnabledFunction::SetEnabledFunction() {
376 }
377
378 SetEnabledFunction::~SetEnabledFunction() {
379 }
380
381 bool SetEnabledFunction::RunImpl() {
382 bool enable;
383 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_));
384 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &enable));
385
386 const Extension* extension = service()->GetExtensionById(extension_id_, true);
387 if (!extension) {
388 error_ = ExtensionErrorUtils::FormatErrorMessage(
389 keys::kNoExtensionError, extension_id_);
390 return false;
391 }
392
393 const extensions::ManagementPolicy* policy = extensions::ExtensionSystem::Get(
394 profile())->management_policy();
395 if (!policy->UserMayModifySettings(extension, NULL)) {
396 error_ = ExtensionErrorUtils::FormatErrorMessage(
397 keys::kUserCantModifyError, extension_id_);
398 return false;
399 }
400
401 bool currently_enabled = service()->IsExtensionEnabled(extension_id_);
402
403 if (!currently_enabled && enable) {
404 extensions::ExtensionPrefs* prefs = service()->extension_prefs();
405 if (prefs->DidExtensionEscalatePermissions(extension_id_)) {
406 if (!user_gesture()) {
407 error_ = keys::kGestureNeededForEscalationError;
408 return false;
409 }
410 AddRef(); // Matched in InstallUIProceed/InstallUIAbort
411 install_prompt_.reset(
412 chrome::CreateExtensionInstallPromptWithBrowser(GetCurrentBrowser()));
413 install_prompt_->ConfirmReEnable(this, extension);
414 return true;
415 }
416 service()->EnableExtension(extension_id_);
417 } else if (currently_enabled && !enable) {
418 service()->DisableExtension(extension_id_, Extension::DISABLE_USER_ACTION);
419 }
420
421 BrowserThread::PostTask(
422 BrowserThread::UI,
423 FROM_HERE,
424 base::Bind(&SetEnabledFunction::SendResponse, this, true));
425
426 return true;
427 }
428
429 void SetEnabledFunction::InstallUIProceed() {
430 service()->EnableExtension(extension_id_);
431 SendResponse(true);
432 Release();
433 }
434
435 void SetEnabledFunction::InstallUIAbort(bool user_initiated) {
436 error_ = keys::kUserDidNotReEnableError;
437 SendResponse(false);
438 Release();
439 }
440
441 UninstallFunction::UninstallFunction() {
442 }
443
444 UninstallFunction::~UninstallFunction() {
445 }
446
447 bool UninstallFunction::RunImpl() {
448 bool show_confirm_dialog = false;
449
450 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_));
451
452 if (HasOptionalArgument(1)) {
453 DictionaryValue* options = NULL;
454 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
455
456 if (options->HasKey(keys::kShowConfirmDialogKey)) {
457 EXTENSION_FUNCTION_VALIDATE(options->GetBoolean(
458 keys::kShowConfirmDialogKey, &show_confirm_dialog));
459 }
460 }
461
462 const Extension* extension = service()->GetExtensionById(extension_id_, true);
463 if (!extension) {
464 error_ = ExtensionErrorUtils::FormatErrorMessage(
465 keys::kNoExtensionError, extension_id_);
466 return false;
467 }
468
469 if (!extensions::ExtensionSystem::Get(
470 profile())->management_policy()->UserMayModifySettings(extension, NULL)) {
471 error_ = ExtensionErrorUtils::FormatErrorMessage(
472 keys::kUserCantModifyError, extension_id_);
473 return false;
474 }
475
476 if (auto_confirm_for_test == DO_NOT_SKIP) {
477 if (show_confirm_dialog) {
478 AddRef(); // Balanced in ExtensionUninstallAccepted/Canceled
479 extension_uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
480 GetCurrentBrowser(), this));
481 extension_uninstall_dialog_->ConfirmUninstall(extension);
482 } else {
483 Finish(true);
484 }
485 } else {
486 Finish(auto_confirm_for_test == PROCEED);
487 }
488
489 return true;
490 }
491
492 // static
493 void UninstallFunction::SetAutoConfirmForTest(bool should_proceed) {
494 auto_confirm_for_test = should_proceed ? PROCEED : ABORT;
495 }
496
497 void UninstallFunction::Finish(bool should_uninstall) {
498 if (should_uninstall) {
499 bool success = service()->UninstallExtension(
500 extension_id_,
501 false, /* external uninstall */
502 NULL);
503
504 // TODO set error_ if !success
505 SendResponse(success);
506 } else {
507 error_ = ExtensionErrorUtils::FormatErrorMessage(
508 keys::kUninstallCanceledError, extension_id_);
509 SendResponse(false);
510 }
511
512 }
513
514 void UninstallFunction::ExtensionUninstallAccepted() {
515 Finish(true);
516 Release();
517 }
518
519 void UninstallFunction::ExtensionUninstallCanceled() {
520 Finish(false);
521 Release();
522 }
523
524 ExtensionManagementEventRouter::ExtensionManagementEventRouter(Profile* profile)
525 : profile_(profile) {}
526
527 ExtensionManagementEventRouter::~ExtensionManagementEventRouter() {}
528
529 void ExtensionManagementEventRouter::Init() {
530 int types[] = {
531 chrome::NOTIFICATION_EXTENSION_INSTALLED,
532 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
533 chrome::NOTIFICATION_EXTENSION_LOADED,
534 chrome::NOTIFICATION_EXTENSION_UNLOADED
535 };
536
537 CHECK(registrar_.IsEmpty());
538 for (size_t i = 0; i < arraysize(types); i++) {
539 registrar_.Add(this,
540 types[i],
541 content::Source<Profile>(profile_));
542 }
543 }
544
545 void ExtensionManagementEventRouter::Observe(
546 int type,
547 const content::NotificationSource& source,
548 const content::NotificationDetails& details) {
549 const char* event_name = NULL;
550 Profile* profile = content::Source<Profile>(source).ptr();
551 CHECK(profile);
552 CHECK(profile_->IsSameProfile(profile));
553
554 switch (type) {
555 case chrome::NOTIFICATION_EXTENSION_INSTALLED:
556 event_name = events::kOnExtensionInstalled;
557 break;
558 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
559 event_name = events::kOnExtensionUninstalled;
560 break;
561 case chrome::NOTIFICATION_EXTENSION_LOADED:
562 event_name = events::kOnExtensionEnabled;
563 break;
564 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
565 event_name = events::kOnExtensionDisabled;
566 break;
567 default:
568 NOTREACHED();
569 return;
570 }
571
572 ListValue args;
573 if (event_name == events::kOnExtensionUninstalled) {
574 args.Append(Value::CreateStringValue(
575 content::Details<const extensions::Extension>(details).ptr()->id()));
576 } else {
577 const Extension* extension = NULL;
578 if (event_name == events::kOnExtensionDisabled) {
579 extension = content::Details<extensions::UnloadedExtensionInfo>(
580 details)->extension;
581 } else {
582 extension = content::Details<const Extension>(details).ptr();
583 }
584 CHECK(extension);
585 ExtensionService* service = profile->GetExtensionService();
586 args.Append(CreateExtensionInfo(*extension, service));
587 }
588
589 std::string args_json;
590 base::JSONWriter::Write(&args, &args_json);
591
592 profile->GetExtensionEventRouter()->DispatchEventToRenderers(
593 event_name, args_json, NULL, GURL(), extensions::EventFilteringInfo());
594 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698