Home | History | Annotate | Download | only in views
      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/views/sad_tab_view.h"
      6 
      7 #include <string>
      8 
      9 #include "base/metrics/field_trial.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/ui/browser.h"
     13 #include "chrome/browser/ui/browser_finder.h"
     14 #include "chrome/browser/ui/chrome_pages.h"
     15 #include "chrome/common/url_constants.h"
     16 #include "chrome/grit/generated_resources.h"
     17 #include "components/feedback/feedback_util.h"
     18 #include "content/public/browser/navigation_controller.h"
     19 #include "content/public/browser/web_contents.h"
     20 #include "grit/components_strings.h"
     21 #include "grit/theme_resources.h"
     22 #include "ui/base/l10n/l10n_util.h"
     23 #include "ui/base/resource/resource_bundle.h"
     24 #include "ui/views/background.h"
     25 #include "ui/views/controls/button/label_button.h"
     26 #include "ui/views/controls/button/label_button_border.h"
     27 #include "ui/views/controls/image_view.h"
     28 #include "ui/views/controls/label.h"
     29 #include "ui/views/controls/link.h"
     30 #include "ui/views/layout/grid_layout.h"
     31 #include "ui/views/widget/widget.h"
     32 
     33 using content::OpenURLParams;
     34 using content::WebContents;
     35 
     36 namespace {
     37 
     38 const int kPadding = 20;
     39 const float kMessageSize = 0.65f;
     40 const SkColor kTextColor = SK_ColorWHITE;
     41 const SkColor kCrashColor = SkColorSetRGB(35, 48, 64);
     42 const SkColor kKillColor = SkColorSetRGB(57, 48, 88);
     43 
     44 const char kCategoryTagCrash[] = "Crash";
     45 
     46 }  // namespace
     47 
     48 SadTabView::SadTabView(WebContents* web_contents, chrome::SadTabKind kind)
     49     : web_contents_(web_contents),
     50       kind_(kind),
     51       painted_(false),
     52       message_(NULL),
     53       help_link_(NULL),
     54       feedback_link_(NULL),
     55       reload_button_(NULL) {
     56   DCHECK(web_contents);
     57 
     58   // Sometimes the user will never see this tab, so keep track of the total
     59   // number of creation events to compare to display events.
     60   // TODO(jamescook): Remove this after R20 stable.  Keep it for now so we can
     61   // compare R20 to earlier versions.
     62   UMA_HISTOGRAM_COUNTS("SadTab.Created", kind_);
     63 
     64   // These stats should use the same counting approach and bucket size used for
     65   // tab discard events in chromeos::OomPriorityManager so they can be
     66   // directly compared.
     67   // TODO(jamescook): Maybe track time between sad tabs?
     68   switch (kind_) {
     69     case chrome::SAD_TAB_KIND_CRASHED: {
     70       static int crashed = 0;
     71       crashed++;
     72       UMA_HISTOGRAM_CUSTOM_COUNTS(
     73           "Tabs.SadTab.CrashCreated", crashed, 1, 1000, 50);
     74       break;
     75     }
     76     case chrome::SAD_TAB_KIND_KILLED: {
     77       static int killed = 0;
     78       killed++;
     79       UMA_HISTOGRAM_CUSTOM_COUNTS(
     80           "Tabs.SadTab.KillCreated", killed, 1, 1000, 50);
     81       break;
     82     }
     83     default:
     84       NOTREACHED();
     85   }
     86 
     87   // Set the background color.
     88   set_background(views::Background::CreateSolidBackground(
     89       (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? kCrashColor : kKillColor));
     90 
     91   views::GridLayout* layout = new views::GridLayout(this);
     92   SetLayoutManager(layout);
     93 
     94   const int column_set_id = 0;
     95   views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
     96   columns->AddPaddingColumn(1, kPadding);
     97   columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
     98                      0, views::GridLayout::USE_PREF, 0, 0);
     99   columns->AddPaddingColumn(1, kPadding);
    100 
    101   views::ImageView* image = new views::ImageView();
    102   image->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
    103       (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? IDR_SAD_TAB : IDR_KILLED_TAB));
    104   layout->StartRowWithPadding(0, column_set_id, 1, kPadding);
    105   layout->AddView(image);
    106 
    107   views::Label* title = CreateLabel(l10n_util::GetStringUTF16(
    108       (kind_ == chrome::SAD_TAB_KIND_CRASHED) ?
    109           IDS_SAD_TAB_TITLE : IDS_KILLED_TAB_TITLE));
    110   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    111   title->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
    112   layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
    113   layout->AddView(title);
    114 
    115   message_ = CreateLabel(l10n_util::GetStringUTF16(
    116       (kind_ == chrome::SAD_TAB_KIND_CRASHED) ?
    117           IDS_SAD_TAB_MESSAGE : IDS_KILLED_TAB_MESSAGE));
    118   message_->SetMultiLine(true);
    119   layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
    120   layout->AddView(message_);
    121 
    122   if (web_contents_) {
    123     layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
    124     reload_button_ = new views::LabelButton(
    125         this,
    126         l10n_util::GetStringUTF16(IDS_SAD_TAB_RELOAD_LABEL));
    127     reload_button_->SetStyle(views::Button::STYLE_BUTTON);
    128     // Always render the reload button with chrome style borders; never rely on
    129     // native styles.
    130     reload_button_->SetBorder(scoped_ptr<views::Border>(
    131         new views::LabelButtonBorder(reload_button_->style())));
    132     layout->AddView(reload_button_);
    133 
    134     help_link_ = CreateLink(l10n_util::GetStringUTF16(
    135         (kind_ == chrome::SAD_TAB_KIND_CRASHED) ?
    136             IDS_SAD_TAB_HELP_LINK : IDS_LEARN_MORE));
    137 
    138     if (kind_ == chrome::SAD_TAB_KIND_CRASHED) {
    139       size_t offset = 0;
    140       base::string16 help_text(
    141           l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE,
    142                                      base::string16(), &offset));
    143       views::Label* help_prefix = CreateLabel(help_text.substr(0, offset));
    144       views::Label* help_suffix = CreateLabel(help_text.substr(offset));
    145 
    146       const int help_column_set_id = 1;
    147       views::ColumnSet* help_columns = layout->AddColumnSet(help_column_set_id);
    148       help_columns->AddPaddingColumn(1, kPadding);
    149       // Center three middle columns for the help's [prefix][link][suffix].
    150       for (size_t column = 0; column < 3; column++)
    151         help_columns->AddColumn(views::GridLayout::CENTER,
    152             views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
    153       help_columns->AddPaddingColumn(1, kPadding);
    154 
    155       layout->StartRowWithPadding(0, help_column_set_id, 0, kPadding);
    156       layout->AddView(help_prefix);
    157       layout->AddView(help_link_);
    158       layout->AddView(help_suffix);
    159     } else {
    160       layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
    161       layout->AddView(help_link_);
    162 
    163       feedback_link_ = CreateLink(
    164           l10n_util::GetStringUTF16(IDS_KILLED_TAB_FEEDBACK_LINK));
    165       layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
    166       layout->AddView(feedback_link_);
    167     }
    168   }
    169   layout->AddPaddingRow(1, kPadding);
    170 }
    171 
    172 SadTabView::~SadTabView() {}
    173 
    174 void SadTabView::LinkClicked(views::Link* source, int event_flags) {
    175   DCHECK(web_contents_);
    176   if (source == help_link_) {
    177     GURL help_url((kind_ == chrome::SAD_TAB_KIND_CRASHED) ?
    178         chrome::kCrashReasonURL : chrome::kKillReasonURL);
    179     OpenURLParams params(
    180         help_url, content::Referrer(), CURRENT_TAB,
    181         ui::PAGE_TRANSITION_LINK, false);
    182     web_contents_->OpenURL(params);
    183   } else if (source == feedback_link_) {
    184     chrome::ShowFeedbackPage(
    185         chrome::FindBrowserWithWebContents(web_contents_),
    186         l10n_util::GetStringUTF8(IDS_KILLED_TAB_FEEDBACK_MESSAGE),
    187         std::string(kCategoryTagCrash));
    188   }
    189 }
    190 
    191 void SadTabView::ButtonPressed(views::Button* sender,
    192                                const ui::Event& event) {
    193   DCHECK(web_contents_);
    194   DCHECK_EQ(reload_button_, sender);
    195   web_contents_->GetController().Reload(true);
    196 }
    197 
    198 void SadTabView::Layout() {
    199   // Specify the maximum message width explicitly.
    200   message_->SizeToFit(static_cast<int>(width() * kMessageSize));
    201   View::Layout();
    202 }
    203 
    204 void SadTabView::OnPaint(gfx::Canvas* canvas) {
    205   if (!painted_) {
    206     // User actually saw the error, keep track for user experience stats.
    207     // TODO(jamescook): Remove this after R20 stable.  Keep it for now so we can
    208     // compare R20 to earlier versions.
    209     UMA_HISTOGRAM_COUNTS("SadTab.Displayed", kind_);
    210 
    211     // These stats should use the same counting approach and bucket size used
    212     // for tab discard events in chromeos::OomPriorityManager so they
    213     // can be directly compared.
    214     switch (kind_) {
    215       case chrome::SAD_TAB_KIND_CRASHED: {
    216         static int crashed = 0;
    217         UMA_HISTOGRAM_CUSTOM_COUNTS(
    218             "Tabs.SadTab.CrashDisplayed", ++crashed, 1, 1000, 50);
    219         break;
    220       }
    221       case chrome::SAD_TAB_KIND_KILLED: {
    222         static int killed = 0;
    223         UMA_HISTOGRAM_CUSTOM_COUNTS(
    224             "Tabs.SadTab.KillDisplayed", ++killed, 1, 1000, 50);
    225         break;
    226       }
    227       default:
    228         NOTREACHED();
    229     }
    230     painted_ = true;
    231   }
    232   View::OnPaint(canvas);
    233 }
    234 
    235 void SadTabView::Show() {
    236   views::Widget::InitParams sad_tab_params(
    237       views::Widget::InitParams::TYPE_CONTROL);
    238 
    239   // It is not possible to create a native_widget_win that has no parent in
    240   // and later re-parent it.
    241   // TODO(avi): This is a cheat. Can this be made cleaner?
    242   sad_tab_params.parent = web_contents_->GetNativeView();
    243 
    244   set_owned_by_client();
    245 
    246   views::Widget* sad_tab = new views::Widget;
    247   sad_tab->Init(sad_tab_params);
    248   sad_tab->SetContentsView(this);
    249 
    250   views::Widget::ReparentNativeView(sad_tab->GetNativeView(),
    251                                     web_contents_->GetNativeView());
    252   gfx::Rect bounds = web_contents_->GetContainerBounds();
    253   sad_tab->SetBounds(gfx::Rect(bounds.size()));
    254 }
    255 
    256 void SadTabView::Close() {
    257   if (GetWidget())
    258     GetWidget()->Close();
    259 }
    260 
    261 views::Label* SadTabView::CreateLabel(const base::string16& text) {
    262   views::Label* label = new views::Label(text);
    263   label->SetBackgroundColor(background()->get_color());
    264   label->SetEnabledColor(kTextColor);
    265   return label;
    266 }
    267 
    268 views::Link* SadTabView::CreateLink(const base::string16& text) {
    269   views::Link* link = new views::Link(text);
    270   link->SetBackgroundColor(background()->get_color());
    271   link->SetEnabledColor(kTextColor);
    272   link->set_listener(this);
    273   return link;
    274 }
    275 
    276 namespace chrome {
    277 
    278 SadTab* SadTab::Create(content::WebContents* web_contents,
    279                        SadTabKind kind) {
    280   return new SadTabView(web_contents, kind);
    281 }
    282 
    283 }  // namespace chrome
    284