blob: be89aa5da0ff0214b426d0de9ed44bcae5fb8f81 [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/external_protocol_dialog.h"
#include <utility>
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/prefs/pref_service.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/text_elider.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/message_box_view.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
using content::WebContents;
namespace {
base::string16 GetMessageTextForOrigin(
const base::Optional<url::Origin>& origin) {
if (!origin || origin->opaque())
return l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_MESSAGE);
return l10n_util::GetStringFUTF16(
IDS_EXTERNAL_PROTOCOL_MESSAGE_WITH_INITIATING_ORIGIN,
url_formatter::FormatOriginForSecurityDisplay(*origin));
}
} // namespace
#if !defined(OS_CHROMEOS)
// static
void ExternalProtocolHandler::RunExternalProtocolDialog(
const GURL& url,
WebContents* web_contents,
ui::PageTransition ignored_page_transition,
bool ignored_has_user_gesture,
const base::Optional<url::Origin>& initiating_origin) {
DCHECK(web_contents);
base::string16 program_name =
shell_integration::GetApplicationNameForProtocol(url);
if (program_name.empty()) {
// ShellExecute won't do anything. Don't bother warning the user.
return;
}
// Windowing system takes ownership.
new ExternalProtocolDialog(web_contents, url, program_name,
initiating_origin);
}
#endif // !defined(OS_CHROMEOS)
ExternalProtocolDialog::ExternalProtocolDialog(
WebContents* web_contents,
const GURL& url,
const base::string16& program_name,
const base::Optional<url::Origin>& initiating_origin)
: content::WebContentsObserver(web_contents),
url_(url),
program_name_(program_name),
initiating_origin_(initiating_origin) {
SetDefaultButton(ui::DIALOG_BUTTON_CANCEL);
SetButtonLabel(ui::DIALOG_BUTTON_OK,
l10n_util::GetStringFUTF16(
IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT, program_name_));
SetButtonLabel(
ui::DIALOG_BUTTON_CANCEL,
l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_CANCEL_BUTTON_TEXT));
SetAcceptCallback(base::BindOnce(&ExternalProtocolDialog::OnDialogAccepted,
base::Unretained(this)));
SetCancelCallback(base::BindOnce(
&ExternalProtocolHandler::RecordHandleStateMetrics,
false /* checkbox_selected */, ExternalProtocolHandler::BLOCK));
SetCloseCallback(base::BindOnce(
&ExternalProtocolHandler::RecordHandleStateMetrics,
false /* checkbox_selected */, ExternalProtocolHandler::BLOCK));
message_box_view_ =
new views::MessageBoxView(GetMessageTextForOrigin(initiating_origin_));
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
set_margins(
provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT));
SetLayoutManager(std::make_unique<views::FillLayout>());
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
// The checkbox allows the user to opt-in to relaxed security
// (i.e. skipping future prompts) for the combination of the
// protocol and the origin of the page initiating this external
// protocol launch. The checkbox is offered so long as the
// group policy to show the checkbox is not explicitly disabled
// and there is a trustworthy initiating origin.
bool show_remember_selection_checkbox =
profile->GetPrefs()->GetBoolean(
prefs::kExternalProtocolDialogShowAlwaysOpenCheckbox) &&
ExternalProtocolHandler::MayRememberAllowDecisionsForThisOrigin(
base::OptionalOrNullptr(initiating_origin_));
if (show_remember_selection_checkbox) {
message_box_view_->SetCheckBoxLabel(l10n_util::GetStringFUTF16(
IDS_EXTERNAL_PROTOCOL_CHECKBOX_PER_ORIGIN_TEXT,
url_formatter::FormatOriginForSecurityDisplay(
initiating_origin_.value(),
/*scheme_display = */ url_formatter::SchemeDisplay::
OMIT_CRYPTOGRAPHIC)));
}
constrained_window::ShowWebModalDialogViews(this, web_contents);
chrome::RecordDialogCreation(chrome::DialogIdentifier::EXTERNAL_PROTOCOL);
}
ExternalProtocolDialog::~ExternalProtocolDialog() = default;
gfx::Size ExternalProtocolDialog::CalculatePreferredSize() const {
constexpr int kDialogContentWidth = 400;
return gfx::Size(kDialogContentWidth, GetHeightForWidth(kDialogContentWidth));
}
bool ExternalProtocolDialog::ShouldShowCloseButton() const {
return false;
}
base::string16 ExternalProtocolDialog::GetWindowTitle() const {
constexpr int kMaxCommandCharsToDisplay = 32;
base::string16 elided;
gfx::ElideString(program_name_, kMaxCommandCharsToDisplay, &elided);
return l10n_util::GetStringFUTF16(IDS_EXTERNAL_PROTOCOL_TITLE, elided);
}
void ExternalProtocolDialog::OnDialogAccepted() {
const bool remember = message_box_view_->IsCheckBoxSelected();
ExternalProtocolHandler::RecordHandleStateMetrics(
remember, ExternalProtocolHandler::DONT_BLOCK);
if (!web_contents()) {
// Dialog outlasted the WebContents.
return;
}
if (remember) {
DCHECK(initiating_origin_);
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
ExternalProtocolHandler::SetBlockState(url_.scheme(), *initiating_origin_,
ExternalProtocolHandler::DONT_BLOCK,
profile);
}
ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_, web_contents());
}
views::View* ExternalProtocolDialog::GetContentsView() {
return message_box_view_;
}
ui::ModalType ExternalProtocolDialog::GetModalType() const {
return ui::MODAL_TYPE_CHILD;
}
views::Widget* ExternalProtocolDialog::GetWidget() {
return message_box_view_->GetWidget();
}
const views::Widget* ExternalProtocolDialog::GetWidget() const {
return message_box_view_->GetWidget();
}
void ExternalProtocolDialog::SetRememberSelectionCheckboxCheckedForTesting(
bool checked) {
message_box_view_->SetCheckBoxSelected(checked);
}