diff options
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; } |