blob: 4e4c791f976c80032206a19b0d60d2a7f0674dd8 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libcef/browser/javascript_dialog_manager.h"
#include <utility>
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/thread_util.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "components/url_formatter/elide_url.h"
namespace {
class CefJSDialogCallbackImpl : public CefJSDialogCallback {
public:
using CallbackType = content::JavaScriptDialogManager::DialogClosedCallback;
CefJSDialogCallbackImpl(CallbackType callback)
: callback_(std::move(callback)) {}
~CefJSDialogCallbackImpl() override {
if (!callback_.is_null()) {
// The callback is still pending. Cancel it now.
if (CEF_CURRENTLY_ON_UIT()) {
CancelNow(std::move(callback_));
} else {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefJSDialogCallbackImpl::CancelNow,
std::move(callback_)));
}
}
}
void Continue(bool success, const CefString& user_input) override {
if (CEF_CURRENTLY_ON_UIT()) {
if (!callback_.is_null()) {
std::move(callback_).Run(success, user_input);
}
} else {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefJSDialogCallbackImpl::Continue,
this, success, user_input));
}
}
CallbackType Disconnect() WARN_UNUSED_RESULT { return std::move(callback_); }
private:
static void CancelNow(CallbackType callback) {
CEF_REQUIRE_UIT();
std::move(callback).Run(false, base::string16());
}
CallbackType callback_;
IMPLEMENT_REFCOUNTING(CefJSDialogCallbackImpl);
};
} // namespace
CefJavaScriptDialogManager::CefJavaScriptDialogManager(
CefBrowserHostImpl* browser,
std::unique_ptr<CefJavaScriptDialogRunner> runner)
: browser_(browser),
runner_(std::move(runner)),
dialog_running_(false),
weak_ptr_factory_(this) {}
CefJavaScriptDialogManager::~CefJavaScriptDialogManager() {}
void CefJavaScriptDialogManager::Destroy() {
if (runner_.get()) {
runner_.reset(nullptr);
}
}
void CefJavaScriptDialogManager::RunJavaScriptDialog(
content::WebContents* web_contents,
content::RenderFrameHost* render_frame_host,
content::JavaScriptDialogType message_type,
const base::string16& message_text,
const base::string16& default_prompt_text,
DialogClosedCallback callback,
bool* did_suppress_message) {
const GURL& origin_url = render_frame_host->GetLastCommittedURL();
CefRefPtr<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
if (handler.get()) {
*did_suppress_message = false;
CefRefPtr<CefJSDialogCallbackImpl> callbackPtr(
new CefJSDialogCallbackImpl(std::move(callback)));
// Execute the user callback.
bool handled = handler->OnJSDialog(
browser_, origin_url.spec(),
static_cast<cef_jsdialog_type_t>(message_type), message_text,
default_prompt_text, callbackPtr.get(), *did_suppress_message);
if (handled) {
// Invalid combination of values. Crash sooner rather than later.
CHECK(!*did_suppress_message);
return;
}
// |callback| may be null if the user executed it despite returning false.
callback = callbackPtr->Disconnect();
if (callback.is_null() || *did_suppress_message)
return;
}
}
*did_suppress_message = false;
if (!runner_.get() || dialog_running_) {
// Suppress the dialog if there is no platform runner or if the dialog is
// currently running.
if (!runner_.get())
LOG(WARNING) << "No javascript dialog runner available for this platform";
*did_suppress_message = true;
return;
}
dialog_running_ = true;
const base::string16& display_url =
url_formatter::FormatUrlForSecurityDisplay(origin_url);
DCHECK(!callback.is_null());
runner_->Run(
browser_, message_type, display_url, message_text, default_prompt_text,
base::BindOnce(&CefJavaScriptDialogManager::DialogClosed,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void CefJavaScriptDialogManager::RunBeforeUnloadDialog(
content::WebContents* web_contents,
content::RenderFrameHost* render_frame_host,
bool is_reload,
DialogClosedCallback callback) {
if (browser_->destruction_state() >=
CefBrowserHostImpl::DESTRUCTION_STATE_ACCEPTED) {
// Currently destroying the browser. Accept the unload without showing
// the prompt.
std::move(callback).Run(true, base::string16());
return;
}
const base::string16& message_text =
base::ASCIIToUTF16("Is it OK to leave/reload this page?");
CefRefPtr<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
if (handler.get()) {
CefRefPtr<CefJSDialogCallbackImpl> callbackPtr(
new CefJSDialogCallbackImpl(std::move(callback)));
// Execute the user callback.
bool handled = handler->OnBeforeUnloadDialog(
browser_, message_text, is_reload, callbackPtr.get());
if (handled)
return;
// |callback| may be null if the user executed it despite returning false.
callback = callbackPtr->Disconnect();
if (callback.is_null())
return;
}
}
if (!runner_.get() || dialog_running_) {
if (!runner_.get())
LOG(WARNING) << "No javascript dialog runner available for this platform";
// Suppress the dialog if there is no platform runner or if the dialog is
// currently running.
std::move(callback).Run(true, base::string16());
return;
}
dialog_running_ = true;
DCHECK(!callback.is_null());
runner_->Run(
browser_, content::JAVASCRIPT_DIALOG_TYPE_CONFIRM,
base::string16(), // display_url
message_text,
base::string16(), // default_prompt_text
base::BindOnce(&CefJavaScriptDialogManager::DialogClosed,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void CefJavaScriptDialogManager::CancelDialogs(
content::WebContents* web_contents,
bool reset_state) {
CefRefPtr<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
if (handler.get()) {
// Execute the user callback.
handler->OnResetDialogState(browser_);
}
}
if (runner_.get() && dialog_running_) {
runner_->Cancel();
dialog_running_ = false;
}
}
void CefJavaScriptDialogManager::DialogClosed(
DialogClosedCallback callback,
bool success,
const base::string16& user_input) {
CefRefPtr<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
if (handler.get())
handler->OnDialogClosed(browser_);
}
DCHECK(runner_.get());
DCHECK(dialog_running_);
dialog_running_ = false;
std::move(callback).Run(success, user_input);
}