summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/file_util.h8
-rw-r--r--base/file_util_win.cc20
-rw-r--r--chrome/app/generated_resources.grd9
-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
-rwxr-xr-xchrome/chrome.gyp6
-rw-r--r--chrome/common/chrome_constants.cc1
-rw-r--r--chrome/common/chrome_constants.h1
-rw-r--r--chrome/common/pref_names.cc7
-rw-r--r--chrome/common/pref_names.h7
-rw-r--r--chrome/test/test_browser_window.h3
27 files changed, 1077 insertions, 137 deletions
diff --git a/base/file_util.h b/base/file_util.h
index 782a6a2..c86e6e4 100644
--- a/base/file_util.h
+++ b/base/file_util.h
@@ -237,6 +237,14 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
const wchar_t *description, const wchar_t *icon,
int icon_index);
+// Pins a shortcut to the Windows 7 taskbar. The shortcut file must already
+// exist and be a shortcut that points to an executable.
+bool TaskbarPinShortcutLink(const wchar_t* shortcut);
+
+// Unpins a shortcut from the Windows 7 taskbar. The shortcut must exist and
+// already be pinned to the taskbar.
+bool TaskbarUnpinShortcutLink(const wchar_t* shortcut);
+
// Return true if the given directory is empty
bool IsDirectoryEmpty(const std::wstring& dir_path);
diff --git a/base/file_util_win.cc b/base/file_util_win.cc
index 3105d37..e050324 100644
--- a/base/file_util_win.cc
+++ b/base/file_util_win.cc
@@ -413,6 +413,26 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
return SUCCEEDED(result);
}
+bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
+ // "Pin to taskbar" is only supported after Win7.
+ if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
+ return false;
+
+ int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut,
+ NULL, NULL, 0));
+ return result > 32;
+}
+
+bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
+ // "Unpin from taskbar" is only supported after Win7.
+ if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
+ return false;
+
+ int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
+ shortcut, NULL, NULL, 0));
+ return result > 32;
+}
+
bool IsDirectoryEmpty(const std::wstring& dir_path) {
FileEnumerator files(FilePath(dir_path), false,
static_cast<FileEnumerator::FILE_TYPE>(
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f41512f..9fe14a0 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2251,6 +2251,15 @@ each locale. -->
<message name="IDS_CREATE_SHORTCUTS_MENU_CHKBOX" desc="Label of the checkbox to create an application shortcut in the system's applications menu.">
Applications menu
</message>
+ <message name="IDS_CREATE_SHORTCUTS_START_MENU_CHKBOX" desc="Label of the checkbox to create an application shortcut in the start menu.">
+ Start menu
+ </message>
+ <message name="IDS_CREATE_SHORTCUTS_QUICK_LAUNCH_BAR_CHKBOX" desc="Label of the checkbox to create an application shortcut in quick launch bar.">
+ Quick launch bar
+ </message>
+ <message name="IDS_PIN_TO_TASKBAR_CHKBOX" desc="Label of the checkbox to pin an application shortcut to taskbar.">
+ Pin to Taskbar
+ </message>
<message name="IDS_CREATE_SHORTCUTS_COMMIT" desc="Title of the button to actually create the shortcuts.">
Create
</message>
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_
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 4b3b096..e3fb15e 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -2292,6 +2292,8 @@
'browser/views/constrained_window_win.h',
'browser/views/confirm_message_box_dialog.cc',
'browser/views/confirm_message_box_dialog.h',
+ 'browser/views/create_application_shortcut_view.cc',
+ 'browser/views/create_application_shortcut_view.h',
'browser/views/detachable_toolbar_view.cc',
'browser/views/detachable_toolbar_view.h',
'browser/views/dialog_stubs_gtk.cc',
@@ -2508,6 +2510,8 @@
'browser/webdata/web_database.cc',
'browser/webdata/web_database.h',
'browser/webdata/web_database_win.cc',
+ 'browser/web_applications/web_app.cc',
+ 'browser/web_applications/web_app.h',
'browser/web_resource/web_resource_service.h',
'browser/web_resource/web_resource_service.cc',
'browser/window_sizer.cc',
@@ -2830,6 +2834,8 @@
['include', '^browser/views/chrome_views_delegate.cc'],
['include', '^browser/views/clear_browsing_data.cc'],
['include', '^browser/views/clear_browsing_data.h'],
+ ['include', '^browser/views/create_application_shortcut_view.cc'],
+ ['include', '^browser/views/create_application_shortcut_view.h'],
['include', '^browser/views/detachable_toolbar_view.h'],
['include', '^browser/views/detachable_toolbar_view.cc'],
['include', '^browser/views/dialog_stubs_gtk.cc'],
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 6a6ad28..1226fe5 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -97,6 +97,7 @@ const FilePath::CharType kCustomDictionaryFileName[] =
FPL("Custom Dictionary.txt");
const FilePath::CharType kLoginDataFileName[] = FPL("Login Data");
const FilePath::CharType kJumpListIconDirname[] = FPL("JumpListIcons");
+const FilePath::CharType kWebAppDirname[] = FPL("Web Applications");
// This number used to be limited to 32 in the past (see b/535234).
const unsigned int kMaxRendererProcessCount = 42;
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index 12da2f6..946767f 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -57,6 +57,7 @@ extern const FilePath::CharType kHistoryBookmarksFileName[];
extern const FilePath::CharType kCustomDictionaryFileName[];
extern const FilePath::CharType kLoginDataFileName[];
extern const FilePath::CharType kJumpListIconDirname[];
+extern const FilePath::CharType kWebAppDirname[];
extern const unsigned int kMaxRendererProcessCount;
extern const int kStatsMaxThreads;
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index db3ec5c..7041d9b 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -590,4 +590,11 @@ const wchar_t kSyncLastSyncedTime[] = L"sync.last_synced_time";
// Boolean specifying whether the user finished setting up sync.
const wchar_t kSyncHasSetupCompleted[] = L"sync.has_setup_completed";
+// Create web application shortcut dialog preferences.
+const wchar_t kWebAppCreateOnDesktop[] = L"browser.web_app.create_on_desktop";
+const wchar_t kWebAppCreateInAppsMenu[] =
+ L"browser.web_app.create_in_apps_menu";
+const wchar_t kWebAppCreateInQuickLaunchBar[] =
+ L"browser.web_app.create_in_quick_launch_bar";
+
} // namespace prefs
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index d809e5e..f821180 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -225,6 +225,11 @@ extern const wchar_t kDevToolsSplitLocation[];
extern const wchar_t kSyncLastSyncedTime[];
extern const wchar_t kSyncHasSetupCompleted[];
-}
+
+extern const wchar_t kWebAppCreateOnDesktop[];
+extern const wchar_t kWebAppCreateInAppsMenu[];
+extern const wchar_t kWebAppCreateInQuickLaunchBar[];
+
+} // namespace prefs
#endif // CHROME_COMMON_PREF_NAMES_H_
diff --git a/chrome/test/test_browser_window.h b/chrome/test/test_browser_window.h
index e4cb589..af73777 100644
--- a/chrome/test/test_browser_window.h
+++ b/chrome/test/test_browser_window.h
@@ -51,9 +51,10 @@ class TestBrowserWindow : public BrowserWindow {
virtual void ShowPageMenu() {}
virtual void ShowAppMenu() {}
virtual int GetCommandId(const NativeWebKeyboardEvent& event) { return -1; }
+ virtual void ShowCreateShortcutsDialog(TabContents* tab_contents) {}
#if defined(TOOLKIT_VIEWS)
virtual void ToggleCompactNavigationBar() {}
-#endif
+#endif // defined(TOOLKIT_VIEWS)
virtual bool IsBookmarkBarVisible() const { return false; }
virtual bool IsToolbarVisible() const { return false; }