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/api/management/management_api.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/json/json_writer.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "chrome/browser/extensions/api/management/management_api_constants.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_ui_util.h"
24 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
25 #include "chrome/browser/extensions/launch_util.h"
26 #include "chrome/browser/favicon/favicon_service_factory.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/ui/browser_dialogs.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/browser_window.h"
31 #include "chrome/browser/ui/extensions/application_launch.h"
32 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
33 #include "chrome/common/chrome_switches.h"
34 #include "chrome/common/chrome_utility_messages.h"
35 #include "chrome/common/extensions/api/management.h"
36 #include "chrome/common/extensions/extension_constants.h"
37 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
38 #include "chrome/common/extensions/manifest_url_handler.h"
39 #include "content/public/browser/utility_process_host.h"
40 #include "content/public/browser/utility_process_host_client.h"
41 #include "extensions/browser/event_router.h"
42 #include "extensions/browser/extension_prefs.h"
43 #include "extensions/browser/extension_registry.h"
44 #include "extensions/browser/extension_system.h"
45 #include "extensions/browser/management_policy.h"
46 #include "extensions/common/constants.h"
47 #include "extensions/common/error_utils.h"
48 #include "extensions/common/extension.h"
49 #include "extensions/common/extension_icon_set.h"
50 #include "extensions/common/manifest_handlers/icons_handler.h"
51 #include "extensions/common/manifest_handlers/offline_enabled_info.h"
52 #include "extensions/common/permissions/permission_set.h"
53 #include "extensions/common/permissions/permissions_data.h"
54 #include "extensions/common/url_pattern.h"
55 #include "ui/gfx/favicon_size.h"
56
57 #if !defined(OS_ANDROID)
58 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
59 #endif
60
61 using base::IntToString;
62 using content::BrowserThread;
63 using content::UtilityProcessHost;
64 using content::UtilityProcessHostClient;
65
66 namespace keys = extension_management_api_constants;
67
68 namespace extensions {
69
70 namespace management = api::management;
71
72 namespace {
73
74 typedef std::vector<linked_ptr<management::ExtensionInfo> > ExtensionInfoList;
75 typedef std::vector<linked_ptr<management::IconInfo> > IconInfoList;
76
77 enum AutoConfirmForTest {
78 DO_NOT_SKIP = 0,
79 PROCEED,
80 ABORT
81 };
82
83 AutoConfirmForTest auto_confirm_for_test = DO_NOT_SKIP;
84
CreateWarningsList(const Extension * extension)85 std::vector<std::string> CreateWarningsList(const Extension* extension) {
86 std::vector<std::string> warnings_list;
87 PermissionMessages warnings =
88 extension->permissions_data()->GetPermissionMessages();
89 for (PermissionMessages::const_iterator iter = warnings.begin();
90 iter != warnings.end(); ++iter) {
91 warnings_list.push_back(base::UTF16ToUTF8(iter->message()));
92 }
93
94 return warnings_list;
95 }
96
GetAvailableLaunchTypes(const Extension & extension)97 std::vector<management::LaunchType> GetAvailableLaunchTypes(
98 const Extension& extension) {
99 std::vector<management::LaunchType> launch_type_list;
100 if (extension.is_platform_app()) {
101 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_WINDOW);
102 return launch_type_list;
103 }
104
105 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_REGULAR_TAB);
106
107 #if !defined(OS_MACOSX)
108 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_WINDOW);
109 #endif
110
111 if (!CommandLine::ForCurrentProcess()->HasSwitch(
112 switches::kEnableStreamlinedHostedApps)) {
113 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_PINNED_TAB);
114 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_FULL_SCREEN);
115 }
116 return launch_type_list;
117 }
118
CreateExtensionInfo(const Extension & extension,ExtensionSystem * system)119 scoped_ptr<management::ExtensionInfo> CreateExtensionInfo(
120 const Extension& extension,
121 ExtensionSystem* system) {
122 scoped_ptr<management::ExtensionInfo> info(new management::ExtensionInfo());
123 ExtensionService* service = system->extension_service();
124
125 info->id = extension.id();
126 info->name = extension.name();
127 info->short_name = extension.short_name();
128 info->enabled = service->IsExtensionEnabled(info->id);
129 info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&extension);
130 info->version = extension.VersionString();
131 info->description = extension.description();
132 info->options_url = ManifestURL::GetOptionsPage(&extension).spec();
133 info->homepage_url.reset(new std::string(
134 ManifestURL::GetHomepageURL(&extension).spec()));
135 info->may_disable = system->management_policy()->
136 UserMayModifySettings(&extension, NULL);
137 info->is_app = extension.is_app();
138 if (info->is_app) {
139 if (extension.is_legacy_packaged_app())
140 info->type = management::ExtensionInfo::TYPE_LEGACY_PACKAGED_APP;
141 else if (extension.is_hosted_app())
142 info->type = management::ExtensionInfo::TYPE_HOSTED_APP;
143 else
144 info->type = management::ExtensionInfo::TYPE_PACKAGED_APP;
145 } else if (extension.is_theme()) {
146 info->type = management::ExtensionInfo::TYPE_THEME;
147 } else {
148 info->type = management::ExtensionInfo::TYPE_EXTENSION;
149 }
150
151 if (info->enabled) {
152 info->disabled_reason = management::ExtensionInfo::DISABLED_REASON_NONE;
153 } else {
154 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
155 if (prefs->DidExtensionEscalatePermissions(extension.id())) {
156 info->disabled_reason =
157 management::ExtensionInfo::DISABLED_REASON_PERMISSIONS_INCREASE;
158 } else {
159 info->disabled_reason =
160 management::ExtensionInfo::DISABLED_REASON_UNKNOWN;
161 }
162 }
163
164 if (!ManifestURL::GetUpdateURL(&extension).is_empty()) {
165 info->update_url.reset(new std::string(
166 ManifestURL::GetUpdateURL(&extension).spec()));
167 }
168
169 if (extension.is_app()) {
170 info->app_launch_url.reset(new std::string(
171 AppLaunchInfo::GetFullLaunchURL(&extension).spec()));
172 }
173
174 const ExtensionIconSet::IconMap& icons =
175 IconsInfo::GetIcons(&extension).map();
176 if (!icons.empty()) {
177 info->icons.reset(new IconInfoList());
178 ExtensionIconSet::IconMap::const_iterator icon_iter;
179 for (icon_iter = icons.begin(); icon_iter != icons.end(); ++icon_iter) {
180 management::IconInfo* icon_info = new management::IconInfo();
181 icon_info->size = icon_iter->first;
182 GURL url = ExtensionIconSource::GetIconURL(
183 &extension, icon_info->size, ExtensionIconSet::MATCH_EXACTLY, false,
184 NULL);
185 icon_info->url = url.spec();
186 info->icons->push_back(make_linked_ptr<management::IconInfo>(icon_info));
187 }
188 }
189
190 const std::set<std::string> perms =
191 extension.permissions_data()->active_permissions()->GetAPIsAsStrings();
192 if (!perms.empty()) {
193 std::set<std::string>::const_iterator perms_iter;
194 for (perms_iter = perms.begin(); perms_iter != perms.end(); ++perms_iter)
195 info->permissions.push_back(*perms_iter);
196 }
197
198 if (!extension.is_hosted_app()) {
199 // Skip host permissions for hosted apps.
200 const URLPatternSet host_perms =
201 extension.permissions_data()->active_permissions()->explicit_hosts();
202 if (!host_perms.is_empty()) {
203 for (URLPatternSet::const_iterator iter = host_perms.begin();
204 iter != host_perms.end(); ++iter) {
205 info->host_permissions.push_back(iter->GetAsString());
206 }
207 }
208 }
209
210 switch (extension.location()) {
211 case Manifest::INTERNAL:
212 info->install_type = management::ExtensionInfo::INSTALL_TYPE_NORMAL;
213 break;
214 case Manifest::UNPACKED:
215 case Manifest::COMMAND_LINE:
216 info->install_type = management::ExtensionInfo::INSTALL_TYPE_DEVELOPMENT;
217 break;
218 case Manifest::EXTERNAL_PREF:
219 case Manifest::EXTERNAL_REGISTRY:
220 case Manifest::EXTERNAL_PREF_DOWNLOAD:
221 info->install_type = management::ExtensionInfo::INSTALL_TYPE_SIDELOAD;
222 break;
223 case Manifest::EXTERNAL_POLICY:
224 case Manifest::EXTERNAL_POLICY_DOWNLOAD:
225 info->install_type = management::ExtensionInfo::INSTALL_TYPE_ADMIN;
226 break;
227 case Manifest::NUM_LOCATIONS:
228 NOTREACHED();
229 case Manifest::INVALID_LOCATION:
230 case Manifest::COMPONENT:
231 case Manifest::EXTERNAL_COMPONENT:
232 info->install_type = management::ExtensionInfo::INSTALL_TYPE_OTHER;
233 break;
234 }
235
236 info->launch_type = management::LAUNCH_TYPE_NONE;
237 if (extension.is_app()) {
238 LaunchType launch_type;
239 if (extension.is_platform_app()) {
240 launch_type = LAUNCH_TYPE_WINDOW;
241 } else {
242 launch_type =
243 GetLaunchType(ExtensionPrefs::Get(service->profile()), &extension);
244 }
245
246 switch (launch_type) {
247 case LAUNCH_TYPE_PINNED:
248 info->launch_type = management::LAUNCH_TYPE_OPEN_AS_PINNED_TAB;
249 break;
250 case LAUNCH_TYPE_REGULAR:
251 info->launch_type = management::LAUNCH_TYPE_OPEN_AS_REGULAR_TAB;
252 break;
253 case LAUNCH_TYPE_FULLSCREEN:
254 info->launch_type = management::LAUNCH_TYPE_OPEN_FULL_SCREEN;
255 break;
256 case LAUNCH_TYPE_WINDOW:
257 info->launch_type = management::LAUNCH_TYPE_OPEN_AS_WINDOW;
258 break;
259 case LAUNCH_TYPE_INVALID:
260 case NUM_LAUNCH_TYPES:
261 NOTREACHED();
262 }
263
264 info->available_launch_types.reset(new std::vector<management::LaunchType>(
265 GetAvailableLaunchTypes(extension)));
266 }
267
268 return info.Pass();
269 }
270
AddExtensionInfo(const ExtensionSet & extensions,ExtensionSystem * system,ExtensionInfoList * extension_list,content::BrowserContext * context)271 void AddExtensionInfo(const ExtensionSet& extensions,
272 ExtensionSystem* system,
273 ExtensionInfoList* extension_list,
274 content::BrowserContext* context) {
275 for (ExtensionSet::const_iterator iter = extensions.begin();
276 iter != extensions.end(); ++iter) {
277 const Extension& extension = *iter->get();
278
279 if (ui_util::ShouldNotBeVisible(&extension, context))
280 continue; // Skip built-in extensions/apps.
281
282 extension_list->push_back(make_linked_ptr<management::ExtensionInfo>(
283 CreateExtensionInfo(extension, system).release()));
284 }
285 }
286
287 } // namespace
288
service()289 ExtensionService* ManagementFunction::service() {
290 return GetProfile()->GetExtensionService();
291 }
292
service()293 ExtensionService* AsyncManagementFunction::service() {
294 return GetProfile()->GetExtensionService();
295 }
296
RunSync()297 bool ManagementGetAllFunction::RunSync() {
298 ExtensionInfoList extensions;
299 ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile());
300 ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
301
302 AddExtensionInfo(registry->enabled_extensions(),
303 system, &extensions, browser_context());
304 AddExtensionInfo(registry->disabled_extensions(),
305 system, &extensions, browser_context());
306 AddExtensionInfo(registry->terminated_extensions(),
307 system, &extensions, browser_context());
308
309 results_ = management::GetAll::Results::Create(extensions);
310 return true;
311 }
312
RunSync()313 bool ManagementGetFunction::RunSync() {
314 scoped_ptr<management::Get::Params> params(
315 management::Get::Params::Create(*args_));
316 EXTENSION_FUNCTION_VALIDATE(params.get());
317
318 const Extension* extension = service()->GetExtensionById(params->id, true);
319 if (!extension) {
320 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
321 params->id);
322 return false;
323 }
324
325 scoped_ptr<management::ExtensionInfo> info =
326 CreateExtensionInfo(*extension, ExtensionSystem::Get(GetProfile()));
327 results_ = management::Get::Results::Create(*info);
328
329 return true;
330 }
331
RunSync()332 bool ManagementGetPermissionWarningsByIdFunction::RunSync() {
333 scoped_ptr<management::GetPermissionWarningsById::Params> params(
334 management::GetPermissionWarningsById::Params::Create(*args_));
335 EXTENSION_FUNCTION_VALIDATE(params.get());
336
337 const Extension* extension = service()->GetExtensionById(params->id, true);
338 if (!extension) {
339 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
340 params->id);
341 return false;
342 }
343
344 std::vector<std::string> warnings = CreateWarningsList(extension);
345 results_ = management::GetPermissionWarningsById::Results::Create(warnings);
346 return true;
347 }
348
349 namespace {
350
351 // This class helps ManagementGetPermissionWarningsByManifestFunction manage
352 // sending manifest JSON strings to the utility process for parsing.
353 class SafeManifestJSONParser : public UtilityProcessHostClient {
354 public:
SafeManifestJSONParser(ManagementGetPermissionWarningsByManifestFunction * client,const std::string & manifest)355 SafeManifestJSONParser(
356 ManagementGetPermissionWarningsByManifestFunction* client,
357 const std::string& manifest)
358 : client_(client),
359 manifest_(manifest) {}
360
Start()361 void Start() {
362 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363 BrowserThread::PostTask(
364 BrowserThread::IO,
365 FROM_HERE,
366 base::Bind(&SafeManifestJSONParser::StartWorkOnIOThread, this));
367 }
368
StartWorkOnIOThread()369 void StartWorkOnIOThread() {
370 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
371 UtilityProcessHost* host = UtilityProcessHost::Create(
372 this, base::MessageLoopProxy::current().get());
373 host->Send(new ChromeUtilityMsg_ParseJSON(manifest_));
374 }
375
OnMessageReceived(const IPC::Message & message)376 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
377 bool handled = true;
378 IPC_BEGIN_MESSAGE_MAP(SafeManifestJSONParser, message)
379 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded,
380 OnJSONParseSucceeded)
381 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed,
382 OnJSONParseFailed)
383 IPC_MESSAGE_UNHANDLED(handled = false)
384 IPC_END_MESSAGE_MAP()
385 return handled;
386 }
387
OnJSONParseSucceeded(const base::ListValue & wrapper)388 void OnJSONParseSucceeded(const base::ListValue& wrapper) {
389 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
390 const base::Value* value = NULL;
391 CHECK(wrapper.Get(0, &value));
392 if (value->IsType(base::Value::TYPE_DICTIONARY))
393 parsed_manifest_.reset(
394 static_cast<const base::DictionaryValue*>(value)->DeepCopy());
395 else
396 error_ = keys::kManifestParseError;
397
398 BrowserThread::PostTask(
399 BrowserThread::UI,
400 FROM_HERE,
401 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this));
402 }
403
OnJSONParseFailed(const std::string & error)404 void OnJSONParseFailed(const std::string& error) {
405 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
406 error_ = error;
407 BrowserThread::PostTask(
408 BrowserThread::UI,
409 FROM_HERE,
410 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this));
411 }
412
ReportResultFromUIThread()413 void ReportResultFromUIThread() {
414 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415 if (error_.empty() && parsed_manifest_.get())
416 client_->OnParseSuccess(parsed_manifest_.Pass());
417 else
418 client_->OnParseFailure(error_);
419 }
420
421 private:
~SafeManifestJSONParser()422 virtual ~SafeManifestJSONParser() {}
423
424 // The client who we'll report results back to.
425 ManagementGetPermissionWarningsByManifestFunction* client_;
426
427 // Data to parse.
428 std::string manifest_;
429
430 // Results of parsing.
431 scoped_ptr<base::DictionaryValue> parsed_manifest_;
432
433 std::string error_;
434 };
435
436 } // namespace
437
RunAsync()438 bool ManagementGetPermissionWarningsByManifestFunction::RunAsync() {
439 scoped_ptr<management::GetPermissionWarningsByManifest::Params> params(
440 management::GetPermissionWarningsByManifest::Params::Create(*args_));
441 EXTENSION_FUNCTION_VALIDATE(params.get());
442
443 scoped_refptr<SafeManifestJSONParser> parser =
444 new SafeManifestJSONParser(this, params->manifest_str);
445 parser->Start();
446
447 // Matched with a Release() in OnParseSuccess/Failure().
448 AddRef();
449
450 // Response is sent async in OnParseSuccess/Failure().
451 return true;
452 }
453
OnParseSuccess(scoped_ptr<base::DictionaryValue> parsed_manifest)454 void ManagementGetPermissionWarningsByManifestFunction::OnParseSuccess(
455 scoped_ptr<base::DictionaryValue> parsed_manifest) {
456 CHECK(parsed_manifest.get());
457
458 scoped_refptr<Extension> extension = Extension::Create(
459 base::FilePath(), Manifest::INVALID_LOCATION, *parsed_manifest,
460 Extension::NO_FLAGS, &error_);
461 if (!extension.get()) {
462 OnParseFailure(keys::kExtensionCreateError);
463 return;
464 }
465
466 std::vector<std::string> warnings = CreateWarningsList(extension.get());
467 results_ =
468 management::GetPermissionWarningsByManifest::Results::Create(warnings);
469 SendResponse(true);
470
471 // Matched with AddRef() in RunAsync().
472 Release();
473 }
474
OnParseFailure(const std::string & error)475 void ManagementGetPermissionWarningsByManifestFunction::OnParseFailure(
476 const std::string& error) {
477 error_ = error;
478 SendResponse(false);
479
480 // Matched with AddRef() in RunAsync().
481 Release();
482 }
483
RunSync()484 bool ManagementLaunchAppFunction::RunSync() {
485 scoped_ptr<management::LaunchApp::Params> params(
486 management::LaunchApp::Params::Create(*args_));
487 EXTENSION_FUNCTION_VALIDATE(params.get());
488 const Extension* extension = service()->GetExtensionById(params->id, true);
489 if (!extension) {
490 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
491 params->id);
492 return false;
493 }
494 if (!extension->is_app()) {
495 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError,
496 params->id);
497 return false;
498 }
499
500 // Look at prefs to find the right launch container.
501 // If the user has not set a preference, the default launch value will be
502 // returned.
503 LaunchContainer launch_container =
504 GetLaunchContainer(ExtensionPrefs::Get(GetProfile()), extension);
505 OpenApplication(AppLaunchParams(
506 GetProfile(), extension, launch_container, NEW_FOREGROUND_TAB));
507 #if !defined(OS_ANDROID)
508 CoreAppLauncherHandler::RecordAppLaunchType(
509 extension_misc::APP_LAUNCH_EXTENSION_API,
510 extension->GetType());
511 #endif
512
513 return true;
514 }
515
ManagementSetEnabledFunction()516 ManagementSetEnabledFunction::ManagementSetEnabledFunction() {
517 }
518
~ManagementSetEnabledFunction()519 ManagementSetEnabledFunction::~ManagementSetEnabledFunction() {
520 }
521
RunAsync()522 bool ManagementSetEnabledFunction::RunAsync() {
523 scoped_ptr<management::SetEnabled::Params> params(
524 management::SetEnabled::Params::Create(*args_));
525 EXTENSION_FUNCTION_VALIDATE(params.get());
526
527 extension_id_ = params->id;
528
529 const Extension* extension =
530 ExtensionRegistry::Get(GetProfile())
531 ->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING);
532 if (!extension || ui_util::ShouldNotBeVisible(extension, browser_context())) {
533 error_ = ErrorUtils::FormatErrorMessage(
534 keys::kNoExtensionError, extension_id_);
535 return false;
536 }
537
538 const ManagementPolicy* policy =
539 ExtensionSystem::Get(GetProfile())->management_policy();
540 if (!policy->UserMayModifySettings(extension, NULL) ||
541 (!params->enabled && policy->MustRemainEnabled(extension, NULL)) ||
542 (params->enabled && policy->MustRemainDisabled(extension, NULL, NULL))) {
543 error_ = ErrorUtils::FormatErrorMessage(
544 keys::kUserCantModifyError, extension_id_);
545 return false;
546 }
547
548 bool currently_enabled = service()->IsExtensionEnabled(extension_id_);
549
550 if (!currently_enabled && params->enabled) {
551 ExtensionPrefs* prefs = ExtensionPrefs::Get(GetProfile());
552 if (prefs->DidExtensionEscalatePermissions(extension_id_)) {
553 if (!user_gesture()) {
554 error_ = keys::kGestureNeededForEscalationError;
555 return false;
556 }
557 AddRef(); // Matched in InstallUIProceed/InstallUIAbort
558 install_prompt_.reset(
559 new ExtensionInstallPrompt(GetAssociatedWebContents()));
560 install_prompt_->ConfirmReEnable(this, extension);
561 return true;
562 }
563 service()->EnableExtension(extension_id_);
564 } else if (currently_enabled && !params->enabled) {
565 service()->DisableExtension(extension_id_, Extension::DISABLE_USER_ACTION);
566 }
567
568 BrowserThread::PostTask(
569 BrowserThread::UI,
570 FROM_HERE,
571 base::Bind(&ManagementSetEnabledFunction::SendResponse, this, true));
572
573 return true;
574 }
575
InstallUIProceed()576 void ManagementSetEnabledFunction::InstallUIProceed() {
577 service()->EnableExtension(extension_id_);
578 SendResponse(true);
579 Release();
580 }
581
InstallUIAbort(bool user_initiated)582 void ManagementSetEnabledFunction::InstallUIAbort(bool user_initiated) {
583 error_ = keys::kUserDidNotReEnableError;
584 SendResponse(false);
585 Release();
586 }
587
ManagementUninstallFunctionBase()588 ManagementUninstallFunctionBase::ManagementUninstallFunctionBase() {
589 }
590
~ManagementUninstallFunctionBase()591 ManagementUninstallFunctionBase::~ManagementUninstallFunctionBase() {
592 }
593
Uninstall(const std::string & target_extension_id,bool show_confirm_dialog)594 bool ManagementUninstallFunctionBase::Uninstall(
595 const std::string& target_extension_id,
596 bool show_confirm_dialog) {
597 extension_id_ = target_extension_id;
598 const Extension* target_extension =
599 service()->GetExtensionById(extension_id_, true);
600 if (!target_extension ||
601 ui_util::ShouldNotBeVisible(target_extension, browser_context())) {
602 error_ = ErrorUtils::FormatErrorMessage(
603 keys::kNoExtensionError, extension_id_);
604 return false;
605 }
606
607 if (!ExtensionSystem::Get(GetProfile())
608 ->management_policy()
609 ->UserMayModifySettings(target_extension, NULL)) {
610 error_ = ErrorUtils::FormatErrorMessage(
611 keys::kUserCantModifyError, extension_id_);
612 return false;
613 }
614
615 if (auto_confirm_for_test == DO_NOT_SKIP) {
616 if (show_confirm_dialog) {
617 AddRef(); // Balanced in ExtensionUninstallAccepted/Canceled
618 extension_uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
619 GetProfile(), GetCurrentBrowser(), this));
620 if (extension_id() != target_extension_id) {
621 extension_uninstall_dialog_->ConfirmProgrammaticUninstall(
622 target_extension, GetExtension());
623 } else {
624 // If this is a self uninstall, show the generic uninstall dialog.
625 extension_uninstall_dialog_->ConfirmUninstall(target_extension);
626 }
627 } else {
628 Finish(true);
629 }
630 } else {
631 Finish(auto_confirm_for_test == PROCEED);
632 }
633
634 return true;
635 }
636
637 // static
SetAutoConfirmForTest(bool should_proceed)638 void ManagementUninstallFunctionBase::SetAutoConfirmForTest(
639 bool should_proceed) {
640 auto_confirm_for_test = should_proceed ? PROCEED : ABORT;
641 }
642
Finish(bool should_uninstall)643 void ManagementUninstallFunctionBase::Finish(bool should_uninstall) {
644 if (should_uninstall) {
645 // The extension can be uninstalled in another window while the UI was
646 // showing. Do nothing in that case.
647 ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile());
648 const Extension* extension = registry->GetExtensionById(
649 extension_id_, ExtensionRegistry::EVERYTHING);
650 if (!extension) {
651 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
652 extension_id_);
653 SendResponse(false);
654 } else {
655 bool success =
656 service()->UninstallExtension(extension_id_,
657 false, /* external uninstall */
658 NULL);
659
660 // TODO set error_ if !success
661 SendResponse(success);
662 }
663 } else {
664 error_ = ErrorUtils::FormatErrorMessage(
665 keys::kUninstallCanceledError, extension_id_);
666 SendResponse(false);
667 }
668
669 }
670
ExtensionUninstallAccepted()671 void ManagementUninstallFunctionBase::ExtensionUninstallAccepted() {
672 Finish(true);
673 Release();
674 }
675
ExtensionUninstallCanceled()676 void ManagementUninstallFunctionBase::ExtensionUninstallCanceled() {
677 Finish(false);
678 Release();
679 }
680
ManagementUninstallFunction()681 ManagementUninstallFunction::ManagementUninstallFunction() {
682 }
683
~ManagementUninstallFunction()684 ManagementUninstallFunction::~ManagementUninstallFunction() {
685 }
686
RunAsync()687 bool ManagementUninstallFunction::RunAsync() {
688 scoped_ptr<management::Uninstall::Params> params(
689 management::Uninstall::Params::Create(*args_));
690 EXTENSION_FUNCTION_VALIDATE(extension_);
691 EXTENSION_FUNCTION_VALIDATE(params.get());
692
693 bool show_confirm_dialog = true;
694 // By default confirmation dialog isn't shown when uninstalling self, but this
695 // can be overridden with showConfirmDialog.
696 if (params->id == extension_->id()) {
697 show_confirm_dialog = params->options.get() &&
698 params->options->show_confirm_dialog.get() &&
699 *params->options->show_confirm_dialog;
700 }
701 if (show_confirm_dialog && !user_gesture()) {
702 error_ = keys::kGestureNeededForUninstallError;
703 return false;
704 }
705 return Uninstall(params->id, show_confirm_dialog);
706 }
707
ManagementUninstallSelfFunction()708 ManagementUninstallSelfFunction::ManagementUninstallSelfFunction() {
709 }
710
~ManagementUninstallSelfFunction()711 ManagementUninstallSelfFunction::~ManagementUninstallSelfFunction() {
712 }
713
RunAsync()714 bool ManagementUninstallSelfFunction::RunAsync() {
715 scoped_ptr<management::UninstallSelf::Params> params(
716 management::UninstallSelf::Params::Create(*args_));
717 EXTENSION_FUNCTION_VALIDATE(params.get());
718
719 bool show_confirm_dialog = false;
720 if (params->options.get() && params->options->show_confirm_dialog.get())
721 show_confirm_dialog = *params->options->show_confirm_dialog;
722 return Uninstall(extension_->id(), show_confirm_dialog);
723 }
724
ManagementCreateAppShortcutFunction()725 ManagementCreateAppShortcutFunction::ManagementCreateAppShortcutFunction() {
726 }
727
~ManagementCreateAppShortcutFunction()728 ManagementCreateAppShortcutFunction::~ManagementCreateAppShortcutFunction() {
729 }
730
731 // static
SetAutoConfirmForTest(bool should_proceed)732 void ManagementCreateAppShortcutFunction::SetAutoConfirmForTest(
733 bool should_proceed) {
734 auto_confirm_for_test = should_proceed ? PROCEED : ABORT;
735 }
736
OnCloseShortcutPrompt(bool created)737 void ManagementCreateAppShortcutFunction::OnCloseShortcutPrompt(bool created) {
738 if (!created)
739 error_ = keys::kCreateShortcutCanceledError;
740 SendResponse(created);
741 Release();
742 }
743
RunAsync()744 bool ManagementCreateAppShortcutFunction::RunAsync() {
745 if (!user_gesture()) {
746 error_ = keys::kGestureNeededForCreateAppShortcutError;
747 return false;
748 }
749
750 scoped_ptr<management::CreateAppShortcut::Params> params(
751 management::CreateAppShortcut::Params::Create(*args_));
752 EXTENSION_FUNCTION_VALIDATE(params.get());
753 const Extension* extension = service()->GetExtensionById(params->id, true);
754 if (!extension) {
755 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
756 params->id);
757 return false;
758 }
759
760 if (!extension->is_app()) {
761 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError, params->id);
762 return false;
763 }
764
765 #if defined(OS_MACOSX)
766 if (!extension->is_platform_app()) {
767 error_ = keys::kCreateOnlyPackagedAppShortcutMac;
768 return false;
769 }
770 #endif
771
772 Browser* browser = chrome::FindBrowserWithProfile(
773 GetProfile(), chrome::HOST_DESKTOP_TYPE_NATIVE);
774 if (!browser) {
775 // Shouldn't happen if we have user gesture.
776 error_ = keys::kNoBrowserToCreateShortcut;
777 return false;
778 }
779
780 // Matched with a Release() in OnCloseShortcutPrompt().
781 AddRef();
782
783 if (auto_confirm_for_test == DO_NOT_SKIP) {
784 chrome::ShowCreateChromeAppShortcutsDialog(
785 browser->window()->GetNativeWindow(), browser->profile(), extension,
786 base::Bind(&ManagementCreateAppShortcutFunction::OnCloseShortcutPrompt,
787 this));
788 } else {
789 OnCloseShortcutPrompt(auto_confirm_for_test == PROCEED);
790 }
791
792 // Response is sent async in OnCloseShortcutPrompt().
793 return true;
794 }
795
RunSync()796 bool ManagementSetLaunchTypeFunction::RunSync() {
797 if (!user_gesture()) {
798 error_ = keys::kGestureNeededForSetLaunchTypeError;
799 return false;
800 }
801
802 scoped_ptr<management::SetLaunchType::Params> params(
803 management::SetLaunchType::Params::Create(*args_));
804 EXTENSION_FUNCTION_VALIDATE(params.get());
805 const Extension* extension = service()->GetExtensionById(params->id, true);
806 if (!extension) {
807 error_ =
808 ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, params->id);
809 return false;
810 }
811
812 if (!extension->is_app()) {
813 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError, params->id);
814 return false;
815 }
816
817 std::vector<management::LaunchType> available_launch_types =
818 GetAvailableLaunchTypes(*extension);
819
820 management::LaunchType app_launch_type = params->launch_type;
821 if (std::find(available_launch_types.begin(),
822 available_launch_types.end(),
823 app_launch_type) == available_launch_types.end()) {
824 error_ = keys::kLaunchTypeNotAvailableError;
825 return false;
826 }
827
828 LaunchType launch_type = LAUNCH_TYPE_DEFAULT;
829 switch (app_launch_type) {
830 case management::LAUNCH_TYPE_OPEN_AS_PINNED_TAB:
831 launch_type = LAUNCH_TYPE_PINNED;
832 break;
833 case management::LAUNCH_TYPE_OPEN_AS_REGULAR_TAB:
834 launch_type = LAUNCH_TYPE_REGULAR;
835 break;
836 case management::LAUNCH_TYPE_OPEN_FULL_SCREEN:
837 launch_type = LAUNCH_TYPE_FULLSCREEN;
838 break;
839 case management::LAUNCH_TYPE_OPEN_AS_WINDOW:
840 launch_type = LAUNCH_TYPE_WINDOW;
841 break;
842 case management::LAUNCH_TYPE_NONE:
843 NOTREACHED();
844 }
845
846 SetLaunchType(service(), params->id, launch_type);
847
848 return true;
849 }
850
ManagementGenerateAppForLinkFunction()851 ManagementGenerateAppForLinkFunction::ManagementGenerateAppForLinkFunction() {
852 }
853
~ManagementGenerateAppForLinkFunction()854 ManagementGenerateAppForLinkFunction::~ManagementGenerateAppForLinkFunction() {
855 }
856
FinishCreateBookmarkApp(const Extension * extension,const WebApplicationInfo & web_app_info)857 void ManagementGenerateAppForLinkFunction::FinishCreateBookmarkApp(
858 const Extension* extension,
859 const WebApplicationInfo& web_app_info) {
860 if (extension) {
861 scoped_ptr<management::ExtensionInfo> info =
862 CreateExtensionInfo(*extension, ExtensionSystem::Get(GetProfile()));
863 results_ = management::GenerateAppForLink::Results::Create(*info);
864
865 SendResponse(true);
866 Release();
867 } else {
868 error_ = keys::kGenerateAppForLinkInstallError;
869 SendResponse(false);
870 Release();
871 }
872 }
873
OnFaviconForApp(const favicon_base::FaviconImageResult & image_result)874 void ManagementGenerateAppForLinkFunction::OnFaviconForApp(
875 const favicon_base::FaviconImageResult& image_result) {
876 WebApplicationInfo web_app;
877 web_app.title = base::UTF8ToUTF16(title_);
878 web_app.app_url = launch_url_;
879
880 if (!image_result.image.IsEmpty()) {
881 WebApplicationInfo::IconInfo icon;
882 icon.data = image_result.image.AsBitmap();
883 icon.width = icon.data.width();
884 icon.height = icon.data.height();
885 web_app.icons.push_back(icon);
886 }
887
888 bookmark_app_helper_.reset(new BookmarkAppHelper(service(), web_app, NULL));
889 bookmark_app_helper_->Create(base::Bind(
890 &ManagementGenerateAppForLinkFunction::FinishCreateBookmarkApp, this));
891 }
892
RunAsync()893 bool ManagementGenerateAppForLinkFunction::RunAsync() {
894 if (!user_gesture()) {
895 error_ = keys::kGestureNeededForGenerateAppForLinkError;
896 return false;
897 }
898
899 scoped_ptr<management::GenerateAppForLink::Params> params(
900 management::GenerateAppForLink::Params::Create(*args_));
901 EXTENSION_FUNCTION_VALIDATE(params.get());
902
903 GURL launch_url(params->url);
904 if (!launch_url.is_valid() || !launch_url.SchemeIsHTTPOrHTTPS()) {
905 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidURLError,
906 params->url);
907 return false;
908 }
909
910 if (params->title.empty()) {
911 error_ = keys::kEmptyTitleError;
912 return false;
913 }
914
915 FaviconService* favicon_service =
916 FaviconServiceFactory::GetForProfile(GetProfile(),
917 Profile::EXPLICIT_ACCESS);
918 DCHECK(favicon_service);
919
920 title_ = params->title;
921 launch_url_ = launch_url;
922
923 favicon_service->GetFaviconImageForPageURL(
924 FaviconService::FaviconForPageURLParams(
925 launch_url, favicon_base::FAVICON, gfx::kFaviconSize),
926 base::Bind(&ManagementGenerateAppForLinkFunction::OnFaviconForApp, this),
927 &cancelable_task_tracker_);
928
929 // Matched with a Release() in OnExtensionLoaded().
930 AddRef();
931
932 // Response is sent async in OnExtensionLoaded().
933 return true;
934 }
935
ManagementEventRouter(content::BrowserContext * context)936 ManagementEventRouter::ManagementEventRouter(content::BrowserContext* context)
937 : browser_context_(context), extension_registry_observer_(this) {
938 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
939 }
940
~ManagementEventRouter()941 ManagementEventRouter::~ManagementEventRouter() {}
942
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)943 void ManagementEventRouter::OnExtensionLoaded(
944 content::BrowserContext* browser_context,
945 const Extension* extension) {
946 BroadcastEvent(extension, management::OnEnabled::kEventName);
947 }
948
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)949 void ManagementEventRouter::OnExtensionUnloaded(
950 content::BrowserContext* browser_context,
951 const Extension* extension,
952 UnloadedExtensionInfo::Reason reason) {
953 BroadcastEvent(extension, management::OnDisabled::kEventName);
954 }
955
OnExtensionInstalled(content::BrowserContext * browser_context,const Extension * extension)956 void ManagementEventRouter::OnExtensionInstalled(
957 content::BrowserContext* browser_context,
958 const Extension* extension) {
959 BroadcastEvent(extension, management::OnInstalled::kEventName);
960 }
961
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension)962 void ManagementEventRouter::OnExtensionUninstalled(
963 content::BrowserContext* browser_context,
964 const Extension* extension) {
965 BroadcastEvent(extension, management::OnUninstalled::kEventName);
966 }
967
BroadcastEvent(const Extension * extension,const char * event_name)968 void ManagementEventRouter::BroadcastEvent(const Extension* extension,
969 const char* event_name) {
970 if (ui_util::ShouldNotBeVisible(extension, browser_context_))
971 return; // Don't dispatch events for built-in extenions.
972 scoped_ptr<base::ListValue> args(new base::ListValue());
973 if (event_name == management::OnUninstalled::kEventName) {
974 args->Append(new base::StringValue(extension->id()));
975 } else {
976 scoped_ptr<management::ExtensionInfo> info =
977 CreateExtensionInfo(*extension, ExtensionSystem::Get(browser_context_));
978 args->Append(info->ToValue().release());
979 }
980
981 EventRouter::Get(browser_context_)
982 ->BroadcastEvent(scoped_ptr<Event>(new Event(event_name, args.Pass())));
983 }
984
ManagementAPI(content::BrowserContext * context)985 ManagementAPI::ManagementAPI(content::BrowserContext* context)
986 : browser_context_(context) {
987 EventRouter* event_router = EventRouter::Get(browser_context_);
988 event_router->RegisterObserver(this, management::OnInstalled::kEventName);
989 event_router->RegisterObserver(this, management::OnUninstalled::kEventName);
990 event_router->RegisterObserver(this, management::OnEnabled::kEventName);
991 event_router->RegisterObserver(this, management::OnDisabled::kEventName);
992 }
993
~ManagementAPI()994 ManagementAPI::~ManagementAPI() {
995 }
996
Shutdown()997 void ManagementAPI::Shutdown() {
998 EventRouter::Get(browser_context_)->UnregisterObserver(this);
999 }
1000
1001 static base::LazyInstance<BrowserContextKeyedAPIFactory<ManagementAPI> >
1002 g_factory = LAZY_INSTANCE_INITIALIZER;
1003
1004 // static
1005 BrowserContextKeyedAPIFactory<ManagementAPI>*
GetFactoryInstance()1006 ManagementAPI::GetFactoryInstance() {
1007 return g_factory.Pointer();
1008 }
1009
OnListenerAdded(const EventListenerInfo & details)1010 void ManagementAPI::OnListenerAdded(const EventListenerInfo& details) {
1011 management_event_router_.reset(new ManagementEventRouter(browser_context_));
1012 EventRouter::Get(browser_context_)->UnregisterObserver(this);
1013 }
1014
1015 } // namespace extensions
1016