Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_modal_dialogs / javascript_dialog_manager.cc
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.
4
5 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
6
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/i18n/rtl.h"
10 #include "base/memory/singleton.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
14 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
15 #include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
16 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/content_client.h"
21 #include "content/public/common/javascript_message_type.h"
22 #include "net/base/net_util.h"
23 #include "ui/base/l10n/l10n_util.h"
24
25 #if defined(ENABLE_EXTENSIONS)
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/process_manager.h"
28 #endif  // defined(ENABLE_EXTENSIONS)
29
30 using content::BrowserContext;
31 using content::JavaScriptDialogManager;
32 using content::WebContents;
33
34 #if defined(ENABLE_EXTENSIONS)
35 using extensions::Extension;
36 #endif  // defined(ENABLE_EXTENSIONS)
37
38 namespace {
39
40 #if defined(ENABLE_EXTENSIONS)
41 // Returns the ProcessManager for the browser context from |web_contents|.
42 extensions::ProcessManager* GetExtensionsProcessManager(
43     WebContents* web_contents) {
44   return extensions::ExtensionSystem::Get(
45       web_contents->GetBrowserContext())->process_manager();
46 }
47
48 // Returns the extension associated with |web_contents| or NULL if there is no
49 // associated extension (or extensions are not supported).
50 const Extension* GetExtensionForWebContents(WebContents* web_contents) {
51   extensions::ProcessManager* pm = GetExtensionsProcessManager(web_contents);
52   return pm->GetExtensionForRenderViewHost(web_contents->GetRenderViewHost());
53 }
54 #endif  // defined(ENABLE_EXTENSIONS)
55
56 // Keeps an |extension| from shutting down its lazy background page. If an
57 // extension opens a dialog its lazy background page must stay alive until the
58 // dialog closes.
59 void IncrementLazyKeepaliveCount(WebContents* web_contents) {
60 #if defined(ENABLE_EXTENSIONS)
61   const Extension* extension = GetExtensionForWebContents(web_contents);
62   if (extension == NULL)
63     return;
64
65   DCHECK(web_contents);
66   extensions::ProcessManager* pm = GetExtensionsProcessManager(web_contents);
67   if (pm)
68     pm->IncrementLazyKeepaliveCount(extension);
69 #endif  // defined(ENABLE_EXTENSIONS)
70 }
71
72 // Allows an |extension| to shut down its lazy background page after a dialog
73 // closes (if nothing else is keeping it open).
74 void DecrementLazyKeepaliveCount(WebContents* web_contents) {
75 #if defined(ENABLE_EXTENSIONS)
76   const Extension* extension = GetExtensionForWebContents(web_contents);
77   if (extension == NULL)
78     return;
79
80   DCHECK(web_contents);
81   extensions::ProcessManager* pm = GetExtensionsProcessManager(web_contents);
82   if (pm)
83     pm->DecrementLazyKeepaliveCount(extension);
84 #endif  // defined(ENABLE_EXTENSIONS)
85 }
86
87 class ChromeJavaScriptDialogManager : public JavaScriptDialogManager {
88  public:
89   static ChromeJavaScriptDialogManager* GetInstance();
90
91   virtual void RunJavaScriptDialog(
92       WebContents* web_contents,
93       const GURL& origin_url,
94       const std::string& accept_lang,
95       content::JavaScriptMessageType message_type,
96       const base::string16& message_text,
97       const base::string16& default_prompt_text,
98       const DialogClosedCallback& callback,
99       bool* did_suppress_message) OVERRIDE;
100
101   virtual void RunBeforeUnloadDialog(
102       WebContents* web_contents,
103       const base::string16& message_text,
104       bool is_reload,
105       const DialogClosedCallback& callback) OVERRIDE;
106
107   virtual bool HandleJavaScriptDialog(
108       WebContents* web_contents,
109       bool accept,
110       const base::string16* prompt_override) OVERRIDE;
111
112   virtual void CancelActiveAndPendingDialogs(
113       WebContents* web_contents) OVERRIDE;
114
115   virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
116
117  private:
118   friend struct DefaultSingletonTraits<ChromeJavaScriptDialogManager>;
119
120   ChromeJavaScriptDialogManager();
121   virtual ~ChromeJavaScriptDialogManager();
122
123   base::string16 GetTitle(WebContents* web_contents,
124                           const GURL& origin_url,
125                           const std::string& accept_lang,
126                           bool is_alert);
127
128   // Wrapper around a DialogClosedCallback so that we can intercept it before
129   // passing it onto the original callback.
130   void OnDialogClosed(WebContents* web_contents,
131                       DialogClosedCallback callback,
132                       bool success,
133                       const base::string16& user_input);
134
135   // Mapping between the WebContents and their extra data. The key
136   // is a void* because the pointer is just a cookie and is never dereferenced.
137   JavaScriptAppModalDialog::ExtraDataMap javascript_dialog_extra_data_;
138
139   DISALLOW_COPY_AND_ASSIGN(ChromeJavaScriptDialogManager);
140 };
141
142 ////////////////////////////////////////////////////////////////////////////////
143 // ChromeJavaScriptDialogManager, public:
144
145 ChromeJavaScriptDialogManager::ChromeJavaScriptDialogManager() {
146 }
147
148 ChromeJavaScriptDialogManager::~ChromeJavaScriptDialogManager() {
149 }
150
151 // static
152 ChromeJavaScriptDialogManager* ChromeJavaScriptDialogManager::GetInstance() {
153   return Singleton<ChromeJavaScriptDialogManager>::get();
154 }
155
156 void ChromeJavaScriptDialogManager::RunJavaScriptDialog(
157     WebContents* web_contents,
158     const GURL& origin_url,
159     const std::string& accept_lang,
160     content::JavaScriptMessageType message_type,
161     const base::string16& message_text,
162     const base::string16& default_prompt_text,
163     const DialogClosedCallback& callback,
164     bool* did_suppress_message)  {
165   *did_suppress_message = false;
166
167   ChromeJavaScriptDialogExtraData* extra_data =
168       &javascript_dialog_extra_data_[web_contents];
169
170   if (extra_data->suppress_javascript_messages_) {
171     *did_suppress_message = true;
172     return;
173   }
174
175   base::TimeDelta time_since_last_message = base::TimeTicks::Now() -
176       extra_data->last_javascript_message_dismissal_;
177   bool display_suppress_checkbox = false;
178   // Show a checkbox offering to suppress further messages if this message is
179   // being displayed within kJavaScriptMessageExpectedDelay of the last one.
180   if (time_since_last_message <
181       base::TimeDelta::FromMilliseconds(
182           chrome::kJavaScriptMessageExpectedDelay)) {
183     display_suppress_checkbox = true;
184   } else {
185     display_suppress_checkbox = false;
186   }
187
188   bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT;
189   base::string16 dialog_title =
190       GetTitle(web_contents, origin_url, accept_lang, is_alert);
191
192   IncrementLazyKeepaliveCount(web_contents);
193
194   AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
195       web_contents,
196       &javascript_dialog_extra_data_,
197       dialog_title,
198       message_type,
199       message_text,
200       default_prompt_text,
201       display_suppress_checkbox,
202       false,  // is_before_unload_dialog
203       false,  // is_reload
204       base::Bind(&ChromeJavaScriptDialogManager::OnDialogClosed,
205                  base::Unretained(this), web_contents, callback)));
206 }
207
208 void ChromeJavaScriptDialogManager::RunBeforeUnloadDialog(
209     WebContents* web_contents,
210     const base::string16& message_text,
211     bool is_reload,
212     const DialogClosedCallback& callback) {
213   const base::string16 title = l10n_util::GetStringUTF16(is_reload ?
214       IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE);
215   const base::string16 footer = l10n_util::GetStringUTF16(is_reload ?
216       IDS_BEFORERELOAD_MESSAGEBOX_FOOTER : IDS_BEFOREUNLOAD_MESSAGEBOX_FOOTER);
217
218   base::string16 full_message =
219       message_text + base::ASCIIToUTF16("\n\n") + footer;
220
221   IncrementLazyKeepaliveCount(web_contents);
222
223   AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
224       web_contents,
225       &javascript_dialog_extra_data_,
226       title,
227       content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM,
228       full_message,
229       base::string16(),  // default_prompt_text
230       false,       // display_suppress_checkbox
231       true,        // is_before_unload_dialog
232       is_reload,
233       base::Bind(&ChromeJavaScriptDialogManager::OnDialogClosed,
234                  base::Unretained(this), web_contents, callback)));
235 }
236
237 bool ChromeJavaScriptDialogManager::HandleJavaScriptDialog(
238     WebContents* web_contents,
239     bool accept,
240     const base::string16* prompt_override) {
241   AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
242   if (!dialog_queue->HasActiveDialog() ||
243       !dialog_queue->active_dialog()->IsJavaScriptModalDialog() ||
244       dialog_queue->active_dialog()->web_contents() != web_contents) {
245     return false;
246   }
247   JavaScriptAppModalDialog* dialog = static_cast<JavaScriptAppModalDialog*>(
248       dialog_queue->active_dialog());
249   if (accept) {
250     if (prompt_override)
251       dialog->SetOverridePromptText(*prompt_override);
252     dialog->native_dialog()->AcceptAppModalDialog();
253   } else {
254     dialog->native_dialog()->CancelAppModalDialog();
255   }
256   return true;
257 }
258
259 void ChromeJavaScriptDialogManager::WebContentsDestroyed(
260     WebContents* web_contents) {
261   CancelActiveAndPendingDialogs(web_contents);
262   javascript_dialog_extra_data_.erase(web_contents);
263 }
264
265 base::string16 ChromeJavaScriptDialogManager::GetTitle(
266     WebContents* web_contents,
267     const GURL& origin_url,
268     const std::string& accept_lang,
269     bool is_alert) {
270   // If the URL hasn't any host, return the default string.
271   if (!origin_url.has_host()) {
272       return l10n_util::GetStringUTF16(
273           is_alert ? IDS_JAVASCRIPT_ALERT_DEFAULT_TITLE
274                    : IDS_JAVASCRIPT_MESSAGEBOX_DEFAULT_TITLE);
275   }
276
277   // For extensions, show the extension name, but only if the origin of
278   // the alert matches the top-level WebContents.
279 #if defined(ENABLE_EXTENSIONS)
280   const Extension* extension = GetExtensionForWebContents(web_contents);
281   if (extension &&
282       web_contents->GetLastCommittedURL().GetOrigin() == origin_url) {
283     return base::UTF8ToUTF16(extension->name());
284   }
285 #endif  // defined(ENABLE_EXTENSIONS)
286
287   // Otherwise, return the formatted URL.
288   // In this case, force URL to have LTR directionality.
289   base::string16 url_string = net::FormatUrl(origin_url, accept_lang);
290   return l10n_util::GetStringFUTF16(
291       is_alert ? IDS_JAVASCRIPT_ALERT_TITLE
292       : IDS_JAVASCRIPT_MESSAGEBOX_TITLE,
293       base::i18n::GetDisplayStringInLTRDirectionality(url_string));
294 }
295
296 void ChromeJavaScriptDialogManager::CancelActiveAndPendingDialogs(
297     WebContents* web_contents) {
298   AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
299   AppModalDialog* active_dialog = queue->active_dialog();
300   if (active_dialog && active_dialog->web_contents() == web_contents)
301     active_dialog->Invalidate();
302   for (AppModalDialogQueue::iterator i = queue->begin();
303        i != queue->end(); ++i) {
304     if ((*i)->web_contents() == web_contents)
305       (*i)->Invalidate();
306   }
307 }
308
309 void ChromeJavaScriptDialogManager::OnDialogClosed(
310     WebContents* web_contents,
311     DialogClosedCallback callback,
312     bool success,
313     const base::string16& user_input) {
314   // If an extension opened this dialog then the extension may shut down its
315   // lazy background page after the dialog closes. (Dialogs are closed before
316   // their WebContents is destroyed so |web_contents| is still valid here.)
317   DecrementLazyKeepaliveCount(web_contents);
318
319   callback.Run(success, user_input);
320 }
321
322 }  // namespace
323
324 content::JavaScriptDialogManager* GetJavaScriptDialogManagerInstance() {
325   return ChromeJavaScriptDialogManager::GetInstance();
326 }