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 = 2; 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 extensions::UninstallReason reason) { 143 web_app::DeleteAllShortcuts(profile_, extension); 144 } 145 146 void AppShortcutManager::OnProfileWillBeRemoved( 147 const base::FilePath& profile_path) { 148 if (profile_path != profile_->GetPath()) 149 return; 150 content::BrowserThread::PostTask( 151 content::BrowserThread::FILE, FROM_HERE, 152 base::Bind(&web_app::internals::DeleteAllShortcutsForProfile, 153 profile_path)); 154 } 155 156 void AppShortcutManager::UpdateShortcutsForAllAppsIfNeeded() { 157 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) 158 return; 159 160 int last_version = prefs_->GetInteger(prefs::kAppShortcutsVersion); 161 if (last_version >= kCurrentAppShortcutsVersion) 162 return; 163 164 content::BrowserThread::PostDelayedTask( 165 content::BrowserThread::UI, 166 FROM_HERE, 167 base::Bind(&web_app::UpdateShortcutsForAllApps, 168 profile_, 169 base::Bind(&SetCurrentAppShortcutsVersion, prefs_)), 170 base::TimeDelta::FromSeconds(kUpdateShortcutsForAllAppsDelay)); 171 } 172