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