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/web_applications/web_app.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/file_util.h" 10 #include "base/i18n/file_util_icu.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/threading/thread.h" 14 #include "chrome/common/chrome_constants.h" 15 #include "chrome/common/chrome_version_info.h" 16 #include "chrome/common/extensions/extension.h" 17 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" 18 #include "chrome/common/url_constants.h" 19 #include "content/public/browser/browser_thread.h" 20 #include "extensions/common/constants.h" 21 #include "grit/chromium_strings.h" 22 #include "ui/base/l10n/l10n_util.h" 23 24 using content::BrowserThread; 25 26 namespace { 27 28 #if defined(TOOLKIT_VIEWS) 29 // Predicator for sorting images from largest to smallest. 30 bool IconPrecedes(const WebApplicationInfo::IconInfo& left, 31 const WebApplicationInfo::IconInfo& right) { 32 return left.width < right.width; 33 } 34 #endif 35 36 void DeleteShortcutsOnFileThread( 37 const ShellIntegration::ShortcutInfo& shortcut_info) { 38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 39 40 base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory( 41 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); 42 return web_app::internals::DeletePlatformShortcuts( 43 shortcut_data_dir, shortcut_info); 44 } 45 46 void UpdateShortcutsOnFileThread( 47 const string16& old_app_title, 48 const ShellIntegration::ShortcutInfo& shortcut_info) { 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 50 51 base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory( 52 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); 53 return web_app::internals::UpdatePlatformShortcuts( 54 shortcut_data_dir, old_app_title, shortcut_info); 55 } 56 57 } // namespace 58 59 namespace web_app { 60 61 // The following string is used to build the directory name for 62 // shortcuts to chrome applications (the kind which are installed 63 // from a CRX). Application shortcuts to URLs use the {host}_{path} 64 // for the name of this directory. Hosts can't include an underscore. 65 // By starting this string with an underscore, we ensure that there 66 // are no naming conflicts. 67 static const char* kCrxAppPrefix = "_crx_"; 68 69 namespace internals { 70 71 base::FilePath GetSanitizedFileName(const string16& name) { 72 #if defined(OS_WIN) 73 string16 file_name = name; 74 #else 75 std::string file_name = UTF16ToUTF8(name); 76 #endif 77 file_util::ReplaceIllegalCharactersInPath(&file_name, '_'); 78 return base::FilePath(file_name); 79 } 80 81 } // namespace internals 82 83 base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path, 84 const std::string& extension_id, 85 const GURL& url) { 86 DCHECK(!profile_path.empty()); 87 base::FilePath app_data_dir(profile_path.Append(chrome::kWebAppDirname)); 88 89 if (!extension_id.empty()) { 90 return app_data_dir.AppendASCII( 91 GenerateApplicationNameFromExtensionId(extension_id)); 92 } 93 94 std::string host(url.host()); 95 std::string scheme(url.has_scheme() ? url.scheme() : "http"); 96 std::string port(url.has_port() ? url.port() : "80"); 97 std::string scheme_port(scheme + "_" + port); 98 99 #if defined(OS_WIN) 100 base::FilePath::StringType host_path(UTF8ToUTF16(host)); 101 base::FilePath::StringType scheme_port_path(UTF8ToUTF16(scheme_port)); 102 #elif defined(OS_POSIX) 103 base::FilePath::StringType host_path(host); 104 base::FilePath::StringType scheme_port_path(scheme_port); 105 #endif 106 107 return app_data_dir.Append(host_path).Append(scheme_port_path); 108 } 109 110 base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path, 111 const extensions::Extension& extension) { 112 return GetWebAppDataDirectory( 113 profile_path, 114 extension.id(), 115 GURL(extensions::AppLaunchInfo::GetLaunchWebURL(&extension))); 116 } 117 118 std::string GenerateApplicationNameFromInfo( 119 const ShellIntegration::ShortcutInfo& shortcut_info) { 120 if (!shortcut_info.extension_id.empty()) { 121 return web_app::GenerateApplicationNameFromExtensionId( 122 shortcut_info.extension_id); 123 } else { 124 return web_app::GenerateApplicationNameFromURL( 125 shortcut_info.url); 126 } 127 } 128 129 std::string GenerateApplicationNameFromURL(const GURL& url) { 130 std::string t; 131 t.append(url.host()); 132 t.append("_"); 133 t.append(url.path()); 134 return t; 135 } 136 137 std::string GenerateApplicationNameFromExtensionId(const std::string& id) { 138 std::string t(web_app::kCrxAppPrefix); 139 t.append(id); 140 return t; 141 } 142 143 std::string GetExtensionIdFromApplicationName(const std::string& app_name) { 144 std::string prefix(kCrxAppPrefix); 145 if (app_name.substr(0, prefix.length()) != prefix) 146 return std::string(); 147 return app_name.substr(prefix.length()); 148 } 149 150 void CreateShortcuts( 151 const ShellIntegration::ShortcutInfo& shortcut_info, 152 const ShellIntegration::ShortcutLocations& creation_locations, 153 ShortcutCreationReason creation_reason) { 154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 155 156 BrowserThread::PostTask( 157 BrowserThread::FILE, 158 FROM_HERE, 159 base::Bind(base::IgnoreResult(&CreateShortcutsOnFileThread), 160 shortcut_info, creation_locations, creation_reason)); 161 } 162 163 void DeleteAllShortcuts(const ShellIntegration::ShortcutInfo& shortcut_info) { 164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 165 166 BrowserThread::PostTask( 167 BrowserThread::FILE, 168 FROM_HERE, 169 base::Bind(&DeleteShortcutsOnFileThread, shortcut_info)); 170 } 171 172 void UpdateAllShortcuts(const string16& old_app_title, 173 const ShellIntegration::ShortcutInfo& shortcut_info) { 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 175 176 BrowserThread::PostTask( 177 BrowserThread::FILE, 178 FROM_HERE, 179 base::Bind(&UpdateShortcutsOnFileThread, old_app_title, shortcut_info)); 180 } 181 182 bool CreateShortcutsOnFileThread( 183 const ShellIntegration::ShortcutInfo& shortcut_info, 184 const ShellIntegration::ShortcutLocations& creation_locations, 185 ShortcutCreationReason creation_reason) { 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 187 188 base::FilePath shortcut_data_dir = GetWebAppDataDirectory( 189 shortcut_info.profile_path, shortcut_info.extension_id, 190 shortcut_info.url); 191 return internals::CreatePlatformShortcuts(shortcut_data_dir, shortcut_info, 192 creation_locations, 193 creation_reason); 194 } 195 196 bool IsValidUrl(const GURL& url) { 197 static const char* const kValidUrlSchemes[] = { 198 chrome::kFileScheme, 199 chrome::kFileSystemScheme, 200 chrome::kFtpScheme, 201 chrome::kHttpScheme, 202 chrome::kHttpsScheme, 203 extensions::kExtensionScheme, 204 }; 205 206 for (size_t i = 0; i < arraysize(kValidUrlSchemes); ++i) { 207 if (url.SchemeIs(kValidUrlSchemes[i])) 208 return true; 209 } 210 211 return false; 212 } 213 214 #if defined(TOOLKIT_VIEWS) 215 void GetIconsInfo(const WebApplicationInfo& app_info, 216 IconInfoList* icons) { 217 DCHECK(icons); 218 219 icons->clear(); 220 for (size_t i = 0; i < app_info.icons.size(); ++i) { 221 // We only take square shaped icons (i.e. width == height). 222 if (app_info.icons[i].width == app_info.icons[i].height) { 223 icons->push_back(app_info.icons[i]); 224 } 225 } 226 227 std::sort(icons->begin(), icons->end(), &IconPrecedes); 228 } 229 #endif 230 231 #if defined(TOOLKIT_GTK) 232 std::string GetWMClassFromAppName(std::string app_name) { 233 file_util::ReplaceIllegalCharactersInPath(&app_name, '_'); 234 TrimString(app_name, "_", &app_name); 235 return app_name; 236 } 237 #endif 238 239 string16 GetAppShortcutsSubdirName() { 240 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 241 if (channel == chrome::VersionInfo::CHANNEL_CANARY) 242 return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY); 243 return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME); 244 } 245 246 } // namespace web_app 247