[email protected] | 7f070d4 | 2011-03-09 20:25:32 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 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 | |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 7 | #if defined(OS_WIN) |
[email protected] | 48dc9e1 | 2010-08-26 19:49:57 | [diff] [blame] | 8 | #include <shlobj.h> |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 9 | #endif // defined(OS_WIN) |
| 10 | |
[email protected] | b1039293 | 2011-03-08 21:28:14 | [diff] [blame] | 11 | #include "base/command_line.h" |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 12 | #include "base/file_util.h" |
[email protected] | a0b60cfd | 2011-04-06 18:02:41 | [diff] [blame^] | 13 | #include "base/i18n/file_util_icu.h" |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 14 | #include "base/md5.h" |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 15 | #include "base/path_service.h" |
[email protected] | a0b60cfd | 2011-04-06 18:02:41 | [diff] [blame^] | 16 | #include "base/string_util.h" |
[email protected] | 7f070d4 | 2011-03-09 20:25:32 | [diff] [blame] | 17 | #include "base/threading/thread.h" |
[email protected] | 1cb92b8 | 2010-03-08 23:12:15 | [diff] [blame] | 18 | #include "base/utf_string_conversions.h" |
[email protected] | 935aa54 | 2010-10-15 01:59:15 | [diff] [blame] | 19 | #include "base/win/windows_version.h" |
[email protected] | 5a2388a | 2010-03-26 16:13:39 | [diff] [blame] | 20 | #include "chrome/browser/download/download_util.h" |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 21 | #include "chrome/common/chrome_constants.h" |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 22 | #include "chrome/common/chrome_paths.h" |
[email protected] | 12ea22a | 2009-11-19 07:17:23 | [diff] [blame] | 23 | #include "chrome/common/url_constants.h" |
[email protected] | 1625ffd | 2011-03-01 17:51:50 | [diff] [blame] | 24 | #include "content/browser/browser_thread.h" |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 25 | |
[email protected] | 9bb6c457 | 2010-03-12 11:03:24 | [diff] [blame] | 26 | #if defined(OS_LINUX) |
[email protected] | 76b90d31 | 2010-08-03 03:00:50 | [diff] [blame] | 27 | #include "base/environment.h" |
[email protected] | 9bb6c457 | 2010-03-12 11:03:24 | [diff] [blame] | 28 | #endif // defined(OS_LINUX) |
| 29 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 30 | #if defined(OS_WIN) |
[email protected] | 08397d5 | 2011-02-05 01:53:38 | [diff] [blame] | 31 | #include "ui/gfx/icon_util.h" |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 32 | #endif // defined(OS_WIN) |
| 33 | |
| 34 | namespace { |
| 35 | |
[email protected] | be3df2b | 2010-06-25 21:39:07 | [diff] [blame] | 36 | #if defined(OS_WIN) |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 37 | const FilePath::CharType kIconChecksumFileExt[] = FILE_PATH_LITERAL(".ico.md5"); |
| 38 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 39 | // Returns true if |ch| is in visible ASCII range and not one of |
| 40 | // "/ \ : * ? " < > | ; ,". |
| 41 | bool IsValidFilePathChar(char16 c) { |
| 42 | if (c < 32) |
| 43 | return false; |
| 44 | |
| 45 | switch (c) { |
| 46 | case '/': |
| 47 | case '\\': |
| 48 | case ':': |
| 49 | case '*': |
| 50 | case '?': |
| 51 | case '"': |
| 52 | case '<': |
| 53 | case '>': |
| 54 | case '|': |
| 55 | case ';': |
| 56 | case ',': |
| 57 | return false; |
| 58 | }; |
| 59 | |
| 60 | return true; |
| 61 | } |
| 62 | |
[email protected] | be3df2b | 2010-06-25 21:39:07 | [diff] [blame] | 63 | #endif // defined(OS_WIN) |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 64 | |
| 65 | // Returns relative directory of given web app url. |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 66 | FilePath GetWebAppDir(const ShellIntegration::ShortcutInfo& info) { |
| 67 | if (!info.extension_id.empty()) { |
[email protected] | b1039293 | 2011-03-08 21:28:14 | [diff] [blame] | 68 | std::string app_name = |
| 69 | web_app::GenerateApplicationNameFromExtensionId(info.extension_id); |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 70 | #if defined(OS_WIN) |
| 71 | return FilePath(UTF8ToWide(app_name)); |
| 72 | #elif defined(OS_POSIX) |
| 73 | return FilePath(app_name); |
| 74 | #endif |
| 75 | } else { |
| 76 | FilePath::StringType host; |
| 77 | FilePath::StringType scheme_port; |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 78 | |
| 79 | #if defined(OS_WIN) |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 80 | host = UTF8ToWide(info.url.host()); |
| 81 | scheme_port = (info.url.has_scheme() ? UTF8ToWide(info.url.scheme()) |
| 82 | : L"http") + FILE_PATH_LITERAL("_") + |
| 83 | (info.url.has_port() ? UTF8ToWide(info.url.port()) : L"80"); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 84 | #elif defined(OS_POSIX) |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 85 | host = info.url.host(); |
| 86 | scheme_port = info.url.scheme() + FILE_PATH_LITERAL("_") + info.url.port(); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 87 | #endif |
| 88 | |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 89 | return FilePath(host).Append(scheme_port); |
| 90 | } |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 91 | } |
| 92 | |
[email protected] | 16f76563 | 2010-09-21 21:31:27 | [diff] [blame] | 93 | #if defined(TOOLKIT_VIEWS) |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 94 | // Predicator for sorting images from largest to smallest. |
[email protected] | 38789d8 | 2010-11-17 06:03:44 | [diff] [blame] | 95 | bool IconPrecedes(const WebApplicationInfo::IconInfo& left, |
| 96 | const WebApplicationInfo::IconInfo& right) { |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 97 | return left.width < right.width; |
| 98 | } |
[email protected] | 16f76563 | 2010-09-21 21:31:27 | [diff] [blame] | 99 | #endif |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 100 | |
[email protected] | 16f76563 | 2010-09-21 21:31:27 | [diff] [blame] | 101 | #if defined(OS_WIN) |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 102 | // Calculates image checksum using MD5. |
| 103 | void GetImageCheckSum(const SkBitmap& image, MD5Digest* digest) { |
| 104 | DCHECK(digest); |
| 105 | |
| 106 | SkAutoLockPixels image_lock(image); |
| 107 | MD5Sum(image.getPixels(), image.getSize(), digest); |
| 108 | } |
| 109 | |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 110 | // Saves |image| as an |icon_file| with the checksum. |
| 111 | bool SaveIconWithCheckSum(const FilePath& icon_file, const SkBitmap& image) { |
[email protected] | 3865810 | 2010-07-30 14:01:59 | [diff] [blame] | 112 | if (!IconUtil::CreateIconFileFromSkBitmap(image, icon_file)) |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 113 | return false; |
| 114 | |
| 115 | MD5Digest digest; |
| 116 | GetImageCheckSum(image, &digest); |
| 117 | |
| 118 | FilePath cheksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt)); |
| 119 | return file_util::WriteFile(cheksum_file, |
| 120 | reinterpret_cast<const char*>(&digest), |
| 121 | sizeof(digest)) == sizeof(digest); |
| 122 | } |
| 123 | |
| 124 | // Returns true if |icon_file| is missing or different from |image|. |
| 125 | bool ShouldUpdateIcon(const FilePath& icon_file, const SkBitmap& image) { |
| 126 | FilePath checksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt)); |
| 127 | |
| 128 | // Returns true if icon_file or checksum file is missing. |
| 129 | if (!file_util::PathExists(icon_file) || |
| 130 | !file_util::PathExists(checksum_file)) |
| 131 | return true; |
| 132 | |
| 133 | MD5Digest persisted_image_checksum; |
| 134 | if (sizeof(persisted_image_checksum) != file_util::ReadFile(checksum_file, |
| 135 | reinterpret_cast<char*>(&persisted_image_checksum), |
| 136 | sizeof(persisted_image_checksum))) |
| 137 | return true; |
| 138 | |
| 139 | MD5Digest downloaded_image_checksum; |
| 140 | GetImageCheckSum(image, &downloaded_image_checksum); |
| 141 | |
| 142 | // Update icon if checksums are not equal. |
| 143 | return memcmp(&persisted_image_checksum, &downloaded_image_checksum, |
| 144 | sizeof(MD5Digest)) != 0; |
| 145 | } |
| 146 | |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 147 | #endif // defined(OS_WIN) |
| 148 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 149 | // Represents a task that creates web application shortcut. This runs on |
| 150 | // file thread and schedules the callback (if any) on the calling thread |
| 151 | // when finished (either success or failure). |
| 152 | class CreateShortcutTask : public Task { |
| 153 | public: |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 154 | CreateShortcutTask(const FilePath& profile_path, |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 155 | const ShellIntegration::ShortcutInfo& shortcut_info, |
| 156 | web_app::CreateShortcutCallback* callback); |
| 157 | |
| 158 | private: |
| 159 | class CreateShortcutCallbackTask : public Task { |
| 160 | public: |
| 161 | CreateShortcutCallbackTask(web_app::CreateShortcutCallback* callback, |
| 162 | bool success) |
| 163 | : callback_(callback), |
| 164 | success_(success) { |
| 165 | } |
| 166 | |
| 167 | // Overridden from Task: |
| 168 | virtual void Run() { |
| 169 | callback_->Run(success_); |
| 170 | } |
| 171 | |
| 172 | private: |
| 173 | web_app::CreateShortcutCallback* callback_; |
| 174 | bool success_; |
| 175 | }; |
| 176 | |
| 177 | // Overridden from Task: |
| 178 | virtual void Run(); |
| 179 | |
| 180 | // Returns true if shortcut is created successfully. |
| 181 | bool CreateShortcut(); |
| 182 | |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 183 | // Path to store persisted data for web app. |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 184 | FilePath web_app_path_; |
| 185 | |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 186 | // Out copy of profile path. |
| 187 | FilePath profile_path_; |
| 188 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 189 | // Our copy of short cut data. |
| 190 | ShellIntegration::ShortcutInfo shortcut_info_; |
| 191 | |
| 192 | // Callback when task is finished. |
| 193 | web_app::CreateShortcutCallback* callback_; |
| 194 | MessageLoop* message_loop_; |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 195 | |
| 196 | DISALLOW_COPY_AND_ASSIGN(CreateShortcutTask); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 197 | }; |
| 198 | |
| 199 | CreateShortcutTask::CreateShortcutTask( |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 200 | const FilePath& profile_path, |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 201 | const ShellIntegration::ShortcutInfo& shortcut_info, |
| 202 | web_app::CreateShortcutCallback* callback) |
[email protected] | f847e608 | 2011-03-24 00:08:26 | [diff] [blame] | 203 | : web_app_path_(web_app::internals::GetWebAppDataDirectory( |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 204 | web_app::GetDataDir(profile_path), |
| 205 | shortcut_info)), |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 206 | profile_path_(profile_path), |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 207 | shortcut_info_(shortcut_info), |
| 208 | callback_(callback), |
| 209 | message_loop_(MessageLoop::current()) { |
| 210 | DCHECK(message_loop_ != NULL); |
| 211 | } |
| 212 | |
| 213 | void CreateShortcutTask::Run() { |
| 214 | bool success = CreateShortcut(); |
| 215 | |
| 216 | if (callback_ != NULL) |
| 217 | message_loop_->PostTask(FROM_HERE, |
| 218 | new CreateShortcutCallbackTask(callback_, success)); |
| 219 | } |
| 220 | |
| 221 | bool CreateShortcutTask::CreateShortcut() { |
[email protected] | 67e59fc | 2010-10-07 02:42:13 | [diff] [blame] | 222 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
[email protected] | 620942e | 2010-02-16 10:12:12 | [diff] [blame] | 223 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 224 | #if defined(OS_LINUX) |
[email protected] | 76b90d31 | 2010-08-03 03:00:50 | [diff] [blame] | 225 | scoped_ptr<base::Environment> env(base::Environment::Create()); |
[email protected] | af71d64 | 2010-03-12 10:29:04 | [diff] [blame] | 226 | |
[email protected] | 620942e | 2010-02-16 10:12:12 | [diff] [blame] | 227 | std::string shortcut_template; |
[email protected] | 76b90d31 | 2010-08-03 03:00:50 | [diff] [blame] | 228 | if (!ShellIntegration::GetDesktopShortcutTemplate(env.get(), |
[email protected] | af71d64 | 2010-03-12 10:29:04 | [diff] [blame] | 229 | &shortcut_template)) { |
[email protected] | 620942e | 2010-02-16 10:12:12 | [diff] [blame] | 230 | return false; |
[email protected] | af71d64 | 2010-03-12 10:29:04 | [diff] [blame] | 231 | } |
[email protected] | 620942e | 2010-02-16 10:12:12 | [diff] [blame] | 232 | ShellIntegration::CreateDesktopShortcut(shortcut_info_, shortcut_template); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 233 | return true; // assuming always success. |
| 234 | #elif defined(OS_WIN) |
| 235 | // Shortcut paths under which to create shortcuts. |
| 236 | std::vector<FilePath> shortcut_paths; |
| 237 | |
| 238 | // Locations to add to shortcut_paths. |
| 239 | struct { |
| 240 | const bool& use_this_location; |
| 241 | int location_id; |
| 242 | const wchar_t* sub_dir; |
| 243 | } locations[] = { |
| 244 | { |
| 245 | shortcut_info_.create_on_desktop, |
| 246 | chrome::DIR_USER_DESKTOP, |
| 247 | NULL |
| 248 | }, { |
| 249 | shortcut_info_.create_in_applications_menu, |
| 250 | base::DIR_START_MENU, |
| 251 | NULL |
| 252 | }, { |
| 253 | shortcut_info_.create_in_quick_launch_bar, |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 254 | // For Win7, create_in_quick_launch_bar means pinning to taskbar. Use |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 255 | // base::PATH_START as a flag for this case. |
[email protected] | 935aa54 | 2010-10-15 01:59:15 | [diff] [blame] | 256 | (base::win::GetVersion() >= base::win::VERSION_WIN7) ? |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 257 | base::PATH_START : base::DIR_APP_DATA, |
[email protected] | 935aa54 | 2010-10-15 01:59:15 | [diff] [blame] | 258 | (base::win::GetVersion() >= base::win::VERSION_WIN7) ? |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 259 | NULL : L"Microsoft\\Internet Explorer\\Quick Launch" |
| 260 | } |
| 261 | }; |
| 262 | |
| 263 | // Populate shortcut_paths. |
| 264 | for (int i = 0; i < arraysize(locations); ++i) { |
| 265 | if (locations[i].use_this_location) { |
| 266 | FilePath path; |
| 267 | |
| 268 | // Skip the Win7 case. |
| 269 | if (locations[i].location_id == base::PATH_START) |
| 270 | continue; |
| 271 | |
| 272 | if (!PathService::Get(locations[i].location_id, &path)) { |
| 273 | NOTREACHED(); |
| 274 | return false; |
| 275 | } |
| 276 | |
| 277 | if (locations[i].sub_dir != NULL) |
| 278 | path = path.Append(locations[i].sub_dir); |
| 279 | |
| 280 | shortcut_paths.push_back(path); |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | bool pin_to_taskbar = |
| 285 | shortcut_info_.create_in_quick_launch_bar && |
[email protected] | 935aa54 | 2010-10-15 01:59:15 | [diff] [blame] | 286 | (base::win::GetVersion() >= base::win::VERSION_WIN7); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 287 | |
| 288 | // For Win7's pinning support, any shortcut could be used. So we only create |
| 289 | // the shortcut file when there is no shortcut file will be created. That is, |
| 290 | // user only selects "Pin to taskbar". |
| 291 | if (pin_to_taskbar && shortcut_paths.empty()) { |
| 292 | // Creates the shortcut in web_app_path_ in this case. |
| 293 | shortcut_paths.push_back(web_app_path_); |
| 294 | } |
| 295 | |
| 296 | if (shortcut_paths.empty()) { |
| 297 | NOTREACHED(); |
| 298 | return false; |
| 299 | } |
| 300 | |
| 301 | // Ensure web_app_path_ exists. |
| 302 | if (!file_util::PathExists(web_app_path_) && |
| 303 | !file_util::CreateDirectory(web_app_path_)) { |
| 304 | NOTREACHED(); |
| 305 | return false; |
| 306 | } |
| 307 | |
| 308 | // Generates file name to use with persisted ico and shortcut file. |
[email protected] | f847e608 | 2011-03-24 00:08:26 | [diff] [blame] | 309 | FilePath file_name = |
| 310 | web_app::internals::GetSanitizedFileName(shortcut_info_.title); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 311 | |
| 312 | // Creates an ico file to use with shortcut. |
| 313 | FilePath icon_file = web_app_path_.Append(file_name).ReplaceExtension( |
| 314 | FILE_PATH_LITERAL(".ico")); |
[email protected] | f847e608 | 2011-03-24 00:08:26 | [diff] [blame] | 315 | if (!web_app::internals::CheckAndSaveIcon(icon_file, |
| 316 | shortcut_info_.favicon)) { |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 317 | NOTREACHED(); |
| 318 | return false; |
| 319 | } |
| 320 | |
[email protected] | 08729cc | 2010-05-24 03:49:01 | [diff] [blame] | 321 | FilePath chrome_exe; |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 322 | if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { |
| 323 | NOTREACHED(); |
| 324 | return false; |
| 325 | } |
| 326 | |
| 327 | // Working directory. |
[email protected] | 08729cc | 2010-05-24 03:49:01 | [diff] [blame] | 328 | FilePath chrome_folder = chrome_exe.DirName(); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 329 | |
[email protected] | b1039293 | 2011-03-08 21:28:14 | [diff] [blame] | 330 | CommandLine cmd_line = |
| 331 | ShellIntegration::CommandLineArgsForLauncher(shortcut_info_.url, |
| 332 | shortcut_info_.extension_id); |
| 333 | // TODO(evan): we rely on the fact that command_line_string() is |
| 334 | // properly quoted for a Windows command line. The method on |
| 335 | // CommandLine should probably be renamed to better reflect that |
| 336 | // fact. |
| 337 | std::wstring wide_switches(cmd_line.command_line_string()); |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 338 | |
[email protected] | 4349dcf4 | 2010-02-23 01:17:35 | [diff] [blame] | 339 | // Sanitize description |
| 340 | if (shortcut_info_.description.length() >= MAX_PATH) |
| 341 | shortcut_info_.description.resize(MAX_PATH - 1); |
| 342 | |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 343 | // Generates app id from web app url and profile path. |
[email protected] | a0b60cfd | 2011-04-06 18:02:41 | [diff] [blame^] | 344 | std::string app_name = |
| 345 | web_app::GenerateApplicationNameFromInfo(shortcut_info_); |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 346 | std::wstring app_id = ShellIntegration::GetAppId( |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 347 | UTF8ToWide(app_name), profile_path_); |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 348 | |
[email protected] | 5a2388a | 2010-03-26 16:13:39 | [diff] [blame] | 349 | FilePath shortcut_to_pin; |
| 350 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 351 | bool success = true; |
| 352 | for (size_t i = 0; i < shortcut_paths.size(); ++i) { |
| 353 | FilePath shortcut_file = shortcut_paths[i].Append(file_name). |
| 354 | ReplaceExtension(FILE_PATH_LITERAL(".lnk")); |
[email protected] | 5a2388a | 2010-03-26 16:13:39 | [diff] [blame] | 355 | |
| 356 | int unique_number = download_util::GetUniquePathNumber(shortcut_file); |
| 357 | if (unique_number == -1) { |
| 358 | success = false; |
| 359 | continue; |
| 360 | } else if (unique_number > 0) { |
| 361 | download_util::AppendNumberToPath(&shortcut_file, unique_number); |
| 362 | } |
| 363 | |
[email protected] | 08729cc | 2010-05-24 03:49:01 | [diff] [blame] | 364 | success &= file_util::CreateShortcutLink(chrome_exe.value().c_str(), |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 365 | shortcut_file.value().c_str(), |
[email protected] | 08729cc | 2010-05-24 03:49:01 | [diff] [blame] | 366 | chrome_folder.value().c_str(), |
[email protected] | b1039293 | 2011-03-08 21:28:14 | [diff] [blame] | 367 | wide_switches.c_str(), |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 368 | shortcut_info_.description.c_str(), |
| 369 | icon_file.value().c_str(), |
[email protected] | 86b5401 | 2009-11-19 09:18:50 | [diff] [blame] | 370 | 0, |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 371 | app_id.c_str()); |
[email protected] | 5a2388a | 2010-03-26 16:13:39 | [diff] [blame] | 372 | |
| 373 | // Any shortcut would work for the pinning. We use the first one. |
| 374 | if (success && pin_to_taskbar && shortcut_to_pin.empty()) |
| 375 | shortcut_to_pin = shortcut_file; |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 376 | } |
| 377 | |
| 378 | if (success && pin_to_taskbar) { |
[email protected] | 5a2388a | 2010-03-26 16:13:39 | [diff] [blame] | 379 | if (!shortcut_to_pin.empty()) { |
| 380 | success &= file_util::TaskbarPinShortcutLink( |
| 381 | shortcut_to_pin.value().c_str()); |
| 382 | } else { |
| 383 | NOTREACHED(); |
| 384 | success = false; |
| 385 | } |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 386 | } |
| 387 | |
| 388 | return success; |
| 389 | #else |
| 390 | NOTIMPLEMENTED(); |
| 391 | return false; |
| 392 | #endif |
| 393 | } |
| 394 | |
[email protected] | f847e608 | 2011-03-24 00:08:26 | [diff] [blame] | 395 | } // namespace |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 396 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 397 | namespace web_app { |
| 398 | |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 399 | // The following string is used to build the directory name for |
| 400 | // shortcuts to chrome applications (the kind which are installed |
| 401 | // from a CRX). Application shortcuts to URLs use the {host}_{path} |
| 402 | // for the name of this directory. Hosts can't include an underscore. |
| 403 | // By starting this string with an underscore, we ensure that there |
| 404 | // are no naming conflicts. |
| 405 | static const char* kCrxAppPrefix = "_crx_"; |
| 406 | |
[email protected] | f847e608 | 2011-03-24 00:08:26 | [diff] [blame] | 407 | namespace internals { |
| 408 | |
| 409 | #if defined(OS_WIN) |
| 410 | // Returns sanitized name that could be used as a file name |
| 411 | FilePath GetSanitizedFileName(const string16& name) { |
| 412 | string16 file_name; |
| 413 | |
| 414 | for (size_t i = 0; i < name.length(); ++i) { |
| 415 | char16 c = name[i]; |
| 416 | if (!IsValidFilePathChar(c)) |
| 417 | c = '_'; |
| 418 | |
| 419 | file_name += c; |
| 420 | } |
| 421 | |
| 422 | return FilePath(file_name); |
| 423 | } |
| 424 | |
| 425 | // Saves |image| to |icon_file| if the file is outdated and refresh shell's |
| 426 | // icon cache to ensure correct icon is displayed. Returns true if icon_file |
| 427 | // is up to date or successfully updated. |
| 428 | bool CheckAndSaveIcon(const FilePath& icon_file, const SkBitmap& image) { |
| 429 | if (ShouldUpdateIcon(icon_file, image)) { |
| 430 | if (SaveIconWithCheckSum(icon_file, image)) { |
| 431 | // Refresh shell's icon cache. This call is quite disruptive as user would |
| 432 | // see explorer rebuilding the icon cache. It would be great that we find |
| 433 | // a better way to achieve this. |
| 434 | SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, |
| 435 | NULL, NULL); |
| 436 | } else { |
| 437 | return false; |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | return true; |
| 442 | } |
| 443 | #endif // OS_WIN |
| 444 | |
| 445 | // Returns data directory for given web app url |
| 446 | FilePath GetWebAppDataDirectory(const FilePath& root_dir, |
| 447 | const ShellIntegration::ShortcutInfo& info) { |
| 448 | return root_dir.Append(GetWebAppDir(info)); |
| 449 | } |
| 450 | |
| 451 | } // namespace internals |
| 452 | |
[email protected] | a0b60cfd | 2011-04-06 18:02:41 | [diff] [blame^] | 453 | std::string GenerateApplicationNameFromInfo( |
| 454 | const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 455 | if (!shortcut_info.extension_id.empty()) { |
| 456 | return web_app::GenerateApplicationNameFromExtensionId( |
| 457 | shortcut_info.extension_id); |
| 458 | } else { |
| 459 | return web_app::GenerateApplicationNameFromURL( |
| 460 | shortcut_info.url); |
| 461 | } |
| 462 | } |
| 463 | |
[email protected] | 57ecc4b | 2010-08-11 03:02:51 | [diff] [blame] | 464 | std::string GenerateApplicationNameFromURL(const GURL& url) { |
[email protected] | 86b5401 | 2009-11-19 09:18:50 | [diff] [blame] | 465 | std::string t; |
| 466 | t.append(url.host()); |
| 467 | t.append("_"); |
| 468 | t.append(url.path()); |
[email protected] | 57ecc4b | 2010-08-11 03:02:51 | [diff] [blame] | 469 | return t; |
[email protected] | 86b5401 | 2009-11-19 09:18:50 | [diff] [blame] | 470 | } |
| 471 | |
[email protected] | 2f1c09d | 2011-01-14 14:58:14 | [diff] [blame] | 472 | std::string GenerateApplicationNameFromExtensionId(const std::string& id) { |
| 473 | std::string t(web_app::kCrxAppPrefix); |
| 474 | t.append(id); |
| 475 | return t; |
| 476 | } |
| 477 | |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 478 | void CreateShortcut( |
| 479 | const FilePath& data_dir, |
| 480 | const ShellIntegration::ShortcutInfo& shortcut_info, |
| 481 | CreateShortcutCallback* callback) { |
[email protected] | 67e59fc | 2010-10-07 02:42:13 | [diff] [blame] | 482 | BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
[email protected] | ed543187 | 2009-11-17 08:39:51 | [diff] [blame] | 483 | new CreateShortcutTask(data_dir, shortcut_info, callback)); |
| 484 | } |
| 485 | |
[email protected] | 12ea22a | 2009-11-19 07:17:23 | [diff] [blame] | 486 | bool IsValidUrl(const GURL& url) { |
| 487 | static const char* const kValidUrlSchemes[] = { |
| 488 | chrome::kFileScheme, |
| 489 | chrome::kFtpScheme, |
| 490 | chrome::kHttpScheme, |
| 491 | chrome::kHttpsScheme, |
[email protected] | 1677d260 | 2010-05-26 18:15:11 | [diff] [blame] | 492 | chrome::kExtensionScheme, |
[email protected] | 12ea22a | 2009-11-19 07:17:23 | [diff] [blame] | 493 | }; |
| 494 | |
| 495 | for (size_t i = 0; i < arraysize(kValidUrlSchemes); ++i) { |
| 496 | if (url.SchemeIs(kValidUrlSchemes[i])) |
| 497 | return true; |
| 498 | } |
| 499 | |
| 500 | return false; |
| 501 | } |
| 502 | |
[email protected] | 12f520c | 2010-01-06 18:11:15 | [diff] [blame] | 503 | FilePath GetDataDir(const FilePath& profile_path) { |
| 504 | return profile_path.Append(chrome::kWebAppDirname); |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 505 | } |
| 506 | |
[email protected] | be3df2b | 2010-06-25 21:39:07 | [diff] [blame] | 507 | #if defined(TOOLKIT_VIEWS) |
[email protected] | 38789d8 | 2010-11-17 06:03:44 | [diff] [blame] | 508 | void GetIconsInfo(const WebApplicationInfo& app_info, |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 509 | IconInfoList* icons) { |
| 510 | DCHECK(icons); |
| 511 | |
| 512 | icons->clear(); |
| 513 | for (size_t i = 0; i < app_info.icons.size(); ++i) { |
| 514 | // We only take square shaped icons (i.e. width == height). |
| 515 | if (app_info.icons[i].width == app_info.icons[i].height) { |
| 516 | icons->push_back(app_info.icons[i]); |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | std::sort(icons->begin(), icons->end(), &IconPrecedes); |
| 521 | } |
[email protected] | be3df2b | 2010-06-25 21:39:07 | [diff] [blame] | 522 | #endif |
[email protected] | eabfdae9 | 2009-12-11 06:13:51 | [diff] [blame] | 523 | |
[email protected] | a0b60cfd | 2011-04-06 18:02:41 | [diff] [blame^] | 524 | #if defined(TOOLKIT_GTK) |
| 525 | std::string GetWMClassFromAppName(std::string app_name) { |
| 526 | file_util::ReplaceIllegalCharactersInPath(&app_name, '_'); |
| 527 | TrimString(app_name, "_", &app_name); |
| 528 | return app_name; |
| 529 | } |
| 530 | #endif |
| 531 | |
[email protected] | f847e608 | 2011-03-24 00:08:26 | [diff] [blame] | 532 | } // namespace web_app |