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