blob: 4a048847b4fd7d49fbd650dccae6f94e0e065b52 [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/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 "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.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/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();
// Configure layout.
views::GridLayout* layout =
SetLayoutManager(std::make_unique<views::GridLayout>());
constexpr int kColumnId = 0;
views::ColumnSet* label_col_set = layout->AddColumnSet(kColumnId);
label_col_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
1.0, views::GridLayout::USE_PREF, 0, 0);
// Add text description.
layout->StartRow(views::GridLayout::kFixedSize, kColumnId);
auto text_label = std::make_unique<views::Label>(l10n_util::GetStringUTF16(
safety_tips::GetSafetyTipDescriptionId(safety_tip_status)));
text_label->SetMultiLine(true);
text_label->SetLineHeight(20);
text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
text_label->SizeToFit(
layout_provider->GetDistanceMetric(DISTANCE_BUBBLE_PREFERRED_WIDTH));
layout->AddView(std::move(text_label));
// Add leave site button.
const int hover_list_spacing = layout_provider->GetDistanceMetric(
DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
layout->StartRowWithPadding(views::GridLayout::kFixedSize, kColumnId,
views::GridLayout::kFixedSize,
hover_list_spacing);
std::unique_ptr<views::Button> button(
views::MdTextButton::CreateSecondaryUiBlueButton(
this,
l10n_util::GetStringUTF16(
safety_tips::GetSafetyTipLeaveButtonId(safety_tip_status))));
button->SetID(PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_LEAVE_SITE);
leave_button_ =
layout->AddView(std::move(button), 1, 1, views::GridLayout::TRAILING,
views::GridLayout::LEADING);
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;
auto url = safety_tip_status_ == SafetyTipStatus::kLookalike
? suggested_url_
: GURL(safety_tips::kSafeUrl);
safety_tips::LeaveSite(web_contents(), url);
return;
}
NOTREACHED();
}
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));
}