blob: d56289355f1aaeeaddf9ba9f516661edaa4e4dc4 [file] [log] [blame]
// Copyright 2019 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/page_info/safety_tip_page_info_bubble_view.h"
#include "chrome/browser/lookalikes/safety_tips/reputation_service.h"
#include "chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
#include "chrome/browser/ui/views/bubble_anchor_util_views.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
#include "chrome/grit/theme_resources.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/style/typography.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_client_view.h"
#include "url/gurl.h"
using security_state::SafetyTipStatus;
SafetyTipPageInfoBubbleView::SafetyTipPageInfoBubbleView(
views::View* anchor_view,
const gfx::Rect& anchor_rect,
gfx::NativeView parent_window,
content::WebContents* web_contents,
security_state::SafetyTipStatus safety_tip_status,
const GURL& url,
const GURL& suggested_url,
base::OnceCallback<void(safety_tips::SafetyTipInteraction)> close_callback)
: PageInfoBubbleViewBase(anchor_view,
anchor_rect,
parent_window,
PageInfoBubbleViewBase::BUBBLE_SAFETY_TIP,
web_contents),
safety_tip_status_(safety_tip_status),
url_(url),
suggested_url_(suggested_url),
close_callback_(std::move(close_callback)) {
// Keep the bubble open until explicitly closed (or we navigate away, a tab is
// created over it, etc).
set_close_on_deactivate(false);
const base::string16 title_text =
safety_tips::GetSafetyTipTitle(safety_tip_status, suggested_url);
set_window_title(title_text);
views::BubbleDialogDelegateView::CreateBubble(this);
// Replace the original title view with our formatted title.
views::Label* original_title =
static_cast<views::Label*>(GetBubbleFrameView()->title());
views::StyledLabel::RangeStyleInfo name_style;
const auto kSizeDeltaInPixels = 3;
name_style.custom_font = original_title->GetDefaultFontList().Derive(
kSizeDeltaInPixels, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::BOLD);
views::StyledLabel::RangeStyleInfo base_style;
base_style.custom_font = original_title->GetDefaultFontList().Derive(
kSizeDeltaInPixels, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL);
auto new_title = std::make_unique<views::StyledLabel>(title_text, nullptr);
new_title->AddStyleRange(gfx::Range(0, title_text.length()), name_style);
GetBubbleFrameView()->SetTitleView(std::move(new_title));
ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
gfx::Insets insets =
layout_provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT);
set_margins(gfx::Insets(0, 0, insets.bottom(), 0));
// Configure layout.
views::GridLayout* bubble_layout =
SetLayoutManager(std::make_unique<views::GridLayout>());
constexpr int kColumnId = 0;
views::ColumnSet* bubble_col_set = bubble_layout->AddColumnSet(kColumnId);
bubble_col_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
1.0, views::GridLayout::USE_PREF, 0, 0);
// TODO(crbug/996731): Add banner once available. See crrev/c/1816805/7.
auto bottom_view = std::make_unique<views::View>();
views::GridLayout* bottom_layout =
bottom_view->SetLayoutManager(std::make_unique<views::GridLayout>());
views::ColumnSet* bottom_column_set = bottom_layout->AddColumnSet(0);
bottom_column_set->AddPaddingColumn(views::GridLayout::kFixedSize,
insets.left());
bottom_column_set->AddColumn(views::GridLayout::LEADING,
views::GridLayout::FILL, 1.0,
views::GridLayout::USE_PREF, 0, 0);
bottom_column_set->AddPaddingColumn(views::GridLayout::kFixedSize,
insets.right());
// Add text description.
const int spacing =
layout_provider->GetDistanceMetric(DISTANCE_CONTROL_LIST_VERTICAL);
bottom_layout->StartRowWithPadding(views::GridLayout::kFixedSize, kColumnId,
views::GridLayout::kFixedSize, spacing);
auto text_label = std::make_unique<views::Label>(
safety_tips::GetSafetyTipDescription(safety_tip_status, suggested_url_));
text_label->SetMultiLine(true);
text_label->SetLineHeight(20);
text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
text_label->SizeToFit(
layout_provider->GetDistanceMetric(DISTANCE_BUBBLE_PREFERRED_WIDTH) -
insets.left() - insets.right());
bottom_layout->AddView(std::move(text_label));
// Add buttons.
// To make the rest of the layout simpler, they live in their own grid layout.
auto button_view = std::make_unique<views::View>();
views::GridLayout* button_layout =
button_view->SetLayoutManager(std::make_unique<views::GridLayout>());
views::ColumnSet* button_column_set = button_layout->AddColumnSet(0);
button_column_set->AddColumn(views::GridLayout::LEADING,
views::GridLayout::CENTER, 0.0,
views::GridLayout::USE_PREF, 0, 0);
button_column_set->AddPaddingColumn(1.f, 1);
button_column_set->AddColumn(views::GridLayout::TRAILING,
views::GridLayout::FILL, 0.0,
views::GridLayout::USE_PREF, 0, 0);
button_column_set->AddPaddingColumn(
views::GridLayout::kFixedSize,
layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_BUTTON_HORIZONTAL));
button_column_set->AddColumn(views::GridLayout::TRAILING,
views::GridLayout::FILL, 0.0,
views::GridLayout::USE_PREF, 0, 0);
button_layout->StartRow(views::GridLayout::kFixedSize, kColumnId);
// More info button.
auto info_text =
l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_MORE_INFO_LINK);
auto info_link = std::make_unique<views::StyledLabel>(info_text, this);
views::StyledLabel::RangeStyleInfo link_style =
views::StyledLabel::RangeStyleInfo::CreateForLink();
gfx::Range details_range(0, info_text.length());
info_link->AddStyleRange(details_range, link_style);
info_link->SizeToFit(0);
info_button_ = button_layout->AddView(std::move(info_link));
// Ignore button.
std::unique_ptr<views::Button> ignore_button(
views::MdTextButton::CreateSecondaryUiButton(
this,
l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_IGNORE_BUTTON)));
ignore_button->SetID(
PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_IGNORE_WARNING);
ignore_button_ = button_layout->AddView(std::move(ignore_button));
// Leave site button.
std::unique_ptr<views::Button> leave_button(
views::MdTextButton::CreateSecondaryUiBlueButton(
this,
l10n_util::GetStringUTF16(
safety_tips::GetSafetyTipLeaveButtonId(safety_tip_status))));
leave_button->SetID(PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_LEAVE_SITE);
leave_button_ = button_layout->AddView(std::move(leave_button));
bottom_layout->StartRowWithPadding(views::GridLayout::kFixedSize, kColumnId,
views::GridLayout::kFixedSize, spacing);
bottom_layout->AddView(
std::move(button_view), 1, 1, views::GridLayout::LEADING,
views::GridLayout::LEADING,
layout_provider->GetDistanceMetric(DISTANCE_BUBBLE_PREFERRED_WIDTH) -
insets.left() - insets.right(),
0);
bubble_layout->StartRow(views::GridLayout::kFixedSize, kColumnId);
bubble_layout->AddView(std::move(bottom_view));
Layout();
SizeToContents();
}
SafetyTipPageInfoBubbleView::~SafetyTipPageInfoBubbleView() {}
void SafetyTipPageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
PageInfoBubbleViewBase::OnWidgetDestroying(widget);
bool should_set_ignore = false;
switch (widget->closed_reason()) {
case views::Widget::ClosedReason::kUnspecified:
case views::Widget::ClosedReason::kLostFocus:
// We require that the user explicitly interact with the bubble, so do
// nothing in these cases.
break;
case views::Widget::ClosedReason::kAcceptButtonClicked:
// If they've left the site, we can still ignore the result; if they
// stumble there again, we should warn again.
break;
case views::Widget::ClosedReason::kEscKeyPressed:
action_taken_ = safety_tips::SafetyTipInteraction::kDismissWithEsc;
should_set_ignore = true;
break;
case views::Widget::ClosedReason::kCloseButtonClicked:
action_taken_ = safety_tips::SafetyTipInteraction::kDismissWithClose;
should_set_ignore = true;
break;
case views::Widget::ClosedReason::kCancelButtonClicked:
action_taken_ = safety_tips::SafetyTipInteraction::kDismissWithIgnore;
should_set_ignore = true;
break;
}
std::move(close_callback_).Run(action_taken_);
if (should_set_ignore) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
if (browser) {
safety_tips::ReputationService::Get(browser->profile())
->SetUserIgnore(web_contents(), url_, action_taken_);
}
}
}
void SafetyTipPageInfoBubbleView::ButtonPressed(views::Button* button,
const ui::Event& event) {
switch (button->GetID()) {
case PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_LEAVE_SITE:
action_taken_ = safety_tips::SafetyTipInteraction::kLeaveSite;
safety_tips::LeaveSite(web_contents(),
safety_tip_status_ == SafetyTipStatus::kLookalike
? suggested_url_
: GURL(safety_tips::kSafeUrl));
return;
case PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_IGNORE_WARNING:
action_taken_ = safety_tips::SafetyTipInteraction::kDismiss;
GetWidget()->CloseWithReason(
views::Widget::ClosedReason::kCancelButtonClicked);
return;
}
NOTREACHED();
}
void SafetyTipPageInfoBubbleView::StyledLabelLinkClicked(
views::StyledLabel* label,
const gfx::Range& range,
int event_flags) {
action_taken_ = safety_tips::SafetyTipInteraction::kLearnMore;
safety_tips::OpenHelpCenter(web_contents());
}
namespace safety_tips {
void ShowSafetyTipDialog(
content::WebContents* web_contents,
security_state::SafetyTipStatus safety_tip_status,
const GURL& virtual_url,
const GURL& suggested_url,
base::OnceCallback<void(SafetyTipInteraction)> close_callback) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
if (!browser)
return;
bubble_anchor_util::AnchorConfiguration configuration =
bubble_anchor_util::GetPageInfoAnchorConfiguration(
browser, bubble_anchor_util::kLocationBar);
gfx::Rect anchor_rect =
configuration.anchor_view
? gfx::Rect()
: bubble_anchor_util::GetPageInfoAnchorRect(browser);
gfx::NativeWindow parent_window = browser->window()->GetNativeWindow();
gfx::NativeView parent_view = platform_util::GetViewForWindow(parent_window);
views::BubbleDialogDelegateView* bubble = new SafetyTipPageInfoBubbleView(
configuration.anchor_view, anchor_rect, parent_view, web_contents,
safety_tip_status, virtual_url, suggested_url, std::move(close_callback));
bubble->SetHighlightedButton(configuration.highlighted_button);
bubble->SetArrow(configuration.bubble_arrow);
bubble->GetWidget()->Show();
}
} // namespace safety_tips
PageInfoBubbleViewBase* CreateSafetyTipBubbleForTesting(
gfx::NativeView parent_view,
content::WebContents* web_contents,
security_state::SafetyTipStatus safety_tip_status,
const GURL& virtual_url,
const GURL& suggested_url,
base::OnceCallback<void(safety_tips::SafetyTipInteraction)>
close_callback) {
return new SafetyTipPageInfoBubbleView(
nullptr, gfx::Rect(), parent_view, web_contents, safety_tip_status,
virtual_url, suggested_url, std::move(close_callback));
}