diff options
author | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 23:58:34 +0000 |
---|---|---|
committer | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 23:58:34 +0000 |
commit | bfc815fb1b153c83822a60cce82792caaea80231 (patch) | |
tree | 7c47150d804d718753349ea0c21a02e8a300c685 | |
parent | 3f21aecc705b7cd278794d86d338b9c5086be619 (diff) | |
download | chromium_src-bfc815fb1b153c83822a60cce82792caaea80231.zip chromium_src-bfc815fb1b153c83822a60cce82792caaea80231.tar.gz chromium_src-bfc815fb1b153c83822a60cce82792caaea80231.tar.bz2 |
Port notifications to linux. BalloonView and BalloonViewHost should be equivalent to their counterparts under browser/views/notifications; CL also enables the exposure of notifications in WebKit on linux platform.
BUG=23954
TEST=none
Review URL: http://codereview.chromium.org/525050
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36434 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/gtk/notifications/balloon_view_gtk.cc | 374 | ||||
-rw-r--r-- | chrome/browser/gtk/notifications/balloon_view_gtk.h | 156 | ||||
-rw-r--r-- | chrome/browser/gtk/notifications/balloon_view_host_gtk.cc | 143 | ||||
-rw-r--r-- | chrome/browser/gtk/notifications/balloon_view_host_gtk.h | 133 | ||||
-rw-r--r-- | chrome/browser/gtk/notifications/notification_options_menu_model.cc | 85 | ||||
-rw-r--r-- | chrome/browser/gtk/notifications/notification_options_menu_model.h | 42 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 6 | ||||
-rw-r--r-- | chrome/renderer/render_thread.cc | 4 |
8 files changed, 941 insertions, 2 deletions
diff --git a/chrome/browser/gtk/notifications/balloon_view_gtk.cc b/chrome/browser/gtk/notifications/balloon_view_gtk.cc new file mode 100644 index 0000000..fb2d436 --- /dev/null +++ b/chrome/browser/gtk/notifications/balloon_view_gtk.cc @@ -0,0 +1,374 @@ +// Copyright (c) 2010 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/gtk/notifications/balloon_view_gtk.h" + +#include <string> +#include <vector> + +#include "app/gfx/canvas.h" +#include "app/gfx/insets.h" +#include "app/gfx/native_widget_types.h" +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "app/slide_animation.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/browser/browser_theme_provider.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/gtk/gtk_chrome_button.h" +#include "chrome/browser/gtk/gtk_theme_provider.h" +#include "chrome/browser/gtk/info_bubble_gtk.h" +#include "chrome/browser/gtk/menu_gtk.h" +#include "chrome/browser/gtk/nine_box.h" +#include "chrome/browser/gtk/notifications/balloon_view_host_gtk.h" +#include "chrome/browser/gtk/notifications/notification_options_menu_model.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/desktop_notification_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/gtk_util.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +namespace { + +// Margin, in pixels, between the notification frame and the contents +// of the notification. +const int kTopMargin = 1; +const int kBottomMargin = 1; +const int kLeftMargin = 1; +const int kRightMargin = 1; + +// How many pixels of overlap there is between the shelf top and the +// balloon bottom. +const int kShelfBorderTopOverlap = 3; + +// Properties of the dismiss button. +const int kDismissButtonWidth = 60; +const int kDismissButtonHeight = 20; + +// Properties of the options menu. +const int kOptionsMenuWidth = 60; +const int kOptionsMenuHeight = 20; + +// Properties of the origin label. +const int kLeftLabelMargin = 5; + +// TODO(johnnyg): Add a shadow for the frame. +const int kLeftShadowWidth = 0; +const int kRightShadowWidth = 0; +const int kTopShadowWidth = 0; +const int kBottomShadowWidth = 0; + +// Space in pixels between text and icon on the buttons. +const int kButtonIconSpacing = 3; + +// Number of characters to show in the origin label before ellipsis. +const int kOriginLabelCharacters = 18; + +// The shelf height for the system default font size. It is scaled +// with changes in the default font size. +const int kDefaultShelfHeight = 24; + +} // namespace + +BalloonViewImpl::BalloonViewImpl() + : balloon_(NULL), + frame_container_(NULL), + html_container_(NULL), + html_contents_(NULL), + method_factory_(this), + close_button_(NULL), + animation_(NULL) { + // Load the sprites for the frames. + // Insets are such because the sprites have 3x3 corners. + shelf_background_.reset(new NineBox(IDR_BALLOON_SHELF, 3, 3, 3, 3)); + balloon_background_.reset(new NineBox(IDR_BALLOON_BORDER, 3, 3, 3, 3)); +} + +BalloonViewImpl::~BalloonViewImpl() { +} + +void BalloonViewImpl::Close(bool by_user) { + MessageLoop::current()->PostTask(FROM_HERE, + method_factory_.NewRunnableMethod( + &BalloonViewImpl::DelayedClose, by_user)); +} + +gfx::Size BalloonViewImpl::GetSize() const { + // BalloonView has no size if it hasn't been shown yet (which is when + // balloon_ is set). + if (!balloon_) + return gfx::Size(); + + // Although this may not be the instantaneous size of the balloon if + // called in the middle of an animation, it is the effective size that + // will result from the animation. + return gfx::Size(GetDesiredTotalWidth(), GetDesiredTotalHeight()); +} + +void BalloonViewImpl::DelayedClose(bool by_user) { + html_contents_->Shutdown(); + gtk_widget_hide(frame_container_); + balloon_->OnClose(by_user); +} + +void BalloonViewImpl::InitToolbarStyle() { + // This only needs to happen once. + static bool initialized = false; + if (!initialized) { + gtk_rc_parse_string( + "style \"chrome-notification-toolbar\" {" + " xthickness = 0\n" + " ythickness = 0\n" + " GtkWidget::focus-padding = 0\n" + " GtkContainer::border-width = 0\n" + " GtkToolBar::internal-padding = 2\n" + " GtkToolBar::shadow-type = GTK_SHADOW_NONE\n" + "}\n" + "widget \"*chrome-notification-toolbar\"" + "style \"chrome-notification-toolbar\""); + initialized = true; + } +} + +void BalloonViewImpl::RepositionToBalloon() { + DCHECK(frame_container_); + DCHECK(balloon_); + + // Create an amination from the current position to the desired one. + int start_x; + int start_y; + int start_w; + int start_h; + gtk_window_get_position(GTK_WINDOW(frame_container_), &start_x, &start_y); + gtk_window_get_size(GTK_WINDOW(frame_container_), &start_w, &start_h); + + int end_x = balloon_->position().x(); + int end_y = balloon_->position().y(); + int end_w = GetDesiredTotalWidth(); + int end_h = GetDesiredTotalHeight(); + + anim_frame_start_ = gfx::Rect(start_x, start_y, start_w, start_h); + anim_frame_end_ = gfx::Rect(end_x, end_y, end_w, end_h); + animation_.reset(new SlideAnimation(this)); + animation_->Show(); +} + +void BalloonViewImpl::AnimationProgressed(const Animation* animation) { + DCHECK_EQ(animation, animation_.get()); + + // Linear interpolation from start to end position. + double end = animation->GetCurrentValue(); + double start = 1.0 - end; + + gfx::Rect frame_position( + static_cast<int>(start * anim_frame_start_.x() + + end * anim_frame_end_.x()), + static_cast<int>(start * anim_frame_start_.y() + + end * anim_frame_end_.y()), + static_cast<int>(start * anim_frame_start_.width() + + end * anim_frame_end_.width()), + static_cast<int>(start * anim_frame_start_.height() + + end * anim_frame_end_.height())); + gtk_window_resize(GTK_WINDOW(frame_container_), + frame_position.width(), frame_position.height()); + gtk_window_move(GTK_WINDOW(frame_container_), + frame_position.x(), frame_position.y()); + + gfx::Rect contents_rect = GetContentsRectangle(); + html_contents_->UpdateActualSize(contents_rect.size()); +} + +void PrepareButtonWithIcon(GtkWidget* button, + const std::string& text_utf8, int icon_id) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + GdkPixbuf* pixbuf = rb.GetPixbufNamed(icon_id); + GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + + GtkWidget* box = gtk_hbox_new(FALSE, kButtonIconSpacing); + + GtkWidget* label = gtk_label_new(text_utf8.c_str()); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0); + + GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 1, 1, 1, 1); + gtk_container_add(GTK_CONTAINER(alignment), box); + gtk_container_add(GTK_CONTAINER(button), alignment); + + gtk_widget_show_all(alignment); +} + +void BalloonViewImpl::Show(Balloon* balloon) { + GtkThemeProvider* theme_provider = GtkThemeProvider::GetFrom( + balloon->profile()); + + const std::string source_label_text = l10n_util::GetStringFUTF8( + IDS_NOTIFICATION_BALLOON_SOURCE_LABEL, + WideToUTF16(balloon->notification().display_source())); + const std::string options_text = + l10n_util::GetStringUTF8(IDS_NOTIFICATION_OPTIONS_MENU_LABEL); + const std::string dismiss_text = + l10n_util::GetStringUTF8(IDS_NOTIFICATION_BALLOON_DISMISS_LABEL); + + balloon_ = balloon; + frame_container_ = gtk_window_new(GTK_WINDOW_POPUP); + + // Construct the options menu. + options_menu_model_.reset(new NotificationOptionsMenuModel(balloon_)); + options_menu_.reset(new MenuGtk(this, options_menu_model_.get())); + + // Create a BalloonViewHost to host the HTML contents of this balloon. + html_contents_ = new BalloonViewHost(balloon); + html_contents_->Init(); + gfx::NativeView contents = html_contents_->native_view(); + + gtk_widget_set_app_paintable(frame_container_, TRUE); + gtk_widget_realize(frame_container_); + + // Divide the frame vertically into the content area and the shelf. + GtkWidget* vbox = gtk_vbox_new(0, 0); + gtk_container_add(GTK_CONTAINER(frame_container_), vbox); + + GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding( + GTK_ALIGNMENT(alignment), + kTopMargin, kBottomMargin, kLeftMargin, kRightMargin); + gtk_widget_show_all(alignment); + gtk_container_add(GTK_CONTAINER(alignment), contents); + gtk_container_add(GTK_CONTAINER(vbox), alignment); + + shelf_ = gtk_hbox_new(0, 0); + GtkWidget* alignment2 = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(alignment2), 0, 0, 10, 0); + gtk_container_add(GTK_CONTAINER(vbox), shelf_); + + // Create a toolbar and add it to the shelf. + toolbar_ = gtk_toolbar_new(); + gtk_widget_set_name(toolbar_, "chrome-notification-toolbar"); + gtk_util::SuppressDefaultPainting(toolbar_); + gtk_widget_set_size_request(GTK_WIDGET(toolbar_), -1, GetShelfHeight()); + gtk_container_add(GTK_CONTAINER(alignment2), toolbar_); + gtk_container_add(GTK_CONTAINER(shelf_), alignment2); + gtk_widget_show_all(vbox); + + InitToolbarStyle(); + + g_signal_connect(frame_container_, "expose-event", + G_CALLBACK(HandleExposeThunk), this); + + // Create a label for the source of the notification and add it to the + // toolbar. + GtkWidget* source_label_ = gtk_label_new(source_label_text.c_str()); + gtk_label_set_max_width_chars(GTK_LABEL(source_label_), + kOriginLabelCharacters); + gtk_label_set_ellipsize(GTK_LABEL(source_label_), PANGO_ELLIPSIZE_END); + GtkToolItem* label_toolitem = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(label_toolitem), source_label_); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar_), label_toolitem, 0); + gtk_widget_show_all(GTK_WIDGET(label_toolitem)); + + // Create a button for showing the options menu, and add it to the toolbar. + options_menu_button_ = theme_provider->BuildChromeButton(); + g_signal_connect(G_OBJECT(options_menu_button_), "clicked", + G_CALLBACK(HandleOptionsMenuButtonThunk), this); + PrepareButtonWithIcon(options_menu_button_, options_text, + IDR_BALLOON_OPTIONS_ARROW_HOVER); + GtkToolItem* options_menu_toolitem = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(options_menu_toolitem), options_menu_button_); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar_), options_menu_toolitem, 1); + gtk_widget_show_all(GTK_WIDGET(options_menu_toolitem)); + + // Create a button to dismiss the balloon and add it to the toolbar. + close_button_ = theme_provider->BuildChromeButton(); + g_signal_connect(G_OBJECT(close_button_), "clicked", + G_CALLBACK(HandleCloseButtonThunk), this); + PrepareButtonWithIcon(close_button_, dismiss_text, IDR_BALLOON_CLOSE_HOVER); + GtkToolItem* close_button_toolitem = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(close_button_toolitem), close_button_); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar_), close_button_toolitem, 2); + gtk_widget_show_all(GTK_WIDGET(close_button_toolitem)); + + // Position the view elements according to the balloon position and show. + RepositionToBalloon(); + gtk_widget_show(frame_container_); + + notification_registrar_.Add(this, + NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon)); +} + +void BalloonViewImpl::RunOptionsMenu() { + options_menu_->PopupAsContext(gtk_get_current_event_time()); +} + +gfx::Point BalloonViewImpl::GetContentsOffset() const { + return gfx::Point(kTopShadowWidth + kTopMargin, + kLeftShadowWidth + kLeftMargin); +} + +int BalloonViewImpl::GetShelfHeight() const { + // TODO(johnnyg): add scaling here. + return kDefaultShelfHeight; +} + +int BalloonViewImpl::GetBalloonFrameHeight() const { + return GetDesiredTotalHeight() - GetShelfHeight(); +} + +int BalloonViewImpl::GetDesiredTotalWidth() const { + return balloon_->content_size().width() + + kLeftMargin + kRightMargin + kLeftShadowWidth + kRightShadowWidth; +} + +int BalloonViewImpl::GetDesiredTotalHeight() const { + return balloon_->content_size().height() + + kTopMargin + kBottomMargin + kTopShadowWidth + kBottomShadowWidth + + GetShelfHeight(); +} + +gfx::Rect BalloonViewImpl::GetContentsRectangle() const { + if (!frame_container_) + return gfx::Rect(); + + gfx::Size content_size = balloon_->content_size(); + gfx::Point offset = GetContentsOffset(); + int x = 0, y = 0; + gtk_window_get_position(GTK_WINDOW(frame_container_), &x, &y); + return gfx::Rect(x + offset.x(), y + offset.y(), + content_size.width(), content_size.height()); +} + +gboolean BalloonViewImpl::HandleExpose() { + // Draw the background images. + balloon_background_->RenderToWidget(frame_container_); + shelf_background_->RenderToWidget(shelf_); + return FALSE; +} + +void BalloonViewImpl::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type != NotificationType::NOTIFY_BALLOON_DISCONNECTED) { + NOTREACHED(); + return; + } + + // If the renderer process attached to this balloon is disconnected + // (e.g., because of a crash), we want to close the balloon. + notification_registrar_.Remove(this, + NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_)); + Close(false); +} diff --git a/chrome/browser/gtk/notifications/balloon_view_gtk.h b/chrome/browser/gtk/notifications/balloon_view_gtk.h new file mode 100644 index 0000000..b0cf8ed --- /dev/null +++ b/chrome/browser/gtk/notifications/balloon_view_gtk.h @@ -0,0 +1,156 @@ +// Copyright (c) 2010 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. + +// Draws the view for the balloons. + +#ifndef CHROME_BROWSER_GTK_NOTIFICATIONS_BALLOON_VIEW_GTK_H_ +#define CHROME_BROWSER_GTK_NOTIFICATIONS_BALLOON_VIEW_GTK_H_ + +#include "app/animation.h" +#include "base/basictypes.h" +#include "base/gfx/point.h" +#include "base/gfx/rect.h" +#include "base/gfx/size.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/gtk/menu_gtk.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" + + +class BalloonViewHost; +class MenuGtk; +class NineBox; +class NotificationDetails; +class NotificationOptionsMenuModel; +class NotificationSource; +class SlideAnimation; + +// A balloon view is the UI component for desktop notification toasts. +// It draws a border, and within the border an HTML renderer. +class BalloonViewImpl : public BalloonView, + public MenuGtk::Delegate, + public NotificationObserver, + public AnimationDelegate { + public: + BalloonViewImpl(); + ~BalloonViewImpl(); + + // BalloonView interface. + virtual void Show(Balloon* balloon); + virtual void RepositionToBalloon(); + virtual void Close(bool by_user); + virtual gfx::Size GetSize() const; + + private: + // MenuGtk::Delegate interface. These methods shouldn't actually be + // called because we are using a MenuModel which handles these callbacks. + virtual bool IsCommandEnabled(int command_id) const { + NOTIMPLEMENTED(); + return true; + } + virtual void ExecuteCommand(int command_id) { + NOTIMPLEMENTED(); + } + + // Initializes the toolbar style with GTK. + void InitToolbarStyle(); + + // NotificationObserver interface. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // AnimationDelegate interface. + virtual void AnimationProgressed(const Animation* animation); + + // Launches the options menu. + void RunOptionsMenu(); + + // Adjust the contents window size to be appropriate for the frame. + void SizeContentsWindow(); + + // Do the delayed close work. + void DelayedClose(bool by_user); + + // The height of the balloon's shelf. + // The shelf is where is close button is located. + int GetShelfHeight() const; + + // The height of the part of the frame around the balloon. + int GetBalloonFrameHeight() const; + + // The width and height that the frame should be. If the balloon inside + // changes size, this will not be the same as the actual frame size until + // RepositionToBalloon() has been called and the animation completes. + int GetDesiredTotalWidth() const; + int GetDesiredTotalHeight() const; + + // Where the balloon contents should be placed with respect to the top left + // of the frame. + gfx::Point GetContentsOffset() const; + + // Where the balloon contents should be in screen coordinates. + gfx::Rect GetContentsRectangle() const; + + static gboolean HandleExposeThunk(GtkWidget* widget, + GdkEventExpose* event, + gpointer user_data) { + return reinterpret_cast<BalloonViewImpl*>(user_data)->HandleExpose(); + } + gboolean HandleExpose(); + + static void HandleCloseButtonThunk(GtkWidget* widget, gpointer user_data) { + reinterpret_cast<BalloonViewImpl*>(user_data)->Close(true); + } + + static void HandleOptionsMenuButtonThunk(GtkWidget* widget, + gpointer user_data) { + reinterpret_cast<BalloonViewImpl*>(user_data)->RunOptionsMenu(); + } + + // Non-owned pointer to the balloon which owns this object. + Balloon* balloon_; + + // The window that contains the frame of the notification. + GtkWidget* frame_container_; + + // The widget that contains the shelf. + GtkWidget* shelf_; + + // The toolbar widget within the shelf that contains the buttons. + GtkWidget* toolbar_; + + // The window that contains the contents of the notification. + GtkWidget* html_container_; + + // The renderer of the HTML contents. Pointer owned by the views hierarchy. + BalloonViewHost* html_contents_; + + // The following factory is used to call methods at a later time. + ScopedRunnableMethodFactory<BalloonViewImpl> method_factory_; + + // Image painters for the frame of the toast. + scoped_ptr<NineBox> shelf_background_; + scoped_ptr<NineBox> balloon_background_; + + // Pointer to sub-view is owned by the View sub-class. + GtkWidget* close_button_; + + // An animation to move the balloon on the screen as its position changes. + scoped_ptr<SlideAnimation> animation_; + gfx::Rect anim_frame_start_; + gfx::Rect anim_frame_end_; + + // The options menu. + scoped_ptr<MenuGtk> options_menu_; + scoped_ptr<NotificationOptionsMenuModel> options_menu_model_; + GtkWidget* options_menu_button_; + + NotificationRegistrar notification_registrar_; + + DISALLOW_COPY_AND_ASSIGN(BalloonViewImpl); +}; + +#endif // CHROME_BROWSER_GTK_NOTIFICATIONS_BALLOON_VIEW_GTK_H_ diff --git a/chrome/browser/gtk/notifications/balloon_view_host_gtk.cc b/chrome/browser/gtk/notifications/balloon_view_host_gtk.cc new file mode 100644 index 0000000..d80b202 --- /dev/null +++ b/chrome/browser/gtk/notifications/balloon_view_host_gtk.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2010 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/gtk/notifications/balloon_view_host_gtk.h" + +#include "base/string_util.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/in_process_webkit/dom_storage_context.h" +#include "chrome/browser/in_process_webkit/webkit_context.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/notification.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" +#include "chrome/browser/renderer_host/site_instance.h" +#include "chrome/browser/renderer_preferences_util.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/renderer_preferences.h" + +BalloonViewHost::BalloonViewHost(Balloon* balloon) + : initialized_(false), + balloon_(balloon), + site_instance_(SiteInstance::CreateSiteInstance(balloon->profile())), + render_view_host_(NULL), + should_notify_on_disconnect_(false) { + DCHECK(balloon_); +} + +void BalloonViewHost::Shutdown() { + if (render_view_host_) { + render_view_host_->Shutdown(); + render_view_host_ = NULL; + } +} + +WebPreferences BalloonViewHost::GetWebkitPrefs() { + WebPreferences prefs; + prefs.allow_scripts_to_close_windows = true; + return prefs; +} + +RendererPreferences BalloonViewHost::GetRendererPrefs(Profile* profile) const { + RendererPreferences prefs; + renderer_preferences_util::UpdateFromSystemSettings(&prefs, profile); + // We want links (a.k.a. top_level_requests) to be forwarded to the browser so + // that we can open them in a new tab rather than in the balloon. + prefs.browser_handles_top_level_requests = true; + return prefs; +} + +void BalloonViewHost::RequestOpenURL(const GURL& url, + const GURL& referrer, + WindowOpenDisposition disposition) { + // Always open a link triggered within the notification balloon in a new tab. + // TODO(johnnyg): this new tab should always be in the same workspace as the + // notification. + BrowserList::GetLastActive()->AddTabWithURL(url, referrer, + PageTransition::LINK, true, 0, 0, GetSiteInstance()); +} + +void BalloonViewHost::Close(RenderViewHost* render_view_host) { + balloon_->CloseByScript(); +} + +void BalloonViewHost::RenderViewCreated(RenderViewHost* render_view_host) { + render_view_host->Send(new ViewMsg_EnablePreferredSizeChangedMode( + render_view_host->routing_id())); +} + +void BalloonViewHost::RendererReady(RenderViewHost* render_view_host) { + should_notify_on_disconnect_ = true; + NotificationService::current()->Notify( + NotificationType::NOTIFY_BALLOON_CONNECTED, + Source<Balloon>(balloon_), NotificationService::NoDetails()); +} + +void BalloonViewHost::RendererGone(RenderViewHost* render_view_host) { + if (!should_notify_on_disconnect_) + return; + + should_notify_on_disconnect_ = false; + NotificationService::current()->Notify( + NotificationType::NOTIFY_BALLOON_DISCONNECTED, + Source<Balloon>(balloon_), NotificationService::NoDetails()); +} + +// RenderViewHostDelegate::View methods implemented to allow links to +// open pages in new tabs. +void BalloonViewHost::CreateNewWindow(int route_id) { + delegate_view_helper_.CreateNewWindow( + route_id, balloon_->profile(), site_instance_.get(), + DOMUIFactory::GetDOMUIType(balloon_->notification().content_url()), NULL); +} + +void BalloonViewHost::ShowCreatedWindow(int route_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + const GURL& creator_url) { + // Don't allow pop-ups from notifications. + if (disposition == NEW_POPUP) + return; + + TabContents* contents = delegate_view_helper_.GetCreatedWindow(route_id); + if (contents) { + Browser* browser = BrowserList::GetLastActive(); + browser->AddTabContents(contents, disposition, initial_pos, user_gesture); + } +} + +void BalloonViewHost::UpdatePreferredSize(const gfx::Size& new_size) { + balloon_->SetContentPreferredSize(new_size); +} + +void BalloonViewHost::UpdateActualSize(const gfx::Size& new_size) { + render_widget_host_view_->SetSize(new_size); + // gfx::Size(new_size.width(), new_size.height())); + gtk_widget_set_size_request( + native_view(), new_size.width(), new_size.height()); +} + +void BalloonViewHost::Init() { + DCHECK(!render_view_host_) << "BalloonViewHost already initialized."; + + int64 session_storage_namespace_id = balloon_->profile()->GetWebKitContext()-> + dom_storage_context()->AllocateSessionStorageNamespaceId(); + + render_view_host_ = new RenderViewHost(site_instance_.get(), + this, MSG_ROUTING_NONE, + session_storage_namespace_id); + + render_widget_host_view_ = new RenderWidgetHostViewGtk(render_view_host_); + render_widget_host_view_->InitAsChild(); + + render_view_host_->set_view(render_widget_host_view_); + render_view_host_->CreateRenderView(GetProfile()->GetRequestContext()); + render_view_host_->NavigateToURL(balloon_->notification().content_url()); + initialized_ = true; +} diff --git a/chrome/browser/gtk/notifications/balloon_view_host_gtk.h b/chrome/browser/gtk/notifications/balloon_view_host_gtk.h new file mode 100644 index 0000000..957ef97 --- /dev/null +++ b/chrome/browser/gtk/notifications/balloon_view_host_gtk.h @@ -0,0 +1,133 @@ +// Copyright (c) 2010 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_GTK_NOTIFICATIONS_BALLOON_VIEW_HOST_GTK_H_ +#define CHROME_BROWSER_GTK_NOTIFICATIONS_BALLOON_VIEW_HOST_GTK_H_ + +#include "app/gfx/native_widget_types.h" +#include "chrome/browser/gtk/extension_view_gtk.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" +#include "chrome/browser/renderer_host/site_instance.h" +#include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" +#include "webkit/glue/webpreferences.h" + +class Profile; +class RenderViewHost; + +// BalloonViewHost class is a delegate to the renderer host for the HTML +// notification. When initialized it creates a new RenderViewHost and loads +// the contents of the toast into it. It also handles links within the toast, +// loading them into a new tab. +class BalloonViewHost : public RenderViewHostDelegate, + public RenderViewHostDelegate::View { + public: + explicit BalloonViewHost(Balloon* balloon); + + ~BalloonViewHost() { + Shutdown(); + } + + // Initialize the view. + void Init(); + + // Stops showing the balloon. + void Shutdown(); + + // RenderViewHostDelegate overrides. + virtual WebPreferences GetWebkitPrefs(); + virtual RendererPreferences GetRendererPrefs(Profile* profile) const; + virtual SiteInstance* GetSiteInstance() const { + return site_instance_.get(); + } + virtual Profile* GetProfile() const { return balloon_->profile(); } + virtual const GURL& GetURL() const { + return balloon_->notification().content_url(); + } + virtual void RequestOpenURL(const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition); + virtual void Close(RenderViewHost* render_view_host); + virtual void RenderViewCreated(RenderViewHost* render_view_host); + virtual void DidStopLoading() {} + virtual void RendererReady(RenderViewHost* render_view_host); + virtual void RendererGone(RenderViewHost* render_view_host); + virtual void UpdateTitle(RenderViewHost* /* render_view_host */, + int32 /* page_id */, const std::wstring& title) { + title_ = title; + } + virtual int GetBrowserWindowID() const { return -1; } + virtual ViewType::Type GetRenderViewType() const { + return ViewType::TAB_CONTENTS; + } + virtual RenderViewHostDelegate::View* GetViewDelegate() { + return this; + } + + void UpdateActualSize(const gfx::Size& new_size); + + // RenderViewHostDelegate::View methods. Only the ones for opening new + // windows are currently implemented. + virtual void CreateNewWindow(int route_id); + virtual void CreateNewWidget(int route_id, bool activatable) {} + virtual void ShowCreatedWindow(int route_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + const GURL& creator_url); + virtual void ShowCreatedWidget(int route_id, + const gfx::Rect& initial_pos) {} + virtual void ShowContextMenu(const ContextMenuParams& params) {} + virtual void StartDragging(const WebDropData& drop_data, + WebKit::WebDragOperationsMask allowed_ops) {} + virtual void UpdateDragCursor(WebKit::WebDragOperation operation) {} + virtual void GotFocus() {} + virtual void TakeFocus(bool reverse) {} + virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut) { + return false; + } + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {} + virtual void HandleMouseEvent() {} + virtual void HandleMouseLeave() {} + virtual void UpdatePreferredSize(const gfx::Size& pref_size); + + // Accessors. + RenderViewHost* render_view_host() const { return render_view_host_; } + gfx::NativeView native_view() const { + return render_widget_host_view_->native_view(); + } + const std::wstring& title() const { return title_; } + + private: + // True after Init() has completed. + bool initialized_; + + // Non-owned pointer to the associated balloon. + Balloon* balloon_; + + // Site instance for the balloon/profile, to be used for opening new links. + scoped_refptr<SiteInstance> site_instance_; + + // Owned pointer to to host for the renderer process. + RenderViewHost* render_view_host_; + + // Indicates whether we should notify about disconnection of this balloon. + // This is used to ensure disconnection notifications only happen if + // a connection notification has happened and that they happen only once. + bool should_notify_on_disconnect_; + + // The title of the balloon page. + std::wstring title_; + + // The GTK-specific widget host view. + RenderWidgetHostViewGtk* render_widget_host_view_; + + // Common implementations of some RenderViewHostDelegate::View methods. + RenderViewHostDelegateViewHelper delegate_view_helper_; + + DISALLOW_COPY_AND_ASSIGN(BalloonViewHost); +}; + +#endif // CHROME_BROWSER_GTK_NOTIFICATIONS_BALLOON_VIEW_HOST_GTK_H_ diff --git a/chrome/browser/gtk/notifications/notification_options_menu_model.cc b/chrome/browser/gtk/notifications/notification_options_menu_model.cc new file mode 100644 index 0000000..0cbb81e --- /dev/null +++ b/chrome/browser/gtk/notifications/notification_options_menu_model.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2010 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 "app/l10n_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/gtk/notifications/notification_options_menu_model.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/desktop_notification_service.h" +#include "chrome/browser/profile.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +using menus::MenuModel; + +NotificationOptionsMenuModel::NotificationOptionsMenuModel(Balloon* balloon) + : balloon_(balloon) { +} + +NotificationOptionsMenuModel::~NotificationOptionsMenuModel() { +} + +bool NotificationOptionsMenuModel::HasIcons() const { + return false; +} + +int NotificationOptionsMenuModel::GetItemCount() const { + return 1; +} + +MenuModel::ItemType NotificationOptionsMenuModel::GetTypeAt(int index) const { + return MenuModel::TYPE_COMMAND; +} + +int NotificationOptionsMenuModel::GetCommandIdAt(int index) const { + return index; +} + +string16 NotificationOptionsMenuModel::GetLabelAt(int index) const { + DCHECK_EQ(0, index); + + return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE, + WideToUTF16(balloon_->notification().display_source())); +} + +bool NotificationOptionsMenuModel::IsLabelDynamicAt(int index) const { + return false; +} + +bool NotificationOptionsMenuModel::GetAcceleratorAt( + int index, menus::Accelerator* accelerator) const { + return false; +} + +bool NotificationOptionsMenuModel::IsItemCheckedAt(int index) const { + return false; +} + +int NotificationOptionsMenuModel::GetGroupIdAt(int index) const { + return 0; +} + +bool NotificationOptionsMenuModel::GetIconAt(int index, SkBitmap* icon) const { + return false; +} + +bool NotificationOptionsMenuModel::IsEnabledAt(int index) const { + return true; +} + +MenuModel* NotificationOptionsMenuModel::GetSubmenuModelAt(int index) const { + return NULL; +} + +void NotificationOptionsMenuModel::HighlightChangedTo(int index) { +} + +void NotificationOptionsMenuModel::ActivatedAt(int index) { + DCHECK_EQ(0, index); + + DesktopNotificationService* service = + balloon_->profile()->GetDesktopNotificationService(); + + service->DenyPermission(balloon_->notification().origin_url()); +} diff --git a/chrome/browser/gtk/notifications/notification_options_menu_model.h b/chrome/browser/gtk/notifications/notification_options_menu_model.h new file mode 100644 index 0000000..f4b67f3 --- /dev/null +++ b/chrome/browser/gtk/notifications/notification_options_menu_model.h @@ -0,0 +1,42 @@ +// Copyright (c) 2010 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_GTK_NOTIFICATIONS_NOTIFICATION_OPTIONS_MENU_MODEL_H_ +#define CHROME_BROWSER_GTK_NOTIFICATIONS_NOTIFICATION_OPTIONS_MENU_MODEL_H_ + +#include "app/menus/menu_model.h" + +class Balloon; + +// Model for the options menu on the notification balloon. +class NotificationOptionsMenuModel : public menus::MenuModel { + public: + explicit NotificationOptionsMenuModel(Balloon* balloon); + ~NotificationOptionsMenuModel(); + + // menus::MenuModel methods. + virtual bool HasIcons() const; + virtual int GetItemCount() const; + virtual ItemType GetTypeAt(int index) const; + virtual int GetCommandIdAt(int index) const; + virtual string16 GetLabelAt(int index) const; + virtual bool IsLabelDynamicAt(int index) const; + virtual bool GetAcceleratorAt(int index, + menus::Accelerator* accelerator) const; + virtual bool IsItemCheckedAt(int index) const; + virtual int GetGroupIdAt(int index) const; + virtual bool GetIconAt(int index, SkBitmap* icon) const; + virtual bool IsEnabledAt(int index) const; + virtual MenuModel* GetSubmenuModelAt(int index) const; + virtual void HighlightChangedTo(int index); + virtual void ActivatedAt(int index); + + private: + // Non-owned pointer to the balloon involved. + Balloon* balloon_; + + DISALLOW_COPY_AND_ASSIGN(NotificationOptionsMenuModel); +}; + +#endif // CHROME_BROWSER_GTK_NOTIFICATIONS_NOTIFICATION_OPTIONS_MENU_MODEL_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 9cbcc2f..804b539 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -934,6 +934,12 @@ 'browser/gtk/menu_gtk.h', 'browser/gtk/nine_box.cc', 'browser/gtk/nine_box.h', + 'browser/gtk/notifications/balloon_view_gtk.cc', + 'browser/gtk/notifications/balloon_view_gtk.h', + 'browser/gtk/notifications/balloon_view_host_gtk.cc', + 'browser/gtk/notifications/balloon_view_host_gtk.h', + 'browser/gtk/notifications/notification_options_menu_model.cc', + 'browser/gtk/notifications/notification_options_menu_model.h', 'browser/gtk/options/advanced_contents_gtk.cc', 'browser/gtk/options/advanced_contents_gtk.h', 'browser/gtk/options/advanced_page_gtk.cc', diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 657e440..9df544a 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -617,8 +617,8 @@ void RenderThread::EnsureWebKitInitialized() { WebRuntimeFeatures::enableApplicationCache( command_line.HasSwitch(switches::kEnableApplicationCache)); -#if defined(OS_WIN) - // We don't yet support notifications on non-Windows. +#if defined(OS_WIN) || defined(OS_LINUX) + // Notifications are supported on Windows and Linux only. WebRuntimeFeatures::enableNotifications( !command_line.HasSwitch(switches::kDisableDesktopNotifications)); #endif |