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