summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-17 08:39:51 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-17 08:39:51 +0000
commited54318708245efdfcf7454d3aaebfd419f4448c (patch)
tree8e233da9e3ca2c2cb8b57f04d75dde3d1cf1f704 /chrome/browser
parent80cf356e08919b18f502f20864d6f6b72b48eb60 (diff)
downloadchromium_src-ed54318708245efdfcf7454d3aaebfd419f4448c.zip
chromium_src-ed54318708245efdfcf7454d3aaebfd419f4448c.tar.gz
chromium_src-ed54318708245efdfcf7454d3aaebfd419f4448c.tar.bz2
Implement web app shortcuts natively per issue 25528
- Implement a CreateApplicationShortcutView similar to the current Gears-based one; - Add a few profile prefs to persist user's last checked shortcut locations - Implement a web_app::CreateShortcut that stores icon under "<profile>/Web Applications" in a similar layout as gears (i.e. <host>/<scheme_port>/<web_app_title>.ico) and calls file_util code to creates shortcut on Windows; - Add Win7 taskbar pin/unpin support function to file_util; - Update TabContents to replace gears with new code; Note: - Gears dialog is modaless but this one is a modal dialog. - Gear's icon store is not migrated because gears icons could still be used by shortcuts created by gears and thus we could not delete them even after migration. And we are not using the local ico files in the dialog even if they exists. - New CreateApplicationShortcutView is included when TOOLKIT_VIEW is defined. However on platforms other than Windows, the actual CreateShortCut code is not implemented. Right now it calls ShellIntegration's CreateDesktopShort if OS_LINUX is defined and NOTREACHED() for other platforms. BUG=25528 TEST=Verify the new UI provides the same functionality as gears under XP/Vista and support pinning on Win 7. Review URL: http://codereview.chromium.org/372012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32165 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.cc28
-rw-r--r--chrome/browser/browser.h2
-rw-r--r--chrome/browser/browser_window.h5
-rw-r--r--chrome/browser/cocoa/browser_window_cocoa.h1
-rw-r--r--chrome/browser/cocoa/browser_window_cocoa.mm4
-rw-r--r--chrome/browser/gtk/browser_window_gtk.cc13
-rw-r--r--chrome/browser/gtk/browser_window_gtk.h1
-rw-r--r--chrome/browser/shell_integration.h28
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc93
-rw-r--r--chrome/browser/tab_contents/tab_contents.h37
-rw-r--r--chrome/browser/tab_contents/tab_contents_delegate.h9
-rw-r--r--chrome/browser/views/browser_dialogs.h4
-rw-r--r--chrome/browser/views/create_application_shortcut_view.cc475
-rw-r--r--chrome/browser/views/create_application_shortcut_view.h114
-rw-r--r--chrome/browser/views/frame/browser_view.cc4
-rw-r--r--chrome/browser/views/frame/browser_view.h3
-rw-r--r--chrome/browser/web_applications/web_app.cc301
-rw-r--r--chrome/browser/web_applications/web_app.h30
18 files changed, 1017 insertions, 135 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 6728e5d..900a121 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -1148,7 +1148,17 @@ void Browser::OpenFile() {
void Browser::OpenCreateShortcutsDialog() {
UserMetrics::RecordAction(L"CreateShortcut", profile_);
#if defined(OS_WIN) || defined(OS_LINUX)
- GetSelectedTabContents()->CreateShortcut();
+ TabContents* current_tab = GetSelectedTabContents();
+ DCHECK(current_tab && current_tab->FavIconIsValid()) <<
+ "Menu item should be disabled.";
+
+ NavigationEntry* entry = current_tab->controller().GetLastCommittedEntry();
+ if (!entry)
+ return;
+
+ // Start fetching web app info for CreateApplicatoinShortcut dialog and
+ // show the dialog when the data is available in OnDidGetApplicationInfo.
+ current_tab->render_view_host()->GetApplicationInfo(entry->page_id());
#else
NOTIMPLEMENTED();
#endif
@@ -1348,6 +1358,9 @@ void Browser::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kShowOmniboxSearchHint, true);
prefs->RegisterIntegerPref(prefs::kNTPPromoRemaining, 5);
prefs->RegisterBooleanPref(prefs::kShowExtensionShelf, true);
+ prefs->RegisterBooleanPref(prefs::kWebAppCreateOnDesktop, true);
+ prefs->RegisterBooleanPref(prefs::kWebAppCreateInAppsMenu, true);
+ prefs->RegisterBooleanPref(prefs::kWebAppCreateInQuickLaunchBar, true);
}
// static
@@ -2204,6 +2217,19 @@ bool Browser::ShouldAddNavigationsToHistory() const {
return !IsApplication();
}
+void Browser::OnDidGetApplicationInfo(TabContents* tab_contents,
+ int32 page_id) {
+ TabContents* current_tab = GetSelectedTabContents();
+ if (current_tab != tab_contents)
+ return;
+
+ NavigationEntry* entry = current_tab->controller().GetLastCommittedEntry();
+ if (!entry || (entry->page_id() != page_id))
+ return;
+
+ window()->ShowCreateShortcutsDialog(current_tab);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Browser, SelectFileDialog::Listener implementation:
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 6e65c6a..149f7d6 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -572,6 +572,8 @@ class Browser : public TabStripModelDelegate,
virtual bool IsReservedAccelerator(const NativeWebKeyboardEvent& event);
virtual void ShowRepostFormWarningDialog(TabContents* tab_contents);
virtual bool ShouldAddNavigationsToHistory() const;
+ virtual void OnDidGetApplicationInfo(TabContents* tab_contents,
+ int32 page_id);
// Overridden from SelectFileDialog::Listener:
virtual void FileSelected(const FilePath& path, int index, void* params);
diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h
index de9823e..5c6feba 100644
--- a/chrome/browser/browser_window.h
+++ b/chrome/browser/browser_window.h
@@ -271,10 +271,13 @@ class BrowserWindow {
// keyboard event if one exists, otherwise -1.
virtual int GetCommandId(const NativeWebKeyboardEvent& event) = 0;
+ // Shows the create web app shortcut dialog box.
+ virtual void ShowCreateShortcutsDialog(TabContents* tab_contents) = 0;
+
#if defined(TOOLKIT_VIEWS)
// Toggles compact navigation bar.
virtual void ToggleCompactNavigationBar() = 0;
-#endif
+#endif // defined(TOOLKIT_VIEWS)
// Construct a BrowserWindow implementation for the specified |browser|.
static BrowserWindow* CreateBrowserWindow(Browser* browser);
diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h
index b5ef31f..e7883e1 100644
--- a/chrome/browser/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/cocoa/browser_window_cocoa.h
@@ -92,6 +92,7 @@ class BrowserWindowCocoa : public BrowserWindow,
virtual void ShowPageMenu();
virtual void ShowAppMenu();
virtual int GetCommandId(const NativeWebKeyboardEvent& event);
+ virtual void ShowCreateShortcutsDialog(TabContents* tab_contents);
// Overridden from NotificationObserver
virtual void Observe(NotificationType type,
diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm
index f38dd2d..eacab5a 100644
--- a/chrome/browser/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/cocoa/browser_window_cocoa.mm
@@ -416,6 +416,10 @@ int BrowserWindowCocoa::GetCommandId(const NativeWebKeyboardEvent& event) {
return -1;
}
+void BrowserWindowCocoa::ShowCreateShortcutsDialog(TabContents* tab_contents) {
+ NOTIMPLEMENTED();
+}
+
void BrowserWindowCocoa::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc
index 53cc6ad..9a2bb4c 100644
--- a/chrome/browser/gtk/browser_window_gtk.cc
+++ b/chrome/browser/gtk/browser_window_gtk.cc
@@ -42,6 +42,7 @@
#include "chrome/browser/gtk/browser_toolbar_gtk.h"
#include "chrome/browser/gtk/cairo_cached_surface.h"
#include "chrome/browser/gtk/clear_browsing_data_dialog_gtk.h"
+#include "chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h"
#include "chrome/browser/gtk/download_in_progress_dialog_gtk.h"
#include "chrome/browser/gtk/download_shelf_gtk.h"
#include "chrome/browser/gtk/edit_search_engine_dialog.h"
@@ -1218,6 +1219,16 @@ int BrowserWindowGtk::GetCommandId(const NativeWebKeyboardEvent& event) {
return command;
}
+void BrowserWindowGtk::ShowCreateShortcutsDialog(TabContents* tab_contents) {
+ SkBitmap bitmap;
+ if (tab_contents->FavIconIsValid())
+ bitmap = tab_contents->GetFavIcon();
+ CreateApplicationShortcutsDialogGtk::Show(window_,
+ tab_contents->GetURL(),
+ tab_contents->GetTitle(),
+ bitmap);
+}
+
void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() {
new DownloadInProgressDialogGtk(browser());
}
@@ -1435,7 +1446,7 @@ void BrowserWindowGtk::OnStateChanged(GdkWindowState state,
tabstrip_->Hide();
if (IsBookmarkBarSupported())
bookmark_bar_->EnterFullscreen();
- bool is_kiosk =
+ bool is_kiosk =
CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
if (!is_kiosk) {
fullscreen_exit_bubble_.reset(new FullscreenExitBubbleGtk(
diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h
index f531a65..7d55e6c 100644
--- a/chrome/browser/gtk/browser_window_gtk.h
+++ b/chrome/browser/gtk/browser_window_gtk.h
@@ -122,6 +122,7 @@ class BrowserWindowGtk : public BrowserWindow,
virtual void ShowPageMenu();
virtual void ShowAppMenu();
virtual int GetCommandId(const NativeWebKeyboardEvent& event);
+ virtual void ShowCreateShortcutsDialog(TabContents* tab_contents);
// Overridden from NotificationObserver:
virtual void Observe(NotificationType type,
diff --git a/chrome/browser/shell_integration.h b/chrome/browser/shell_integration.h
index 7558121..9e7b8bb 100644
--- a/chrome/browser/shell_integration.h
+++ b/chrome/browser/shell_integration.h
@@ -11,10 +11,7 @@
#include "base/ref_counted.h"
#include "base/string16.h"
#include "googleurl/src/gurl.h"
-
-#if defined(OS_LINUX)
#include "third_party/skia/include/core/SkBitmap.h"
-#endif
class FilePath;
@@ -44,6 +41,22 @@ class ShellIntegration {
// user. This method is very fast so it can be invoked in the UI thread.
static bool IsFirefoxDefaultBrowser();
+ struct ShortcutInfo {
+ GURL url;
+ string16 title;
+ string16 description;
+ SkBitmap favicon;
+
+ bool create_on_desktop;
+ bool create_in_applications_menu;
+
+ // For Windows, this refers to quick launch bar prior to Win7. In Win7,
+ // this means "pin to taskbar". For Mac/Linux, this could be used for
+ // Mac dock or the gnome/kde application launcher. However, those are not
+ // implemented yet.
+ bool create_in_quick_launch_bar;
+ };
+
#if defined(OS_LINUX)
// Returns filename for .desktop file based on |url|, sanitized for security.
static FilePath GetDesktopShortcutFilename(const GURL& url);
@@ -55,15 +68,6 @@ class ShellIntegration {
const std::string& template_contents, const GURL& url,
const string16& title, const std::string& icon_name);
- struct ShortcutInfo {
- GURL url;
- string16 title;
- SkBitmap favicon;
-
- bool create_on_desktop;
- bool create_in_applications_menu;
- };
-
// Creates a desktop shortcut. It is not guaranteed to exist immediately after
// returning from this function, because actual file operation is done on the
// file thread.
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index c373a6a..055d68f 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -29,7 +29,6 @@
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/favicon_service.h"
#include "chrome/browser/form_field_history_manager.h"
-#include "chrome/browser/gears_integration.h"
#include "chrome/browser/google_util.h"
#include "chrome/browser/hung_renderer_dialog.h"
#include "chrome/browser/jsmessage_box_handler.h"
@@ -78,11 +77,6 @@
#include <gdk/gdk.h>
#endif // defined(OS_CHROMEOS)
-#if defined(OS_LINUX)
-#include "chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h"
-#include "chrome/browser/gtk/gtk_theme_provider.h"
-#endif // defined(OS_LINUX)
-
// Cross-Site Navigations
//
// If a TabContents is told to navigate to a different web site (as determined
@@ -211,23 +205,6 @@ void MakeNavigateParams(Profile* profile, const NavigationEntry& entry,
// static
int TabContents::find_request_id_counter_ = -1;
-class TabContents::GearsCreateShortcutCallbackFunctor {
- public:
- explicit GearsCreateShortcutCallbackFunctor(TabContents* contents)
- : contents_(contents) {}
-
- void Run(const GearsShortcutData2& shortcut_data, bool success) {
- if (contents_)
- contents_->OnGearsCreateShortcutDone(shortcut_data, success);
- delete this;
- }
- void Cancel() {
- contents_ = NULL;
- }
-
- private:
- TabContents* contents_;
-};
TabContents::TabContents(Profile* profile,
SiteInstance* site_instance,
@@ -249,7 +226,6 @@ TabContents::TabContents(Profile* profile,
plugin_installer_(),
ALLOW_THIS_IN_INITIALIZER_LIST(fav_icon_helper_(this)),
select_file_dialog_(),
- pending_install_(),
is_loading_(false),
is_crashed_(false),
waiting_for_response_(false),
@@ -283,9 +259,6 @@ TabContents::TabContents(Profile* profile,
suppress_javascript_messages_(false),
is_showing_before_unload_dialog_(false),
opener_dom_ui_type_(DOMUIFactory::kNoDOMUI) {
- pending_install_.page_id = 0;
- pending_install_.callback_functor = NULL;
-
#if defined(OS_CHROMEOS)
// Make sure the thumbnailer is started before starting the render manager.
// The thumbnailer will want to listen for RVH creations, one of which will
@@ -348,9 +321,6 @@ TabContents::~TabContents() {
NotifyDisconnected();
HungRendererDialog::HideForTabContents(this);
- if (pending_install_.callback_functor)
- pending_install_.callback_functor->Cancel();
-
// First cleanly close all child windows.
// TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
// some of these to close. CloseWindows is async, so it might get called
@@ -780,38 +750,6 @@ TabContents* TabContents::Clone() {
return tc;
}
-void TabContents::CreateShortcut() {
- NavigationEntry* entry = controller_.GetLastCommittedEntry();
- if (!entry)
- return;
-
-#if defined(OS_LINUX) && !defined(TOOLKIT_VIEWS)
- SkBitmap bitmap;
- if (FavIconIsValid())
- bitmap = GetFavIcon();
- CreateApplicationShortcutsDialogGtk::Show(view()->GetTopLevelNativeWindow(),
- GetURL(), GetTitle(), bitmap);
-#else
- // We only allow one pending install request. By resetting the page id we
- // effectively cancel the pending install request.
- pending_install_.page_id = entry->page_id();
- pending_install_.icon = GetFavIcon();
- pending_install_.title = GetTitle();
- pending_install_.url = GetURL();
- if (pending_install_.callback_functor) {
- pending_install_.callback_functor->Cancel();
- pending_install_.callback_functor = NULL;
- }
- DCHECK(!pending_install_.icon.isNull()) << "Menu item should be disabled.";
- if (pending_install_.title.empty())
- pending_install_.title = UTF8ToUTF16(GetURL().spec());
-
- // Request the application info. When done OnDidGetApplicationInfo is invoked
- // and we'll create the shortcut.
- render_view_host()->GetApplicationInfo(pending_install_.page_id);
-#endif
-}
-
void TabContents::ShowPageInfo(const GURL& url,
const NavigationEntry::SSLStatus& ssl,
bool show_history) {
@@ -1296,24 +1234,6 @@ void TabContents::ExpireInfoBars(
}
}
-void TabContents::OnGearsCreateShortcutDone(
- const GearsShortcutData2& shortcut_data, bool success) {
- NavigationEntry* current_entry = controller_.GetLastCommittedEntry();
- bool same_page =
- current_entry && pending_install_.page_id == current_entry->page_id();
-
- if (success && same_page) {
- // Only switch to app mode if the user chose to create a shortcut and
- // we're still on the same page that it corresponded to.
- if (delegate())
- delegate()->ConvertContentsToApplication(this);
- }
-
- // Reset the page id to indicate no requests are pending.
- pending_install_.page_id = 0;
- pending_install_.callback_functor = NULL;
-}
-
DOMUI* TabContents::GetDOMUIForCurrentState() {
// When there is a pending navigation entry, we want to use the pending DOMUI
// that goes along with it to control the basic flags. For example, we want to
@@ -1789,15 +1709,10 @@ void TabContents::OnCrashedWorker() {
void TabContents::OnDidGetApplicationInfo(
int32 page_id,
const webkit_glue::WebApplicationInfo& info) {
- if (pending_install_.page_id != page_id)
- return; // The user clicked create on a separate page. Ignore this.
-
- pending_install_.callback_functor =
- new GearsCreateShortcutCallbackFunctor(this);
- GearsCreateShortcut(
- info, pending_install_.title, pending_install_.url, pending_install_.icon,
- NewCallback(pending_install_.callback_functor,
- &GearsCreateShortcutCallbackFunctor::Run));
+ web_app_info_ = info;
+
+ if (delegate())
+ delegate()->OnDidGetApplicationInfo(this, page_id);
}
void TabContents::DidStartProvisionalLoadForFrame(
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index b8e84a7..1b1bd13 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -32,12 +32,12 @@
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/page_navigator.h"
#include "chrome/browser/tab_contents/render_view_host_manager.h"
-#include "chrome/common/gears_api.h"
#include "chrome/common/navigation_types.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/property_bag.h"
#include "chrome/common/renderer_preferences.h"
#include "net/base/load_states.h"
+#include "webkit/glue/dom_operations.h"
#include "webkit/glue/password_form.h"
#include "webkit/glue/webpreferences.h"
@@ -61,9 +61,6 @@ namespace base {
class WaitableEvent;
}
-namespace webkit_glue {
-struct WebApplicationInfo;
-}
namespace IPC {
class Message;
@@ -245,6 +242,10 @@ class TabContents : public PageNavigator,
encoding_.clear();
}
+ const webkit_glue::WebApplicationInfo& web_app_info() const {
+ return web_app_info_;
+ }
+
// Internal state ------------------------------------------------------------
// This flag indicates whether the tab contents is currently being
@@ -332,9 +333,6 @@ class TabContents : public PageNavigator,
// heap-allocated pointer is owned by the caller.
virtual TabContents* Clone();
- // Tell Gears to create a shortcut for the current page.
- void CreateShortcut();
-
// Shows the page info.
void ShowPageInfo(const GURL& url,
const NavigationEntry::SSLStatus& ssl,
@@ -657,21 +655,6 @@ class TabContents : public PageNavigator,
// So InterstitialPage can access SetIsLoading.
friend class InterstitialPage;
- // When CreateShortcut is invoked RenderViewHost::GetApplicationInfo is
- // invoked. CreateShortcut caches the state of the page needed to create the
- // shortcut in PendingInstall. When OnDidGetApplicationInfo is invoked, it
- // uses the information from PendingInstall and the WebApplicationInfo
- // to create the shortcut.
- class GearsCreateShortcutCallbackFunctor;
- struct PendingInstall {
- int32 page_id;
- SkBitmap icon;
- string16 title;
- GURL url;
- // This object receives the GearsCreateShortcutCallback and routes the
- // message back to the TabContents, if we haven't been deleted.
- GearsCreateShortcutCallbackFunctor* callback_functor;
- };
// TODO(brettw) TestTabContents shouldn't exist!
friend class TestTabContents;
@@ -720,12 +703,6 @@ class TabContents : public PageNavigator,
void ExpireInfoBars(
const NavigationController::LoadCommittedDetails& details);
- // Called when the user dismisses the shortcut creation dialog. 'success' is
- // true if the shortcut was created.
- void OnGearsCreateShortcutDone(const GearsShortcutData2& shortcut_data,
- bool success);
-
-
// Returns the DOMUI for the current state of the tab. This will either be
// the pending DOMUI, the committed DOMUI, or NULL.
DOMUI* GetDOMUIForCurrentState();
@@ -1035,8 +1012,8 @@ class TabContents : public PageNavigator,
// Dialog box used for choosing files to upload from file form fields.
scoped_refptr<SelectFileDialog> select_file_dialog_;
- // Web app installation.
- PendingInstall pending_install_;
+ // Cached web app info data.
+ webkit_glue::WebApplicationInfo web_app_info_;
// Data for loading state ----------------------------------------------------
diff --git a/chrome/browser/tab_contents/tab_contents_delegate.h b/chrome/browser/tab_contents/tab_contents_delegate.h
index b4d3009..a9f52d2 100644
--- a/chrome/browser/tab_contents/tab_contents_delegate.h
+++ b/chrome/browser/tab_contents/tab_contents_delegate.h
@@ -25,6 +25,10 @@ class RenderViewHost;
class TabContents;
class TemplateURL;
+namespace webkit_glue {
+struct WebApplicationInfo;
+}
+
// Objects implement this interface to get notified about changes in the
// TabContents and to provide necessary functionality.
class TabContentsDelegate {
@@ -247,6 +251,11 @@ class TabContentsDelegate {
// Returns whether this tab contents should add navigations to history.
virtual bool ShouldAddNavigationsToHistory() const { return true; }
+ // Notification when web app info data is available
+ virtual void OnDidGetApplicationInfo(TabContents* tab_contents,
+ int32 page_id) {
+ }
+
protected:
~TabContentsDelegate() {}
};
diff --git a/chrome/browser/views/browser_dialogs.h b/chrome/browser/views/browser_dialogs.h
index 691eee6..cdfc36a 100644
--- a/chrome/browser/views/browser_dialogs.h
+++ b/chrome/browser/views/browser_dialogs.h
@@ -101,6 +101,10 @@ void EditSearchEngine(gfx::NativeWindow parent,
void ShowRepostFormWarningDialog(gfx::NativeWindow parent_window,
TabContents* tab_contents);
+// Shows the create web app shortcut dialog box.
+void ShowCreateShortcutsDialog(gfx::NativeWindow parent_window,
+ TabContents* tab_contents);
+
} // namespace browser
#endif // CHROME_BROWSER_VIEWS_BROWSER_DIALOGS_H_
diff --git a/chrome/browser/views/create_application_shortcut_view.cc b/chrome/browser/views/create_application_shortcut_view.cc
new file mode 100644
index 0000000..16b5631
--- /dev/null
+++ b/chrome/browser/views/create_application_shortcut_view.cc
@@ -0,0 +1,475 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/create_application_shortcut_view.h"
+
+#include "app/gfx/canvas.h"
+#include "app/gfx/codec/png_codec.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "net/base/load_flags.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "views/controls/button/checkbox.h"
+#include "views/controls/image_view.h"
+#include "views/controls/label.h"
+#include "views/grid_layout.h"
+#include "views/standard_layout.h"
+#include "views/window/window.h"
+
+#if defined(OS_WIN)
+#include "base/win_util.h"
+#endif // defined(OS_WIN)
+
+namespace {
+
+const int kAppIconSize = 32;
+
+bool IconPrecedes(
+ const webkit_glue::WebApplicationInfo::IconInfo& left,
+ const webkit_glue::WebApplicationInfo::IconInfo& right) {
+ return left.width < right.width;
+}
+
+// AppInfoView shows the application icon and title.
+class AppInfoView : public views::View {
+ public:
+ AppInfoView(const string16& title,
+ const string16& description,
+ const SkBitmap& icon);
+
+ // Updates the title/description of the web app.
+ void UpdateText(const string16& title, const string16& description);
+
+ // Updates the icon of the web app.
+ void UpdateIcon(const SkBitmap& new_icon);
+
+ // Overridden from views::View:
+ virtual void Paint(gfx::Canvas* canvas);
+
+ private:
+ // Initializes the controls
+ void Init(const string16& title,
+ const string16& description, const SkBitmap& icon);
+
+ // Creates or updates description label.
+ void PrepareDescriptionLabel(const string16& description);
+
+ // Sets up layout manager.
+ void SetupLayout();
+
+ views::ImageView* icon_;
+ views::Label* title_;
+ views::Label* description_;
+};
+
+AppInfoView::AppInfoView(const string16& title,
+ const string16& description,
+ const SkBitmap& icon)
+ : icon_(NULL),
+ title_(NULL),
+ description_(NULL) {
+ Init(title, description, icon);
+}
+
+void AppInfoView::Init(const string16& title_text,
+ const string16& description_text,
+ const SkBitmap& icon) {
+ icon_ = new views::ImageView();
+ icon_->SetImage(icon);
+ icon_->SetImageSize(gfx::Size(kAppIconSize, kAppIconSize));
+
+ title_ = new views::Label(UTF16ToWide(title_text));
+ title_->SetMultiLine(true);
+ title_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ title_->SetFont(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont).DeriveFont(0, gfx::Font::BOLD));
+
+ if (!description_text.empty()) {
+ PrepareDescriptionLabel(description_text);
+ }
+
+ SetupLayout();
+}
+
+void AppInfoView::PrepareDescriptionLabel(const string16& description) {
+ DCHECK(!description.empty());
+
+ static const size_t kMaxLength = 200;
+ static const wchar_t* const kEllipsis = L" ... ";
+
+ std::wstring text = UTF16ToWide(description);
+ if (text.length() > kMaxLength) {
+ text = text.substr(0, kMaxLength);
+ text += kEllipsis;
+ }
+
+ if (description_) {
+ description_->SetText(text);
+ } else {
+ description_ = new views::Label(text);
+ description_->SetMultiLine(true);
+ description_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ }
+}
+
+void AppInfoView::SetupLayout() {
+ views::GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ static const int kColumnSetId = 0;
+ views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
+ column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING,
+ 20.0f, views::GridLayout::FIXED,
+ kAppIconSize, kAppIconSize);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
+ 80.0f, views::GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, kColumnSetId);
+ layout->AddView(icon_, 1, description_ ? 2 : 1);
+ layout->AddView(title_);
+
+ if (description_) {
+ layout->StartRow(0, kColumnSetId);
+ layout->SkipColumns(1);
+ layout->AddView(description_);
+ }
+}
+
+void AppInfoView::UpdateText(const string16& title,
+ const string16& description) {
+ title_->SetText(UTF16ToWide(title));
+ PrepareDescriptionLabel(description);
+
+ SetupLayout();
+}
+
+void AppInfoView::UpdateIcon(const SkBitmap& new_icon) {
+ DCHECK(icon_ != NULL);
+
+ icon_->SetImage(new_icon);
+}
+
+void AppInfoView::Paint(gfx::Canvas* canvas) {
+ gfx::Rect bounds = GetLocalBounds(true);
+
+ SkRect border_rect = {
+ SkIntToScalar(bounds.x()),
+ SkIntToScalar(bounds.y()),
+ SkIntToScalar(bounds.right()),
+ SkIntToScalar(bounds.bottom())
+ };
+
+ SkPaint border_paint;
+ border_paint.setAntiAlias(true);
+ border_paint.setARGB(0xFF, 0xC8, 0xC8, 0xC8);
+
+ canvas->drawRoundRect(border_rect, SkIntToScalar(2), SkIntToScalar(2),
+ border_paint);
+
+ SkRect inner_rect = {
+ border_rect.fLeft + SkDoubleToScalar(0.5),
+ border_rect.fTop + SkDoubleToScalar(0.5),
+ border_rect.fRight - SkDoubleToScalar(0.5),
+ border_rect.fBottom - SkDoubleToScalar(0.5),
+ };
+
+ SkPaint inner_paint;
+ inner_paint.setAntiAlias(true);
+ inner_paint.setARGB(0xFF, 0xF8, 0xF8, 0xF8);
+ canvas->drawRoundRect(inner_rect, SkIntToScalar(1.5), SkIntToScalar(1.5),
+ inner_paint);
+}
+
+}; // namespace
+
+namespace browser {
+
+void ShowCreateShortcutsDialog(gfx::NativeWindow parent_window,
+ TabContents* tab_contents) {
+ views::Window::CreateChromeWindow(parent_window, gfx::Rect(),
+ new CreateApplicationShortcutView(tab_contents))->Show();
+}
+
+}; // namespace browser
+
+CreateApplicationShortcutView::CreateApplicationShortcutView(
+ TabContents* tab_contents)
+ : tab_contents_(tab_contents) {
+ Init();
+}
+
+CreateApplicationShortcutView::~CreateApplicationShortcutView() {
+}
+
+void CreateApplicationShortcutView::Init() {
+ // Prepare data
+ const webkit_glue::WebApplicationInfo& app_info =
+ tab_contents_->web_app_info();
+
+ url_ = app_info.app_url.is_empty() ? tab_contents_->GetURL() :
+ app_info.app_url;
+ title_ = app_info.title.empty() ? tab_contents_->GetTitle() :
+ app_info.title;
+ description_ = app_info.description;
+
+ icon_ = tab_contents_->GetFavIcon();
+ if (!app_info.icons.empty()) {
+ SetIconsInfo(app_info.icons);
+ FetchIcon();
+ }
+
+ if (title_.empty())
+ title_ = UTF8ToUTF16(url_.spec());
+
+ // Create controls
+ app_info_ = new AppInfoView(title_, description_, icon_);
+ create_shortcuts_label_ = new views::Label(
+ l10n_util::GetString(IDS_CREATE_SHORTCUTS_LABEL));
+ create_shortcuts_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+
+ Profile* profile = tab_contents_->profile();
+
+ desktop_check_box_ = AddCheckbox(
+ l10n_util::GetString(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX),
+ profile->GetPrefs()->GetBoolean(prefs::kWebAppCreateOnDesktop));
+
+ menu_check_box_ = NULL;
+ quick_launch_check_box_ = NULL;
+
+#if defined(OS_WIN)
+ menu_check_box_ = AddCheckbox(
+ l10n_util::GetString(IDS_CREATE_SHORTCUTS_START_MENU_CHKBOX),
+ profile->GetPrefs()->GetBoolean(prefs::kWebAppCreateInAppsMenu));
+
+ quick_launch_check_box_ = AddCheckbox(
+ (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) ?
+ l10n_util::GetString(IDS_PIN_TO_TASKBAR_CHKBOX) :
+ l10n_util::GetString(IDS_CREATE_SHORTCUTS_QUICK_LAUNCH_BAR_CHKBOX),
+ profile->GetPrefs()->GetBoolean(prefs::kWebAppCreateInQuickLaunchBar));
+#elif defined(OS_LINUX)
+ menu_check_box_ = AddCheckbox(
+ l10n_util::GetString(IDS_CREATE_SHORTCUTS_MENU_CHKBOX),
+ profile->GetPrefs()->GetBoolean(prefs::kWebAppCreateInAppsMenu));
+#endif
+
+ // Layout controls
+ views::GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ static const int kHeaderColumnSetId = 0;
+ views::ColumnSet* column_set = layout->AddColumnSet(kHeaderColumnSetId);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
+ 100.0f, views::GridLayout::FIXED, 0, 0);
+
+ static const int kTableColumnSetId = 1;
+ column_set = layout->AddColumnSet(kTableColumnSetId);
+ column_set->AddPaddingColumn(5.0f, 10);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 100.0f, views::GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, kHeaderColumnSetId);
+ layout->AddView(app_info_);
+
+ layout->AddPaddingRow(0, kPanelSubVerticalSpacing);
+ layout->StartRow(0, kHeaderColumnSetId);
+ layout->AddView(create_shortcuts_label_);
+
+ layout->AddPaddingRow(0, kLabelToControlVerticalSpacing);
+ layout->StartRow(0, kTableColumnSetId);
+ layout->AddView(desktop_check_box_);
+
+ if (menu_check_box_ != NULL) {
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, kTableColumnSetId);
+ layout->AddView(menu_check_box_);
+ }
+
+ if (quick_launch_check_box_ != NULL) {
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, kTableColumnSetId);
+ layout->AddView(quick_launch_check_box_);
+ }
+}
+
+gfx::Size CreateApplicationShortcutView::GetPreferredSize() {
+ static const int kDialogWidth = 360;
+ int height = GetLayoutManager()->GetPreferredHeightForWidth(this,
+ kDialogWidth);
+ return gfx::Size(kDialogWidth, height);
+}
+
+std::wstring CreateApplicationShortcutView::GetDialogButtonLabel(
+ MessageBoxFlags::DialogButton button) const {
+ if (button == MessageBoxFlags::DIALOGBUTTON_OK) {
+ return l10n_util::GetString(IDS_CREATE_SHORTCUTS_COMMIT);
+ }
+
+ return std::wstring();
+}
+
+bool CreateApplicationShortcutView::IsDialogButtonEnabled(
+ MessageBoxFlags::DialogButton button) const {
+ if (button == MessageBoxFlags::DIALOGBUTTON_OK)
+ return desktop_check_box_->checked() ||
+ ((menu_check_box_ != NULL) &&
+ menu_check_box_->checked()) ||
+ ((quick_launch_check_box_ != NULL) &&
+ quick_launch_check_box_->checked());
+
+ return true;
+}
+
+bool CreateApplicationShortcutView::CanResize() const {
+ return false;
+}
+
+bool CreateApplicationShortcutView::CanMaximize() const {
+ return false;
+}
+
+bool CreateApplicationShortcutView::IsAlwaysOnTop() const {
+ return false;
+}
+
+bool CreateApplicationShortcutView::HasAlwaysOnTopMenu() const {
+ return false;
+}
+
+bool CreateApplicationShortcutView::IsModal() const {
+ return true;
+}
+
+std::wstring CreateApplicationShortcutView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_CREATE_SHORTCUTS_TITLE);
+}
+
+bool CreateApplicationShortcutView::Accept() {
+ if (!IsDialogButtonEnabled(MessageBoxFlags::DIALOGBUTTON_OK)) {
+ return false;
+ }
+
+ ShellIntegration::ShortcutInfo shortcut_info;
+ shortcut_info.url = url_;
+ shortcut_info.title = title_;
+ shortcut_info.description = description_;
+ shortcut_info.favicon = icon_;
+ shortcut_info.create_on_desktop = desktop_check_box_->checked();
+ shortcut_info.create_in_applications_menu = menu_check_box_ == NULL ? false :
+ menu_check_box_->checked();
+
+#if defined(OS_WIN)
+ shortcut_info.create_in_quick_launch_bar = quick_launch_check_box_ == NULL ?
+ NULL : quick_launch_check_box_->checked();
+#elif defined(OS_POSIX)
+ // Create shortcut in Mac dock or as Linux (gnome/kde) application launcher
+ // are not implemented yet.
+ shortcut_info.create_in_quick_launch_bar = false;
+#endif
+
+ web_app::CreateShortcut(
+ tab_contents_->profile()->GetPath().Append(chrome::kWebAppDirname),
+ shortcut_info, NULL);
+
+ if (tab_contents_->delegate())
+ tab_contents_->delegate()->ConvertContentsToApplication(tab_contents_);
+ return true;
+}
+
+views::View* CreateApplicationShortcutView::GetContentsView() {
+ return this;
+}
+
+views::Checkbox* CreateApplicationShortcutView::AddCheckbox(
+ const std::wstring& text, bool checked) {
+ views::Checkbox* checkbox = new views::Checkbox(text);
+ checkbox->SetChecked(checked);
+ checkbox->set_listener(this);
+ return checkbox;
+}
+
+void CreateApplicationShortcutView::SetIconsInfo(const IconInfoList& icons) {
+ unprocessed_icons_.clear();
+ for (size_t i = 0; i < icons.size(); ++i) {
+ // We only take square shaped icons (i.e. width == height).
+ if (icons[i].width == icons[i].height) {
+ unprocessed_icons_.push_back(icons[i]);
+ }
+ }
+
+ std::sort(unprocessed_icons_.begin(), unprocessed_icons_.end(),
+ &IconPrecedes);
+}
+
+void CreateApplicationShortcutView::FetchIcon() {
+ // There should only be fetch job at a time.
+ DCHECK(icon_fetcher_ == NULL);
+
+ if (unprocessed_icons_.empty()) {
+ // No icons to fetch.
+ return;
+ }
+
+ icon_fetcher_.reset(new URLFetcher(unprocessed_icons_.back().url,
+ URLFetcher::GET,
+ this));
+ DCHECK(icon_fetcher_.get() != NULL);
+ unprocessed_icons_.pop_back();
+
+ icon_fetcher_->set_load_flags(icon_fetcher_->load_flags() |
+ net::LOAD_IS_DOWNLOAD);
+ icon_fetcher_->set_request_context(
+ tab_contents_->profile()->GetRequestContext());
+ icon_fetcher_->Start();
+}
+
+void CreateApplicationShortcutView::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ Profile* profile = tab_contents_->profile();
+ if (sender == desktop_check_box_)
+ profile->GetPrefs()->SetBoolean(prefs::kWebAppCreateOnDesktop,
+ desktop_check_box_->checked() ? true : false);
+ else if (sender == menu_check_box_)
+ profile->GetPrefs()->SetBoolean(prefs::kWebAppCreateInAppsMenu,
+ menu_check_box_->checked() ? true : false);
+ else if (sender == quick_launch_check_box_)
+ profile->GetPrefs()->SetBoolean(prefs::kWebAppCreateInQuickLaunchBar,
+ quick_launch_check_box_->checked() ? true : false);
+
+ // When no checkbox is checked we should not have the action button enabled.
+ GetDialogClientView()->UpdateDialogButtons();
+}
+
+void CreateApplicationShortcutView::OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ // Delete the fetcher on this function's exit.
+ scoped_ptr<URLFetcher> clean_up_fetcher(icon_fetcher_.release());
+
+ bool success = status.is_success() && (response_code == 200) && !data.empty();
+
+ if (success) {
+ success = gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(data.c_str()),
+ data.size(),
+ &icon_);
+
+ if (success)
+ static_cast<AppInfoView*>(app_info_)->UpdateIcon(icon_);
+ }
+
+ if (!success)
+ FetchIcon();
+}
diff --git a/chrome/browser/views/create_application_shortcut_view.h b/chrome/browser/views/create_application_shortcut_view.h
new file mode 100644
index 0000000..28972ca
--- /dev/null
+++ b/chrome/browser/views/create_application_shortcut_view.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VIEWS_CREATE_APPLICATION_SHORTCUT_VIEW_H_
+#define CHROME_BROWSER_VIEWS_CREATE_APPLICATION_SHORTCUT_VIEW_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/net/url_fetcher.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "views/controls/label.h"
+#include "views/view.h"
+#include "views/window/dialog_delegate.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "webkit/glue/dom_operations.h"
+
+namespace views {
+class Checkbox;
+class Label;
+class Window;
+}; // namespace views
+
+class MessageLoop;
+class Profile;
+class TabContents;
+
+// CreateShortcutView implements a dialog that asks user where to create
+// the shortcut for given web app.
+class CreateApplicationShortcutView : public views::View,
+ public views::DialogDelegate,
+ public views::ButtonListener,
+ public URLFetcher::Delegate {
+ public:
+ explicit CreateApplicationShortcutView(TabContents* tab_contents);
+ virtual ~CreateApplicationShortcutView();
+
+ // Initialize the controls on the dialog.
+ void Init();
+
+ // Overridden from views::View:
+ virtual gfx::Size GetPreferredSize();
+
+ // Overridden from views::DialogDelegate:
+ virtual std::wstring GetDialogButtonLabel(
+ MessageBoxFlags::DialogButton button) const;
+ virtual bool IsDialogButtonEnabled(
+ MessageBoxFlags::DialogButton button) const;
+ virtual bool CanResize() const;
+ virtual bool CanMaximize() const;
+ virtual bool IsAlwaysOnTop() const;
+ virtual bool HasAlwaysOnTopMenu() const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+ virtual views::View* GetContentsView();
+
+ // Overridden from views::ButtonListener:
+ virtual void ButtonPressed(views::Button* sender, const views::Event& event);
+
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+
+ private:
+ typedef std::vector<webkit_glue::WebApplicationInfo::IconInfo> IconInfoList;
+
+ // Adds a new check-box as a child to the view.
+ views::Checkbox* AddCheckbox(const std::wstring& text, bool checked);
+
+ // Set icons info from passed-in WebApplicationInfo.
+ void SetIconsInfo(const IconInfoList& icons);
+
+ // Fetch the largest unprocessed icon.
+ // The first largest icon downloaded and decoded successfully will be used.
+ void FetchIcon();
+
+ // TabContents of the page that we want to create shortcut.
+ TabContents* tab_contents_;
+
+ // UI elements on the dialog.
+ views::View* app_info_;
+ views::Label* create_shortcuts_label_;
+ views::Checkbox* desktop_check_box_;
+ views::Checkbox* menu_check_box_;
+ views::Checkbox* quick_launch_check_box_;
+
+ // Target url of the app shortcut.
+ GURL url_;
+
+ // Title of the app shortcut.
+ string16 title_;
+
+ // Description of the app shortcut.
+ string16 description_;
+
+ // Icon of the app shortcut.
+ SkBitmap icon_;
+
+ // Unprocessed icons from the WebApplicationInfo passed in.
+ IconInfoList unprocessed_icons_;
+
+ // Icon fetcher.
+ scoped_ptr<URLFetcher> icon_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(CreateApplicationShortcutView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_CREATE_APPLICATION_SHORTCUT_VIEW_H_
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 348e0fb..1a1a888 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -1160,6 +1160,10 @@ void BrowserView::ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
browser::ShowHtmlDialogView(parent, browser_.get(), delegate);
}
+void BrowserView::ShowCreateShortcutsDialog(TabContents* tab_contents) {
+ browser::ShowCreateShortcutsDialog(GetNativeHandle(), tab_contents);
+}
+
void BrowserView::ContinueDraggingDetachedTab(const gfx::Rect& tab_bounds) {
tabstrip_->SetDraggedTabBounds(0, tab_bounds);
frame_->ContinueDraggingDetachedTab();
diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h
index cf4ca78..fcdd20c 100644
--- a/chrome/browser/views/frame/browser_view.h
+++ b/chrome/browser/views/frame/browser_view.h
@@ -274,9 +274,10 @@ class BrowserView : public BrowserWindow,
virtual void ShowAppMenu();
virtual void ShowPageMenu();
virtual int GetCommandId(const NativeWebKeyboardEvent& event);
+ virtual void ShowCreateShortcutsDialog(TabContents* tab_contents);
#if defined(TOOLKIT_VIEWS)
virtual void ToggleCompactNavigationBar();
-#endif
+#endif // defined(TOOLKIT_VIEWS)
// Overridden from BrowserWindowTesting:
virtual BookmarkBarView* GetBookmarkBarView() const;
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
new file mode 100644
index 0000000..1c55c4b
--- /dev/null
+++ b/chrome/browser/web_applications/web_app.cc
@@ -0,0 +1,301 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/web_app.h"
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/thread.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_plugin_util.h"
+
+#if defined(OS_WIN)
+#include "app/gfx/icon_util.h"
+#include "base/win_util.h"
+#endif // defined(OS_WIN)
+
+namespace {
+
+// Returns true if |ch| is in visible ASCII range and not one of
+// "/ \ : * ? " < > | ; ,".
+bool IsValidFilePathChar(char16 c) {
+ if (c < 32)
+ return false;
+
+ switch (c) {
+ case '/':
+ case '\\':
+ case ':':
+ case '*':
+ case '?':
+ case '"':
+ case '<':
+ case '>':
+ case '|':
+ case ';':
+ case ',':
+ return false;
+ };
+
+ return true;
+}
+
+// Returns sanitized name that could be used as a file name
+FilePath GetSanitizedFileName(const string16& name) {
+ string16 file_name;
+
+ for (size_t i = 0; i < name.length(); ++i) {
+ char16 c = name[i];
+ if (!IsValidFilePathChar(c))
+ c = '_';
+
+ file_name += c;
+ }
+
+#if defined(OS_WIN)
+ return FilePath(file_name);
+#elif defined(OS_POSIX)
+ return FilePath(UTF16ToUTF8(file_name));
+#endif
+}
+
+// Returns relative directory of given web app url.
+FilePath GetWebAppDir(const GURL& url) {
+ FilePath::StringType host;
+ FilePath::StringType scheme_port;
+
+#if defined(OS_WIN)
+ host = UTF8ToWide(url.host());
+ scheme_port = (url.has_scheme() ? UTF8ToWide(url.scheme()) : L"http") +
+ FILE_PATH_LITERAL("_") +
+ (url.has_port() ? UTF8ToWide(url.port()) : L"80");
+#elif defined(OS_POSIX)
+ host = url.host();
+ scheme_port = url.scheme() + FILE_PATH_LITERAL("_") + url.port();
+#endif
+
+ return FilePath(host).Append(scheme_port);
+}
+
+// Returns data directory for given web app url
+FilePath GetWebAppDataDirectory(const FilePath& root_dir,
+ const GURL& url) {
+ return root_dir.Append(GetWebAppDir(url));
+}
+
+// Represents a task that creates web application shortcut. This runs on
+// file thread and schedules the callback (if any) on the calling thread
+// when finished (either success or failure).
+class CreateShortcutTask : public Task {
+ public:
+ CreateShortcutTask(const FilePath& root_dir,
+ const ShellIntegration::ShortcutInfo& shortcut_info,
+ web_app::CreateShortcutCallback* callback);
+
+ private:
+ class CreateShortcutCallbackTask : public Task {
+ public:
+ CreateShortcutCallbackTask(web_app::CreateShortcutCallback* callback,
+ bool success)
+ : callback_(callback),
+ success_(success) {
+ }
+
+ // Overridden from Task:
+ virtual void Run() {
+ callback_->Run(success_);
+ }
+
+ private:
+ web_app::CreateShortcutCallback* callback_;
+ bool success_;
+ };
+
+ // Overridden from Task:
+ virtual void Run();
+
+ // Returns true if shortcut is created successfully.
+ bool CreateShortcut();
+
+ // Path to store persisted data for web_app.
+ FilePath web_app_path_;
+
+ // Our copy of short cut data.
+ ShellIntegration::ShortcutInfo shortcut_info_;
+
+ // Callback when task is finished.
+ web_app::CreateShortcutCallback* callback_;
+ MessageLoop* message_loop_;
+};
+
+CreateShortcutTask::CreateShortcutTask(
+ const FilePath& root_dir,
+ const ShellIntegration::ShortcutInfo& shortcut_info,
+ web_app::CreateShortcutCallback* callback)
+ : web_app_path_(GetWebAppDataDirectory(root_dir, shortcut_info.url)),
+ shortcut_info_(shortcut_info),
+ callback_(callback),
+ message_loop_(MessageLoop::current()) {
+ DCHECK(message_loop_ != NULL);
+}
+
+void CreateShortcutTask::Run() {
+ bool success = CreateShortcut();
+
+ if (callback_ != NULL)
+ message_loop_->PostTask(FROM_HERE,
+ new CreateShortcutCallbackTask(callback_, success));
+}
+
+bool CreateShortcutTask::CreateShortcut() {
+#if defined(OS_LINUX)
+ ShellIntegration::CreateDesktopShortcut(shortcut_info_);
+ return true; // assuming always success.
+#elif defined(OS_WIN)
+ // Shortcut paths under which to create shortcuts.
+ std::vector<FilePath> shortcut_paths;
+
+ // Locations to add to shortcut_paths.
+ struct {
+ const bool& use_this_location;
+ int location_id;
+ const wchar_t* sub_dir;
+ } locations[] = {
+ {
+ shortcut_info_.create_on_desktop,
+ chrome::DIR_USER_DESKTOP,
+ NULL
+ }, {
+ shortcut_info_.create_in_applications_menu,
+ base::DIR_START_MENU,
+ NULL
+ }, {
+ shortcut_info_.create_in_quick_launch_bar,
+ // For Win7, create_in_quick_launch_bar means pining to taskbar. Use
+ // base::PATH_START as a flag for this case.
+ (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) ?
+ base::PATH_START : base::DIR_APP_DATA,
+ (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) ?
+ NULL : L"Microsoft\\Internet Explorer\\Quick Launch"
+ }
+ };
+
+ // Populate shortcut_paths.
+ for (int i = 0; i < arraysize(locations); ++i) {
+ if (locations[i].use_this_location) {
+ FilePath path;
+
+ // Skip the Win7 case.
+ if (locations[i].location_id == base::PATH_START)
+ continue;
+
+ if (!PathService::Get(locations[i].location_id, &path)) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (locations[i].sub_dir != NULL)
+ path = path.Append(locations[i].sub_dir);
+
+ shortcut_paths.push_back(path);
+ }
+ }
+
+ bool pin_to_taskbar =
+ shortcut_info_.create_in_quick_launch_bar &&
+ (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7);
+
+ // For Win7's pinning support, any shortcut could be used. So we only create
+ // the shortcut file when there is no shortcut file will be created. That is,
+ // user only selects "Pin to taskbar".
+ if (pin_to_taskbar && shortcut_paths.empty()) {
+ // Creates the shortcut in web_app_path_ in this case.
+ shortcut_paths.push_back(web_app_path_);
+ }
+
+ if (shortcut_paths.empty()) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Ensure web_app_path_ exists.
+ if (!file_util::PathExists(web_app_path_) &&
+ !file_util::CreateDirectory(web_app_path_)) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Generates file name to use with persisted ico and shortcut file.
+ FilePath file_name = GetSanitizedFileName(shortcut_info_.title);
+
+ // Creates an ico file to use with shortcut.
+ FilePath icon_file = web_app_path_.Append(file_name).ReplaceExtension(
+ FILE_PATH_LITERAL(".ico"));
+ if (!IconUtil::CreateIconFileFromSkBitmap(shortcut_info_.favicon,
+ icon_file.value())) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::wstring chrome_exe;
+ if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Working directory.
+ std::wstring chrome_folder = file_util::GetDirectoryFromPath(chrome_exe);
+
+ // Gets the command line switches.
+ std::string switches;
+ if (CPB_GetCommandLineArgumentsCommon(shortcut_info_.url.spec().c_str(),
+ &switches) != CPERR_SUCCESS) {
+ NOTREACHED();
+ return false;
+ }
+ std::wstring wide_switchs(UTF8ToWide(switches));
+
+ bool success = true;
+ for (size_t i = 0; i < shortcut_paths.size(); ++i) {
+ FilePath shortcut_file = shortcut_paths[i].Append(file_name).
+ ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
+ success &= file_util::CreateShortcutLink(chrome_exe.c_str(),
+ shortcut_file.value().c_str(),
+ chrome_folder.c_str(),
+ wide_switchs.c_str(),
+ shortcut_info_.description.c_str(),
+ icon_file.value().c_str(),
+ 0);
+ }
+
+ if (success && pin_to_taskbar) {
+ // Any shortcut would work for the pinning. We use the first one.
+ FilePath shortcut_file = shortcut_paths[0].Append(file_name).
+ ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
+ success &= file_util::TaskbarPinShortcutLink(shortcut_file.value().c_str());
+ }
+
+ return success;
+#else
+ NOTIMPLEMENTED();
+ return false;
+#endif
+}
+
+}; // namespace
+
+namespace web_app {
+
+void CreateShortcut(
+ const FilePath& data_dir,
+ const ShellIntegration::ShortcutInfo& shortcut_info,
+ CreateShortcutCallback* callback) {
+ g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
+ new CreateShortcutTask(data_dir, shortcut_info, callback));
+}
+
+}; // namespace web_app
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
new file mode 100644
index 0000000..3e3c9b0
--- /dev/null
+++ b/chrome/browser/web_applications/web_app.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
+
+#include "base/file_path.h"
+#include "base/task.h"
+#include "chrome/browser/shell_integration.h"
+
+namespace web_app {
+
+// Callback after user dismisses CreateShortcutView. "true" indicates
+// shortcut is created successfully. Otherwise, it is false.
+typedef Callback1<bool>::Type CreateShortcutCallback;
+
+// Creates a shortcut for web application based on given shortcut data.
+// |root_dir| is used as root directory for persisted data such as icon.
+// Directory layout is similar to what Gears has, i.e. an web application's
+// file is stored under "#/host_name/scheme_port", where '#' is the
+// |root_dir|.
+void CreateShortcut(
+ const FilePath& data_dir,
+ const ShellIntegration::ShortcutInfo& shortcut_info,
+ CreateShortcutCallback* callback);
+
+}; // namespace web_app
+
+#endif // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_