OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/web_applications/web_app.h" | 5 #include "chrome/browser/web_applications/web_app.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/i18n/file_util_icu.h" | 10 #include "base/i18n/file_util_icu.h" |
| 11 #include "base/prefs/pref_service.h" |
11 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
12 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
13 #include "base/threading/thread.h" | 14 #include "base/threading/thread.h" |
| 15 #include "chrome/browser/extensions/image_loader.h" |
| 16 #include "chrome/browser/profiles/profile.h" |
14 #include "chrome/common/chrome_constants.h" | 17 #include "chrome/common/chrome_constants.h" |
15 #include "chrome/common/chrome_version_info.h" | 18 #include "chrome/common/chrome_version_info.h" |
16 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | 19 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" |
| 20 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" |
| 21 #include "chrome/common/pref_names.h" |
17 #include "chrome/common/url_constants.h" | 22 #include "chrome/common/url_constants.h" |
18 #include "content/public/browser/browser_thread.h" | 23 #include "content/public/browser/browser_thread.h" |
19 #include "extensions/common/constants.h" | 24 #include "extensions/common/constants.h" |
20 #include "extensions/common/extension.h" | 25 #include "extensions/common/extension.h" |
| 26 #include "grit/theme_resources.h" |
| 27 #include "skia/ext/image_operations.h" |
| 28 #include "third_party/skia/include/core/SkBitmap.h" |
| 29 #include "ui/base/resource/resource_bundle.h" |
| 30 #include "ui/gfx/image/image.h" |
| 31 #include "ui/gfx/image/image_family.h" |
| 32 #include "ui/gfx/image/image_skia.h" |
| 33 |
| 34 #if defined(OS_WIN) |
| 35 #include "ui/gfx/icon_util.h" |
| 36 #endif |
21 | 37 |
22 using content::BrowserThread; | 38 using content::BrowserThread; |
23 | 39 |
24 namespace { | 40 namespace { |
25 | 41 |
| 42 #if defined(OS_MACOSX) |
| 43 const int kDesiredSizes[] = {16, 32, 128, 256, 512}; |
| 44 const size_t kNumDesiredSizes = arraysize(kDesiredSizes); |
| 45 #elif defined(OS_LINUX) |
| 46 // Linux supports icons of any size. FreeDesktop Icon Theme Specification states |
| 47 // that "Minimally you should install a 48x48 icon in the hicolor theme." |
| 48 const int kDesiredSizes[] = {16, 32, 48, 128, 256, 512}; |
| 49 const size_t kNumDesiredSizes = arraysize(kDesiredSizes); |
| 50 #elif defined(OS_WIN) |
| 51 const int* kDesiredSizes = IconUtil::kIconDimensions; |
| 52 const size_t kNumDesiredSizes = IconUtil::kNumIconDimensions; |
| 53 #else |
| 54 const int kDesiredSizes[] = {32}; |
| 55 const size_t kNumDesiredSizes = arraysize(kDesiredSizes); |
| 56 #endif |
| 57 |
26 #if defined(TOOLKIT_VIEWS) | 58 #if defined(TOOLKIT_VIEWS) |
27 // Predicator for sorting images from largest to smallest. | 59 // Predicator for sorting images from largest to smallest. |
28 bool IconPrecedes(const WebApplicationInfo::IconInfo& left, | 60 bool IconPrecedes(const WebApplicationInfo::IconInfo& left, |
29 const WebApplicationInfo::IconInfo& right) { | 61 const WebApplicationInfo::IconInfo& right) { |
30 return left.width < right.width; | 62 return left.width < right.width; |
31 } | 63 } |
32 #endif | 64 #endif |
33 | 65 |
34 void DeleteShortcutsOnFileThread( | 66 void DeleteShortcutsOnFileThread( |
35 const ShellIntegration::ShortcutInfo& shortcut_info) { | 67 const ShellIntegration::ShortcutInfo& shortcut_info) { |
36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
37 | 69 |
38 base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory( | 70 base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory( |
39 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); | 71 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); |
40 return web_app::internals::DeletePlatformShortcuts( | 72 return web_app::internals::DeletePlatformShortcuts( |
41 shortcut_data_dir, shortcut_info); | 73 shortcut_data_dir, shortcut_info); |
42 } | 74 } |
43 | 75 |
44 void UpdateShortcutsOnFileThread( | 76 void UpdateShortcutsOnFileThread( |
45 const base::string16& old_app_title, | 77 const base::string16& old_app_title, |
46 const ShellIntegration::ShortcutInfo& shortcut_info) { | 78 const ShellIntegration::ShortcutInfo& shortcut_info) { |
47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
48 | 80 |
49 base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory( | 81 base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory( |
50 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); | 82 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); |
51 return web_app::internals::UpdatePlatformShortcuts( | 83 return web_app::internals::UpdatePlatformShortcuts( |
52 shortcut_data_dir, old_app_title, shortcut_info); | 84 shortcut_data_dir, old_app_title, shortcut_info); |
53 } | 85 } |
54 | 86 |
| 87 void OnImageLoaded(ShellIntegration::ShortcutInfo shortcut_info, |
| 88 web_app::ShortcutInfoCallback callback, |
| 89 const gfx::ImageFamily& image_family) { |
| 90 // If the image failed to load (e.g. if the resource being loaded was empty) |
| 91 // use the standard application icon. |
| 92 if (image_family.empty()) { |
| 93 gfx::Image default_icon = |
| 94 ResourceBundle::GetSharedInstance().GetImageNamed(IDR_APP_DEFAULT_ICON); |
| 95 int size = kDesiredSizes[kNumDesiredSizes - 1]; |
| 96 SkBitmap bmp = skia::ImageOperations::Resize( |
| 97 *default_icon.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, |
| 98 size, size); |
| 99 gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bmp); |
| 100 // We are on the UI thread, and this image is needed from the FILE thread, |
| 101 // for creating shortcut icon files. |
| 102 image_skia.MakeThreadSafe(); |
| 103 shortcut_info.favicon.Add(gfx::Image(image_skia)); |
| 104 } else { |
| 105 shortcut_info.favicon = image_family; |
| 106 } |
| 107 |
| 108 callback.Run(shortcut_info); |
| 109 } |
| 110 |
55 } // namespace | 111 } // namespace |
56 | 112 |
57 namespace web_app { | 113 namespace web_app { |
58 | 114 |
59 // The following string is used to build the directory name for | 115 // The following string is used to build the directory name for |
60 // shortcuts to chrome applications (the kind which are installed | 116 // shortcuts to chrome applications (the kind which are installed |
61 // from a CRX). Application shortcuts to URLs use the {host}_{path} | 117 // from a CRX). Application shortcuts to URLs use the {host}_{path} |
62 // for the name of this directory. Hosts can't include an underscore. | 118 // for the name of this directory. Hosts can't include an underscore. |
63 // By starting this string with an underscore, we ensure that there | 119 // By starting this string with an underscore, we ensure that there |
64 // are no naming conflicts. | 120 // are no naming conflicts. |
65 static const char* kCrxAppPrefix = "_crx_"; | 121 static const char* kCrxAppPrefix = "_crx_"; |
66 | 122 |
67 namespace internals { | 123 namespace internals { |
68 | 124 |
69 base::FilePath GetSanitizedFileName(const base::string16& name) { | 125 base::FilePath GetSanitizedFileName(const base::string16& name) { |
70 #if defined(OS_WIN) | 126 #if defined(OS_WIN) |
71 base::string16 file_name = name; | 127 base::string16 file_name = name; |
72 #else | 128 #else |
73 std::string file_name = base::UTF16ToUTF8(name); | 129 std::string file_name = base::UTF16ToUTF8(name); |
74 #endif | 130 #endif |
75 file_util::ReplaceIllegalCharactersInPath(&file_name, '_'); | 131 file_util::ReplaceIllegalCharactersInPath(&file_name, '_'); |
76 return base::FilePath(file_name); | 132 return base::FilePath(file_name); |
77 } | 133 } |
78 | 134 |
79 } // namespace internals | 135 } // namespace internals |
80 | 136 |
| 137 ShellIntegration::ShortcutInfo ShortcutInfoForExtensionAndProfile( |
| 138 const extensions::Extension* app, Profile* profile) { |
| 139 ShellIntegration::ShortcutInfo shortcut_info; |
| 140 shortcut_info.extension_id = app->id(); |
| 141 shortcut_info.is_platform_app = app->is_platform_app(); |
| 142 shortcut_info.url = extensions::AppLaunchInfo::GetLaunchWebURL(app); |
| 143 shortcut_info.title = base::UTF8ToUTF16(app->name()); |
| 144 shortcut_info.description = base::UTF8ToUTF16(app->description()); |
| 145 shortcut_info.extension_path = app->path(); |
| 146 shortcut_info.profile_path = profile->GetPath(); |
| 147 shortcut_info.profile_name = |
| 148 profile->GetPrefs()->GetString(prefs::kProfileName); |
| 149 return shortcut_info; |
| 150 } |
| 151 |
| 152 void UpdateShortcutInfoAndIconForApp( |
| 153 const extensions::Extension* extension, |
| 154 Profile* profile, |
| 155 const web_app::ShortcutInfoCallback& callback) { |
| 156 ShellIntegration::ShortcutInfo shortcut_info = |
| 157 ShortcutInfoForExtensionAndProfile(extension, profile); |
| 158 |
| 159 std::vector<extensions::ImageLoader::ImageRepresentation> info_list; |
| 160 for (size_t i = 0; i < kNumDesiredSizes; ++i) { |
| 161 int size = kDesiredSizes[i]; |
| 162 extensions::ExtensionResource resource = |
| 163 extensions::IconsInfo::GetIconResource( |
| 164 extension, size, ExtensionIconSet::MATCH_EXACTLY); |
| 165 if (!resource.empty()) { |
| 166 info_list.push_back(extensions::ImageLoader::ImageRepresentation( |
| 167 resource, |
| 168 extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE, |
| 169 gfx::Size(size, size), |
| 170 ui::SCALE_FACTOR_100P)); |
| 171 } |
| 172 } |
| 173 |
| 174 if (info_list.empty()) { |
| 175 size_t i = kNumDesiredSizes - 1; |
| 176 int size = kDesiredSizes[i]; |
| 177 |
| 178 // If there is no icon at the desired sizes, we will resize what we can get. |
| 179 // Making a large icon smaller is preferred to making a small icon larger, |
| 180 // so look for a larger icon first: |
| 181 extensions::ExtensionResource resource = |
| 182 extensions::IconsInfo::GetIconResource( |
| 183 extension, size, ExtensionIconSet::MATCH_BIGGER); |
| 184 if (resource.empty()) { |
| 185 resource = extensions::IconsInfo::GetIconResource( |
| 186 extension, size, ExtensionIconSet::MATCH_SMALLER); |
| 187 } |
| 188 info_list.push_back(extensions::ImageLoader::ImageRepresentation( |
| 189 resource, |
| 190 extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE, |
| 191 gfx::Size(size, size), |
| 192 ui::SCALE_FACTOR_100P)); |
| 193 } |
| 194 |
| 195 // |info_list| may still be empty at this point, in which case |
| 196 // LoadImageFamilyAsync will call the OnImageLoaded callback with an empty |
| 197 // image and exit immediately. |
| 198 extensions::ImageLoader::Get(profile)->LoadImageFamilyAsync( |
| 199 extension, |
| 200 info_list, |
| 201 base::Bind(&OnImageLoaded, shortcut_info, callback)); |
| 202 } |
| 203 |
81 base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path, | 204 base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path, |
82 const std::string& extension_id, | 205 const std::string& extension_id, |
83 const GURL& url) { | 206 const GURL& url) { |
84 DCHECK(!profile_path.empty()); | 207 DCHECK(!profile_path.empty()); |
85 base::FilePath app_data_dir(profile_path.Append(chrome::kWebAppDirname)); | 208 base::FilePath app_data_dir(profile_path.Append(chrome::kWebAppDirname)); |
86 | 209 |
87 if (!extension_id.empty()) { | 210 if (!extension_id.empty()) { |
88 return app_data_dir.AppendASCII( | 211 return app_data_dir.AppendASCII( |
89 GenerateApplicationNameFromExtensionId(extension_id)); | 212 GenerateApplicationNameFromExtensionId(extension_id)); |
90 } | 213 } |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 | 351 |
229 #if defined(OS_LINUX) | 352 #if defined(OS_LINUX) |
230 std::string GetWMClassFromAppName(std::string app_name) { | 353 std::string GetWMClassFromAppName(std::string app_name) { |
231 file_util::ReplaceIllegalCharactersInPath(&app_name, '_'); | 354 file_util::ReplaceIllegalCharactersInPath(&app_name, '_'); |
232 base::TrimString(app_name, "_", &app_name); | 355 base::TrimString(app_name, "_", &app_name); |
233 return app_name; | 356 return app_name; |
234 } | 357 } |
235 #endif | 358 #endif |
236 | 359 |
237 } // namespace web_app | 360 } // namespace web_app |
OLD | NEW |