1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/external_protocol_dialog.h"
7 #include "base/metrics/histogram.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/threading/thread.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "base/win/registry.h"
13 #include "chrome/browser/external_protocol/external_protocol_handler.h"
14 #include "chrome/browser/tab_contents/tab_util.h"
15 #include "chrome/browser/ui/views/constrained_window_views.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_view.h"
18 #include "grit/chromium_strings.h"
19 #include "grit/generated_resources.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/gfx/text_elider.h"
22 #include "ui/views/controls/message_box_view.h"
23 #include "ui/views/widget/widget.h"
25 using content::WebContents
;
29 const int kMessageWidth
= 400;
33 ///////////////////////////////////////////////////////////////////////////////
34 // ExternalProtocolHandler
37 void ExternalProtocolHandler::RunExternalProtocolDialog(
38 const GURL
& url
, int render_process_host_id
, int routing_id
) {
39 std::wstring command
=
40 ExternalProtocolDialog::GetApplicationForProtocol(url
);
41 if (command
.empty()) {
42 // ShellExecute won't do anything. Don't bother warning the user.
45 // Windowing system takes ownership.
46 new ExternalProtocolDialog(url
, render_process_host_id
, routing_id
, command
);
49 ///////////////////////////////////////////////////////////////////////////////
50 // ExternalProtocolDialog
52 ExternalProtocolDialog::~ExternalProtocolDialog() {
55 //////////////////////////////////////////////////////////////////////////////
56 // ExternalProtocolDialog, views::DialogDelegate implementation:
58 int ExternalProtocolDialog::GetDefaultDialogButton() const {
59 return ui::DIALOG_BUTTON_CANCEL
;
62 base::string16
ExternalProtocolDialog::GetDialogButtonLabel(
63 ui::DialogButton button
) const {
64 if (button
== ui::DIALOG_BUTTON_OK
)
65 return l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT
);
67 return l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_CANCEL_BUTTON_TEXT
);
70 base::string16
ExternalProtocolDialog::GetWindowTitle() const {
71 return l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_TITLE
);
74 void ExternalProtocolDialog::DeleteDelegate() {
78 bool ExternalProtocolDialog::Cancel() {
79 // We also get called back here if the user closes the dialog or presses
80 // escape. In these cases it would be preferable to ignore the state of the
81 // check box but MessageBox doesn't distinguish this from pressing the cancel
83 if (message_box_view_
->IsCheckBoxSelected()) {
84 ExternalProtocolHandler::SetBlockState(
85 url_
.scheme(), ExternalProtocolHandler::BLOCK
);
88 // Returning true closes the dialog.
92 bool ExternalProtocolDialog::Accept() {
93 // We record how long it takes the user to accept an external protocol. If
94 // users start accepting these dialogs too quickly, we should worry about
96 UMA_HISTOGRAM_LONG_TIMES("clickjacking.launch_url",
97 base::TimeTicks::Now() - creation_time_
);
99 if (message_box_view_
->IsCheckBoxSelected()) {
100 ExternalProtocolHandler::SetBlockState(
101 url_
.scheme(), ExternalProtocolHandler::DONT_BLOCK
);
104 ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
105 url_
, render_process_host_id_
, routing_id_
);
106 // Returning true closes the dialog.
110 views::View
* ExternalProtocolDialog::GetContentsView() {
111 return message_box_view_
;
114 views::Widget
* ExternalProtocolDialog::GetWidget() {
115 return message_box_view_
->GetWidget();
118 const views::Widget
* ExternalProtocolDialog::GetWidget() const {
119 return message_box_view_
->GetWidget();
122 ///////////////////////////////////////////////////////////////////////////////
123 // ExternalProtocolDialog, private:
125 ExternalProtocolDialog::ExternalProtocolDialog(const GURL
& url
,
126 int render_process_host_id
,
128 const std::wstring
& command
)
130 render_process_host_id_(render_process_host_id
),
131 routing_id_(routing_id
),
132 creation_time_(base::TimeTicks::Now()) {
133 const int kMaxUrlWithoutSchemeSize
= 256;
134 const int kMaxCommandSize
= 256;
135 base::string16 elided_url_without_scheme
;
136 base::string16 elided_command
;
137 gfx::ElideString(base::ASCIIToUTF16(url
.possibly_invalid_spec()),
138 kMaxUrlWithoutSchemeSize
, &elided_url_without_scheme
);
139 gfx::ElideString(base::WideToUTF16Hack(command
),
140 kMaxCommandSize
, &elided_command
);
142 base::string16 message_text
= l10n_util::GetStringFUTF16(
143 IDS_EXTERNAL_PROTOCOL_INFORMATION
,
144 base::ASCIIToUTF16(url
.scheme() + ":"),
145 elided_url_without_scheme
) + base::ASCIIToUTF16("\n\n");
147 message_text
+= l10n_util::GetStringFUTF16(
148 IDS_EXTERNAL_PROTOCOL_APPLICATION_TO_LAUNCH
,
149 elided_command
) + base::ASCIIToUTF16("\n\n");
151 message_text
+= l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_WARNING
);
153 views::MessageBoxView::InitParams
params(message_text
);
154 params
.message_width
= kMessageWidth
;
155 message_box_view_
= new views::MessageBoxView(params
);
156 message_box_view_
->SetCheckBoxLabel(
157 l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_CHECKBOX_TEXT
));
159 // Dialog is top level if we don't have a web_contents associated with us.
160 WebContents
* web_contents
= tab_util::GetWebContentsByID(
161 render_process_host_id_
, routing_id_
);
162 gfx::NativeWindow parent_window
= NULL
;
164 parent_window
= web_contents
->GetView()->GetTopLevelNativeWindow();
165 CreateBrowserModalDialogViews(this, parent_window
)->Show();
169 std::wstring
ExternalProtocolDialog::GetApplicationForProtocol(
171 // We shouldn't be accessing the registry from the UI thread, since it can go
172 // to disk. http://crbug.com/61996
173 base::ThreadRestrictions::ScopedAllowIO allow_io
;
175 std::wstring url_spec
= base::ASCIIToWide(url
.possibly_invalid_spec());
176 std::wstring cmd_key_path
=
177 base::ASCIIToWide(url
.scheme() + "\\shell\\open\\command");
178 base::win::RegKey
cmd_key(HKEY_CLASSES_ROOT
, cmd_key_path
.c_str(), KEY_READ
);
179 size_t split_offset
= url_spec
.find(L
':');
180 if (split_offset
== std::wstring::npos
)
181 return std::wstring();
182 std::wstring parameters
= url_spec
.substr(split_offset
+ 1,
183 url_spec
.length() - 1);
184 std::wstring application_to_launch
;
185 if (cmd_key
.ReadValue(NULL
, &application_to_launch
) == ERROR_SUCCESS
) {
186 ReplaceSubstringsAfterOffset(&application_to_launch
, 0, L
"%1", parameters
);
187 return application_to_launch
;
189 return std::wstring();