// 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 #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: CefJSDialogCallbackImpl( const content::JavaScriptDialogManager::DialogClosedCallback& callback) : callback_(callback) { } ~CefJSDialogCallbackImpl() override { if (!callback_.is_null()) { // The callback is still pending. Cancel it now. if (CEF_CURRENTLY_ON_UIT()) { CancelNow(callback_); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefJSDialogCallbackImpl::CancelNow, callback_)); } } } void Continue(bool success, const CefString& user_input) override { if (CEF_CURRENTLY_ON_UIT()) { if (!callback_.is_null()) { callback_.Run(success, user_input); callback_.Reset(); } } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefJSDialogCallbackImpl::Continue, this, success, user_input)); } } void Disconnect() { callback_.Reset(); } private: static void CancelNow( const content::JavaScriptDialogManager::DialogClosedCallback& callback) { CEF_REQUIRE_UIT(); callback.Run(false, base::string16()); } content::JavaScriptDialogManager::DialogClosedCallback callback_; IMPLEMENT_REFCOUNTING(CefJSDialogCallbackImpl); }; } // namespace CefJavaScriptDialogManager::CefJavaScriptDialogManager( CefBrowserHostImpl* browser, std::unique_ptr runner) : browser_(browser), runner_(std::move(runner)), dialog_running_(false), weak_ptr_factory_(this) { } CefJavaScriptDialogManager::~CefJavaScriptDialogManager() { } void CefJavaScriptDialogManager::Destroy() { if (runner_.get()) { DCHECK(!dialog_running_); runner_.reset(NULL); } } void CefJavaScriptDialogManager::RunJavaScriptDialog( content::WebContents* web_contents, const GURL& origin_url, content::JavaScriptMessageType message_type, const base::string16& message_text, const base::string16& default_prompt_text, const DialogClosedCallback& callback, bool* did_suppress_message) { CefRefPtr client = browser_->GetClient(); if (client.get()) { CefRefPtr handler = client->GetJSDialogHandler(); if (handler.get()) { *did_suppress_message = false; CefRefPtr callbackPtr( new CefJSDialogCallbackImpl(callback)); // Execute the user callback. bool handled = handler->OnJSDialog(browser_, origin_url.spec(), static_cast(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; } callbackPtr->Disconnect(); if (*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); runner_->Run(browser_, message_type, display_url, message_text, default_prompt_text, base::Bind(&CefJavaScriptDialogManager::DialogClosed, weak_ptr_factory_.GetWeakPtr(), callback)); } void CefJavaScriptDialogManager::RunBeforeUnloadDialog( content::WebContents* web_contents, bool is_reload, const DialogClosedCallback& callback) { if (browser_->destruction_state() >= CefBrowserHostImpl::DESTRUCTION_STATE_ACCEPTED) { // Currently destroying the browser. Accept the unload without showing // the prompt. callback.Run(true, base::string16()); return; } const base::string16& message_text = base::ASCIIToUTF16("Is it OK to leave/reload this page?"); CefRefPtr client = browser_->GetClient(); if (client.get()) { CefRefPtr handler = client->GetJSDialogHandler(); if (handler.get()) { CefRefPtr callbackPtr( new CefJSDialogCallbackImpl(callback)); // Execute the user callback. bool handled = handler->OnBeforeUnloadDialog(browser_, message_text, is_reload, callbackPtr.get()); if (handled) return; callbackPtr->Disconnect(); } } 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. callback.Run(true, base::string16()); return; } dialog_running_ = true; runner_->Run(browser_, content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM, base::string16(), // display_url message_text, base::string16(), // default_prompt_text base::Bind(&CefJavaScriptDialogManager::DialogClosed, weak_ptr_factory_.GetWeakPtr(), callback)); } void CefJavaScriptDialogManager::CancelDialogs( content::WebContents* web_contents, bool suppress_callbacks, bool reset_state) { CefRefPtr client = browser_->GetClient(); if (client.get()) { CefRefPtr 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( const DialogClosedCallback& callback, bool success, const base::string16& user_input) { CefRefPtr client = browser_->GetClient(); if (client.get()) { CefRefPtr handler = client->GetJSDialogHandler(); if (handler.get()) handler->OnDialogClosed(browser_); } DCHECK(runner_.get()); DCHECK(dialog_running_); dialog_running_ = false; callback.Run(success, user_input); }