- add sources.
[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/compiler_specific.h"
8 #include "base/i18n/rtl.h"
9 #include "base/memory/singleton.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/extensions/extension_host.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
16 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
17 #include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
18 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
19 #include "chrome/common/chrome_constants.h"
20 #include "content/public/browser/notification_observer.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/common/content_client.h"
24 #include "content/public/common/javascript_message_type.h"
25 #include "grit/generated_resources.h"
26 #include "net/base/net_util.h"
27 #include "ui/base/l10n/l10n_util.h"
28
29 using content::JavaScriptDialogManager;
30 using content::WebContents;
31
32 namespace {
33
34 class ChromeJavaScriptDialogManager : public JavaScriptDialogManager,
35                                       public content::NotificationObserver {
36  public:
37   static ChromeJavaScriptDialogManager* GetInstance();
38
39   explicit ChromeJavaScriptDialogManager(
40       extensions::ExtensionHost* extension_host);
41   virtual ~ChromeJavaScriptDialogManager();
42
43   virtual void RunJavaScriptDialog(
44       WebContents* web_contents,
45       const GURL& origin_url,
46       const std::string& accept_lang,
47       content::JavaScriptMessageType message_type,
48       const string16& message_text,
49       const string16& default_prompt_text,
50       const DialogClosedCallback& callback,
51       bool* did_suppress_message) OVERRIDE;
52
53   virtual void RunBeforeUnloadDialog(
54       WebContents* web_contents,
55       const string16& message_text,
56       bool is_reload,
57       const DialogClosedCallback& callback) OVERRIDE;
58
59   virtual bool HandleJavaScriptDialog(
60       WebContents* web_contents,
61       bool accept,
62       const string16* prompt_override) OVERRIDE;
63
64   virtual void CancelActiveAndPendingDialogs(
65       WebContents* web_contents) OVERRIDE;
66
67   virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
68
69  private:
70   ChromeJavaScriptDialogManager();
71
72   friend struct DefaultSingletonTraits<ChromeJavaScriptDialogManager>;
73
74   // Overridden from content::NotificationObserver:
75   virtual void Observe(int type,
76                        const content::NotificationSource& source,
77                        const content::NotificationDetails& details) OVERRIDE;
78
79   string16 GetTitle(const GURL& origin_url,
80                     const std::string& accept_lang,
81                     bool is_alert);
82
83   // Wrapper around a DialogClosedCallback so that we can intercept it before
84   // passing it onto the original callback.
85   void OnDialogClosed(DialogClosedCallback callback,
86                       bool success,
87                       const string16& user_input);
88
89   // Mapping between the WebContents and their extra data. The key
90   // is a void* because the pointer is just a cookie and is never dereferenced.
91   JavaScriptAppModalDialog::ExtraDataMap javascript_dialog_extra_data_;
92
93   // Extension Host which owns the ChromeJavaScriptDialogManager instance.
94   // It's used to get a extension name from a URL.
95   // If it's not owned by any Extension, it should be NULL.
96   extensions::ExtensionHost* extension_host_;
97
98   content::NotificationRegistrar registrar_;
99
100   DISALLOW_COPY_AND_ASSIGN(ChromeJavaScriptDialogManager);
101 };
102
103 ////////////////////////////////////////////////////////////////////////////////
104 // ChromeJavaScriptDialogManager, public:
105
106 ChromeJavaScriptDialogManager::ChromeJavaScriptDialogManager()
107     : extension_host_(NULL) {
108 }
109
110 ChromeJavaScriptDialogManager::~ChromeJavaScriptDialogManager() {
111   extension_host_ = NULL;
112 }
113
114 ChromeJavaScriptDialogManager::ChromeJavaScriptDialogManager(
115     extensions::ExtensionHost* extension_host)
116     : extension_host_(extension_host) {
117   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
118                  content::Source<Profile>(extension_host_->profile()));
119 }
120
121 // static
122 ChromeJavaScriptDialogManager* ChromeJavaScriptDialogManager::GetInstance() {
123   return Singleton<ChromeJavaScriptDialogManager>::get();
124 }
125
126 void ChromeJavaScriptDialogManager::RunJavaScriptDialog(
127     WebContents* web_contents,
128     const GURL& origin_url,
129     const std::string& accept_lang,
130     content::JavaScriptMessageType message_type,
131     const string16& message_text,
132     const string16& default_prompt_text,
133     const DialogClosedCallback& callback,
134     bool* did_suppress_message)  {
135   *did_suppress_message = false;
136
137   ChromeJavaScriptDialogExtraData* extra_data =
138       &javascript_dialog_extra_data_[web_contents];
139
140   if (extra_data->suppress_javascript_messages_) {
141     *did_suppress_message = true;
142     return;
143   }
144
145   base::TimeDelta time_since_last_message = base::TimeTicks::Now() -
146       extra_data->last_javascript_message_dismissal_;
147   bool display_suppress_checkbox = false;
148   // Show a checkbox offering to suppress further messages if this message is
149   // being displayed within kJavaScriptMessageExpectedDelay of the last one.
150   if (time_since_last_message <
151       base::TimeDelta::FromMilliseconds(
152           chrome::kJavaScriptMessageExpectedDelay)) {
153     display_suppress_checkbox = true;
154   } else {
155     display_suppress_checkbox = false;
156   }
157
158   bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT;
159   string16 dialog_title = GetTitle(origin_url, accept_lang, is_alert);
160
161   if (extension_host_)
162     extension_host_->WillRunJavaScriptDialog();
163
164   AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
165       web_contents,
166       &javascript_dialog_extra_data_,
167       dialog_title,
168       message_type,
169       message_text,
170       default_prompt_text,
171       display_suppress_checkbox,
172       false,  // is_before_unload_dialog
173       false,  // is_reload
174       base::Bind(&ChromeJavaScriptDialogManager::OnDialogClosed,
175                  base::Unretained(this), callback)));
176 }
177
178 void ChromeJavaScriptDialogManager::RunBeforeUnloadDialog(
179     WebContents* web_contents,
180     const string16& message_text,
181     bool is_reload,
182     const DialogClosedCallback& callback) {
183   const string16 title = l10n_util::GetStringUTF16(is_reload ?
184       IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE);
185   const string16 footer = l10n_util::GetStringUTF16(is_reload ?
186       IDS_BEFORERELOAD_MESSAGEBOX_FOOTER : IDS_BEFOREUNLOAD_MESSAGEBOX_FOOTER);
187
188   string16 full_message = message_text + ASCIIToUTF16("\n\n") + footer;
189
190   if (extension_host_)
191     extension_host_->WillRunJavaScriptDialog();
192
193   AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
194       web_contents,
195       &javascript_dialog_extra_data_,
196       title,
197       content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM,
198       full_message,
199       string16(),  // default_prompt_text
200       false,       // display_suppress_checkbox
201       true,        // is_before_unload_dialog
202       is_reload,
203       base::Bind(&ChromeJavaScriptDialogManager::OnDialogClosed,
204                  base::Unretained(this), callback)));
205 }
206
207 bool ChromeJavaScriptDialogManager::HandleJavaScriptDialog(
208     WebContents* web_contents,
209     bool accept,
210     const string16* prompt_override) {
211   AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
212   if (!dialog_queue->HasActiveDialog() ||
213       !dialog_queue->active_dialog()->IsJavaScriptModalDialog() ||
214       dialog_queue->active_dialog()->web_contents() != web_contents) {
215     return false;
216   }
217   JavaScriptAppModalDialog* dialog = static_cast<JavaScriptAppModalDialog*>(
218       dialog_queue->active_dialog());
219   if (accept) {
220     if (prompt_override)
221       dialog->SetOverridePromptText(*prompt_override);
222     dialog->native_dialog()->AcceptAppModalDialog();
223   } else {
224     dialog->native_dialog()->CancelAppModalDialog();
225   }
226   return true;
227 }
228
229 void ChromeJavaScriptDialogManager::WebContentsDestroyed(
230     WebContents* web_contents) {
231   CancelActiveAndPendingDialogs(web_contents);
232   javascript_dialog_extra_data_.erase(web_contents);
233 }
234
235 void ChromeJavaScriptDialogManager::Observe(
236     int type,
237     const content::NotificationSource& source,
238     const content::NotificationDetails& details) {
239   DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED);
240   extension_host_ = NULL;
241 }
242
243 string16 ChromeJavaScriptDialogManager::GetTitle(const GURL& origin_url,
244                                                  const std::string& accept_lang,
245                                                  bool is_alert) {
246   // If the URL hasn't any host, return the default string.
247   if (!origin_url.has_host()) {
248       return l10n_util::GetStringUTF16(
249           is_alert ? IDS_JAVASCRIPT_ALERT_DEFAULT_TITLE
250                    : IDS_JAVASCRIPT_MESSAGEBOX_DEFAULT_TITLE);
251   }
252
253   // If the URL is a chrome extension one, return the extension name.
254   if (extension_host_) {
255     const extensions::Extension* extension = extension_host_->
256       profile()->GetExtensionService()->extensions()->
257       GetExtensionOrAppByURL(origin_url);
258     if (extension) {
259       return UTF8ToUTF16(base::StringPiece(extension->name()));
260     }
261   }
262
263   // Otherwise, return the formatted URL.
264   // In this case, force URL to have LTR directionality.
265   string16 url_string = net::FormatUrl(origin_url, accept_lang);
266   return l10n_util::GetStringFUTF16(
267       is_alert ? IDS_JAVASCRIPT_ALERT_TITLE
268       : IDS_JAVASCRIPT_MESSAGEBOX_TITLE,
269       base::i18n::GetDisplayStringInLTRDirectionality(url_string));
270 }
271
272 void ChromeJavaScriptDialogManager::CancelActiveAndPendingDialogs(
273     WebContents* web_contents) {
274   AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
275   AppModalDialog* active_dialog = queue->active_dialog();
276   if (active_dialog && active_dialog->web_contents() == web_contents)
277     active_dialog->Invalidate();
278   for (AppModalDialogQueue::iterator i = queue->begin();
279        i != queue->end(); ++i) {
280     if ((*i)->web_contents() == web_contents)
281       (*i)->Invalidate();
282   }
283 }
284
285 void ChromeJavaScriptDialogManager::OnDialogClosed(
286     DialogClosedCallback callback,
287     bool success,
288     const string16& user_input) {
289   if (extension_host_)
290     extension_host_->DidCloseJavaScriptDialog();
291   callback.Run(success, user_input);
292 }
293
294 }  // namespace
295
296 content::JavaScriptDialogManager* GetJavaScriptDialogManagerInstance() {
297   return ChromeJavaScriptDialogManager::GetInstance();
298 }
299
300 content::JavaScriptDialogManager* CreateJavaScriptDialogManagerInstance(
301     extensions::ExtensionHost* extension_host) {
302   return new ChromeJavaScriptDialogManager(extension_host);
303 }