Stop UI jumping when clicking Contextual Search Results in Custom Tab
[chromium-blink-merge.git] / components / app_modal / javascript_dialog_manager.cc
blobccf566ac2d7b134231b7fe9ba2777b6bf1d4f1e4
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"
7 #include "base/bind.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_dialog_extensions_client.h"
13 #include "components/app_modal/javascript_native_dialog_factory.h"
14 #include "components/app_modal/native_app_modal_dialog.h"
15 #include "components/url_formatter/url_formatter.h"
16 #include "content/public/common/javascript_message_type.h"
17 #include "grit/components_strings.h"
18 #include "net/base/net_util.h"
19 #include "ui/base/l10n/l10n_util.h"
21 namespace app_modal {
22 namespace {
24 class DefaultExtensionsClient : public JavaScriptDialogExtensionsClient {
25 public:
26 DefaultExtensionsClient() {}
27 ~DefaultExtensionsClient() override {}
29 private:
30 // JavaScriptDialogExtensionsClient:
31 void OnDialogOpened(content::WebContents* web_contents) override {}
32 void OnDialogClosed(content::WebContents* web_contents) override {}
33 bool GetExtensionName(content::WebContents* web_contents,
34 const GURL& origin_url,
35 std::string* name_out) override {
36 return false;
39 DISALLOW_COPY_AND_ASSIGN(DefaultExtensionsClient);
42 bool ShouldDisplaySuppressCheckbox(
43 ChromeJavaScriptDialogExtraData* extra_data) {
44 base::TimeDelta time_since_last_message = base::TimeTicks::Now() -
45 extra_data->last_javascript_message_dismissal_;
47 // If a WebContents is impolite and displays a second JavaScript
48 // alert within kJavaScriptMessageExpectedDelay of a previous
49 // JavaScript alert being dismissed, show a checkbox offering to
50 // suppress future alerts from this WebContents.
51 const int kJavaScriptMessageExpectedDelay = 1000;
53 return time_since_last_message <
54 base::TimeDelta::FromMilliseconds(kJavaScriptMessageExpectedDelay);
57 } // namespace
59 ////////////////////////////////////////////////////////////////////////////////
60 // JavaScriptDialogManager, public:
62 // static
63 JavaScriptDialogManager* JavaScriptDialogManager::GetInstance() {
64 return Singleton<JavaScriptDialogManager>::get();
67 void JavaScriptDialogManager::SetNativeDialogFactory(
68 scoped_ptr<JavaScriptNativeDialogFactory> factory) {
69 native_dialog_factory_ = factory.Pass();
72 void JavaScriptDialogManager::SetExtensionsClient(
73 scoped_ptr<JavaScriptDialogExtensionsClient> extensions_client) {
74 extensions_client_ = extensions_client.Pass();
77 ////////////////////////////////////////////////////////////////////////////////
78 // JavaScriptDialogManager, private:
80 JavaScriptDialogManager::JavaScriptDialogManager()
81 : extensions_client_(new DefaultExtensionsClient) {
84 JavaScriptDialogManager::~JavaScriptDialogManager() {
87 void JavaScriptDialogManager::RunJavaScriptDialog(
88 content::WebContents* web_contents,
89 const GURL& origin_url,
90 const std::string& accept_lang,
91 content::JavaScriptMessageType message_type,
92 const base::string16& message_text,
93 const base::string16& default_prompt_text,
94 const DialogClosedCallback& callback,
95 bool* did_suppress_message) {
96 *did_suppress_message = false;
98 ChromeJavaScriptDialogExtraData* extra_data =
99 &javascript_dialog_extra_data_[web_contents];
101 if (extra_data->suppress_javascript_messages_) {
102 *did_suppress_message = true;
103 return;
106 bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT;
107 base::string16 dialog_title =
108 GetTitle(web_contents, origin_url, accept_lang, is_alert);
110 extensions_client_->OnDialogOpened(web_contents);
112 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
113 web_contents,
114 &javascript_dialog_extra_data_,
115 dialog_title,
116 message_type,
117 message_text,
118 default_prompt_text,
119 ShouldDisplaySuppressCheckbox(extra_data),
120 false, // is_before_unload_dialog
121 false, // is_reload
122 base::Bind(&JavaScriptDialogManager::OnDialogClosed,
123 base::Unretained(this), web_contents, callback)));
126 void JavaScriptDialogManager::RunBeforeUnloadDialog(
127 content::WebContents* web_contents,
128 const base::string16& message_text,
129 bool is_reload,
130 const DialogClosedCallback& callback) {
131 ChromeJavaScriptDialogExtraData* extra_data =
132 &javascript_dialog_extra_data_[web_contents];
134 if (extra_data->suppress_javascript_messages_) {
135 // If a site harassed the user enough for them to put it on mute, then it
136 // lost its privilege to deny unloading.
137 callback.Run(true, base::string16());
138 return;
141 const base::string16 title = l10n_util::GetStringUTF16(is_reload ?
142 IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE);
143 const base::string16 footer = l10n_util::GetStringUTF16(is_reload ?
144 IDS_BEFORERELOAD_MESSAGEBOX_FOOTER : IDS_BEFOREUNLOAD_MESSAGEBOX_FOOTER);
146 base::string16 full_message =
147 message_text + base::ASCIIToUTF16("\n\n") + footer;
149 extensions_client_->OnDialogOpened(web_contents);
151 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
152 web_contents,
153 &javascript_dialog_extra_data_,
154 title,
155 content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM,
156 full_message,
157 base::string16(), // default_prompt_text
158 ShouldDisplaySuppressCheckbox(extra_data),
159 true, // is_before_unload_dialog
160 is_reload,
161 base::Bind(&JavaScriptDialogManager::OnDialogClosed,
162 base::Unretained(this), web_contents, callback)));
165 bool JavaScriptDialogManager::HandleJavaScriptDialog(
166 content::WebContents* web_contents,
167 bool accept,
168 const base::string16* prompt_override) {
169 AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
170 if (!dialog_queue->HasActiveDialog() ||
171 !dialog_queue->active_dialog()->IsJavaScriptModalDialog() ||
172 dialog_queue->active_dialog()->web_contents() != web_contents) {
173 return false;
175 JavaScriptAppModalDialog* dialog = static_cast<JavaScriptAppModalDialog*>(
176 dialog_queue->active_dialog());
177 if (accept) {
178 if (prompt_override)
179 dialog->SetOverridePromptText(*prompt_override);
180 dialog->native_dialog()->AcceptAppModalDialog();
181 } else {
182 dialog->native_dialog()->CancelAppModalDialog();
184 return true;
187 void JavaScriptDialogManager::ResetDialogState(
188 content::WebContents* web_contents) {
189 CancelActiveAndPendingDialogs(web_contents);
190 javascript_dialog_extra_data_.erase(web_contents);
193 base::string16 JavaScriptDialogManager::GetTitle(
194 content::WebContents* web_contents,
195 const GURL& origin_url,
196 const std::string& accept_lang,
197 bool is_alert) {
198 // If the URL hasn't any host, return the default string.
199 if (!origin_url.has_host()) {
200 return l10n_util::GetStringUTF16(
201 is_alert ? IDS_JAVASCRIPT_ALERT_DEFAULT_TITLE
202 : IDS_JAVASCRIPT_MESSAGEBOX_DEFAULT_TITLE);
205 // For extensions, show the extension name, but only if the origin of
206 // the alert matches the top-level WebContents.
207 std::string name;
208 if (extensions_client_->GetExtensionName(web_contents, origin_url, &name))
209 return base::UTF8ToUTF16(name);
211 // Otherwise, return the formatted URL.
212 // In this case, force URL to have LTR directionality.
213 base::string16 url_string = url_formatter::FormatUrl(origin_url, accept_lang);
214 return l10n_util::GetStringFUTF16(
215 is_alert ? IDS_JAVASCRIPT_ALERT_TITLE
216 : IDS_JAVASCRIPT_MESSAGEBOX_TITLE,
217 base::i18n::GetDisplayStringInLTRDirectionality(url_string));
220 void JavaScriptDialogManager::CancelActiveAndPendingDialogs(
221 content::WebContents* web_contents) {
222 AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
223 AppModalDialog* active_dialog = queue->active_dialog();
224 for (AppModalDialogQueue::iterator i = queue->begin();
225 i != queue->end(); ++i) {
226 // Invalidating the active dialog might trigger showing a not-yet
227 // invalidated dialog, so invalidate the active dialog last.
228 if ((*i) == active_dialog)
229 continue;
230 if ((*i)->web_contents() == web_contents)
231 (*i)->Invalidate();
233 if (active_dialog && active_dialog->web_contents() == web_contents)
234 active_dialog->Invalidate();
237 void JavaScriptDialogManager::OnDialogClosed(
238 content::WebContents* web_contents,
239 DialogClosedCallback callback,
240 bool success,
241 const base::string16& user_input) {
242 // If an extension opened this dialog then the extension may shut down its
243 // lazy background page after the dialog closes. (Dialogs are closed before
244 // their WebContents is destroyed so |web_contents| is still valid here.)
245 extensions_client_->OnDialogClosed(web_contents);
247 callback.Run(success, user_input);
250 } // namespace app_modal