Skip to content

Commit

Permalink
Implement external message pump, still experimental (#246).
Browse files Browse the repository at this point in the history
Add new option ApplicationSettings.external_message_pump. This is
experimental, actually it makes app slower, reported issue in upstream.

Add cefpython.GetAppSetting() func.

Show CEF Python version in unit test runner.
  • Loading branch information
cztomczak committed Sep 25, 2016
1 parent b51332e commit f8286e0
Show file tree
Hide file tree
Showing 25 changed files with 1,404 additions and 88 deletions.
25 changes: 25 additions & 0 deletions api/ApplicationSettings.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Table of contents:
* [command_line_args_disabled](#command_line_args_disabled)
* [context_menu](#context_menu)
* [downloads_enabled](#downloads_enabled)
* [external_message_pump](#external_message_pump)
* [ignore_certificate_errors](#ignore_certificate_errors)
* [javascript_flags](#javascript_flags)
* [locale](#locale)
Expand Down Expand Up @@ -148,6 +149,30 @@ Default: True
Downloads are handled automatically. A default `SaveAs` file dialog provided by OS is displayed. See also the [DownloadHandler](DownloadHandler.md) wiki page.


### external_message_pump

(bool)
Default: False

EXPERIMENTAL: currently this option makes browser slower, so don't use it.
Reported issue in upstream, see Issue #246 for details.

It is recommended to use this option as a replacement for calls to
cefpython.MessageLoopWork(). CEF Python will do these calls automatically
using CEF's OnScheduleMessagePumpWork. This results in improved performance
and resolves some bugs. See Issue #246 for more details.

Description from upstream CEF:
> Set to true (1) to control browser process main (UI) thread message pump
> scheduling via the CefBrowserProcessHandler::OnScheduleMessagePumpWork()
> callback. This option is recommended for use in combination with the
> CefDoMessageLoopWork() function in cases where the CEF message loop must be
> integrated into an existing application message loop (see additional
> comments and warnings on CefDoMessageLoopWork). Enabling this option is not
> recommended for most users; leave this option disabled and use either the
> CefRunMessageLoop() function or multi_threaded_message_loop if possible.

### ignore_certificate_errors

(bool)
Expand Down
27 changes: 14 additions & 13 deletions api/cefpython.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ source code of ExceptHook in the cefpython/src/helpers.pyx file.
| key | string |
| __Return__ | object |

Returns [ApplicationSettings](ApplicationSettings.md) option that was passed to Initialize(). Returns None if key is not found.
Returns [ApplicationSettings](ApplicationSettings.md) option that was passed
to Initialize(). Returns None if key is not found.


### GetAppPath
Expand Down Expand Up @@ -147,8 +148,8 @@ Get the cefpython module directory. This method is useful to get full path to CE

| Parameter | Type |
| --- | --- |
| [ApplicationSettings](ApplicationSettings.md)=None | dict |
| [CommandLineSwitches](CommandLineSwitches.md)=None | dict |
| settings (optional) | [ApplicationSettings](ApplicationSettings.md) |
| switches (optional) | [CommandLineSwitches](CommandLineSwitches.md) |
| __Return__ | bool |

This function should be called on the main application thread (UI thread) to initialize CEF when the application is started. A call to Initialize() must have a corresponding call to Shutdown() so that CEF exits cleanly. Otherwise when application closes data (eg. storage, cookies) might not be saved to disk or the process might freeze (experienced on Windows XP).
Expand Down Expand Up @@ -186,8 +187,14 @@ List of threads in the Renderer process:
| __Return__ | void |

Run the CEF message loop. Use this function instead of an application-
provided message loop to get the best balance between performance and CPU usage. This function should only be called on the main application thread (UI thread) and only if cefpython.Initialize() is called with a
[ApplicationSettings](ApplicationSettings.md).multi_threaded_message_loop value of false. This function will block until a quit message is received by the system.
provided message loop to get the best balance between performance and
CPU usage. This function should only be called on the main application
thread (UI thread) and only if cefpython.Initialize() is called with a
[ApplicationSettings](ApplicationSettings.md).multi_threaded_message_loop
value of false. This function will block until a quit message is received
by the system.

See also MessageLoopWork().


### MessageLoopWork
Expand All @@ -196,8 +203,9 @@ provided message loop to get the best balance between performance and CPU usage.
| --- | --- |
| __Return__ | void |

Description from upstream CEF:
Call this function in a periodic timer (eg. 10ms).

Description from upstream CEF:
> Perform a single iteration of CEF message loop processing. This function is
> provided for cases where the CEF message loop must be integrated into an
> existing application message loop. Use of this function is not recommended
Expand All @@ -211,13 +219,6 @@ Description from upstream CEF:
> with a CefSettings.multi_threaded_message_loop value of false. This function
> will not block.
Alternatively you could create a periodic timer (with 10 ms interval) that calls
cefpython.MessageLoopWork().

MessageLoopWork() is not tested on OS X and there are known issues - according to
[this post](http:https://www.magpcss.org/ceforum/viewtopic.php?p=27124#p27124) by
Marshall.


### PostTask

Expand Down
43 changes: 36 additions & 7 deletions src/cefpython.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ from libc.string cimport memcpy
# preincrement and dereference must be "as" otherwise not seen.
# noinspection PyUnresolvedReferences
from cython.operator cimport preincrement as preinc, dereference as deref
# noinspection PyUnresolvedReferences

# from cython.operator cimport address as addr # Address of an c++ object?

Expand Down Expand Up @@ -427,6 +428,8 @@ from cef_jsdialog_handler cimport *
from cef_path_util cimport *
from cef_drag_data cimport *
from cef_image cimport *
from main_message_loop cimport *
from cef_scoped_ptr cimport scoped_ptr


# -----------------------------------------------------------------------------
Expand All @@ -442,6 +445,8 @@ g_debugFile = "debug.log"
g_applicationSettings = {"string_encoding": "utf-8"}
g_commandLineSwitches = {}

cdef scoped_ptr[MainMessageLoopExternalPump] g_external_message_pump

# noinspection PyUnresolvedReferences
cdef cpp_bool _MessageLoopWork_wasused = False

Expand Down Expand Up @@ -567,7 +572,13 @@ cdef public int CommandLineSwitches_GetInt(const char* key) except * with gil:
# Linux, then do not to run any of the CEF code until Initialize()
# is called. See Issue #73 in the CEF Python Issue Tracker.

def Initialize(applicationSettings=None, commandLineSwitches=None):
def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):

# Alternative names for existing parameters
if "settings" in kwargs:
applicationSettings = kwargs["settings"]
if "switches" in kwargs:
commandLineSwitches = kwargs["switches"]

# Fix Issue #231 - Discovery of the "icudtl.dat" file fails on Linux.
# Apply patch for all platforms just in case.
Expand Down Expand Up @@ -698,6 +709,11 @@ def Initialize(applicationSettings=None, commandLineSwitches=None):
g_commandLineSwitches[key] = copy.deepcopy(
commandLineSwitches[key])

# External message pump
if GetAppSetting("external_message_pump")\
and not g_external_message_pump.get():
g_external_message_pump.Assign(MainMessageLoopExternalPump.Create())

Debug("CefInitialize()")
cdef cpp_bool ret
with nogil:
Expand All @@ -720,20 +736,24 @@ def CreateBrowserSync(windowInfo=None,
browserSettings=None,
navigateUrl="",
**kwargs):
# Alternative names for existing parameters
if "window_info" in kwargs:
windowInfo = kwargs["window_info"]
if "settings" in kwargs:
browserSettings = kwargs["settings"]
if "url" in kwargs:
navigateUrl = kwargs["url"]

Debug("CreateBrowserSync() called")
assert IsThread(TID_UI), (
"cefpython.CreateBrowserSync() may only be called on the UI thread")

if "window_info" in kwargs:
windowInfo = kwargs["window_info"]
if not windowInfo:
windowInfo = WindowInfo()
windowInfo.SetAsChild(0)
elif not isinstance(windowInfo, WindowInfo):
raise Exception("CreateBrowserSync() failed: windowInfo: invalid object")

if "settings" in kwargs:
browserSettings = kwargs["settings"]
if not browserSettings:
browserSettings = {}

Expand All @@ -743,8 +763,7 @@ def CreateBrowserSync(windowInfo=None,
cdef CefWindowInfo cefWindowInfo
SetCefWindowInfo(cefWindowInfo, windowInfo)

if "url" in kwargs:
navigateUrl = kwargs["url"]

navigateUrl = GetNavigateUrl(navigateUrl)
Debug("navigateUrl: %s" % navigateUrl)
cdef CefString cefNavigateUrl
Expand Down Expand Up @@ -882,6 +901,11 @@ def Shutdown():
for i in range(10):
CefDoMessageLoopWork()

# Release external message pump, as in cefclient after Shutdown
if g_external_message_pump.get():
# Reset will set it to NULL
g_external_message_pump.reset()


def SetOsModalLoop(py_bool modalLoop):
cdef cpp_bool cefModalLoop = bool(modalLoop)
Expand All @@ -903,3 +927,8 @@ cpdef object GetGlobalClientCallback(py_string name):
else:
return None

cpdef object GetAppSetting(py_string key):
global g_applicationSettings
if key in g_applicationSettings:
return g_applicationSettings[key]
return None
20 changes: 10 additions & 10 deletions src/client_handler/client_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@


class ClientHandler : public CefClient,
ContextMenuHandler,
DisplayHandler,
DownloadHandler,
FocusHandler,
JSDialogHandler,
KeyboardHandler,
LifespanHandler,
LoadHandler,
RenderHandler,
RequestHandler
public ContextMenuHandler,
public DisplayHandler,
public DownloadHandler,
public FocusHandler,
public JSDialogHandler,
public KeyboardHandler,
public LifespanHandler,
public LoadHandler,
public RenderHandler,
public RequestHandler
{
public:
ClientHandler(){}
Expand Down
1 change: 0 additions & 1 deletion src/extern/cef/cef_ptr.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@ cdef extern from "include/internal/cef_ptr.h":
T* get()
# noinspection PyUnresolvedReferences
void swap(CefRefPtr[T]& r)

13 changes: 13 additions & 0 deletions src/extern/cef/cef_scoped_ptr.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2016 CEF Python. See the Authors and License files.

cdef extern from "include/base/cef_scoped_ptr.h":
cdef cppclass scoped_ptr[T]:
scoped_ptr()
# noinspection PyUnresolvedReferences
scoped_ptr(T* p)
# noinspection PyUnresolvedReferences
void reset()
# noinspection PyUnresolvedReferences
T* get()
# noinspection PyUnresolvedReferences
scoped_ptr[T]& Assign "operator="(scoped_ptr[T]& p)
2 changes: 2 additions & 0 deletions src/extern/cef/cef_types.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ from libcpp cimport bool as cpp_bool
# noinspection PyUnresolvedReferences
from libc.stddef cimport wchar_t
from cef_string cimport cef_string_t
# noinspection PyUnresolvedReferences
from libc.limits cimport UINT_MAX

cdef extern from "include/internal/cef_types.h":
Expand Down Expand Up @@ -50,6 +51,7 @@ cdef extern from "include/internal/cef_types.h":
cef_string_t user_data_path
int windowless_rendering_enabled
int no_sandbox
int external_message_pump

ctypedef struct CefBrowserSettings:
cef_string_t accept_language_list
Expand Down
10 changes: 10 additions & 0 deletions src/extern/main_message_loop.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2016 CEF Python. See the Authors and License files.

from cef_scoped_ptr cimport scoped_ptr

cdef extern from \
"subprocess/main_message_loop/main_message_loop_external_pump.h":

cdef cppclass MainMessageLoopExternalPump:
@staticmethod
scoped_ptr[MainMessageLoopExternalPump] Create()
3 changes: 3 additions & 0 deletions src/settings.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ cdef void SetApplicationSettings(
elif key == "windowless_rendering_enabled":
cefAppSettings.windowless_rendering_enabled = \
int(appSettings[key])
elif key == "external_message_pump":
cefAppSettings.external_message_pump = \
int(appSettings[key])
else:
raise Exception("Invalid appSettings key: %s" % key)

Expand Down
12 changes: 10 additions & 2 deletions src/subprocess/Makefile-libcefpythonapp
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@ CCFLAGS = -fPIC -std=gnu++11 -Wall -Werror -DBROWSER_PROCESS \
$(CEF_CCFLAGS)

ifeq ($(UNAME_S), Linux)
CPP_FILES = print_handler_gtk.cpp
CPP_FILES = print_handler_gtk.cpp \
main_message_loop/main_message_loop_external_pump_linux.cpp
else ifeq ($(UNAME_S), Darwin)
CPP_FILES = \
main_message_loop/main_message_loop_external_pump_mac.mm
else
CPP_FILES =
endif


SRC = cefpython_app.cpp v8function_handler.cpp v8utils.cpp \
javascript_callback.cpp $(CPP_FILES)
javascript_callback.cpp \
main_message_loop/main_message_loop.cpp \
main_message_loop/main_message_loop_std.cpp \
main_message_loop/main_message_loop_external_pump.cpp \
$(CPP_FILES)
OBJ = $(SRC:.cpp=.o)
OUT = libcefpythonapp.a

Expand Down
36 changes: 26 additions & 10 deletions src/subprocess/cefpython_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include "javascript_callback.h"
#include "v8function_handler.h"

#ifdef BROWSER_PROCESS
#include "main_message_loop/main_message_loop_external_pump.h"
#endif

bool g_debug = false;
std::string g_logFile = "debug.log";

Expand Down Expand Up @@ -83,19 +87,17 @@ CefRefPtr<CefRenderProcessHandler> CefPythonApp::GetRenderProcessHandler() {
return this;
}

CefRefPtr<CefPrintHandler> CefPythonApp::GetPrintHandler() {
return print_handler_;
}

// -----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// CefBrowserProcessHandler
// -----------------------------------------------------------------------------
// ----------------------------------------------------------------------------

void CefPythonApp::OnContextInitialized() {
#ifdef BROWSER_PROCESS
REQUIRE_UI_THREAD();
#if defined(OS_LINUX)
print_handler_ = new ClientPrintHandlerGtk();
#endif
#endif // OS_LINUX
#endif // BROWSER_PROCESS
}

void CefPythonApp::OnBeforeChildProcessLaunch(
Expand All @@ -104,11 +106,12 @@ void CefPythonApp::OnBeforeChildProcessLaunch(
// This is included only in the Browser process, when building
// the libcefpythonapp library.
BrowserProcessHandler_OnBeforeChildProcessLaunch(command_line);
#endif
#endif // BROWSER_PROCESS
}

void CefPythonApp::OnRenderProcessThreadCreated(
CefRefPtr<CefListValue> extra_info) {
#ifdef BROWSER_PROCESS
// If you have an existing CefListValue that you would like
// to provide, do this:
// | extra_info = mylist.get()
Expand All @@ -117,11 +120,24 @@ void CefPythonApp::OnRenderProcessThreadCreated(
REQUIRE_IO_THREAD();
extra_info->SetBool(0, g_debug);
extra_info->SetString(1, g_logFile);
#ifdef BROWSER_PROCESS
// This is included only in the Browser process, when building
// the libcefpythonapp library.
BrowserProcessHandler_OnRenderProcessThreadCreated(extra_info);
#endif
#endif // BROWSER_PROCESS
}

CefRefPtr<CefPrintHandler> CefPythonApp::GetPrintHandler() {
return print_handler_;
}

void CefPythonApp::OnScheduleMessagePumpWork(int64 delay_ms) {
#ifdef BROWSER_PROCESS
MainMessageLoopExternalPump* message_pump =\
MainMessageLoopExternalPump::Get();
if (message_pump) {
message_pump->OnScheduleMessagePumpWork(delay_ms);
}
#endif // BROWSER_PROCESS
}

// -----------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit f8286e0

Please sign in to comment.