Home | History | Annotate | Download | only in apps
      1 // Copyright 2013 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/apps/shortcut_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/compiler_specific.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/strings/string16.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/extensions/extension_service.h"
     15 #include "chrome/browser/extensions/extension_ui_util.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/profiles/profile_info_cache.h"
     18 #include "chrome/browser/profiles/profile_manager.h"
     19 #include "chrome/browser/shell_integration.h"
     20 #include "chrome/browser/web_applications/web_app.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "components/pref_registry/pref_registry_syncable.h"
     24 #include "content/public/browser/browser_thread.h"
     25 #include "extensions/browser/extension_registry.h"
     26 #include "extensions/browser/extension_system.h"
     27 #include "extensions/browser/extension_util.h"
     28 #include "extensions/common/extension_set.h"
     29 #include "extensions/common/one_shot_event.h"
     30 
     31 using extensions::Extension;
     32 
     33 namespace {
     34 
     35 // This version number is stored in local prefs to check whether app shortcuts
     36 // need to be recreated. This might happen when we change various aspects of app
     37 // shortcuts like command-line flags or associated icons, binaries, etc.
     38 #if defined(OS_MACOSX)
     39 const int kCurrentAppShortcutsVersion = 1;
     40 #else
     41 const int kCurrentAppShortcutsVersion = 0;
     42 #endif
     43 
     44 // Delay in seconds before running UpdateShortcutsForAllApps.
     45 const int kUpdateShortcutsForAllAppsDelay = 10;
     46 
     47 void CreateShortcutsForApp(Profile* profile, const Extension* app) {
     48   web_app::ShortcutLocations creation_locations;
     49 
     50   if (extensions::util::IsEphemeralApp(app->id(), profile)) {
     51     // Ephemeral apps should not have visible shortcuts, but may still require
     52     // platform-specific handling.
     53     creation_locations.applications_menu_location =
     54         web_app::APP_MENU_LOCATION_HIDDEN;
     55   } else {
     56     // Creates a shortcut for an app in the Chrome Apps subdir of the
     57     // applications menu, if there is not already one present.
     58     creation_locations.applications_menu_location =
     59         web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
     60   }
     61 
     62   web_app::CreateShortcuts(
     63       web_app::SHORTCUT_CREATION_AUTOMATED, creation_locations, profile, app);
     64 }
     65 
     66 void SetCurrentAppShortcutsVersion(PrefService* prefs) {
     67   prefs->SetInteger(prefs::kAppShortcutsVersion, kCurrentAppShortcutsVersion);
     68 }
     69 
     70 }  // namespace
     71 
     72 // static
     73 void AppShortcutManager::RegisterProfilePrefs(
     74     user_prefs::PrefRegistrySyncable* registry) {
     75   // Indicates whether app shortcuts have been created.
     76   registry->RegisterIntegerPref(
     77       prefs::kAppShortcutsVersion, 0,
     78       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     79 }
     80 
     81 AppShortcutManager::AppShortcutManager(Profile* profile)
     82     : profile_(profile),
     83       is_profile_info_cache_observer_(false),
     84       prefs_(profile->GetPrefs()),
     85       extension_registry_observer_(this),
     86       weak_ptr_factory_(this) {
     87   // Use of g_browser_process requires that we are either on the UI thread, or
     88   // there are no threads initialized (such as in unit tests).
     89   DCHECK(!content::BrowserThread::IsThreadInitialized(
     90              content::BrowserThread::UI) ||
     91          content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     92 
     93   extension_registry_observer_.Add(
     94       extensions::ExtensionRegistry::Get(profile_));
     95   // Wait for extensions to be ready before running
     96   // UpdateShortcutsForAllAppsIfNeeded.
     97   extensions::ExtensionSystem::Get(profile)->ready().Post(
     98       FROM_HERE,
     99       base::Bind(&AppShortcutManager::UpdateShortcutsForAllAppsIfNeeded,
    100                  weak_ptr_factory_.GetWeakPtr()));
    101 
    102   ProfileManager* profile_manager = g_browser_process->profile_manager();
    103   // profile_manager might be NULL in testing environments.
    104   if (profile_manager) {
    105     profile_manager->GetProfileInfoCache().AddObserver(this);
    106     is_profile_info_cache_observer_ = true;
    107   }
    108 }
    109 
    110 AppShortcutManager::~AppShortcutManager() {
    111   if (g_browser_process && is_profile_info_cache_observer_) {
    112     ProfileManager* profile_manager = g_browser_process->profile_manager();
    113     // profile_manager might be NULL in testing environments or during shutdown.
    114     if (profile_manager)
    115       profile_manager->GetProfileInfoCache().RemoveObserver(this);
    116   }
    117 }
    118 
    119 void AppShortcutManager::OnExtensionWillBeInstalled(
    120     content::BrowserContext* browser_context,
    121     const Extension* extension,
    122     bool is_update,
    123     bool from_ephemeral,
    124     const std::string& old_name) {
    125   if (!extension->is_app())
    126     return;
    127 
    128   // If the app is being updated, update any existing shortcuts but do not
    129   // create new ones. If it is being installed, automatically create a
    130   // shortcut in the applications menu (e.g., Start Menu).
    131   if (is_update && !from_ephemeral) {
    132     web_app::UpdateAllShortcuts(
    133         base::UTF8ToUTF16(old_name), profile_, extension);
    134   } else {
    135     CreateShortcutsForApp(profile_, extension);
    136   }
    137 }
    138 
    139 void AppShortcutManager::OnExtensionUninstalled(
    140     content::BrowserContext* browser_context,
    141     const Extension* extension) {
    142   web_app::DeleteAllShortcuts(profile_, extension);
    143 }
    144 
    145 void AppShortcutManager::OnProfileWillBeRemoved(
    146     const base::FilePath& profile_path) {
    147   if (profile_path != profile_->GetPath())
    148     return;
    149   content::BrowserThread::PostTask(
    150       content::BrowserThread::FILE, FROM_HERE,
    151       base::Bind(&web_app::internals::DeleteAllShortcutsForProfile,
    152                  profile_path));
    153 }
    154 
    155 void AppShortcutManager::UpdateShortcutsForAllAppsIfNeeded() {
    156   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType))
    157     return;
    158 
    159   int last_version = prefs_->GetInteger(prefs::kAppShortcutsVersion);
    160   if (last_version >= kCurrentAppShortcutsVersion)
    161     return;
    162 
    163   content::BrowserThread::PostDelayedTask(
    164       content::BrowserThread::UI,
    165       FROM_HERE,
    166       base::Bind(&web_app::UpdateShortcutsForAllApps,
    167                  profile_,
    168                  base::Bind(&SetCurrentAppShortcutsVersion, prefs_)),
    169       base::TimeDelta::FromSeconds(kUpdateShortcutsForAllAppsDelay));
    170 }
    171