Home | History | Annotate | Download | only in management
      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 
     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 
     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 
    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 
    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 
    289 ExtensionService* ManagementFunction::service() {
    290   return GetProfile()->GetExtensionService();
    291 }
    292 
    293 ExtensionService* AsyncManagementFunction::service() {
    294   return GetProfile()->GetExtensionService();
    295 }
    296 
    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 
    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 
    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:
    355   SafeManifestJSONParser(
    356       ManagementGetPermissionWarningsByManifestFunction* client,
    357       const std::string& manifest)
    358       : client_(client),
    359         manifest_(manifest) {}
    360 
    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 
    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 
    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 
    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 
    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 
    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:
    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 
    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 
    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 
    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 
    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 
    516 ManagementSetEnabledFunction::ManagementSetEnabledFunction() {
    517 }
    518 
    519 ManagementSetEnabledFunction::~ManagementSetEnabledFunction() {
    520 }
    521 
    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 
    576 void ManagementSetEnabledFunction::InstallUIProceed() {
    577   service()->EnableExtension(extension_id_);
    578   SendResponse(true);
    579   Release();
    580 }
    581 
    582 void ManagementSetEnabledFunction::InstallUIAbort(bool user_initiated) {
    583   error_ = keys::kUserDidNotReEnableError;
    584   SendResponse(false);
    585   Release();
    586 }
    587 
    588 ManagementUninstallFunctionBase::ManagementUninstallFunctionBase() {
    589 }
    590 
    591 ManagementUninstallFunctionBase::~ManagementUninstallFunctionBase() {
    592 }
    593 
    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
    638 void ManagementUninstallFunctionBase::SetAutoConfirmForTest(
    639     bool should_proceed) {
    640   auto_confirm_for_test = should_proceed ? PROCEED : ABORT;
    641 }
    642 
    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 
    671 void ManagementUninstallFunctionBase::ExtensionUninstallAccepted() {
    672   Finish(true);
    673   Release();
    674 }
    675 
    676 void ManagementUninstallFunctionBase::ExtensionUninstallCanceled() {
    677   Finish(false);
    678   Release();
    679 }
    680 
    681 ManagementUninstallFunction::ManagementUninstallFunction() {
    682 }
    683 
    684 ManagementUninstallFunction::~ManagementUninstallFunction() {
    685 }
    686 
    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 
    708 ManagementUninstallSelfFunction::ManagementUninstallSelfFunction() {
    709 }
    710 
    711 ManagementUninstallSelfFunction::~ManagementUninstallSelfFunction() {
    712 }
    713 
    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 
    725 ManagementCreateAppShortcutFunction::ManagementCreateAppShortcutFunction() {
    726 }
    727 
    728 ManagementCreateAppShortcutFunction::~ManagementCreateAppShortcutFunction() {
    729 }
    730 
    731 // static
    732 void ManagementCreateAppShortcutFunction::SetAutoConfirmForTest(
    733     bool should_proceed) {
    734   auto_confirm_for_test = should_proceed ? PROCEED : ABORT;
    735 }
    736 
    737 void ManagementCreateAppShortcutFunction::OnCloseShortcutPrompt(bool created) {
    738   if (!created)
    739     error_ = keys::kCreateShortcutCanceledError;
    740   SendResponse(created);
    741   Release();
    742 }
    743 
    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 
    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 
    851 ManagementGenerateAppForLinkFunction::ManagementGenerateAppForLinkFunction() {
    852 }
    853 
    854 ManagementGenerateAppForLinkFunction::~ManagementGenerateAppForLinkFunction() {
    855 }
    856 
    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 
    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 
    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 
    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 
    941 ManagementEventRouter::~ManagementEventRouter() {}
    942 
    943 void ManagementEventRouter::OnExtensionLoaded(
    944     content::BrowserContext* browser_context,
    945     const Extension* extension) {
    946   BroadcastEvent(extension, management::OnEnabled::kEventName);
    947 }
    948 
    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 
    956 void ManagementEventRouter::OnExtensionInstalled(
    957     content::BrowserContext* browser_context,
    958     const Extension* extension) {
    959   BroadcastEvent(extension, management::OnInstalled::kEventName);
    960 }
    961 
    962 void ManagementEventRouter::OnExtensionUninstalled(
    963     content::BrowserContext* browser_context,
    964     const Extension* extension) {
    965   BroadcastEvent(extension, management::OnUninstalled::kEventName);
    966 }
    967 
    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 
    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 
    994 ManagementAPI::~ManagementAPI() {
    995 }
    996 
    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>*
   1006 ManagementAPI::GetFactoryInstance() {
   1007   return g_factory.Pointer();
   1008 }
   1009 
   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