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