blob: 0f8eb98bcc0d3219fd462e30d885a4b41c6d084b [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/sad_tab_view.h"
#include <string>
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/bulleted_label_list_view.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "content/public/browser/web_contents.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/native_theme/common_theme.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/link.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/widget/widget.h"
namespace {
constexpr int kMaxContentWidth = 600;
constexpr int kMinColumnWidth = 120;
constexpr int kTitleBottomSpacing = 13;
views::Label* CreateFormattedLabel(const base::string16& message) {
views::Label* label =
new views::Label(message, views::style::CONTEXT_LABEL, STYLE_SECONDARY);
label->SetMultiLine(true);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
label->SetLineHeight(ChromeLayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
return label;
}
} // namespace
// static
const char SadTabView::kViewClassName[] = "SadTabView";
SadTabView::SadTabView(content::WebContents* web_contents, SadTabKind kind)
: SadTab(web_contents, kind) {
// This view gets inserted as a child of a WebView, but we don't want the
// WebView to delete us if the WebView gets deleted before the SadTabHelper
// does.
set_owned_by_client();
SetBackground(views::CreateThemedSolidBackground(
this, ui::NativeTheme::kColorId_DialogBackground));
views::GridLayout* layout =
SetLayoutManager(std::make_unique<views::GridLayout>(this));
const int column_set_id = 0;
views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
// TODO(ananta)
// This view should probably be styled as web UI.
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
const int unrelated_horizontal_spacing = provider->GetDistanceMetric(
DISTANCE_UNRELATED_CONTROL_HORIZONTAL);
columns->AddPaddingColumn(1.0, unrelated_horizontal_spacing);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
views::GridLayout::kFixedSize, views::GridLayout::USE_PREF,
0, kMinColumnWidth);
columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::LEADING,
views::GridLayout::kFixedSize, views::GridLayout::USE_PREF,
0, kMinColumnWidth);
columns->AddPaddingColumn(1.0, unrelated_horizontal_spacing);
views::ImageView* image = new views::ImageView();
image->SetImage(
gfx::CreateVectorIcon(kCrashedTabIcon, 48, gfx::kChromeIconGrey));
const int unrelated_vertical_spacing_large = provider->GetDistanceMetric(
DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE);
layout->AddPaddingRow(1.0, unrelated_vertical_spacing_large);
layout->StartRow(views::GridLayout::kFixedSize, column_set_id);
layout->AddView(image, 2, 1);
title_ = new views::Label(l10n_util::GetStringUTF16(GetTitle()));
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
title_->SetFontList(rb.GetFontList(ui::ResourceBundle::LargeFont));
title_->SetMultiLine(true);
title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
layout->StartRowWithPadding(views::GridLayout::kFixedSize, column_set_id,
views::GridLayout::kFixedSize,
unrelated_vertical_spacing_large);
layout->AddView(title_, 2, 1.0);
message_ = CreateFormattedLabel(l10n_util::GetStringUTF16(GetInfoMessage()));
layout->StartRowWithPadding(views::GridLayout::kFixedSize, column_set_id,
views::GridLayout::kFixedSize,
kTitleBottomSpacing);
layout->AddView(message_, 2, 1.0, views::GridLayout::LEADING,
views::GridLayout::LEADING);
std::vector<int> bullet_string_ids = GetSubMessages();
if (!bullet_string_ids.empty()) {
auto list_view = std::make_unique<BulletedLabelListView>();
for (const auto& id : bullet_string_ids)
list_view->AddLabel(l10n_util::GetStringUTF16(id));
layout->StartRow(views::GridLayout::kFixedSize, column_set_id);
layout->AddView(list_view.release(), 2, 1.0);
}
action_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
this, l10n_util::GetStringUTF16(GetButtonTitle()));
help_link_ = new views::Link(l10n_util::GetStringUTF16(GetHelpLinkTitle()));
help_link_->set_listener(this);
layout->StartRowWithPadding(views::GridLayout::kFixedSize, column_set_id,
views::GridLayout::kFixedSize,
unrelated_vertical_spacing_large);
layout->AddView(help_link_, 1.0, 1.0, views::GridLayout::LEADING,
views::GridLayout::CENTER);
layout->AddView(action_button_, 1.0, 1.0, views::GridLayout::TRAILING,
views::GridLayout::LEADING);
layout->AddPaddingRow(2, provider->GetDistanceMetric(
views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
// Needed to ensure this View is drawn even if a sibling (such as dev tools)
// has a z-order.
SetPaintToLayer();
AttachToWebView();
// Make the accessibility role of this view an alert dialog, and
// put focus on the action button. This causes screen readers to
// immediately announce the text of this view.
GetViewAccessibility().OverrideRole(ax::mojom::Role::kDialog);
if (action_button_->GetWidget() && action_button_->GetWidget()->IsActive())
action_button_->RequestFocus();
}
SadTabView::~SadTabView() {
if (owner_)
owner_->SetCrashedOverlayView(nullptr);
}
void SadTabView::ReinstallInWebView() {
if (owner_) {
owner_->SetCrashedOverlayView(nullptr);
owner_ = nullptr;
}
AttachToWebView();
}
void SadTabView::AttachToWebView() {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
// This can be null during prefetch.
if (!browser)
return;
// In unit tests, browser->window() might not be a real BrowserView.
if (!browser->window()->GetNativeWindow())
return;
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
DCHECK(browser_view);
views::WebView* web_view = browser_view->contents_web_view();
if (web_view->GetWebContents() == web_contents()) {
owner_ = web_view;
owner_->SetCrashedOverlayView(this);
}
}
void SadTabView::LinkClicked(views::Link* source, int event_flags) {
PerformAction(Action::HELP_LINK);
}
void SadTabView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
DCHECK_EQ(action_button_, sender);
PerformAction(Action::BUTTON);
}
void SadTabView::Layout() {
// Specify the maximum message width explicitly.
const int max_width =
std::min(width() - ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_UNRELATED_CONTROL_HORIZONTAL) * 2, kMaxContentWidth);
message_->SizeToFit(max_width);
title_->SizeToFit(max_width);
View::Layout();
}
const char* SadTabView::GetClassName() const {
return kViewClassName;
}
void SadTabView::OnPaint(gfx::Canvas* canvas) {
if (!painted_) {
RecordFirstPaint();
painted_ = true;
}
View::OnPaint(canvas);
}
void SadTabView::RemovedFromWidget() {
owner_ = nullptr;
}
SadTab* SadTab::Create(content::WebContents* web_contents,
SadTabKind kind) {
return new SadTabView(web_contents, kind);
}