OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/app_modal/javascript_dialog_manager.h" | 5 #include "components/app_modal/javascript_dialog_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 } | 50 } |
51 | 51 |
52 DISALLOW_COPY_AND_ASSIGN(DefaultExtensionsClient); | 52 DISALLOW_COPY_AND_ASSIGN(DefaultExtensionsClient); |
53 }; | 53 }; |
54 | 54 |
55 bool ShouldDisplaySuppressCheckbox( | 55 bool ShouldDisplaySuppressCheckbox( |
56 ChromeJavaScriptDialogExtraData* extra_data) { | 56 ChromeJavaScriptDialogExtraData* extra_data) { |
57 return extra_data->has_already_shown_a_dialog_; | 57 return extra_data->has_already_shown_a_dialog_; |
58 } | 58 } |
59 | 59 |
60 enum class DialogType { | 60 void LogUMAMessageLengthStats(const base::string16& message) { |
61 JAVASCRIPT, | 61 UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageCharacters", |
62 ON_BEFORE_UNLOAD, | 62 static_cast<int32_t>(message.length())); |
63 }; | |
64 | |
65 void LogUMAMessageLengthStats(const base::string16& message, DialogType type) { | |
66 if (type == DialogType::JAVASCRIPT) { | |
67 UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageCharacters", | |
68 static_cast<int32_t>(message.length())); | |
69 } else { | |
70 UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfOnBeforeUnloadMessageCharacters", | |
71 static_cast<int32_t>(message.length())); | |
72 } | |
73 | 63 |
74 int32_t newline_count = | 64 int32_t newline_count = |
75 std::count_if(message.begin(), message.end(), | 65 std::count_if(message.begin(), message.end(), |
76 [](const base::char16& c) { return c == '\n'; }); | 66 [](const base::char16& c) { return c == '\n'; }); |
77 if (type == DialogType::JAVASCRIPT) { | 67 UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageNewlines", |
78 UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageNewlines", | 68 newline_count); |
79 newline_count); | |
80 } else { | |
81 UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfOnBeforeUnloadMessageNewlines", | |
82 newline_count); | |
83 } | |
84 } | 69 } |
85 | 70 |
86 } // namespace | 71 } // namespace |
87 | 72 |
88 //////////////////////////////////////////////////////////////////////////////// | 73 //////////////////////////////////////////////////////////////////////////////// |
89 // JavaScriptDialogManager, public: | 74 // JavaScriptDialogManager, public: |
90 | 75 |
91 // static | 76 // static |
92 JavaScriptDialogManager* JavaScriptDialogManager::GetInstance() { | 77 JavaScriptDialogManager* JavaScriptDialogManager::GetInstance() { |
93 return base::Singleton<JavaScriptDialogManager>::get(); | 78 return base::Singleton<JavaScriptDialogManager>::get(); |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
165 now - last_close_time_); | 150 now - last_close_time_); |
166 last_close_time_ = base::TimeTicks(); | 151 last_close_time_ = base::TimeTicks(); |
167 } | 152 } |
168 | 153 |
169 bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT; | 154 bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT; |
170 base::string16 dialog_title = | 155 base::string16 dialog_title = |
171 GetTitle(web_contents, origin_url, accept_lang, is_alert); | 156 GetTitle(web_contents, origin_url, accept_lang, is_alert); |
172 | 157 |
173 extensions_client_->OnDialogOpened(web_contents); | 158 extensions_client_->OnDialogOpened(web_contents); |
174 | 159 |
175 LogUMAMessageLengthStats(message_text, DialogType::JAVASCRIPT); | 160 LogUMAMessageLengthStats(message_text); |
176 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( | 161 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( |
177 web_contents, | 162 web_contents, |
178 &javascript_dialog_extra_data_, | 163 &javascript_dialog_extra_data_, |
179 dialog_title, | 164 dialog_title, |
180 message_type, | 165 message_type, |
181 message_text, | 166 message_text, |
182 default_prompt_text, | 167 default_prompt_text, |
183 ShouldDisplaySuppressCheckbox(extra_data), | 168 ShouldDisplaySuppressCheckbox(extra_data), |
184 false, // is_before_unload_dialog | 169 false, // is_before_unload_dialog |
185 false, // is_reload | 170 false, // is_reload |
186 base::Bind(&JavaScriptDialogManager::OnDialogClosed, | 171 base::Bind(&JavaScriptDialogManager::OnDialogClosed, |
187 base::Unretained(this), web_contents, callback))); | 172 base::Unretained(this), web_contents, callback))); |
188 } | 173 } |
189 | 174 |
190 void JavaScriptDialogManager::RunBeforeUnloadDialog( | 175 void JavaScriptDialogManager::RunBeforeUnloadDialog( |
191 content::WebContents* web_contents, | 176 content::WebContents* web_contents, |
192 const base::string16& message_text, | |
193 bool is_reload, | 177 bool is_reload, |
194 const DialogClosedCallback& callback) { | 178 const DialogClosedCallback& callback) { |
195 ChromeJavaScriptDialogExtraData* extra_data = | 179 ChromeJavaScriptDialogExtraData* extra_data = |
196 &javascript_dialog_extra_data_[web_contents]; | 180 &javascript_dialog_extra_data_[web_contents]; |
197 | 181 |
198 if (extra_data->suppress_javascript_messages_) { | 182 if (extra_data->suppress_javascript_messages_) { |
199 // If a site harassed the user enough for them to put it on mute, then it | 183 // If a site harassed the user enough for them to put it on mute, then it |
200 // lost its privilege to deny unloading. | 184 // lost its privilege to deny unloading. |
201 callback.Run(true, base::string16()); | 185 callback.Run(true, base::string16()); |
202 return; | 186 return; |
203 } | 187 } |
204 | 188 |
| 189 // Build the dialog message. We explicitly do _not_ allow the webpage to |
| 190 // specify the contents of this dialog, because most of the time nowadays it's |
| 191 // used for scams. |
| 192 // |
| 193 // This does not violate the spec. Per |
| 194 // https://html.spec.whatwg.org/#prompt-to-unload-a-document, step 7: |
| 195 // |
| 196 // "The prompt shown by the user agent may include the string of the |
| 197 // returnValue attribute, or some leading subset thereof." |
| 198 // |
| 199 // The prompt MAY include the string. It doesn't any more. Scam web page |
| 200 // authors have abused this, so we're taking away the toys from everyone. This |
| 201 // is why we can't have nice things. |
| 202 |
205 const base::string16 title = l10n_util::GetStringUTF16(is_reload ? | 203 const base::string16 title = l10n_util::GetStringUTF16(is_reload ? |
206 IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE); | 204 IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE); |
207 const base::string16 footer = l10n_util::GetStringUTF16(is_reload ? | 205 const base::string16 message = |
208 IDS_BEFORERELOAD_MESSAGEBOX_FOOTER : IDS_BEFOREUNLOAD_MESSAGEBOX_FOOTER); | 206 l10n_util::GetStringUTF16(IDS_BEFOREUNLOAD_MESSAGEBOX_MESSAGE); |
209 | |
210 base::string16 full_message = | |
211 message_text + base::ASCIIToUTF16("\n\n") + footer; | |
212 | 207 |
213 extensions_client_->OnDialogOpened(web_contents); | 208 extensions_client_->OnDialogOpened(web_contents); |
214 | 209 |
215 LogUMAMessageLengthStats(message_text, DialogType::ON_BEFORE_UNLOAD); | |
216 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( | 210 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( |
217 web_contents, | 211 web_contents, |
218 &javascript_dialog_extra_data_, | 212 &javascript_dialog_extra_data_, |
219 title, | 213 title, |
220 content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM, | 214 content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM, |
221 full_message, | 215 message, |
222 base::string16(), // default_prompt_text | 216 base::string16(), // default_prompt_text |
223 ShouldDisplaySuppressCheckbox(extra_data), | 217 ShouldDisplaySuppressCheckbox(extra_data), |
224 true, // is_before_unload_dialog | 218 true, // is_before_unload_dialog |
225 is_reload, | 219 is_reload, |
226 base::Bind(&JavaScriptDialogManager::OnBeforeUnloadDialogClosed, | 220 base::Bind(&JavaScriptDialogManager::OnBeforeUnloadDialogClosed, |
227 base::Unretained(this), web_contents, callback))); | 221 base::Unretained(this), web_contents, callback))); |
228 } | 222 } |
229 | 223 |
230 bool JavaScriptDialogManager::HandleJavaScriptDialog( | 224 bool JavaScriptDialogManager::HandleJavaScriptDialog( |
231 content::WebContents* web_contents, | 225 content::WebContents* web_contents, |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 // lazy background page after the dialog closes. (Dialogs are closed before | 329 // lazy background page after the dialog closes. (Dialogs are closed before |
336 // their WebContents is destroyed so |web_contents| is still valid here.) | 330 // their WebContents is destroyed so |web_contents| is still valid here.) |
337 extensions_client_->OnDialogClosed(web_contents); | 331 extensions_client_->OnDialogClosed(web_contents); |
338 | 332 |
339 last_close_time_ = base::TimeTicks::Now(); | 333 last_close_time_ = base::TimeTicks::Now(); |
340 | 334 |
341 callback.Run(success, user_input); | 335 callback.Run(success, user_input); |
342 } | 336 } |
343 | 337 |
344 } // namespace app_modal | 338 } // namespace app_modal |
OLD | NEW |