#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/i18n/file_util_icu.h"
+#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
+#include "chrome/browser/extensions/image_loader.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/favicon/favicon_tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/extensions/api/file_handlers/file_handlers_parser.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/icons_handler.h"
+#include "grit/theme_resources.h"
+#include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_family.h"
+#include "ui/gfx/image/image_skia.h"
+
+#if defined(OS_WIN)
+#include "ui/gfx/icon_util.h"
+#endif
using content::BrowserThread;
namespace {
+typedef base::Callback<void(const web_app::ShortcutInfo&,
+ const extensions::FileHandlersInfo&)> InfoCallback;
+
+#if defined(OS_MACOSX)
+const int kDesiredSizes[] = {16, 32, 128, 256, 512};
+const size_t kNumDesiredSizes = arraysize(kDesiredSizes);
+#elif defined(OS_LINUX)
+// Linux supports icons of any size. FreeDesktop Icon Theme Specification states
+// that "Minimally you should install a 48x48 icon in the hicolor theme."
+const int kDesiredSizes[] = {16, 32, 48, 128, 256, 512};
+const size_t kNumDesiredSizes = arraysize(kDesiredSizes);
+#elif defined(OS_WIN)
+const int* kDesiredSizes = IconUtil::kIconDimensions;
+const size_t kNumDesiredSizes = IconUtil::kNumIconDimensions;
+#else
+const int kDesiredSizes[] = {32};
+const size_t kNumDesiredSizes = arraysize(kDesiredSizes);
+#endif
+
#if defined(TOOLKIT_VIEWS)
// Predicator for sorting images from largest to smallest.
bool IconPrecedes(const WebApplicationInfo::IconInfo& left,
}
#endif
+bool CreateShortcutsWithInfoOnFileThread(
+ web_app::ShortcutCreationReason reason,
+ const web_app::ShortcutLocations& locations,
+ const web_app::ShortcutInfo& shortcut_info,
+ const extensions::FileHandlersInfo& file_handlers_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ base::FilePath shortcut_data_dir =
+ web_app::GetWebAppDataDirectory(shortcut_info.profile_path,
+ shortcut_info.extension_id,
+ shortcut_info.url);
+ return web_app::internals::CreatePlatformShortcuts(
+ shortcut_data_dir, shortcut_info, file_handlers_info, locations, reason);
+}
+
void DeleteShortcutsOnFileThread(
- const ShellIntegration::ShortcutInfo& shortcut_info) {
+ const web_app::ShortcutInfo& shortcut_info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory(
void UpdateShortcutsOnFileThread(
const base::string16& old_app_title,
- const ShellIntegration::ShortcutInfo& shortcut_info) {
+ const web_app::ShortcutInfo& shortcut_info,
+ const extensions::FileHandlersInfo& file_handlers_info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory(
shortcut_info.profile_path, shortcut_info.extension_id, GURL());
return web_app::internals::UpdatePlatformShortcuts(
- shortcut_data_dir, old_app_title, shortcut_info);
+ shortcut_data_dir, old_app_title, shortcut_info, file_handlers_info);
+}
+
+void CreateShortcutsWithInfo(
+ web_app::ShortcutCreationReason reason,
+ const web_app::ShortcutLocations& locations,
+ const web_app::ShortcutInfo& shortcut_info,
+ const extensions::FileHandlersInfo& file_handlers_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(
+ base::IgnoreResult(&CreateShortcutsWithInfoOnFileThread),
+ reason, locations, shortcut_info, file_handlers_info));
+}
+
+void UpdateAllShortcutsForShortcutInfo(
+ const base::string16& old_app_title,
+ const web_app::ShortcutInfo& shortcut_info,
+ const extensions::FileHandlersInfo& file_handlers_info) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&UpdateShortcutsOnFileThread,
+ old_app_title, shortcut_info, file_handlers_info));
+}
+
+void OnImageLoaded(web_app::ShortcutInfo shortcut_info,
+ extensions::FileHandlersInfo file_handlers_info,
+ InfoCallback callback,
+ const gfx::ImageFamily& image_family) {
+ // If the image failed to load (e.g. if the resource being loaded was empty)
+ // use the standard application icon.
+ if (image_family.empty()) {
+ gfx::Image default_icon =
+ ResourceBundle::GetSharedInstance().GetImageNamed(IDR_APP_DEFAULT_ICON);
+ int size = kDesiredSizes[kNumDesiredSizes - 1];
+ SkBitmap bmp = skia::ImageOperations::Resize(
+ *default_icon.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST,
+ size, size);
+ gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bmp);
+ // We are on the UI thread, and this image is needed from the FILE thread,
+ // for creating shortcut icon files.
+ image_skia.MakeThreadSafe();
+ shortcut_info.favicon.Add(gfx::Image(image_skia));
+ } else {
+ shortcut_info.favicon = image_family;
+ }
+
+ callback.Run(shortcut_info, file_handlers_info);
+}
+
+void GetInfoForApp(const extensions::Extension* extension,
+ Profile* profile,
+ const InfoCallback& callback) {
+ web_app::ShortcutInfo shortcut_info =
+ web_app::ShortcutInfoForExtensionAndProfile(extension, profile);
+ extensions::FileHandlersInfo file_handlers_info;
+ const std::vector<extensions::FileHandlerInfo>* file_handlers =
+ extensions::FileHandlers::GetFileHandlers(extension);
+ if (file_handlers)
+ file_handlers_info.handlers = *file_handlers;
+
+ std::vector<extensions::ImageLoader::ImageRepresentation> info_list;
+ for (size_t i = 0; i < kNumDesiredSizes; ++i) {
+ int size = kDesiredSizes[i];
+ extensions::ExtensionResource resource =
+ extensions::IconsInfo::GetIconResource(
+ extension, size, ExtensionIconSet::MATCH_EXACTLY);
+ if (!resource.empty()) {
+ info_list.push_back(extensions::ImageLoader::ImageRepresentation(
+ resource,
+ extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
+ gfx::Size(size, size),
+ ui::SCALE_FACTOR_100P));
+ }
+ }
+
+ if (info_list.empty()) {
+ size_t i = kNumDesiredSizes - 1;
+ int size = kDesiredSizes[i];
+
+ // If there is no icon at the desired sizes, we will resize what we can get.
+ // Making a large icon smaller is preferred to making a small icon larger,
+ // so look for a larger icon first:
+ extensions::ExtensionResource resource =
+ extensions::IconsInfo::GetIconResource(
+ extension, size, ExtensionIconSet::MATCH_BIGGER);
+ if (resource.empty()) {
+ resource = extensions::IconsInfo::GetIconResource(
+ extension, size, ExtensionIconSet::MATCH_SMALLER);
+ }
+ info_list.push_back(extensions::ImageLoader::ImageRepresentation(
+ resource,
+ extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
+ gfx::Size(size, size),
+ ui::SCALE_FACTOR_100P));
+ }
+
+ // |info_list| may still be empty at this point, in which case
+ // LoadImageFamilyAsync will call the OnImageLoaded callback with an empty
+ // image and exit immediately.
+ extensions::ImageLoader::Get(profile)->LoadImageFamilyAsync(
+ extension,
+ info_list,
+ base::Bind(&OnImageLoaded, shortcut_info, file_handlers_info, callback));
+}
+
+void IgnoreFileHandlersInfo(
+ const web_app::ShortcutInfoCallback& shortcut_info_callback,
+ const web_app::ShortcutInfo& shortcut_info,
+ const extensions::FileHandlersInfo& file_handlers_info) {
+ shortcut_info_callback.Run(shortcut_info);
}
} // namespace
return base::FilePath(file_name);
}
+bool CreateShortcutsOnFileThread(
+ ShortcutCreationReason reason,
+ const web_app::ShortcutLocations& locations,
+ const web_app::ShortcutInfo& shortcut_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ return CreateShortcutsWithInfoOnFileThread(
+ reason, locations, shortcut_info, extensions::FileHandlersInfo());
+}
+
} // namespace internals
+web_app::ShortcutInfo::ShortcutInfo()
+ : is_platform_app(false) {
+}
+
+web_app::ShortcutInfo::~ShortcutInfo() {}
+
+web_app::ShortcutLocations::ShortcutLocations()
+ : on_desktop(false),
+ applications_menu_location(APP_MENU_LOCATION_NONE),
+ in_quick_launch_bar(false)
+#if defined(OS_POSIX)
+ , hidden(false)
+#endif
+ {
+}
+
+void GetShortcutInfoForTab(content::WebContents* web_contents,
+ web_app::ShortcutInfo* info) {
+ DCHECK(info); // Must provide a valid info.
+
+ const FaviconTabHelper* favicon_tab_helper =
+ FaviconTabHelper::FromWebContents(web_contents);
+ const extensions::TabHelper* extensions_tab_helper =
+ extensions::TabHelper::FromWebContents(web_contents);
+ const WebApplicationInfo& app_info = extensions_tab_helper->web_app_info();
+
+ info->url = app_info.app_url.is_empty() ? web_contents->GetURL() :
+ app_info.app_url;
+ info->title = app_info.title.empty() ?
+ (web_contents->GetTitle().empty() ? base::UTF8ToUTF16(info->url.spec()) :
+ web_contents->GetTitle()) :
+ app_info.title;
+ info->description = app_info.description;
+ info->favicon.Add(favicon_tab_helper->GetFavicon());
+
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ info->profile_path = profile->GetPath();
+}
+
+#if !defined(OS_WIN)
+void UpdateShortcutForTabContents(content::WebContents* web_contents) {}
+#endif
+
+web_app::ShortcutInfo ShortcutInfoForExtensionAndProfile(
+ const extensions::Extension* app, Profile* profile) {
+ web_app::ShortcutInfo shortcut_info;
+ shortcut_info.extension_id = app->id();
+ shortcut_info.is_platform_app = app->is_platform_app();
+ shortcut_info.url = extensions::AppLaunchInfo::GetLaunchWebURL(app);
+ shortcut_info.title = base::UTF8ToUTF16(app->name());
+ shortcut_info.description = base::UTF8ToUTF16(app->description());
+ shortcut_info.extension_path = app->path();
+ shortcut_info.profile_path = profile->GetPath();
+ shortcut_info.profile_name =
+ profile->GetPrefs()->GetString(prefs::kProfileName);
+ return shortcut_info;
+}
+
+void UpdateShortcutInfoAndIconForApp(
+ const extensions::Extension* extension,
+ Profile* profile,
+ const web_app::ShortcutInfoCallback& callback) {
+ GetInfoForApp(extension,
+ profile,
+ base::Bind(&IgnoreFileHandlersInfo, callback));
+}
+
base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path,
const std::string& extension_id,
const GURL& url) {
}
std::string GenerateApplicationNameFromInfo(
- const ShellIntegration::ShortcutInfo& shortcut_info) {
+ const web_app::ShortcutInfo& shortcut_info) {
if (!shortcut_info.extension_id.empty()) {
return web_app::GenerateApplicationNameFromExtensionId(
shortcut_info.extension_id);
return app_name.substr(prefix.length());
}
-void CreateShortcuts(
- const ShellIntegration::ShortcutInfo& shortcut_info,
- const ShellIntegration::ShortcutLocations& creation_locations,
- ShortcutCreationReason creation_reason) {
+void CreateShortcutsForShortcutInfo(
+ web_app::ShortcutCreationReason reason,
+ const web_app::ShortcutLocations& locations,
+ const web_app::ShortcutInfo& shortcut_info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
- base::Bind(base::IgnoreResult(&CreateShortcutsOnFileThread),
- shortcut_info, creation_locations, creation_reason));
+ base::Bind(
+ base::IgnoreResult(&web_app::internals::CreateShortcutsOnFileThread),
+ reason, locations, shortcut_info));
}
-void DeleteAllShortcuts(const ShellIntegration::ShortcutInfo& shortcut_info) {
+void CreateShortcuts(
+ ShortcutCreationReason reason,
+ const web_app::ShortcutLocations& locations,
+ Profile* profile,
+ const extensions::Extension* app) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- BrowserThread::PostTask(
- BrowserThread::FILE,
- FROM_HERE,
- base::Bind(&DeleteShortcutsOnFileThread, shortcut_info));
+ GetInfoForApp(app,
+ profile,
+ base::Bind(&CreateShortcutsWithInfo, reason, locations));
}
-void UpdateAllShortcuts(const base::string16& old_app_title,
- const ShellIntegration::ShortcutInfo& shortcut_info) {
+void DeleteAllShortcuts(Profile* profile, const extensions::Extension* app) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
- base::Bind(&UpdateShortcutsOnFileThread, old_app_title, shortcut_info));
+ base::Bind(&DeleteShortcutsOnFileThread,
+ web_app::ShortcutInfoForExtensionAndProfile(app, profile)));
}
-bool CreateShortcutsOnFileThread(
- const ShellIntegration::ShortcutInfo& shortcut_info,
- const ShellIntegration::ShortcutLocations& creation_locations,
- ShortcutCreationReason creation_reason) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+void UpdateAllShortcuts(const base::string16& old_app_title,
+ Profile* profile,
+ const extensions::Extension* app) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- base::FilePath shortcut_data_dir = GetWebAppDataDirectory(
- shortcut_info.profile_path, shortcut_info.extension_id,
- shortcut_info.url);
- return internals::CreatePlatformShortcuts(shortcut_data_dir, shortcut_info,
- creation_locations,
- creation_reason);
+ GetInfoForApp(app,
+ profile,
+ base::Bind(&UpdateAllShortcutsForShortcutInfo, old_app_title));
}
bool IsValidUrl(const GURL& url) {
content::kFileScheme,
content::kFileSystemScheme,
content::kFtpScheme,
- content::kHttpScheme,
- content::kHttpsScheme,
+ url::kHttpScheme,
+ url::kHttpsScheme,
extensions::kExtensionScheme,
};