blob: b2ceba64707866b946214f82fc720e80f74e85b3 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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 "base/types/optional_util.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.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/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/branded_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/weak_document_ptr.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.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 {
std::u16string GetMessageTextForOrigin(
const std::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 !BUILDFLAG(IS_CHROMEOS)
// static
void ExternalProtocolHandler::RunExternalProtocolDialog(
const GURL& url,
WebContents* web_contents,
ui::PageTransition ignored_page_transition,
bool ignored_has_user_gesture,
bool ignored_is_in_fenced_frame_tree,
const std::optional<url::Origin>& initiating_origin,
content::WeakDocumentPtr initiator_document,
const std::u16string& program_name) {
DCHECK(web_contents);
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,
std::move(initiator_document));
}
#endif // !BUILDFLAG(IS_CHROMEOS)
ExternalProtocolDialog::ExternalProtocolDialog(
WebContents* web_contents,
const GURL& url,
const std::u16string& program_name,
const std::optional<url::Origin>& initiating_origin,
content::WeakDocumentPtr initiator_document)
: web_contents_(web_contents->GetWeakPtr()),
url_(url),
program_name_(program_name),
initiating_origin_(initiating_origin),
initiator_document_(std::move(initiator_document)) {
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));
SetModalType(ui::MODAL_TYPE_CHILD);
message_box_view_ =
new views::MessageBoxView(GetMessageTextForOrigin(initiating_origin_));
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
set_margins(provider->GetDialogInsetsForContentType(
views::DialogContentType::kText, views::DialogContentType::kText));
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::OptionalToPtr(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);
}
ExternalProtocolDialog::~ExternalProtocolDialog() = default;
gfx::Size ExternalProtocolDialog::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
constexpr int kDialogContentWidth = 400;
return gfx::Size(kDialogContentWidth,
GetLayoutManager()->GetPreferredHeightForWidth(
this, kDialogContentWidth));
}
bool ExternalProtocolDialog::ShouldShowCloseButton() const {
return false;
}
std::u16string ExternalProtocolDialog::GetWindowTitle() const {
constexpr int kMaxCommandCharsToDisplay = 32;
std::u16string 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_.get(), initiator_document_);
}
views::View* ExternalProtocolDialog::GetContentsView() {
return message_box_view_;
}
views::Widget* ExternalProtocolDialog::GetWidget() {
return message_box_view_ ? message_box_view_->GetWidget() : nullptr;
}
const views::Widget* ExternalProtocolDialog::GetWidget() const {
return message_box_view_ ? message_box_view_->GetWidget() : nullptr;
}
void ExternalProtocolDialog::SetRememberSelectionCheckboxCheckedForTesting(
bool checked) {
message_box_view_->SetCheckBoxSelected(checked);
}
BEGIN_METADATA(ExternalProtocolDialog)
END_METADATA