• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "libcef/browser/javascript_dialog_manager.h"
7 
8 #include <utility>
9 
10 #include "libcef/browser/alloy/alloy_browser_host_impl.h"
11 #include "libcef/browser/thread_util.h"
12 
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/url_formatter/elide_url.h"
17 
18 namespace {
19 
20 class CefJSDialogCallbackImpl : public CefJSDialogCallback {
21  public:
22   using CallbackType = content::JavaScriptDialogManager::DialogClosedCallback;
23 
CefJSDialogCallbackImpl(CallbackType callback)24   CefJSDialogCallbackImpl(CallbackType callback)
25       : callback_(std::move(callback)) {}
~CefJSDialogCallbackImpl()26   ~CefJSDialogCallbackImpl() override {
27     if (!callback_.is_null()) {
28       // The callback is still pending. Cancel it now.
29       if (CEF_CURRENTLY_ON_UIT()) {
30         CancelNow(std::move(callback_));
31       } else {
32         CEF_POST_TASK(CEF_UIT,
33                       base::BindOnce(&CefJSDialogCallbackImpl::CancelNow,
34                                      std::move(callback_)));
35       }
36     }
37   }
38 
Continue(bool success,const CefString & user_input)39   void Continue(bool success, const CefString& user_input) override {
40     if (CEF_CURRENTLY_ON_UIT()) {
41       if (!callback_.is_null()) {
42         std::move(callback_).Run(success, user_input);
43       }
44     } else {
45       CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefJSDialogCallbackImpl::Continue,
46                                             this, success, user_input));
47     }
48   }
49 
Disconnect()50   CallbackType Disconnect() WARN_UNUSED_RESULT { return std::move(callback_); }
51 
52  private:
CancelNow(CallbackType callback)53   static void CancelNow(CallbackType callback) {
54     CEF_REQUIRE_UIT();
55     std::move(callback).Run(false, std::u16string());
56   }
57 
58   CallbackType callback_;
59 
60   IMPLEMENT_REFCOUNTING(CefJSDialogCallbackImpl);
61 };
62 
63 }  // namespace
64 
CefJavaScriptDialogManager(AlloyBrowserHostImpl * browser,std::unique_ptr<CefJavaScriptDialogRunner> runner)65 CefJavaScriptDialogManager::CefJavaScriptDialogManager(
66     AlloyBrowserHostImpl* browser,
67     std::unique_ptr<CefJavaScriptDialogRunner> runner)
68     : browser_(browser),
69       runner_(std::move(runner)),
70       dialog_running_(false),
71       weak_ptr_factory_(this) {}
72 
~CefJavaScriptDialogManager()73 CefJavaScriptDialogManager::~CefJavaScriptDialogManager() {}
74 
Destroy()75 void CefJavaScriptDialogManager::Destroy() {
76   if (runner_.get()) {
77     runner_.reset(nullptr);
78   }
79 }
80 
RunJavaScriptDialog(content::WebContents * web_contents,content::RenderFrameHost * render_frame_host,content::JavaScriptDialogType message_type,const std::u16string & message_text,const std::u16string & default_prompt_text,DialogClosedCallback callback,bool * did_suppress_message)81 void CefJavaScriptDialogManager::RunJavaScriptDialog(
82     content::WebContents* web_contents,
83     content::RenderFrameHost* render_frame_host,
84     content::JavaScriptDialogType message_type,
85     const std::u16string& message_text,
86     const std::u16string& default_prompt_text,
87     DialogClosedCallback callback,
88     bool* did_suppress_message) {
89   const GURL& origin_url = render_frame_host->GetLastCommittedURL();
90 
91   CefRefPtr<CefClient> client = browser_->GetClient();
92   if (client.get()) {
93     CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
94     if (handler.get()) {
95       *did_suppress_message = false;
96 
97       CefRefPtr<CefJSDialogCallbackImpl> callbackPtr(
98           new CefJSDialogCallbackImpl(std::move(callback)));
99 
100       // Execute the user callback.
101       bool handled = handler->OnJSDialog(
102           browser_, origin_url.spec(),
103           static_cast<cef_jsdialog_type_t>(message_type), message_text,
104           default_prompt_text, callbackPtr.get(), *did_suppress_message);
105       if (handled) {
106         // Invalid combination of values. Crash sooner rather than later.
107         CHECK(!*did_suppress_message);
108         return;
109       }
110 
111       // |callback| may be null if the user executed it despite returning false.
112       callback = callbackPtr->Disconnect();
113       if (callback.is_null() || *did_suppress_message)
114         return;
115     }
116   }
117 
118   *did_suppress_message = false;
119 
120   if (!runner_.get() || dialog_running_) {
121     // Suppress the dialog if there is no platform runner or if the dialog is
122     // currently running.
123     if (!runner_.get())
124       LOG(WARNING) << "No javascript dialog runner available for this platform";
125     *did_suppress_message = true;
126     return;
127   }
128 
129   dialog_running_ = true;
130 
131   const std::u16string& display_url =
132       url_formatter::FormatUrlForSecurityDisplay(origin_url);
133 
134   DCHECK(!callback.is_null());
135   runner_->Run(
136       browser_, message_type, display_url, message_text, default_prompt_text,
137       base::BindOnce(&CefJavaScriptDialogManager::DialogClosed,
138                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
139 }
140 
RunBeforeUnloadDialog(content::WebContents * web_contents,content::RenderFrameHost * render_frame_host,bool is_reload,DialogClosedCallback callback)141 void CefJavaScriptDialogManager::RunBeforeUnloadDialog(
142     content::WebContents* web_contents,
143     content::RenderFrameHost* render_frame_host,
144     bool is_reload,
145     DialogClosedCallback callback) {
146   if (browser_->destruction_state() >=
147       AlloyBrowserHostImpl::DESTRUCTION_STATE_ACCEPTED) {
148     // Currently destroying the browser. Accept the unload without showing
149     // the prompt.
150     std::move(callback).Run(true, std::u16string());
151     return;
152   }
153 
154   const std::u16string& message_text = u"Is it OK to leave/reload this page?";
155 
156   CefRefPtr<CefClient> client = browser_->GetClient();
157   if (client.get()) {
158     CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
159     if (handler.get()) {
160       CefRefPtr<CefJSDialogCallbackImpl> callbackPtr(
161           new CefJSDialogCallbackImpl(std::move(callback)));
162 
163       // Execute the user callback.
164       bool handled = handler->OnBeforeUnloadDialog(
165           browser_, message_text, is_reload, callbackPtr.get());
166       if (handled)
167         return;
168 
169       // |callback| may be null if the user executed it despite returning false.
170       callback = callbackPtr->Disconnect();
171       if (callback.is_null())
172         return;
173     }
174   }
175 
176   if (!runner_.get() || dialog_running_) {
177     if (!runner_.get())
178       LOG(WARNING) << "No javascript dialog runner available for this platform";
179     // Suppress the dialog if there is no platform runner or if the dialog is
180     // currently running.
181     std::move(callback).Run(true, std::u16string());
182     return;
183   }
184 
185   dialog_running_ = true;
186 
187   DCHECK(!callback.is_null());
188   runner_->Run(
189       browser_, content::JAVASCRIPT_DIALOG_TYPE_CONFIRM,
190       std::u16string(),  // display_url
191       message_text,
192       std::u16string(),  // default_prompt_text
193       base::BindOnce(&CefJavaScriptDialogManager::DialogClosed,
194                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
195 }
196 
CancelDialogs(content::WebContents * web_contents,bool reset_state)197 void CefJavaScriptDialogManager::CancelDialogs(
198     content::WebContents* web_contents,
199     bool reset_state) {
200   CefRefPtr<CefClient> client = browser_->GetClient();
201   if (client.get()) {
202     CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
203     if (handler.get()) {
204       // Execute the user callback.
205       handler->OnResetDialogState(browser_);
206     }
207   }
208 
209   if (runner_.get() && dialog_running_) {
210     runner_->Cancel();
211     dialog_running_ = false;
212   }
213 }
214 
DialogClosed(DialogClosedCallback callback,bool success,const std::u16string & user_input)215 void CefJavaScriptDialogManager::DialogClosed(
216     DialogClosedCallback callback,
217     bool success,
218     const std::u16string& user_input) {
219   CefRefPtr<CefClient> client = browser_->GetClient();
220   if (client.get()) {
221     CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
222     if (handler.get())
223       handler->OnDialogClosed(browser_);
224   }
225 
226   DCHECK(runner_.get());
227   DCHECK(dialog_running_);
228 
229   dialog_running_ = false;
230 
231   std::move(callback).Run(success, user_input);
232 }
233