# Copyright (c) 2012 CEF Python, see the Authors file. # All rights reserved. Licensed under BSD 3-clause license. # Project website: https://github.com/cztomczak/cefpython """ CHANGES in CEF since v31..v51. Below are listed new or modified functions/classes, but not all of them. ------------------------------------------------------------------------------- CefEnableHighDPISupport() CefRequestContext NEW BROWSER SETTINGS that can be get/set using request context: * GetAllPreferences - all preferences for browser's request context * SetPreference * many more methods has/get/canset... _cef_request_context_settings_t: cache_path persist_session_cookies persist_user_preferences ignore_certificate_errors PurgePluginListCache GetDefaultCookieManager GetCachePath IsSharingWith - possible to create new context that shares storage with another context more methods... CefRequestContextHandler OnBeforePluginLoad CefBrowserSettings windowless_frame_rate - **OSR** CefBrowserHost GetNavigationEntries PrintToPDF ParentWindowWillClose() - REMOVED, update .py examples SetWindowVisibility() ShowDevTools(WindowInfo, CefClient, BrowserSettings, inspect_element_at) CloseDevTools [DONE] ReplaceMisspelling AddWordToDictionary Invalidate NotifyMoveOrResizeStarted() - call in WM_MOVE, WM_MOVING, WM_SIZING on Win GetWindowlessFrameRate - **OSR** SetWindowlessFrameRate - **OSR** DragTargetDragEnter DragTargetDragOver DragTargetDragLeave DragTargetDrop DragSourceEndedAt DragSourceSystemDragEnded HasDevTools DownloadImage HasView CefRequestHandler OnOpenURLFromTab OnBeforeResourceLoad - new arg CefRequestCallback OnResourceResponse GetResourceResponseFilter - easy way to alter response, no need for the complicated wxpython-response.py example (Issue #229) OnResourceLoadComplete OnCertificateError - new args: browser and ssl_info. No more need to set it using cefpython.SetGlobalClientCallback() OnRenderViewReady Support for handling onbeforeunload in LifespanHandler::DoClose with the use of Browser.TryCloseBrowser() or Browser.CloseBrowser. CefRequest SetReferrer GetReferrerURL GetReferrerPolicy GetIdentifier CefResponse GetError SetError CEF exposes Views/Aura framework as an alternative API for client applications. This can be a replacement for WinAPI/GTK/X11/Cocoa UI frameworks. See for more info: https://bitbucket.org/chromiumembedded/cef/issues/1749 CefPrintHandler - Linux only CefPrintSettings CefDisplayHandler OnFaviconURLChange OnFullscreenModeChange CefRenderHandler OnCursorChange - new args: type and custom_cursor_info StartDragging UpdateDragCursor OnScrollOffsetChanged - new args: x,y In upstream cefclient: 1. g_signal_connect(G_OBJECT(window_), "configure-event", G_CALLBACK(&RootWindowGtk::WindowConfigure), this); browser->GetHost()->NotifyMoveOrResizeStarted(); 2. g_signal_connect(G_OBJECT(window_), "focus-in-event", G_CALLBACK(&RootWindowGtk::WindowFocusIn), this); self->browser_window_->SetFocus(true); When window is minimized set browser size to 0x0 to reduce resource usage. See cefclient: - on Windows see https://github.com/cztomczak/phpdesktop/issues/179 - on Linux see root_window_gtk.cc > WindowState CefContextMenuHandler RunContextMenu CefContextMenuParams GetMisspelledWord GetDictionarySuggestions IsSpellCheckEnabled IsCustomMenu IsPepperMenu CefCompletionCallback - added to many cookie functions to run asynchronously on the IO thread include/cef_parser.h - url/css/json/etc parsers CefResourceBundle CefResponseFilter CefValue cef_get_current_platform_thread_id() cef_get_current_platform_thread_handle() cef_get_xdisplay(); include/cef_ssl_info.h include/wrapper/cef_helpers.h - CefDeleteOnThread() free object on the specified thread include/wrapper/cef_resource_manager.h CefPostData HasExcludedElements ------------------------------------------------------------------------------- END OF: CHANGES in CEF since v31..v47. """ # IMPORTANT notes: # # - cdef/cpdef functions returning something other than a Python object # should have in its declaration "except *", otherwise exceptions are # ignored. Those cdef/cpdef that return "object" have "except *" by # default. The setup/compile.py script will check for functions missing # "except *" and will display an error message about that, but it's # not perfect and won't detect all cases. # # - TODO: add checking for "except * with gil" in functions with the # "public" keyword # # - about acquiring/releasing GIL lock, see discussion here: # https://groups.google.com/forum/?fromgroups=#!topic/cython-users/jcvjpSOZPp0 # # - new ClientHandler() # <...> means to throw an error if the cast is not allowed # # - in client handler callbacks (or others that are called from C++ and # use "except * with gil") must embrace all code in try..except otherwise # the error will be ignored, only printed to the output console, this is the # default behavior of Cython, to remedy this you are supposed to add "except *" # in function declaration, unfortunately it does not work, some conflict with # CEF threading, see topic at cython-users for more details: # https://groups.google.com/d/msg/cython-users/CRxWoX57dnM/aufW3gXMhOUJ. # # - Note that acquiring the GIL is a blocking thread-synchronising operation, # and therefore potentially costly. It might not be worth releasing the GIL # for minor calculations. Usually, I/O operations and substantial # computations in parallel code will benefit from it. # # - In regards to GIL locks see Issue #102 "Remove GIL to avoid deadlocks when # calling CEF functions". # # - CTags requires all functions/methods imported in .pxd files to be preceded with "cdef", # otherwise they are not indexed. # # - __del__ method does not exist in Extension Types (cdef class), # you have to use __dealloc__ instead, try to remember that as # defining __del__ will not raise any warning and could lead to # memory leaks. # # - CefString.c_str() is safe to use only on Windows, on Ubuntu 64bit # for a "Pers" string it returns: "P\x00e\x00r\x00s\x00", which is # most probably not what you expected. # # - You can rename methods when importing in pxd files: # | cdef cppclass _Object "Object": # # - Supporting operators that are not yet supported: # | RetValue& Assign "operator="(T* p) # | object.Assign(T*) # In the same way you can import function with a different name, this one # imports a static method Create() while adding a prefix "CefSome_": # | cdef extern from "..": # | static CefRefPtr[CefSome] CefSome_Create "CefSome::Create"() # # - Declaring C++ classes in Cython. Storing python callbacks # in a C++ class using Py_INCREF, Py_DECREF. Calling from # C++ using PyObject_CallMethod. # | http://stackoverflow.com/a/17070382/623622 # Disadvantage: when calling python callback from the C++ class # declared in Cython there is no easy way to propagate the python # exceptions when they occur during execution of the callback. # # - | cdef char* other_c_string = py_string # This is a very fast operation after which other_c_string points # to the byte string buffer of the Python string itself. It is # tied to the life time of the Python string. When the Python # string is garbage collected, the pointer becomes invalid. # # - When defining cpdef functions returning "cpp_bool": # | cpdef cpp_bool myfunc() except *: # Always do an additional cast when returning value, even when # variable is defined as py_bool: # | cdef py_bool returnValue # | return bool(returnValue) # Otherwise compiler warnings appear: # | cefpython.cpp(26533) : warning C4800: 'int' : forcing value # | to bool 'true' or 'false' (performance warning) # Lots of these warnings results in ignoring them, but sometimes # they are shown for a good reason. For example when you forget # to return a value in a function. # # - Always import bool from libcpp as cpp_bool, if you import it as # "bool" in a pxd file, then Cython will complain about bool casts # like "bool(1)" being invalid, in pyx files. # # - malloc example code: # from libc.stdlib cimport malloc, free # cdef RECT* rect = malloc(sizeof(RECT)) # free(rect) # # All .pyx files need to be included in this file. # Includes being made in other .pyx files are allowed to help # IDE completion, but will be removed during cython compilation. include "compile_time_constants.pxi" # ----------------------------------------------------------------------------- # IMPORTS # noinspection PyUnresolvedReferences import os import sys # noinspection PyUnresolvedReferences import cython # noinspection PyUnresolvedReferences import platform # noinspection PyUnresolvedReferences import traceback # noinspection PyUnresolvedReferences import time # noinspection PyUnresolvedReferences import types # noinspection PyUnresolvedReferences import re # noinspection PyUnresolvedReferences import copy # noinspection PyUnresolvedReferences import inspect # used by JavascriptBindings.__SetObjectMethods() # noinspection PyUnresolvedReferences import urllib # noinspection PyUnresolvedReferences import json # noinspection PyUnresolvedReferences import datetime # noinspection PyUnresolvedReferences import random if sys.version_info.major == 2: # noinspection PyUnresolvedReferences import urlparse else: # noinspection PyUnresolvedReferences from urllib import parse as urlparse if sys.version_info.major == 2: # noinspection PyUnresolvedReferences from urllib import pathname2url as urllib_pathname2url else: # noinspection PyUnresolvedReferences from urllib.request import pathname2url as urllib_pathname2url # noinspection PyUnresolvedReferences from cpython.version cimport PY_MAJOR_VERSION # noinspection PyUnresolvedReferences import weakref # We should allow multiple string types: str, unicode, bytes. # PyToCefString() can handle them all. # Important: # If you set it to basestring, Cython will accept exactly(!) # str/unicode in Py2 and str in Py3. This won't work in Py3 # as we might want to pass bytes as well. Also it will # reject string subtypes, so using it in publi API functions # would be a bad idea. ctypedef object py_string # You can't use "void" along with cpdef function returning None, it is # planned to be added to Cython in the future, creating this virtual # type temporarily. If you change it later to "void" then don't forget # to add "except *". ctypedef object py_void # noinspection PyUnresolvedReferences from cpython cimport PyLong_FromVoidPtr # noinspection PyUnresolvedReferences from cpython cimport bool as py_bool # noinspection PyUnresolvedReferences from libcpp cimport bool as cpp_bool # noinspection PyUnresolvedReferences from libcpp.map cimport map as cpp_map # noinspection PyUnresolvedReferences from multimap cimport multimap as cpp_multimap # noinspection PyUnresolvedReferences from libcpp.pair cimport pair as cpp_pair # noinspection PyUnresolvedReferences from libcpp.vector cimport vector as cpp_vector # noinspection PyUnresolvedReferences from libcpp.string cimport string as cpp_string # noinspection PyUnresolvedReferences from wstring cimport wstring as cpp_wstring # noinspection PyUnresolvedReferences from libc.string cimport strlen # noinspection PyUnresolvedReferences 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? # noinspection PyUnresolvedReferences from libc.stdlib cimport calloc, malloc, free # noinspection PyUnresolvedReferences from libc.stdlib cimport atoi # When pyx file cimports * from a pxd file and that pxd cimports * from another pxd # then these names will be visible in pyx file. # Circular imports are allowed in form "cimport ...", but won't work if you do # "from ... cimport *", this is important to know in pxd files. # noinspection PyUnresolvedReferences from libc.stdint cimport uint64_t # noinspection PyUnresolvedReferences from libc.stdint cimport uintptr_t # noinspection PyUnresolvedReferences ctypedef uintptr_t WindowHandle # noinspection PyUnresolvedReferences cimport ctime IF UNAME_SYSNAME == "Windows": from windows cimport * from dpi_aware_win cimport * ELIF UNAME_SYSNAME == "Linux": from linux cimport * ELIF UNAME_SYSNAME == "Darwin": from mac cimport * from cpp_utils cimport * from task cimport * IF UNAME_SYSNAME == "Linux": cimport x11 from cef_string cimport * cdef extern from *: # noinspection PyUnresolvedReferences ctypedef CefString ConstCefString "const CefString" # cannot cimport *, that would cause name conflicts with constants # noinspection PyUnresolvedReferences from cef_types cimport ( CefSettings, CefBrowserSettings, CefRect, CefSize, CefPoint, CefKeyEvent, CefMouseEvent, CefScreenInfo, PathKey, PK_DIR_EXE, PK_DIR_MODULE, int32, uint32, int64, uint64, ) # noinspection PyUnresolvedReferences from cef_ptr cimport CefRefPtr # noinspection PyUnresolvedReferences from cef_scoped_ptr cimport scoped_ptr from cef_task cimport * from cef_platform cimport * from cef_app cimport * from cef_browser cimport * # noinspection PyUnresolvedReferences cimport cef_browser_static from cef_client cimport * from client_handler cimport * from cef_frame cimport * from cef_time cimport * from cef_values cimport * from cefpython_app cimport * from cef_process_message cimport * from cef_web_plugin cimport * from cef_request_handler cimport * from cef_request cimport * from cef_cookie cimport * from cef_string_visitor cimport * # noinspection PyUnresolvedReferences cimport cef_cookie_manager_namespace from cookie_visitor cimport * from string_visitor cimport * from cef_callback cimport * from cef_response cimport * from cef_resource_handler cimport * from resource_handler cimport * from cef_urlrequest cimport * from web_request_client cimport * from cef_command_line cimport * from cef_request_context cimport * from cef_request_context_handler cimport * from request_context_handler cimport * from cef_jsdialog_handler cimport * from cef_path_util cimport * from cef_drag_data cimport * from cef_image cimport * from main_message_loop cimport * # noinspection PyUnresolvedReferences from cef_views cimport * from cef_log cimport * # ----------------------------------------------------------------------------- # GLOBAL VARIABLES g_debug = False # When put None here and assigned a local dictionary in Initialize(), later # while running app this global variable was garbage collected, see topic: # https://groups.google.com/d/topic/cython-users/0dw3UASh7HY/discussion # The string_encoding key must be set early here and also in Initialize. g_applicationSettings = {"string_encoding": "utf-8"} g_commandLineSwitches = {} # If ApplicationSettings.unique_request_context_per_browser is False # then a shared request context is used for all browsers. Otherwise # a unique one is created for each call to CreateBrowserSync. # noinspection PyUnresolvedReferences cdef CefRefPtr[CefRequestContext] g_shared_request_context cdef scoped_ptr[MainMessageLoopExternalPump] g_external_message_pump cdef py_bool g_MessageLoop_called = False cdef py_bool g_MessageLoopWork_called = False cdef py_bool g_cef_initialized = False cdef dict g_globalClientCallbacks = {} # ----------------------------------------------------------------------------- include "utils.pyx" include "string_utils.pyx" IF UNAME_SYSNAME == "Windows": include "string_utils_win.pyx" include "time_utils.pyx" include "browser.pyx" include "frame.pyx" include "settings.pyx" IF UNAME_SYSNAME == "Windows": include "window_utils_win.pyx" include "dpi_aware_win.pyx" ELIF UNAME_SYSNAME == "Linux": include "window_utils_linux.pyx" ELIF UNAME_SYSNAME == "Darwin": include "window_utils_mac.pyx" include "task.pyx" include "javascript_bindings.pyx" include "virtual_keys.pyx" include "window_info.pyx" include "process_message_utils.pyx" include "javascript_callback.pyx" include "python_callback.pyx" include "web_plugin_info.pyx" include "request.pyx" include "cookie.pyx" include "string_visitor.pyx" include "network_error.pyx" include "paint_buffer.pyx" include "callback.pyx" include "response.pyx" include "web_request.pyx" include "command_line.pyx" include "app.pyx" include "drag_data.pyx" include "helpers.pyx" # Currently used only on Linux via DragData. Do not include on other # platforms otherwise warning about unused function appears. IF UNAME_SYSNAME == "Linux": include "image.pyx" # Handlers include "handlers/browser_process_handler.pyx" include "handlers/display_handler.pyx" include "handlers/focus_handler.pyx" include "handlers/javascript_dialog_handler.pyx" include "handlers/keyboard_handler.pyx" include "handlers/lifespan_handler.pyx" include "handlers/load_handler.pyx" include "handlers/render_handler.pyx" include "handlers/resource_handler.pyx" include "handlers/request_handler.pyx" include "handlers/v8context_handler.pyx" include "handlers/v8function_handler.pyx" # ----------------------------------------------------------------------------- # Utility functions to provide settings to the C++ browser process code. cdef public void cefpython_GetDebugOptions( cpp_bool* debug ) except * with gil: # Called from subprocess/cefpython_app.cpp -> CefPythonApp constructor. try: debug[0] = bool(g_debug) except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) cdef public cpp_bool ApplicationSettings_GetBool(const char* key ) except * with gil: # Called from client_handler/client_handler.cpp for example cdef py_string pyKey = CharToPyString(key) if pyKey in g_applicationSettings: return bool(g_applicationSettings[pyKey]) return False cdef public cpp_bool ApplicationSettings_GetBoolFromDict(const char* key1, const char* key2) except * with gil: cdef py_string pyKey1 = CharToPyString(key1) cdef py_string pyKey2 = CharToPyString(key2) cdef object dictValue # Yet to be checked whether it is `dict` if pyKey1 in g_applicationSettings: dictValue = g_applicationSettings[pyKey1] if type(dictValue) != dict: return False if pyKey2 in dictValue: return bool(dictValue[pyKey2]) return False cdef public cpp_string ApplicationSettings_GetString(const char* key ) except * with gil: cdef py_string pyKey = CharToPyString(key) cdef cpp_string cppString if pyKey in g_applicationSettings: cppString = PyStringToChar(AnyToPyString(g_applicationSettings[pyKey])) return cppString cdef public int CommandLineSwitches_GetInt(const char* key) except * with gil: cdef py_string pyKey = CharToPyString(key) if pyKey in g_commandLineSwitches: return int(g_commandLineSwitches[pyKey]) return 0 # ----------------------------------------------------------------------------- # If you've built custom binaries with tcmalloc hook enabled on # 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, **kwargs): # applicationSettings and commandLineSwitches argument # names are kept for backward compatibility. application_settings = applicationSettings command_line_switches = commandLineSwitches # Alternative names for existing parameters if "settings" in kwargs: assert not applicationSettings, "Bad arguments" application_settings = kwargs["settings"] del kwargs["settings"] if "switches" in kwargs: assert not command_line_switches, "Bad arguments" command_line_switches = kwargs["switches"] del kwargs["switches"] for kwarg in kwargs: raise Exception("Invalid argument: "+kwarg) IF UNAME_SYSNAME == "Linux": # Fix Issue #231 - Discovery of the "icudtl.dat" file fails on Linux. cdef str py_module_dir = GetModuleDirectory() cdef CefString cef_module_dir PyToCefString(py_module_dir, cef_module_dir) CefOverridePath(PK_DIR_EXE, cef_module_dir)\ or Debug("ERROR: CefOverridePath failed") CefOverridePath(PK_DIR_MODULE, cef_module_dir)\ or Debug("ERROR: CefOverridePath failed") # END IF UNAME_SYSNAME == "Linux": if not application_settings: application_settings = {} # Debug settings need to be set before Debug() is called # and before the CefPythonApp class is instantiated. global g_debug if "--debug" in sys.argv: application_settings["debug"] = True application_settings["log_file"] = os.path.join(os.getcwd(), "debug.log") application_settings["log_severity"] = LOGSEVERITY_INFO sys.argv.remove("--debug") if "debug" in application_settings: g_debug = bool(application_settings["debug"]) if "log_severity" in application_settings: if application_settings["log_severity"] new CefPythonApp() IF UNAME_SYSNAME == "Windows": cdef HINSTANCE hInstance = GetModuleHandle(NULL) cdef CefMainArgs cefMainArgs = CefMainArgs(hInstance) ELIF UNAME_SYSNAME == "Linux": # TODO: use the CefMainArgs(int argc, char** argv) constructor. cdef CefMainArgs cefMainArgs ELIF UNAME_SYSNAME == "Darwin": # TODO: use the CefMainArgs(int argc, char** argv) constructor. cdef CefMainArgs cefMainArgs cdef int exitCode = 1 # NOTE: CefExecuteProcess shall not be called here. It should # be called only in the subprocess main.cpp. # Make a copy as applicationSettings is a reference only # that might get destroyed later. global g_applicationSettings for key in application_settings: g_applicationSettings[key] = copy.deepcopy(application_settings[key]) cdef CefSettings cefApplicationSettings # No sandboxing for the subprocesses cefApplicationSettings.no_sandbox = 1 SetApplicationSettings(application_settings, &cefApplicationSettings) if command_line_switches: # Make a copy as commandLineSwitches is a reference only # that might get destroyed later. global g_commandLineSwitches for key in command_line_switches: g_commandLineSwitches[key] = copy.deepcopy( command_line_switches[key]) # External message pump if GetAppSetting("external_message_pump")\ and not g_external_message_pump.get(): g_external_message_pump.reset( MainMessageLoopExternalPump.Create().get()) Debug("CefInitialize()") cdef cpp_bool ret with nogil: ret = CefInitialize(cefMainArgs, cefApplicationSettings, cefApp, NULL) global g_cef_initialized g_cef_initialized = True if not ret: Debug("CefInitialize() failed") IF UNAME_SYSNAME == "Linux": # Install by default. WindowUtils.InstallX11ErrorHandlers() return ret def CreateBrowser(**kwargs): """Create browser asynchronously. TODO. """ CreateBrowserSync(**kwargs) def CreateBrowserSync(windowInfo=None, browserSettings=None, navigateUrl="", window_title="", **kwargs): # Alternative names for existing parameters if "window_info" in kwargs: windowInfo = kwargs["window_info"] del kwargs["window_info"] if "settings" in kwargs: browserSettings = kwargs["settings"] del kwargs["settings"] if "url" in kwargs: navigateUrl = kwargs["url"] del kwargs["url"] for kwarg in kwargs: raise Exception("Invalid argument: "+kwarg) Debug("CreateBrowserSync() called") assert IsThread(TID_UI), ( "cefpython.CreateBrowserSync() may only be called on the UI thread") """ # CEF views # noinspection PyUnresolvedReferences cdef CefRefPtr[CefWindow] cef_window # noinspection PyUnresolvedReferences cdef CefRefPtr[CefBoxLayout] cef_box_layout cdef CefBoxLayoutSettings cef_box_layout_settings cdef CefRefPtr[CefPanel] cef_panel if not windowInfo and browserSettings \ and "window_title" in browserSettings: # noinspection PyUnresolvedReferences cef_window = CefWindow.CreateTopLevelWindow( NULL) Debug("CefWindow.GetChildViewCount = " +str(cef_window.get().GetChildViewCount())) cef_window.get().CenterWindow(CefSize(800, 600)) cef_window.get().SetBounds(CefRect(0, 0, 800, 600)) # noinspection PyUnresolvedReferences #cef_box_layout = cef_window.get().SetToBoxLayout( # cef_box_layout_settings) #cef_box_layout.get().SetFlexForView(cef_window, 1) cef_window.get().SetToFillLayout() # noinspection PyUnresolvedReferences cef_panel = CefPanel.CreatePanel(NULL) cef_window.get().AddChildView(cef_panel) cef_window.get().Layout() cef_window.get().SetVisible(True) cef_window.get().Show() cef_window.get().RequestFocus() windowInfo = WindowInfo() windowInfo.SetAsChild(cef_window.get().GetWindowHandle()) Debug("CefWindow handle = " +str(cef_window.get().GetWindowHandle())) """ # Only title was set in hello_world.py example if windowInfo and not windowInfo.windowType: windowInfo.SetAsChild(0) # No window info provided if not windowInfo: windowInfo = WindowInfo() windowInfo.SetAsChild(0) elif not isinstance(windowInfo, WindowInfo): raise Exception("CreateBrowserSync() failed: windowInfo: invalid object") if window_title and windowInfo.parentWindowHandle == 0: windowInfo.windowName = window_title if not browserSettings: browserSettings = {} cdef CefBrowserSettings cefBrowserSettings SetBrowserSettings(browserSettings, &cefBrowserSettings) cdef CefWindowInfo cefWindowInfo SetCefWindowInfo(cefWindowInfo, windowInfo) navigateUrl = GetNavigateUrl(navigateUrl) Debug("navigateUrl: %s" % navigateUrl) cdef CefString cefNavigateUrl PyToCefString(navigateUrl, cefNavigateUrl) Debug("CefBrowser::CreateBrowserSync()") cdef CefRefPtr[ClientHandler] clientHandler =\ new ClientHandler() cdef CefRefPtr[CefBrowser] cefBrowser # Request context - part 1/2. createSharedRequestContext = bool(not g_shared_request_context.get()) cdef CefRefPtr[CefRequestContext] cefRequestContext cdef CefRefPtr[RequestContextHandler] requestContextHandler =\ new RequestContextHandler( cefBrowser) if g_applicationSettings["unique_request_context_per_browser"]: cefRequestContext = CefRequestContext.CreateContext( CefRequestContext.GetGlobalContext(), requestContextHandler) elif createSharedRequestContext: cefRequestContext = CefRequestContext.CreateContext( CefRequestContext.GetGlobalContext(), requestContextHandler) g_shared_request_context.Assign(cefRequestContext.get()) else: cefRequestContext.Assign(g_shared_request_context.get()) # CEF browser creation. with nogil: cefBrowser = cef_browser_static.CreateBrowserSync( cefWindowInfo, clientHandler, cefNavigateUrl, cefBrowserSettings, cefRequestContext) if cefBrowser == NULL or not cefBrowser.get(): Debug("CefBrowser::CreateBrowserSync() failed") return None else: Debug("CefBrowser::CreateBrowserSync() succeeded") Debug("CefBrowser window handle = " +str(cefBrowser.get().GetHost().get().GetWindowHandle())) # Request context - part 2/2. if g_applicationSettings["unique_request_context_per_browser"]: requestContextHandler.get().SetBrowser(cefBrowser) else: if createSharedRequestContext: requestContextHandler.get().SetBrowser(cefBrowser) cdef PyBrowser pyBrowser = GetPyBrowser(cefBrowser) pyBrowser.SetUserData("__outerWindowHandle", int(windowInfo.parentWindowHandle)) """ if cef_window.get(): cef_window.get().ReorderChildView(cef_panel, -1) cef_window.get().Layout() cef_window.get().Show() cef_window.get().RequestFocus() """ if windowInfo.parentWindowHandle == 0\ and windowInfo.windowType == "child"\ and windowInfo.windowName: # Set window title in hello_world.py example IF UNAME_SYSNAME == "Linux": x11.SetX11WindowTitle(cefBrowser, PyStringToChar(windowInfo.windowName)) ELIF UNAME_SYSNAME == "Darwin": MacSetWindowTitle(cefBrowser, PyStringToChar(windowInfo.windowName)) return pyBrowser def MessageLoop(): Debug("MessageLoop()") if not g_MessageLoop_called: global g_MessageLoop_called g_MessageLoop_called = True with nogil: CefRunMessageLoop() def MessageLoopWork(): # Perform a single iteration of CEF message loop processing. # This function is used to integrate the CEF message loop # into an existing application message loop. # Anything that can block for a significant amount of time # and is thread-safe should release the GIL: # https://groups.google.com/d/msg/cython-users/jcvjpSOZPp0/KHpUEX8IhnAJ # GIL must be released here otherwise we will get dead lock # when calling from c++ to python. if not g_MessageLoopWork_called: global g_MessageLoopWork_called g_MessageLoopWork_called = True with nogil: CefDoMessageLoopWork() def SingleMessageLoop(): # @deprecated, use MessageLoopWork() instead MessageLoopWork() def QuitMessageLoop(): Debug("QuitMessageLoop()") with nogil: CefQuitMessageLoop() def Shutdown(): Debug("Shutdown()") # Release shared request context. This is sometimes causing # segmentation fault, so disabling it for now. See Issue #333: # https://github.com/cztomczak/cefpython/issues/333 # OFF: g_shared_request_context.Assign(NULL) # Run some message loop work, force closing browsers and then run # some message loop work again for the browsers to close cleanly. # # UPDATE: This code needs to be rechecked. There were enhancements # to unrferencing globally stored Browser objects in # g_pyBrowsers. See Issue #330 and its commits. # # CASE 1: # There might be a case when python error occured after creating # browser, but before any message loop was run. In such case # the renderer process won't be terminated unless we run some # message loop work here first, close browser and free # reference, and then run some message loop work again. # # CASE 2: # Application closes browser and then calls CEF shutdown. We need # to run some message loop work so that browser can close cleanly. # Looks like running message loop work is also required when # application runs MessageLoop() (Issue #282 and the hello_world.py # example). # # CASE 3: # Run some message loop work to fix possible errors on shutdown. # See this post: # >> https://magpcss.org/ceforum/viewtopic.php?p=30858#p30858 # May be fixed by host owned message loop, see Issue 1805: # >> https://bitbucket.org/chromiumembedded/cef/issues/1805/ # This 0.2 sec message loop work should close browsers and clean # CEF references. Even when CloseBrowser(True) wasn't called # in client app, then this code below should close it cleanly. # Looks like CEF detects that parent window was destroyed # and closes browser automatically if you give it some time. # If the time was not enough, then there is an emergency plan, # the code block further down that checks len(g_pyBrowsers). for _ in range(20): for __ in range(10): with nogil: CefDoMessageLoopWork() time.sleep(0.01) # Emergency plan in case the code above didn't close browsers, # and neither browsers were closed in client app. This code # will force closing browsers by calling CloseBrowser(True) # and then free global g_pyBrowsers list that keeps CEF # references alive. if len(g_pyBrowsers): browsers_list = [] for browserId in g_pyBrowsers: # Cannot close browser here otherwise error: # > dictionary changed size during iteration browsers_list.append(browserId) browser_close_forced = False for browserId in browsers_list: browser = GetPyBrowserById(browserId) if browser and browserId not in g_closed_browsers: Debug("WARNING: Browser was not closed with CloseBrowser call." " Will close it safely now, but this will delay CEF" " shutdown by 0.2 sec.") browser.CloseBrowser(True) browser_close_forced = True browser = None # free reference RemovePyBrowser(browserId) if browser_close_forced: for _ in range(20): for __ in range(10): with nogil: CefDoMessageLoopWork() time.sleep(0.01) # Message loop work was run, so handlers callbacks might got called # and browsers might still be in g_pyBrowsers. for browserId in browsers_list: if browserId in g_pyBrowsers: RemovePyBrowser(browserId) # If the the two code blocks above, that tried to close browsers # and free CEF references, failed, then display an error about it! if len(g_pyBrowsers): NonCriticalError("Shutdown called, but there are still browser" " references alive") Debug("CefShutdown()") with nogil: CefShutdown() # 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) with nogil: CefSetOSModalLoop(cefModalLoop) cpdef py_void SetGlobalClientCallback(py_string name, object callback): global g_globalClientCallbacks if name in ["OnCertificateError", "OnBeforePluginLoad", "OnAfterCreated"]: g_globalClientCallbacks[name] = callback else: raise Exception("SetGlobalClientCallback() failed: "\ "invalid callback name = %s" % name) cpdef object GetGlobalClientCallback(py_string name): global g_globalClientCallbacks if name in g_globalClientCallbacks: return g_globalClientCallbacks[name] else: return None cpdef object GetAppSetting(py_string key): global g_applicationSettings if key in g_applicationSettings: return g_applicationSettings[key] return None cpdef dict GetVersion(): # These variable are set when building the module. # noinspection PyUnresolvedReferences return dict( version=__version__, chrome_version=__chrome_version__, cef_version=__cef_version__, cef_api_hash_platform=__cef_api_hash_platform__, cef_api_hash_universal=__cef_api_hash_universal__, cef_commit_hash=__cef_commit_hash__, cef_commit_number=__cef_commit_number__, )