shortcut_manager.cc revision 558790d6acca3451cf3a6b497803a5f07d0bec58
1558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#include "chrome/browser/apps/shortcut_manager.h" 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "apps/pref_names.h" 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h" 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/command_line.h" 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/compiler_specific.h" 11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/prefs/pref_service.h" 12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string16.h" 13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/browser_process.h" 157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h" 16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/extensions/extension_service.h" 17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/extensions/extension_system.h" 18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/profiles/profile.h" 19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/profiles/profile_info_cache.h" 20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/profiles/profile_manager.h" 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/shell_integration.h" 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/web_applications/web_app_ui.h" 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/web_applications/web_app.h" 24868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/common/extensions/extension_set.h" 26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/public/browser/browser_thread.h" 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_details.h" 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_source.h" 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension; 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace { 33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Creates a shortcut for an application in the applications menu, if there is 35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// not already one present. 36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void CreateShortcutsInApplicationsMenu( 37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const ShellIntegration::ShortcutInfo& shortcut_info) { 38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ShellIntegration::ShortcutLocations creation_locations; 39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) creation_locations.in_applications_menu = true; 40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Create the shortcut in the Chrome Apps subdir. 41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) creation_locations.applications_menu_subdir = 42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) web_app::GetAppShortcutsSubdirName(); 43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch web_app::CreateShortcuts(shortcut_info, creation_locations, 44558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch web_app::SHORTCUT_CREATION_AUTOMATED); 45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool ShouldCreateShortcutFor(const extensions::Extension* extension) { 48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return extension->is_platform_app() && 49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch extension->location() != extensions::Manifest::COMPONENT && 50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch extension->ShouldDisplayInAppLauncher(); 51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} // namespace 54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 55558790d6acca3451cf3a6b497803a5f07d0bec58Ben MurdochAppShortcutManager::AppShortcutManager(Profile* profile) 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) : profile_(profile), 57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch is_profile_info_cache_observer_(false), 58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch prefs_(profile->GetPrefs()), 59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) weak_factory_(this) { 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::Source<Profile>(profile_)); 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::Source<Profile>(profile_)); 64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Wait for extensions to be ready before running OnceOffCreateShortcuts. 65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, 66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch content::Source<Profile>(profile_)); 67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // TODO(mgiuca): This use of g_browser_process should be DCHECKed for 69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // content::BrowserThread::CurrentlyOn(content::BrowserThread::UI). This check 70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // currently fails browser_tests, due to http://crbug.com/251191. 71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ProfileManager* profile_manager = g_browser_process->profile_manager(); 72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // profile_manager might be NULL in testing environments. 73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (profile_manager) { 74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch profile_manager->GetProfileInfoCache().AddObserver(this); 75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch is_profile_info_cache_observer_ = true; 76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 79558790d6acca3451cf3a6b497803a5f07d0bec58Ben MurdochAppShortcutManager::~AppShortcutManager() { 80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (g_browser_process && is_profile_info_cache_observer_) { 81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ProfileManager* profile_manager = g_browser_process->profile_manager(); 82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // profile_manager might be NULL in testing environments or during shutdown. 83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (profile_manager) 84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch profile_manager->GetProfileInfoCache().RemoveObserver(this); 85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 88558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochvoid AppShortcutManager::Observe(int type, 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const content::NotificationSource& source, 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const content::NotificationDetails& details) { 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) switch (type) { 92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case chrome::NOTIFICATION_EXTENSIONS_READY: { 93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch OnceOffCreateShortcuts(); 94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch break; 95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case chrome::NOTIFICATION_EXTENSION_INSTALLED: { 97868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#if defined(OS_MACOSX) 98868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!CommandLine::ForCurrentProcess()-> 99868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) HasSwitch(switches::kEnableAppShims)) 100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#endif // defined(OS_MACOSX) 102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const extensions::InstalledExtensionInfo* installed_info = 104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) content::Details<const extensions::InstalledExtensionInfo>(details) 105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) .ptr(); 106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const Extension* extension = installed_info->extension; 107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (ShouldCreateShortcutFor(extension)) { 108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If the app is being updated, update any existing shortcuts but do not 109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // create new ones. If it is being installed, automatically create a 110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // shortcut in the applications menu (e.g., Start Menu). 111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::Callback<void(const ShellIntegration::ShortcutInfo&)> 112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) create_or_update; 113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (installed_info->is_update) { 11490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) string16 old_title = UTF8ToUTF16(installed_info->old_name); 11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) create_or_update = base::Bind(&web_app::UpdateAllShortcuts, 11690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) old_title); 117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } else { 118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) create_or_update = base::Bind(&CreateShortcutsInApplicationsMenu); 119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) web_app::UpdateShortcutInfoAndIconForApp(*extension, profile_, 122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) create_or_update); 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Extension* extension = content::Details<const Extension>( 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) details).ptr(); 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DeleteApplicationShortcuts(extension); 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) default: 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) NOTREACHED(); 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 137558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochvoid AppShortcutManager::OnProfileWillBeRemoved( 138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch const base::FilePath& profile_path) { 139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (profile_path != profile_->GetPath()) 140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch content::BrowserThread::PostTask( 142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch content::BrowserThread::FILE, FROM_HERE, 143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::Bind(&web_app::internals::DeleteAllShortcutsForProfile, 144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch profile_path)); 145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 147558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochvoid AppShortcutManager::OnceOffCreateShortcuts() { 148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch bool was_enabled = prefs_->GetBoolean(apps::prefs::kShortcutsHaveBeenCreated); 149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 150eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Creation of shortcuts on Mac currently sits behind --enable-app-shims. 151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Until it is enabled permanently, we need to check the flag, and set the 152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // pref accordingly. 153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#if defined(OS_MACOSX) 154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch bool is_now_enabled = 155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims); 156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#else 157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch bool is_now_enabled = true; 158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#endif // defined(OS_MACOSX) 159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (was_enabled != is_now_enabled) 161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch prefs_->SetBoolean(apps::prefs::kShortcutsHaveBeenCreated, is_now_enabled); 162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (was_enabled || !is_now_enabled) 164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Check if extension system/service are available. They might not be in 167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // tests. 168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch extensions::ExtensionSystem* extension_system; 169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ExtensionServiceInterface* extension_service; 170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!(extension_system = extensions::ExtensionSystem::Get(profile_)) || 171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch !(extension_service = extension_system->extension_service())) 172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 173eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Create an applications menu shortcut for each app in this profile. 175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch const ExtensionSet* apps = extension_service->extensions(); 176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch for (ExtensionSet::const_iterator it = apps->begin(); 177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch it != apps->end(); ++it) { 178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (ShouldCreateShortcutFor(it->get())) 179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch web_app::UpdateShortcutInfoAndIconForApp( 180eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch *it->get(), profile_, base::Bind(&CreateShortcutsInApplicationsMenu)); 181eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 183eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 184558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochvoid AppShortcutManager::DeleteApplicationShortcuts( 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Extension* extension) { 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ShellIntegration::ShortcutInfo delete_info = 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) web_app::ShortcutInfoForExtensionAndProfile(extension, profile_); 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) web_app::DeleteAllShortcuts(delete_info); 1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 190