1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/gtk/sad_tab_gtk.h" 6 7#include "base/metrics/histogram.h" 8#include "base/strings/utf_string_conversions.h" 9#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" 10#include "chrome/browser/ui/gtk/tab_contents/chrome_web_contents_view_delegate_gtk.h" 11#include "chrome/common/url_constants.h" 12#include "content/public/browser/web_contents.h" 13#include "grit/generated_resources.h" 14#include "grit/locale_settings.h" 15#include "grit/theme_resources.h" 16#include "ui/base/gtk/gtk_hig_constants.h" 17#include "ui/base/l10n/l10n_util.h" 18#include "ui/base/resource/resource_bundle.h" 19#include "ui/gfx/image/image.h" 20 21using content::OpenURLParams; 22using content::WebContents; 23 24namespace { 25 26// Background color of the content (a grayish blue) for a crashed tab. 27const GdkColor kCrashedBackgroundColor = GDK_COLOR_RGB(35, 48, 64); 28 29// Background color of the content (a grayish purple) for a killed 30// tab. TODO(gspencer): update this for the "real" color when the UI 31// team provides one. See http://crosbug.com/10711 32const GdkColor kKilledBackgroundColor = GDK_COLOR_RGB(57, 48, 88); 33 34// Construct a centered GtkLabel with a white foreground. 35// |format| is a printf-style format containing a %s; 36// |str| is substituted into the format. 37GtkWidget* MakeWhiteMarkupLabel(const char* format, const std::string& str) { 38 GtkWidget* label = gtk_label_new(NULL); 39 char* markup = g_markup_printf_escaped(format, str.c_str()); 40 gtk_label_set_markup(GTK_LABEL(label), markup); 41 g_free(markup); 42 43 // Center align and justify it. 44 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5); 45 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); 46 47 // Set text to white. 48 GdkColor white = ui::kGdkWhite; 49 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &white); 50 51 return label; 52} 53 54} // namespace 55 56SadTabGtk::SadTabGtk(WebContents* web_contents, chrome::SadTabKind kind) 57 : web_contents_(web_contents), 58 kind_(kind) { 59 DCHECK(web_contents_); 60 61 switch (kind_) { 62 case chrome::SAD_TAB_KIND_CRASHED: { 63 static int crashed = 0; 64 UMA_HISTOGRAM_CUSTOM_COUNTS( 65 "Tabs.SadTab.CrashCreated", ++crashed, 1, 1000, 50); 66 break; 67 } 68 case chrome::SAD_TAB_KIND_KILLED: { 69 static int killed = 0; 70 UMA_HISTOGRAM_CUSTOM_COUNTS( 71 "Tabs.SadTab.KilledCreated", ++killed, 1, 1000, 50); 72 break; 73 } 74 default: 75 NOTREACHED(); 76 } 77 78 // Use an event box to get the background painting correctly. 79 event_box_.Own(gtk_event_box_new()); 80 gtk_widget_modify_bg(event_box_.get(), GTK_STATE_NORMAL, 81 (kind == chrome::SAD_TAB_KIND_CRASHED) ? 82 &kCrashedBackgroundColor : &kKilledBackgroundColor); 83 // Allow ourselves to be resized arbitrarily small. 84 gtk_widget_set_size_request(event_box_.get(), 0, 0); 85 86 GtkWidget* centering = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); 87 gtk_container_add(GTK_CONTAINER(event_box_.get()), centering); 88 89 // Use a vertical box to contain icon, title, message and link. 90 GtkWidget* vbox = gtk_vbox_new(FALSE, 0); 91 gtk_container_add(GTK_CONTAINER(centering), vbox); 92 93 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 94 // Add center-aligned image. 95 GtkWidget* image = gtk_image_new_from_pixbuf(rb.GetNativeImageNamed( 96 (kind == chrome::SAD_TAB_KIND_CRASHED) ? 97 IDR_SAD_TAB : IDR_KILLED_TAB).ToGdkPixbuf()); 98 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5); 99 gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 0); 100 101 // Add spacer between image and title. 102 GtkWidget* spacer = gtk_label_new(NULL); 103 gtk_label_set_markup(GTK_LABEL(spacer), "<span size=\"larger\"> </span>"); 104 gtk_box_pack_start(GTK_BOX(vbox), spacer, FALSE, FALSE, 0); 105 106 // Add center-aligned title. 107 GtkWidget* title = MakeWhiteMarkupLabel( 108 "<span size=\"larger\" style=\"normal\"><b>%s</b></span>", 109 l10n_util::GetStringUTF8((kind == chrome::SAD_TAB_KIND_CRASHED) ? 110 IDS_SAD_TAB_TITLE : IDS_KILLED_TAB_TITLE)); 111 gtk_box_pack_start(GTK_BOX(vbox), title, FALSE, FALSE, 0); 112 113 // Add spacer between title and message. 114 spacer = gtk_label_new(" "); 115 gtk_box_pack_start(GTK_BOX(vbox), spacer, FALSE, FALSE, 0); 116 117 // Add center-aligned message. 118 GtkWidget* message = MakeWhiteMarkupLabel( 119 "<span style=\"normal\">%s</span>", 120 l10n_util::GetStringUTF8((kind == chrome::SAD_TAB_KIND_CRASHED) ? 121 IDS_SAD_TAB_MESSAGE : IDS_KILLED_TAB_MESSAGE)); 122 gtk_label_set_line_wrap(GTK_LABEL(message), TRUE); 123 gtk_box_pack_start(GTK_BOX(vbox), message, FALSE, FALSE, 0); 124 125 // Add spacer between message and link. 126 spacer = gtk_label_new(" "); 127 gtk_box_pack_start(GTK_BOX(vbox), spacer, FALSE, FALSE, 0); 128 129 if (web_contents_) { 130 // Create the help link and alignment. 131 std::string link_text(l10n_util::GetStringUTF8( 132 (kind == chrome::SAD_TAB_KIND_CRASHED) ? 133 IDS_SAD_TAB_HELP_LINK : IDS_LEARN_MORE)); 134 GtkWidget* link = gtk_chrome_link_button_new(link_text.c_str()); 135 gtk_chrome_link_button_set_normal_color(GTK_CHROME_LINK_BUTTON(link), 136 &ui::kGdkWhite); 137 g_signal_connect(link, "clicked", G_CALLBACK(OnLinkButtonClickThunk), this); 138 GtkWidget* help_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); 139 140 if (kind == chrome::SAD_TAB_KIND_CRASHED) { 141 // Use a horizontal box to contain the help text and link. 142 GtkWidget* help_hbox = gtk_hbox_new(FALSE, 0); 143 gtk_container_add(GTK_CONTAINER(vbox), help_hbox); 144 145 size_t offset = 0; 146 base::string16 help_text( 147 l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE, 148 base::string16(), &offset)); 149 std::string help_prefix_text(UTF16ToUTF8(help_text.substr(0, offset))); 150 std::string help_suffix_text(UTF16ToUTF8(help_text.substr(offset))); 151 152 GtkWidget* help_prefix = MakeWhiteMarkupLabel( 153 "<span style=\"normal\">%s</span>", help_prefix_text); 154 GtkWidget* help_suffix = MakeWhiteMarkupLabel( 155 "<span style=\"normal\">%s</span>", help_suffix_text); 156 157 // Add the help link and text to the horizontal box. 158 gtk_box_pack_start(GTK_BOX(help_hbox), help_prefix, FALSE, FALSE, 0); 159 GtkWidget* link_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); 160 gtk_container_add(GTK_CONTAINER(link_alignment), link); 161 gtk_box_pack_start(GTK_BOX(help_hbox), link_alignment, FALSE, FALSE, 0); 162 gtk_box_pack_start(GTK_BOX(help_hbox), help_suffix, FALSE, FALSE, 0); 163 } else { 164 // Add just the help link to a centered alignment. 165 gtk_container_add(GTK_CONTAINER(help_alignment), link); 166 } 167 gtk_box_pack_start(GTK_BOX(vbox), help_alignment, FALSE, FALSE, 0); 168 } 169 170 gtk_widget_show_all(event_box_.get()); 171} 172 173SadTabGtk::~SadTabGtk() { 174 event_box_.Destroy(); 175} 176 177void SadTabGtk::Show() { 178 switch (kind_) { 179 case chrome::SAD_TAB_KIND_CRASHED: { 180 static int crashed = 0; 181 UMA_HISTOGRAM_CUSTOM_COUNTS( 182 "Tabs.SadTab.CrashDisplayed", ++crashed, 1, 1000, 50); 183 break; 184 } 185 case chrome::SAD_TAB_KIND_KILLED: { 186 static int killed = 0; 187 UMA_HISTOGRAM_CUSTOM_COUNTS( 188 "Tabs.SadTab.KilledDisplayed", ++killed, 1, 1000, 50); 189 break; 190 } 191 default: 192 NOTREACHED(); 193 } 194 195 GtkWidget* expanded_container = 196 ChromeWebContentsViewDelegateGtk::GetFor(web_contents_)-> 197 expanded_container(); 198 gtk_container_add(GTK_CONTAINER(expanded_container), event_box_.get()); 199 gtk_widget_show(event_box_.get()); 200} 201 202void SadTabGtk::Close() { 203 GtkWidget* expanded_container = 204 ChromeWebContentsViewDelegateGtk::GetFor(web_contents_)-> 205 expanded_container(); 206 gtk_container_remove(GTK_CONTAINER(expanded_container), event_box_.get()); 207} 208 209void SadTabGtk::OnLinkButtonClick(GtkWidget* sender) { 210 if (web_contents_) { 211 GURL help_url((kind_ == chrome::SAD_TAB_KIND_CRASHED) ? 212 chrome::kCrashReasonURL : chrome::kKillReasonURL); 213 OpenURLParams params(help_url, content::Referrer(), CURRENT_TAB, 214 content::PAGE_TRANSITION_LINK, false); 215 web_contents_->OpenURL(params); 216 } 217} 218 219namespace chrome { 220 221SadTab* SadTab::Create(content::WebContents* web_contents, SadTabKind kind) { 222 return new SadTabGtk(web_contents, kind); 223} 224 225} // namespace chrome 226