1 // Copyright 2014 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 "components/app_modal/javascript_dialog_manager.h"
8 #include "base/i18n/rtl.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/app_modal/app_modal_dialog.h"
11 #include "components/app_modal/app_modal_dialog_queue.h"
12 #include "components/app_modal/javascript_app_modal_dialog.h"
13 #include "components/app_modal/javascript_dialog_extensions_client.h"
14 #include "components/app_modal/javascript_native_dialog_factory.h"
15 #include "components/app_modal/native_app_modal_dialog.h"
16 #include "components/url_formatter/elide_url.h"
17 #include "content/public/common/javascript_message_type.h"
18 #include "grit/components_strings.h"
19 #include "net/base/net_util.h"
20 #include "ui/base/l10n/l10n_util.h"
25 class DefaultExtensionsClient
: public JavaScriptDialogExtensionsClient
{
27 DefaultExtensionsClient() {}
28 ~DefaultExtensionsClient() override
{}
31 // JavaScriptDialogExtensionsClient:
32 void OnDialogOpened(content::WebContents
* web_contents
) override
{}
33 void OnDialogClosed(content::WebContents
* web_contents
) override
{}
34 bool GetExtensionName(content::WebContents
* web_contents
,
35 const GURL
& origin_url
,
36 std::string
* name_out
) override
{
40 DISALLOW_COPY_AND_ASSIGN(DefaultExtensionsClient
);
43 bool ShouldDisplaySuppressCheckbox(
44 ChromeJavaScriptDialogExtraData
* extra_data
) {
45 return extra_data
->has_already_shown_a_dialog_
;
50 ////////////////////////////////////////////////////////////////////////////////
51 // JavaScriptDialogManager, public:
54 JavaScriptDialogManager
* JavaScriptDialogManager::GetInstance() {
55 return base::Singleton
<JavaScriptDialogManager
>::get();
58 void JavaScriptDialogManager::SetNativeDialogFactory(
59 scoped_ptr
<JavaScriptNativeDialogFactory
> factory
) {
60 native_dialog_factory_
= factory
.Pass();
63 void JavaScriptDialogManager::SetExtensionsClient(
64 scoped_ptr
<JavaScriptDialogExtensionsClient
> extensions_client
) {
65 extensions_client_
= extensions_client
.Pass();
68 ////////////////////////////////////////////////////////////////////////////////
69 // JavaScriptDialogManager, private:
71 JavaScriptDialogManager::JavaScriptDialogManager()
72 : extensions_client_(new DefaultExtensionsClient
) {
75 JavaScriptDialogManager::~JavaScriptDialogManager() {
78 void JavaScriptDialogManager::RunJavaScriptDialog(
79 content::WebContents
* web_contents
,
80 const GURL
& origin_url
,
81 const std::string
& accept_lang
,
82 content::JavaScriptMessageType message_type
,
83 const base::string16
& message_text
,
84 const base::string16
& default_prompt_text
,
85 const DialogClosedCallback
& callback
,
86 bool* did_suppress_message
) {
87 *did_suppress_message
= false;
89 ChromeJavaScriptDialogExtraData
* extra_data
=
90 &javascript_dialog_extra_data_
91 [JavaScriptAppModalDialog::GetSerializedOriginForWebContents(
94 if (extra_data
->suppress_javascript_messages_
) {
95 *did_suppress_message
= true;
99 bool is_alert
= message_type
== content::JAVASCRIPT_MESSAGE_TYPE_ALERT
;
100 base::string16 dialog_title
=
101 GetTitle(web_contents
, origin_url
, accept_lang
, is_alert
);
103 extensions_client_
->OnDialogOpened(web_contents
);
105 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
107 &javascript_dialog_extra_data_
,
112 ShouldDisplaySuppressCheckbox(extra_data
),
113 false, // is_before_unload_dialog
115 base::Bind(&JavaScriptDialogManager::OnDialogClosed
,
116 base::Unretained(this), web_contents
, callback
)));
119 void JavaScriptDialogManager::RunBeforeUnloadDialog(
120 content::WebContents
* web_contents
,
121 const base::string16
& message_text
,
123 const DialogClosedCallback
& callback
) {
124 ChromeJavaScriptDialogExtraData
* extra_data
=
125 &javascript_dialog_extra_data_
126 [JavaScriptAppModalDialog::GetSerializedOriginForWebContents(
129 if (extra_data
->suppress_javascript_messages_
) {
130 // If a site harassed the user enough for them to put it on mute, then it
131 // lost its privilege to deny unloading.
132 callback
.Run(true, base::string16());
136 const base::string16 title
= l10n_util::GetStringUTF16(is_reload
?
137 IDS_BEFORERELOAD_MESSAGEBOX_TITLE
: IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE
);
138 const base::string16 footer
= l10n_util::GetStringUTF16(is_reload
?
139 IDS_BEFORERELOAD_MESSAGEBOX_FOOTER
: IDS_BEFOREUNLOAD_MESSAGEBOX_FOOTER
);
141 base::string16 full_message
=
142 message_text
+ base::ASCIIToUTF16("\n\n") + footer
;
144 extensions_client_
->OnDialogOpened(web_contents
);
146 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
148 &javascript_dialog_extra_data_
,
150 content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM
,
152 base::string16(), // default_prompt_text
153 ShouldDisplaySuppressCheckbox(extra_data
),
154 true, // is_before_unload_dialog
156 base::Bind(&JavaScriptDialogManager::OnDialogClosed
,
157 base::Unretained(this), web_contents
, callback
)));
160 bool JavaScriptDialogManager::HandleJavaScriptDialog(
161 content::WebContents
* web_contents
,
163 const base::string16
* prompt_override
) {
164 AppModalDialogQueue
* dialog_queue
= AppModalDialogQueue::GetInstance();
165 if (!dialog_queue
->HasActiveDialog() ||
166 !dialog_queue
->active_dialog()->IsJavaScriptModalDialog() ||
167 dialog_queue
->active_dialog()->web_contents() != web_contents
) {
170 JavaScriptAppModalDialog
* dialog
= static_cast<JavaScriptAppModalDialog
*>(
171 dialog_queue
->active_dialog());
174 dialog
->SetOverridePromptText(*prompt_override
);
175 dialog
->native_dialog()->AcceptAppModalDialog();
177 dialog
->native_dialog()->CancelAppModalDialog();
182 void JavaScriptDialogManager::ResetDialogState(
183 content::WebContents
* web_contents
) {
184 CancelActiveAndPendingDialogs(web_contents
);
185 javascript_dialog_extra_data_
.erase(
186 JavaScriptAppModalDialog::GetSerializedOriginForWebContents(
190 base::string16
JavaScriptDialogManager::GetTitle(
191 content::WebContents
* web_contents
,
192 const GURL
& origin_url
,
193 const std::string
& accept_lang
,
195 // For extensions, show the extension name, but only if the origin of
196 // the alert matches the top-level WebContents.
198 if (extensions_client_
->GetExtensionName(web_contents
, origin_url
, &name
))
199 return base::UTF8ToUTF16(name
);
201 // Otherwise, return the formatted URL.
202 base::string16 url_string
=
203 url_formatter::FormatUrlForSecurityDisplay(origin_url
, accept_lang
);
205 return l10n_util::GetStringFUTF16(
206 is_alert
? IDS_JAVASCRIPT_ALERT_TITLE
207 : IDS_JAVASCRIPT_MESSAGEBOX_TITLE
,
208 base::i18n::GetDisplayStringInLTRDirectionality(url_string
));
211 void JavaScriptDialogManager::CancelActiveAndPendingDialogs(
212 content::WebContents
* web_contents
) {
213 AppModalDialogQueue
* queue
= AppModalDialogQueue::GetInstance();
214 AppModalDialog
* active_dialog
= queue
->active_dialog();
215 for (AppModalDialogQueue::iterator i
= queue
->begin();
216 i
!= queue
->end(); ++i
) {
217 // Invalidating the active dialog might trigger showing a not-yet
218 // invalidated dialog, so invalidate the active dialog last.
219 if ((*i
) == active_dialog
)
221 if ((*i
)->web_contents() == web_contents
)
224 if (active_dialog
&& active_dialog
->web_contents() == web_contents
)
225 active_dialog
->Invalidate();
228 void JavaScriptDialogManager::OnDialogClosed(
229 content::WebContents
* web_contents
,
230 DialogClosedCallback callback
,
232 const base::string16
& user_input
) {
233 // If an extension opened this dialog then the extension may shut down its
234 // lazy background page after the dialog closes. (Dialogs are closed before
235 // their WebContents is destroyed so |web_contents| is still valid here.)
236 extensions_client_
->OnDialogClosed(web_contents
);
238 callback
.Run(success
, user_input
);
241 } // namespace app_modal