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