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 21 using content::OpenURLParams; 22 using content::WebContents; 23 24 namespace { 25 26 // Background color of the content (a grayish blue) for a crashed tab. 27 const 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 32 const 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. 37 GtkWidget* 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 56 SadTabGtk::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 string16 help_text(l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE, 147 string16(), &offset)); 148 std::string help_prefix_text(UTF16ToUTF8(help_text.substr(0, offset))); 149 std::string help_suffix_text(UTF16ToUTF8(help_text.substr(offset))); 150 151 GtkWidget* help_prefix = MakeWhiteMarkupLabel( 152 "<span style=\"normal\">%s</span>", help_prefix_text); 153 GtkWidget* help_suffix = MakeWhiteMarkupLabel( 154 "<span style=\"normal\">%s</span>", help_suffix_text); 155 156 // Add the help link and text to the horizontal box. 157 gtk_box_pack_start(GTK_BOX(help_hbox), help_prefix, FALSE, FALSE, 0); 158 GtkWidget* link_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); 159 gtk_container_add(GTK_CONTAINER(link_alignment), link); 160 gtk_box_pack_start(GTK_BOX(help_hbox), link_alignment, FALSE, FALSE, 0); 161 gtk_box_pack_start(GTK_BOX(help_hbox), help_suffix, FALSE, FALSE, 0); 162 } else { 163 // Add just the help link to a centered alignment. 164 gtk_container_add(GTK_CONTAINER(help_alignment), link); 165 } 166 gtk_box_pack_start(GTK_BOX(vbox), help_alignment, FALSE, FALSE, 0); 167 } 168 169 gtk_widget_show_all(event_box_.get()); 170 } 171 172 SadTabGtk::~SadTabGtk() { 173 event_box_.Destroy(); 174 } 175 176 void SadTabGtk::Show() { 177 switch (kind_) { 178 case chrome::SAD_TAB_KIND_CRASHED: { 179 static int crashed = 0; 180 UMA_HISTOGRAM_CUSTOM_COUNTS( 181 "Tabs.SadTab.CrashDisplayed", ++crashed, 1, 1000, 50); 182 break; 183 } 184 case chrome::SAD_TAB_KIND_KILLED: { 185 static int killed = 0; 186 UMA_HISTOGRAM_CUSTOM_COUNTS( 187 "Tabs.SadTab.KilledDisplayed", ++killed, 1, 1000, 50); 188 break; 189 } 190 default: 191 NOTREACHED(); 192 } 193 194 GtkWidget* expanded_container = 195 ChromeWebContentsViewDelegateGtk::GetFor(web_contents_)-> 196 expanded_container(); 197 gtk_container_add(GTK_CONTAINER(expanded_container), event_box_.get()); 198 gtk_widget_show(event_box_.get()); 199 } 200 201 void SadTabGtk::Close() { 202 GtkWidget* expanded_container = 203 ChromeWebContentsViewDelegateGtk::GetFor(web_contents_)-> 204 expanded_container(); 205 gtk_container_remove(GTK_CONTAINER(expanded_container), event_box_.get()); 206 } 207 208 void SadTabGtk::OnLinkButtonClick(GtkWidget* sender) { 209 if (web_contents_) { 210 GURL help_url((kind_ == chrome::SAD_TAB_KIND_CRASHED) ? 211 chrome::kCrashReasonURL : chrome::kKillReasonURL); 212 OpenURLParams params(help_url, content::Referrer(), CURRENT_TAB, 213 content::PAGE_TRANSITION_LINK, false); 214 web_contents_->OpenURL(params); 215 } 216 } 217 218 namespace chrome { 219 220 SadTab* SadTab::Create(content::WebContents* web_contents, SadTabKind kind) { 221 return new SadTabGtk(web_contents, kind); 222 } 223 224 } // namespace chrome 225