// Copyright (c) 2012-2014 The CEF Python authors. All rights reserved.
// License: New BSD License.
// Website: http://code.google.com/p/cefpython/
// NOTE: clienthandler code is running only in the BROWSER PROCESS.
// cefpythonapp code is running in both BROWSER PROCESS and subprocess
// (see the subprocess/ directory).
#include "client_handler.h"
#include "cefpython_public_api.h"
#include "DebugLog.h"
#include "LOG_DEBUG.h"
#if defined(OS_WIN)
#include
#pragma comment(lib, "Shell32.lib")
#include "dpi_aware.h"
#elif defined(OS_LINUX)
#include
#include
#endif
// ----------------------------------------------------------------------------
// Linux equivalent of ShellExecute
// ----------------------------------------------------------------------------
#if defined(OS_LINUX)
void OpenInExternalBrowser(const std::string& url)
{
if (url.empty()) {
DebugLog("Browser: OpenInExternalBrowser() FAILED: url is empty");
return;
}
std::string msg = "Browser: OpenInExternalBrowser(): url=";
msg.append(url.c_str());
DebugLog(msg.c_str());
// xdg-open is a desktop-independent tool for running
// default applications. Installed by default on Ubuntu.
// xdg-open process is running in the backround until
// cefpython app closes.
std::string prog = "xdg-open";
// Using system() opens up for bugs and exploits, not
// recommended.
// Fork yourself and run in parallel, do not block the
// current proces.
char *args[3];
args[0] = (char*) prog.c_str();
args[1] = (char*) url.c_str();
args[2] = 0;
pid_t pid = fork();
if (!pid) {
execvp(prog.c_str(), args);
}
}
#endif
// ----------------------------------------------------------------------------
// CefClient
// ----------------------------------------------------------------------------
bool ClientHandler::OnProcessMessageReceived(CefRefPtr browser,
CefProcessId source_process,
CefRefPtr message) {
if (source_process != PID_RENDERER) {
return false;
}
std::string messageName = message->GetName().ToString();
std::string logMessage = "Browser: OnProcessMessageReceived(): ";
logMessage.append(messageName.c_str());
DebugLog(logMessage.c_str());
if (messageName == "OnContextCreated") {
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) {
int64 frameId = arguments->GetInt(0);
CefRefPtr frame = browser->GetFrame(frameId);
V8ContextHandler_OnContextCreated(browser, frame);
return true;
} else {
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
", messageName = OnContextCreated");
return false;
}
} else if (messageName == "OnContextReleased") {
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 2 \
&& arguments->GetType(0) == VTYPE_INT \
&& arguments->GetType(1) == VTYPE_INT) {
int browserId = arguments->GetInt(0);
int64 frameId = arguments->GetInt(1);
V8ContextHandler_OnContextReleased(browserId, frameId);
return true;
} else {
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
", messageName = OnContextReleased");
return false;
}
} else if (messageName == "V8FunctionHandler::Execute") {
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 3
&& arguments->GetType(0) == VTYPE_INT // frameId
&& arguments->GetType(1) == VTYPE_STRING // functionName
&& arguments->GetType(2) == VTYPE_LIST) { // functionArguments
int64 frameId = arguments->GetInt(0);
CefString functionName = arguments->GetString(1);
CefRefPtr functionArguments = arguments->GetList(2);
CefRefPtr frame = browser->GetFrame(frameId);
V8FunctionHandler_Execute(browser, frame, functionName, functionArguments);
return true;
} else {
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
", messageName = V8FunctionHandler::Execute");
return false;
}
} else if (messageName == "ExecutePythonCallback") {
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 2
&& arguments->GetType(0) == VTYPE_INT // callbackId
&& arguments->GetType(1) == VTYPE_LIST) { // functionArguments
int callbackId = arguments->GetInt(0);
CefRefPtr functionArguments = arguments->GetList(1);
ExecutePythonCallback(browser, callbackId, functionArguments);
return true;
} else {
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
", messageName = ExecutePythonCallback");
return false;
}
} else if (messageName == "RemovePythonCallbacksForFrame") {
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) {
int frameId = arguments->GetInt(0);
RemovePythonCallbacksForFrame(frameId);
return true;
} else {
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
", messageName = ExecutePythonCallback");
return false;
}
}
return false;
}
// ----------------------------------------------------------------------------
// CefLifeSpanHandler
// ----------------------------------------------------------------------------
bool ClientHandler::OnBeforePopup(CefRefPtr browser,
CefRefPtr frame,
const CefString& target_url,
const CefString& target_frame_name,
WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr& client,
CefBrowserSettings& settings,
bool* no_javascript_access) {
REQUIRE_IO_THREAD();
// Note: passing popupFeatures is not yet supported.
const int popupFeaturesNotImpl = 0;
return LifespanHandler_OnBeforePopup(browser, frame, target_url,
target_frame_name, target_disposition, user_gesture,
popupFeaturesNotImpl, windowInfo, client, settings,
no_javascript_access);
}
void ClientHandler::OnAfterCreated(CefRefPtr browser) {
REQUIRE_UI_THREAD();
#if defined(OS_WIN)
// High DPI support.
CefString auto_zooming = ApplicationSettings_GetString("auto_zooming");
if (!auto_zooming.empty()) {
LOG_DEBUG browser) {
REQUIRE_UI_THREAD();
return LifespanHandler_DoClose(browser);
}
void ClientHandler::OnBeforeClose(CefRefPtr browser) {
REQUIRE_UI_THREAD();
LifespanHandler_OnBeforeClose(browser);
}
// --------------------------------------------------------------------------
// CefDisplayHandler
// --------------------------------------------------------------------------
void ClientHandler::OnAddressChange(CefRefPtr browser,
CefRefPtr frame,
const CefString& url) {
REQUIRE_UI_THREAD();
DisplayHandler_OnAddressChange(browser, frame, url);
}
void ClientHandler::OnTitleChange(CefRefPtr browser,
const CefString& title) {
REQUIRE_UI_THREAD();
DisplayHandler_OnTitleChange(browser, title);
}
bool ClientHandler::OnTooltip(CefRefPtr browser,
CefString& text) {
REQUIRE_UI_THREAD();
return DisplayHandler_OnTooltip(browser, text);
// return false;
}
void ClientHandler::OnStatusMessage(CefRefPtr browser,
const CefString& value) {
REQUIRE_UI_THREAD();
DisplayHandler_OnStatusMessage(browser, value);
}
bool ClientHandler::OnConsoleMessage(CefRefPtr browser,
const CefString& message,
const CefString& source,
int line) {
REQUIRE_UI_THREAD();
return DisplayHandler_OnConsoleMessage(browser, message, source, line);
// return false;
}
// ----------------------------------------------------------------------------
// CefKeyboardHandler
// ----------------------------------------------------------------------------
bool ClientHandler::OnPreKeyEvent(CefRefPtr browser,
const CefKeyEvent& event,
CefEventHandle os_event,
bool* is_keyboard_shortcut) {
REQUIRE_UI_THREAD();
return KeyboardHandler_OnPreKeyEvent(browser, event, os_event,
is_keyboard_shortcut);
// Default: return false;
}
bool ClientHandler::OnKeyEvent(CefRefPtr browser,
const CefKeyEvent& event,
CefEventHandle os_event) {
REQUIRE_UI_THREAD();
return KeyboardHandler_OnKeyEvent(browser, event, os_event);
// Default: return false;
}
// --------------------------------------------------------------------------
// CefRequestHandler
// --------------------------------------------------------------------------
bool ClientHandler::OnBeforeBrowse(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
bool is_redirect) {
REQUIRE_UI_THREAD();
return RequestHandler_OnBeforeBrowse(browser, frame, request, is_redirect);
}
ReturnValue ClientHandler::OnBeforeResourceLoad(
CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
CefRefPtr callback) {
REQUIRE_IO_THREAD();
bool retval = RequestHandler_OnBeforeResourceLoad(browser, frame, request);
if (retval) {
return RV_CANCEL;
} else {
return RV_CONTINUE;
}
// Default: return RV_CONTINUE;
}
CefRefPtr ClientHandler::GetResourceHandler(
CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request) {
REQUIRE_IO_THREAD();
return RequestHandler_GetResourceHandler(browser, frame, request);
}
void ClientHandler::OnResourceRedirect(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
CefString& new_url) {
REQUIRE_IO_THREAD();
RequestHandler_OnResourceRedirect(browser, frame, request->GetURL(),
new_url, request);
}
bool ClientHandler::GetAuthCredentials(CefRefPtr browser,
CefRefPtr frame,
bool isProxy,
const CefString& host,
int port,
const CefString& realm,
const CefString& scheme,
CefRefPtr callback) {
REQUIRE_IO_THREAD();
return RequestHandler_GetAuthCredentials(browser, frame, isProxy, host,
port, realm, scheme, callback);
// Default: return false;
}
bool ClientHandler::OnQuotaRequest(CefRefPtr browser,
const CefString& origin_url,
int64 new_size,
CefRefPtr callback) {
REQUIRE_IO_THREAD();
return RequestHandler_OnQuotaRequest(browser, origin_url, new_size,
callback);
// Default: return false;
}
void ClientHandler::OnProtocolExecution(CefRefPtr browser,
const CefString& url,
bool& allow_os_execution) {
REQUIRE_UI_THREAD();
RequestHandler_OnProtocolExecution(browser, url, allow_os_execution);
}
bool ClientHandler::OnCertificateError(
CefRefPtr browser, // not used
cef_errorcode_t cert_error,
const CefString& request_url,
CefRefPtr ssl_info, // not used
CefRefPtr callback) {
REQUIRE_UI_THREAD();
return RequestHandler_OnCertificateError(
cert_error, request_url, callback);
// Default: return false;
}
void ClientHandler::OnRenderProcessTerminated(CefRefPtr browser,
cef_termination_status_t status) {
REQUIRE_UI_THREAD();
DebugLog("Browser: OnRenderProcessTerminated()");
RequestHandler_OnRendererProcessTerminated(browser, status);
}
void ClientHandler::OnPluginCrashed(CefRefPtr browser,
const CefString& plugin_path) {
REQUIRE_UI_THREAD();
RequestHandler_OnPluginCrashed(browser, plugin_path);
}
// --------------------------------------------------------------------------
// CefLoadHandler
// --------------------------------------------------------------------------
void ClientHandler::OnLoadingStateChange(CefRefPtr browser,
bool isLoading,
bool canGoBack,
bool canGoForward) {
REQUIRE_UI_THREAD();
LoadHandler_OnLoadingStateChange(browser, isLoading, canGoBack,
canGoForward);
}
void ClientHandler::OnLoadStart(CefRefPtr browser,
CefRefPtr frame) {
REQUIRE_UI_THREAD();
LoadHandler_OnLoadStart(browser, frame);
}
void ClientHandler::OnLoadEnd(CefRefPtr browser,
CefRefPtr frame,
int httpStatusCode) {
REQUIRE_UI_THREAD();
LoadHandler_OnLoadEnd(browser, frame, httpStatusCode);
}
void ClientHandler::OnLoadError(CefRefPtr browser,
CefRefPtr frame,
cef_errorcode_t errorCode,
const CefString& errorText,
const CefString& failedUrl) {
REQUIRE_UI_THREAD();
LoadHandler_OnLoadError(browser, frame, errorCode, errorText, failedUrl);
}
// ----------------------------------------------------------------------------
// CefRenderHandler
// ----------------------------------------------------------------------------
bool ClientHandler::GetRootScreenRect(CefRefPtr browser,
CefRect& rect) {
REQUIRE_UI_THREAD();
return RenderHandler_GetRootScreenRect(browser, rect);
}
bool ClientHandler::GetViewRect(CefRefPtr browser, CefRect& rect) {
REQUIRE_UI_THREAD();
return RenderHandler_GetViewRect(browser, rect);
}
bool ClientHandler::GetScreenPoint(CefRefPtr browser,
int viewX,
int viewY,
int& screenX,
int& screenY) {
REQUIRE_UI_THREAD();
return RenderHandler_GetScreenPoint(browser, viewX, viewY, screenX,
screenY);
}
bool ClientHandler::GetScreenInfo(CefRefPtr browser,
CefScreenInfo& screen_info) {
REQUIRE_UI_THREAD();
return RenderHandler_GetScreenInfo(browser, screen_info);
}
void ClientHandler::OnPopupShow(CefRefPtr browser,
bool show) {
REQUIRE_UI_THREAD();
RenderHandler_OnPopupShow(browser, show);
}
void ClientHandler::OnPopupSize(CefRefPtr browser,
const CefRect& rect) {
REQUIRE_UI_THREAD();
RenderHandler_OnPopupSize(browser, rect);
}
void ClientHandler::OnPaint(CefRefPtr browser,
PaintElementType type,
const RectList& dirtyRects,
const void* buffer,
int width, int height) {
REQUIRE_UI_THREAD();
RenderHandler_OnPaint(browser, type, const_cast(dirtyRects), \
buffer, width, height);
};
void ClientHandler::OnCursorChange(CefRefPtr browser,
CefCursorHandle cursor,
CursorType type,
const CefCursorInfo& custom_cursor_info) {
REQUIRE_UI_THREAD();
RenderHandler_OnCursorChange(browser, cursor);
}
void ClientHandler::OnScrollOffsetChanged(CefRefPtr browser,
double x,
double y) {
REQUIRE_UI_THREAD();
RenderHandler_OnScrollOffsetChanged(browser);
}
// ----------------------------------------------------------------------------
// CefJSDialogHandler
// ----------------------------------------------------------------------------
bool ClientHandler::OnJSDialog(CefRefPtr browser,
const CefString& origin_url,
JSDialogType dialog_type,
const CefString& message_text,
const CefString& default_prompt_text,
CefRefPtr callback,
bool& suppress_message) {
REQUIRE_UI_THREAD();
return JavascriptDialogHandler_OnJavascriptDialog(browser, origin_url,
dialog_type, message_text, default_prompt_text,
callback, suppress_message);
}
bool ClientHandler::OnBeforeUnloadDialog(CefRefPtr browser,
const CefString& message_text,
bool is_reload,
CefRefPtr callback) {
REQUIRE_UI_THREAD();
return JavascriptDialogHandler_OnBeforeUnloadJavascriptDialog(browser,
message_text, is_reload, callback);
}
void ClientHandler::OnResetDialogState(CefRefPtr browser) {
REQUIRE_UI_THREAD();
return JavascriptDialogHandler_OnResetJavascriptDialogState(browser);
}
void ClientHandler::OnDialogClosed(CefRefPtr browser) {
REQUIRE_UI_THREAD();
return JavascriptDialogHandler_OnJavascriptDialogClosed(browser);
}
// ----------------------------------------------------------------------------
// CefDownloadHandler
// ----------------------------------------------------------------------------
void ClientHandler::OnBeforeDownload(CefRefPtr browser,
CefRefPtr download_item,
const CefString& suggested_name,
CefRefPtr callback) {
REQUIRE_UI_THREAD();
bool downloads_enabled = ApplicationSettings_GetBool("downloads_enabled");
if (downloads_enabled) {
std::string msg = "Browser: About to download file: ";
msg.append(suggested_name.ToString().c_str());
DebugLog(msg.c_str());
callback->Continue(suggested_name, true);
} else {
DebugLog("Browser: Tried to download file, but downloads are disabled");
}
}
void ClientHandler::OnDownloadUpdated(
CefRefPtr browser,
CefRefPtr download_item,
CefRefPtr callback) {
REQUIRE_UI_THREAD();
if (download_item->IsComplete()) {
std::string msg = "Browser: Download completed, saved to: ";
msg.append(download_item->GetFullPath().ToString().c_str());
DebugLog(msg.c_str());
} else if (download_item->IsCanceled()) {
DebugLog("Browser: Download was cancelled");
}
}
// ----------------------------------------------------------------------------
// CefContextMenuHandler
// ----------------------------------------------------------------------------
#define _MENU_ID_DEVTOOLS MENU_ID_USER_FIRST + 1
#define _MENU_ID_RELOAD_PAGE MENU_ID_USER_FIRST + 2
#define _MENU_ID_OPEN_PAGE_IN_EXTERNAL_BROWSER MENU_ID_USER_FIRST + 3
#define _MENU_ID_OPEN_FRAME_IN_EXTERNAL_BROWSER MENU_ID_USER_FIRST + 4
void ClientHandler::OnBeforeContextMenu(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr params,
CefRefPtr model) {
bool enabled = ApplicationSettings_GetBoolFromDict(\
"context_menu", "enabled");
bool navigation = ApplicationSettings_GetBoolFromDict(\
"context_menu", "navigation");
bool print = ApplicationSettings_GetBoolFromDict(\
"context_menu", "print");
bool view_source = ApplicationSettings_GetBoolFromDict(\
"context_menu", "view_source");
bool external_browser = ApplicationSettings_GetBoolFromDict(\
"context_menu", "external_browser");
bool devtools = ApplicationSettings_GetBoolFromDict(\
"context_menu", "devtools");
if (!enabled) {
model->Clear();
return;
}
if (!navigation) {
model->Remove(MENU_ID_BACK);
model->Remove(MENU_ID_FORWARD);
// Remove separator
model->RemoveAt(0);
}
if (!print) {
model->Remove(MENU_ID_PRINT);
}
if (!view_source) {
model->Remove(MENU_ID_VIEW_SOURCE);
}
if (!params->IsEditable() && params->GetSelectionText().empty()
&& (params->GetPageUrl().length()
|| params->GetFrameUrl().length())) {
if (external_browser) {
model->AddItem(_MENU_ID_OPEN_PAGE_IN_EXTERNAL_BROWSER,
"Open in external browser");
if (params->GetFrameUrl().length()
&& params->GetPageUrl() != params->GetFrameUrl()) {
model->AddItem(_MENU_ID_OPEN_FRAME_IN_EXTERNAL_BROWSER,
"Open frame in external browser");
}
}
if (navigation) {
model->InsertItemAt(2, _MENU_ID_RELOAD_PAGE, "Reload");
}
if (devtools) {
model->AddSeparator();
model->AddItem(_MENU_ID_DEVTOOLS, "Developer Tools");
}
}
}
bool ClientHandler::OnContextMenuCommand(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr params,
int command_id,
EventFlags event_flags) {
if (command_id == _MENU_ID_OPEN_PAGE_IN_EXTERNAL_BROWSER) {
#if defined(OS_WIN)
ShellExecuteA(0, "open", params->GetPageUrl().ToString().c_str(),
0, 0, SW_SHOWNORMAL);
#elif defined(OS_LINUX)
OpenInExternalBrowser(params->GetPageUrl().ToString());
#endif
return true;
} else if (command_id == _MENU_ID_OPEN_FRAME_IN_EXTERNAL_BROWSER) {
#if defined(OS_WIN)
ShellExecuteA(0, "open", params->GetFrameUrl().ToString().c_str(),
0, 0, SW_SHOWNORMAL);
#elif defined(OS_LINUX)
OpenInExternalBrowser(params->GetFrameUrl().ToString());
#endif
return true;
} else if (command_id == _MENU_ID_RELOAD_PAGE) {
browser->ReloadIgnoreCache();
return true;
} else if (command_id == _MENU_ID_DEVTOOLS) {
PyBrowser_ShowDevTools(browser);
return true;
}
return false;
}
void ClientHandler::OnContextMenuDismissed(CefRefPtr browser,
CefRefPtr frame) {
}