diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-06 18:45:56 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-06 18:45:56 +0000 |
commit | 7b9627d188d04b224f972fae709e7cb4f61ae504 (patch) | |
tree | 4d37368f881c9dae023e5172013e87f0b7c58676 | |
parent | 72baf67602ab1dbf95c34a5d8301e0ac5f8b8b41 (diff) | |
download | chromium_src-7b9627d188d04b224f972fae709e7cb4f61ae504.zip chromium_src-7b9627d188d04b224f972fae709e7cb4f61ae504.tar.gz chromium_src-7b9627d188d04b224f972fae709e7cb4f61ae504.tar.bz2 |
Render a "sad tab" on tab crash.
Uses the NotificationRegistrar to notice TAB_CONTENTS_[DIS]CONNECTED events. When it disconnects, add a SadTabGtk to the TabContentsView. Delete it when the tab contents reconnects.
BUG=http://www.crbug.com/11081
TEST=Open http://about:crash. Verify that the sad tab renders properly. Navigate to another page to make sure the SadTabGtk is correctly replaced with a new RenderWidgetHostViewGtk.
Review URL: http://codereview.chromium.org/111003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15435 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/gtk/browser_window_gtk.cc | 7 | ||||
-rw-r--r-- | chrome/browser/gtk/sad_tab_gtk.cc | 148 | ||||
-rw-r--r-- | chrome/browser/gtk/sad_tab_gtk.h | 51 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_gtk.cc | 4 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_gtk.cc | 62 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_gtk.h | 21 | ||||
-rw-r--r-- | chrome/browser/views/sad_tab_view.h | 9 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 |
8 files changed, 277 insertions, 27 deletions
diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index dcc8c98..a1ded9e 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -213,9 +213,10 @@ gboolean HandleCustomAccelerator(guint keyval, GdkModifierType modifier, gboolean OnKeyPress(GtkWindow* window, GdkEventKey* event, Browser* browser) { TabContents* current_tab_contents = browser->tabstrip_model()->GetSelectedTabContents(); - // If there is no current tab contents or it is not focused then let the - // default GtkWindow key handler run. - if (!current_tab_contents || + // If there is no current tab contents or its view is gone (if the renderview + // crashed) or it is not focused then let the default GtkWindow key handler + // run. + if (!current_tab_contents || !current_tab_contents->GetContentNativeView() || !gtk_widget_is_focus(current_tab_contents->GetContentNativeView())) { return HandleCustomAccelerator(event->keyval, static_cast<GdkModifierType>(event->state), browser); diff --git a/chrome/browser/gtk/sad_tab_gtk.cc b/chrome/browser/gtk/sad_tab_gtk.cc new file mode 100644 index 0000000..594b2ad --- /dev/null +++ b/chrome/browser/gtk/sad_tab_gtk.cc @@ -0,0 +1,148 @@ +// 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/gtk/sad_tab_gtk.h" + +#include <string> + +#include "app/gfx/chrome_canvas.h" +#include "app/gfx/chrome_font.h" +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/gfx/size.h" +#include "base/lazy_instance.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "skia/ext/skia_utils.h" +#include "skia/include/SkGradientShader.h" + +namespace { + +// The y offset from the center at which to paint the icon. +const int kSadTabOffset = -64; +// The spacing between the icon and the title. +const int kIconTitleSpacing = 20; +// The spacing between the title and the message. +const int kTitleMessageSpacing = 15; +const SkColor kTitleTextColor = SK_ColorWHITE; +const SkColor kMessageTextColor = SK_ColorWHITE; +const SkColor kBackgroundColor = SkColorSetRGB(35, 48, 64); +const SkColor kBackgroundEndColor = SkColorSetRGB(35, 48, 64); + +struct SadTabGtkConstants { + SadTabGtkConstants() + : sad_tab_bitmap( + ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_SAD_TAB)), + title_font( + ResourceBundle::GetSharedInstance().GetFont( + ResourceBundle::BaseFont).DeriveFont(2, ChromeFont::BOLD)), + message_font( + ResourceBundle::GetSharedInstance().GetFont( + ResourceBundle::BaseFont).DeriveFont(1)), + title(l10n_util::GetString(IDS_SAD_TAB_TITLE)), + message(l10n_util::GetString(IDS_SAD_TAB_MESSAGE)) {} + + const SkBitmap* sad_tab_bitmap; + ChromeFont title_font; + ChromeFont message_font; + std::wstring title; + std::wstring message; +}; + +base::LazyInstance<SadTabGtkConstants> + g_sad_tab_constants(base::LINKER_INITIALIZED); + +} // namespace + +SadTabGtk::SadTabGtk() + : width_(0), + height_(0), + title_y_(0), + message_y_(0), + widget_(gtk_drawing_area_new()) { + gtk_widget_set_double_buffered(widget_.get(), FALSE); + g_signal_connect(widget_.get(), "expose-event", + G_CALLBACK(OnExposeThunk), this); + g_signal_connect(widget_.get(), "configure-event", + G_CALLBACK(OnConfigureThunk), this); +} + +SadTabGtk::~SadTabGtk() { + widget_.Destroy(); +} + +// static +gboolean SadTabGtk::OnExposeThunk(GtkWidget* widget, + GdkEventExpose* event, + const SadTabGtk* sad_tab) { + return sad_tab->OnExpose(widget, event); +} + +gboolean SadTabGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event) const { + ChromeCanvasPaint canvas(event); + SkPaint paint; + paint.setShader(skia::CreateGradientShader(0, height_, + kBackgroundColor, + kBackgroundEndColor))->safeUnref(); + paint.setStyle(SkPaint::kFill_Style); + canvas.drawRectCoords(0, 0, + SkIntToScalar(width_), + SkIntToScalar(height_), + paint); + + const SadTabGtkConstants& sad_tab_constants = + g_sad_tab_constants.Get(); + + // Paint the sad tab icon. + canvas.DrawBitmapInt(*sad_tab_constants.sad_tab_bitmap, + icon_bounds_.x(), + icon_bounds_.y()); + + // Paint the "Aw, snap!" + canvas.DrawStringInt(sad_tab_constants.title, + sad_tab_constants.title_font, + kTitleTextColor, + 0, + title_y_, + width_, + sad_tab_constants.title_font.height(), + ChromeCanvas::TEXT_ALIGN_CENTER); + + // Paint the explanatory message. + canvas.DrawStringInt(sad_tab_constants.message, + sad_tab_constants.message_font, + kMessageTextColor, + 0, + message_y_, + width_, + sad_tab_constants.message_font.height(), + ChromeCanvas::TEXT_ALIGN_CENTER); + return TRUE; +} + +// static +gboolean SadTabGtk::OnConfigureThunk(GtkWidget* widget, + GdkEventConfigure* event, + SadTabGtk* sad_tab) { + return sad_tab->OnConfigure(widget, event); +} + +gboolean SadTabGtk::OnConfigure(GtkWidget* widget, GdkEventConfigure* event) { + const SadTabGtkConstants& sad_tab_constants = + g_sad_tab_constants.Get(); + width_ = event->width; + height_= event->height; + int icon_width = sad_tab_constants.sad_tab_bitmap->width(); + int icon_height = sad_tab_constants.sad_tab_bitmap->height(); + int icon_x = (event->width - icon_width) / 2; + int icon_y = ((event->height - icon_height) / 2) + kSadTabOffset; + icon_bounds_.SetRect(icon_x, icon_y, icon_width, icon_height); + + title_y_ = + icon_bounds_.bottom() + kIconTitleSpacing; + int title_height = sad_tab_constants.title_font.height(); + message_y_ = + title_y_ + title_height + kTitleMessageSpacing; + return TRUE; +} diff --git a/chrome/browser/gtk/sad_tab_gtk.h b/chrome/browser/gtk/sad_tab_gtk.h new file mode 100644 index 0000000..aed2454 --- /dev/null +++ b/chrome/browser/gtk/sad_tab_gtk.h @@ -0,0 +1,51 @@ +// 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_GTK_SAD_TAB_GTK_H_ +#define CHROME_BROWSER_GTK_SAD_TAB_GTK_H_ + +#include <gtk/gtk.h> + +#include "base/basictypes.h" +#include "base/gfx/rect.h" +#include "chrome/common/owned_widget_gtk.h" + +class SadTabGtk { + public: + SadTabGtk(); + ~SadTabGtk(); + + GtkWidget* widget() { return widget_.get(); } + + private: + // expose-event handler that redraws the SadTabGtk. + static gboolean OnExposeThunk(GtkWidget* widget, + GdkEventExpose* event, + const SadTabGtk* sad_tab); + + gboolean OnExpose(GtkWidget* widget, GdkEventExpose* event) const; + + // configure-event handler that gets the new bounds of the SadTabGtk. + static gboolean OnConfigureThunk(GtkWidget* widget, + GdkEventConfigure* event, + SadTabGtk* sad_tab); + + gboolean OnConfigure(GtkWidget* widget, GdkEventConfigure* event); + + // Track the view's width and height from configure-event signals. + int width_; + int height_; + + // Regions within the display for different components, set on a + // configure-event. These are relative to the bounds of the widget. + gfx::Rect icon_bounds_; + int title_y_; + int message_y_; + + OwnedWidgetGtk widget_; + + DISALLOW_COPY_AND_ASSIGN(SadTabGtk); +}; + +#endif // CHROME_BROWSER_GTK_SAD_TAB_GTK_H__ diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc index 35ac146..a31c70c 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc @@ -291,7 +291,7 @@ void RenderWidgetHostViewGtk::DidScrollRect(const gfx::Rect& rect, int dx, } void RenderWidgetHostViewGtk::RenderViewGone() { - NOTIMPLEMENTED(); + Destroy(); } void RenderWidgetHostViewGtk::Destroy() { @@ -374,7 +374,7 @@ void RenderWidgetHostViewGtk::ShowCurrentCursor() { return; GdkCursor* gdk_cursor; - switch(current_cursor_.GetCursorType()) { + switch (current_cursor_.GetCursorType()) { case GDK_CURSOR_IS_PIXMAP: // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is // that calling gdk_window_set_cursor repeatedly is expensive. We should diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/tab_contents/tab_contents_view_gtk.cc index 9c82b47..a55c8fe 100644 --- a/chrome/browser/tab_contents/tab_contents_view_gtk.cc +++ b/chrome/browser/tab_contents/tab_contents_view_gtk.cc @@ -11,6 +11,7 @@ #include "base/gfx/point.h" #include "base/gfx/rect.h" #include "chrome/browser/gtk/browser_window_gtk.h" +#include "chrome/browser/gtk/sad_tab_gtk.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_view_host_factory.h" #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" @@ -18,6 +19,8 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/common/gtk_util.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" namespace { @@ -62,8 +65,11 @@ TabContentsView* TabContentsView::Create(TabContents* tab_contents) { TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents) : TabContentsView(tab_contents), - vbox_(gtk_vbox_new(FALSE, 0)), - content_view_(NULL) { + vbox_(gtk_vbox_new(FALSE, 0)) { + registrar_.Add(this, NotificationType::TAB_CONTENTS_CONNECTED, + Source<TabContents>(tab_contents)); + registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED, + Source<TabContents>(tab_contents)); } TabContentsViewGtk::~TabContentsViewGtk() { @@ -89,19 +95,19 @@ RenderWidgetHostView* TabContentsViewGtk::CreateViewForWidget( RenderWidgetHostViewGtk* view = new RenderWidgetHostViewGtk(render_widget_host); view->InitAsChild(); - content_view_ = view->native_view(); - g_signal_connect(content_view_, "focus", + gfx::NativeView content_view = view->native_view(); + g_signal_connect(content_view, "focus", G_CALLBACK(OnFocus), tab_contents()); - g_signal_connect(view->native_view(), "leave-notify-event", + g_signal_connect(content_view, "leave-notify-event", G_CALLBACK(OnLeaveNotify), tab_contents()); - g_signal_connect(view->native_view(), "motion-notify-event", + g_signal_connect(content_view, "motion-notify-event", G_CALLBACK(OnMouseMove), tab_contents()); - gtk_widget_add_events(view->native_view(), GDK_LEAVE_NOTIFY_MASK | + gtk_widget_add_events(content_view, GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); - g_signal_connect(view->native_view(), "button-press-event", + g_signal_connect(content_view, "button-press-event", G_CALLBACK(OnMouseDown), this); gfx::RemoveAllChildren(vbox_.get()); - gtk_box_pack_start(GTK_BOX(vbox_.get()), content_view_, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox_.get()), content_view, TRUE, TRUE, 0); return view; } @@ -110,9 +116,12 @@ gfx::NativeView TabContentsViewGtk::GetNativeView() const { } gfx::NativeView TabContentsViewGtk::GetContentNativeView() const { - return content_view_; + if (!tab_contents()->render_widget_host_view()) + return NULL; + return tab_contents()->render_widget_host_view()->GetPluginNativeView(); } + gfx::NativeWindow TabContentsViewGtk::GetTopLevelNativeWindow() const { GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW); return window ? GTK_WINDOW(window) : NULL; @@ -135,12 +144,13 @@ void TabContentsViewGtk::OnContentsDestroy() { void TabContentsViewGtk::SetPageTitle(const std::wstring& title) { // Set the window name to include the page title so it's easier to spot // when debugging (e.g. via xwininfo -tree). - if (content_view_ && content_view_->window) - gdk_window_set_title(content_view_->window, WideToUTF8(title).c_str()); + gfx::NativeView content_view = GetContentNativeView(); + if (content_view && content_view->window) + gdk_window_set_title(content_view->window, WideToUTF8(title).c_str()); } void TabContentsViewGtk::Invalidate() { - NOTIMPLEMENTED(); + gtk_widget_queue_draw(sad_tab_->widget()); } void TabContentsViewGtk::SizeContents(const gfx::Size& size) { @@ -173,7 +183,7 @@ void TabContentsViewGtk::SetInitialFocus() { if (tab_contents()->FocusLocationBarByDefault()) tab_contents()->delegate()->SetFocusToLocationBar(); else - gtk_widget_grab_focus(content_view_); + gtk_widget_grab_focus(GetContentNativeView()); } void TabContentsViewGtk::StoreFocus() { @@ -224,6 +234,30 @@ void TabContentsViewGtk::OnFindReply(int request_id, NOTIMPLEMENTED(); } +void TabContentsViewGtk::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::TAB_CONTENTS_CONNECTED: { + // No need to remove the SadTabGtk's widget from the container since + // the new RenderWidgetHostViewGtk instance already removed all the + // vbox's children. + sad_tab_.reset(); + break; + } + case NotificationType::TAB_CONTENTS_DISCONNECTED: { + sad_tab_.reset(new SadTabGtk); + gtk_box_pack_start( + GTK_BOX(vbox_.get()), sad_tab_->widget(), TRUE, TRUE, 0); + gtk_widget_show(sad_tab_->widget()); + break; + } + default: + NOTREACHED() << "Got a notification we didn't register for."; + break; + } +} + void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) { context_menu_.reset(new RenderViewContextMenuGtk(tab_contents(), params, last_mouse_down_time_)); diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.h b/chrome/browser/tab_contents/tab_contents_view_gtk.h index 6d24598..4137f67 100644 --- a/chrome/browser/tab_contents/tab_contents_view_gtk.h +++ b/chrome/browser/tab_contents/tab_contents_view_gtk.h @@ -7,11 +7,15 @@ #include "base/scoped_ptr.h" #include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "chrome/common/owned_widget_gtk.h" class RenderViewContextMenuGtk; +class SadTabGtk; -class TabContentsViewGtk : public TabContentsView { +class TabContentsViewGtk : public TabContentsView, + public NotificationObserver { public: // The corresponding TabContents is passed in the constructor, and manages our // lifetime. This doesn't need to be the case, but is this way currently @@ -55,6 +59,13 @@ class TabContentsViewGtk : public TabContentsView { const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update); + + // NotificationObserver implementation --------------------------------------- + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + private: // We keep track of the timestamp of the latest mousedown event. static gboolean OnMouseDown(GtkWidget* widget, @@ -63,9 +74,6 @@ class TabContentsViewGtk : public TabContentsView { // The native widget for the tab. OwnedWidgetGtk vbox_; - // The native widget for the contents of the tab. We do not own this widget. - GtkWidget* content_view_; - // The context menu is reset every time we show it, but we keep a pointer to // between uses so that it won't go out of scope before we're done with it. scoped_ptr<RenderViewContextMenuGtk> context_menu_; @@ -74,6 +82,11 @@ class TabContentsViewGtk : public TabContentsView { // show context menus. guint32 last_mouse_down_time_; + // Used to get notifications about renderers coming and going. + NotificationRegistrar registrar_; + + scoped_ptr<SadTabGtk> sad_tab_; + DISALLOW_COPY_AND_ASSIGN(TabContentsViewGtk); }; diff --git a/chrome/browser/views/sad_tab_view.h b/chrome/browser/views/sad_tab_view.h index 5e9415d..8605c1d 100644 --- a/chrome/browser/views/sad_tab_view.h +++ b/chrome/browser/views/sad_tab_view.h @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_VIEWS_SAD_TAB_H_ -#define CHROME_BROWSER_VIEWS_SAD_TAB_H_ +#ifndef CHROME_BROWSER_VIEWS_SAD_TAB_VIEW_H_ +#define CHROME_BROWSER_VIEWS_SAD_TAB_VIEW_H_ #include "app/gfx/chrome_font.h" +#include "base/basictypes.h" #include "chrome/views/view.h" class SkBitmap; @@ -51,7 +52,7 @@ class SadTabView : public views::View { gfx::Rect title_bounds_; gfx::Rect message_bounds_; - DISALLOW_EVIL_CONSTRUCTORS(SadTabView); + DISALLOW_COPY_AND_ASSIGN(SadTabView); }; -#endif // CHROME_BROWSER_VIEWS_SAD_TAB_H__ +#endif // CHROME_BROWSER_VIEWS_SAD_TAB_VIEW_H__ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 212c0b0..45f14d9 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -880,6 +880,8 @@ 'browser/gtk/menu_gtk.h', 'browser/gtk/nine_box.cc', 'browser/gtk/nine_box.h', + 'browser/gtk/sad_tab_gtk.cc', + 'browser/gtk/sad_tab_gtk.h', 'browser/gtk/slide_animator_gtk.cc', 'browser/gtk/slide_animator_gtk.h', 'browser/gtk/standard_menus.cc', |