diff --git a/README.md b/README.md index 5b975bb3..8144efed 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ -# CEF Python - ## About 'cefpython49-winxp' branch This is a special branch of Chromium v49 for Windows only -with Windows XP support. This is the latest version of Chromium -that supports Windows XP. This cefpython branch was forked from -cefpython57 branch. +with Windows XP and Vista support. This is the latest version of +Chromium that supports Windows XP. This cefpython branch was forked +from cefpython57 branch. +# CEF Python + Table of contents: +* [About 'cefpython49-winxp' branch](#about-cefpython49-winxp-branch) * [Introduction](#introduction) +* [Thanks to Fivestars for the v49 release](#thanks-to-fivestars-for-the-v49-release) * [Install](#install) * [Tutorial](#tutorial) * [Examples](#examples) @@ -44,6 +46,15 @@ applications. You can use it for web scraping or as a web crawler, or other kind of internet bots. +## Thanks to Fivestars for the v49 release + +Thanks to Fivestars Loyalty, Inc. for sponsoring the +[v49 release](../../releases) of CEF Python. +Please visit its website: https://www.fivestars.com/ . + +[![fivestars360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/fivestars360.png)](https://www.fivestars.com/) + + ## Install You can install [pypi/cefpython3](https://pypi.python.org/pypi/cefpython3) @@ -100,8 +111,12 @@ Windows | 2.7 | 3.4 | Yes | Yes | Windows XP+ - Install with command: `pip install cefpython3==49.0` - Downloads are available on GitHub Releases tagged [v49.0](../../releases/tag/v49.0). -- API reference is available in revision [master](../../tree/master/docs) -- API reference is available in revision [master](../../tree/master/api) +- See [Migration guide](docs/Migration-guide.md) document for changes + in this release +- Documentation is available in the [docs/](../../tree/cefpython49-winxp/docs) + directory +- API reference is available in the [api/](../../tree/cefpython49-winxp/api) + directory **v31 release** @@ -237,8 +252,6 @@ directly. * [command_line_args_disabled](api/ApplicationSettings.md#command_line_args_disabled) * [context_menu](api/ApplicationSettings.md#context_menu) * [downloads_enabled](api/ApplicationSettings.md#downloads_enabled) - * [external_message_pump](api/ApplicationSettings.md#external_message_pump) - * [framework_dir_path](api/ApplicationSettings.md#framework_dir_path) * [ignore_certificate_errors](api/ApplicationSettings.md#ignore_certificate_errors) * [javascript_flags](api/ApplicationSettings.md#javascript_flags) * [locale](api/ApplicationSettings.md#locale) @@ -247,7 +260,6 @@ directly. * [log_file](api/ApplicationSettings.md#log_file) * [log_severity](api/ApplicationSettings.md#log_severity) * [multi_threaded_message_loop](api/ApplicationSettings.md#multi_threaded_message_loop) - * [net_security_expiration_enabled](api/ApplicationSettings.md#net_security_expiration_enabled) * [pack_loading_disabled](api/ApplicationSettings.md#pack_loading_disabled) * [persist_session_cookies](api/ApplicationSettings.md#persist_session_cookies) * [persist_user_preferences](api/ApplicationSettings.md#persist_user_preferences) @@ -332,7 +344,6 @@ directly. * [StopLoad](api/Browser.md#stopload) * [StopFinding](api/Browser.md#stopfinding) * [ToggleFullscreen](api/Browser.md#togglefullscreen) - * [TryCloseBrowser](api/Browser.md#tryclosebrowser) * [WasResized](api/Browser.md#wasresized) * [WasHidden](api/Browser.md#washidden) * [Browser settings](api/BrowserSettings.md#browser-settings) @@ -356,7 +367,6 @@ directly. * [tab_to_links_disabled](api/BrowserSettings.md#tab_to_links_disabled) * [text_area_resize_disabled](api/BrowserSettings.md#text_area_resize_disabled) * [universal_access_from_file_urls_allowed](api/BrowserSettings.md#universal_access_from_file_urls_allowed) - * [user_style_sheet_location](api/BrowserSettings.md#user_style_sheet_location) * [web_security_disabled](api/BrowserSettings.md#web_security_disabled) * [webgl_disabled](api/BrowserSettings.md#webgl_disabled) * [windowless_frame_rate](api/BrowserSettings.md#windowless_frame_rate) @@ -508,6 +518,7 @@ directly. * [_OnAfterCreated](api/LifespanHandler.md#_onaftercreated) * [DoClose](api/LifespanHandler.md#doclose) * [OnBeforeClose](api/LifespanHandler.md#onbeforeclose) + * [RunModal](api/LifespanHandler.md#runmodal) * [LoadHandler (interface)](api/LoadHandler.md#loadhandler-interface) * [OnLoadingStateChange](api/LoadHandler.md#onloadingstatechange) * [OnLoadStart](api/LoadHandler.md#onloadstart) @@ -666,6 +677,7 @@ directly. * [OnEraseBackground ](api/WindowUtils.md#onerasebackground-win) * [SetTitle ](api/WindowUtils.md#settitle-win) * [SetIcon ](api/WindowUtils.md#seticon-win) + * [UpdateBrowserSize ](api/WindowUtils.md#updatebrowsersize-win) * [GetParentHandle](api/WindowUtils.md#getparenthandle) * [IsWindowHandle](api/WindowUtils.md#iswindowhandle) * [gtk_plug_new ](api/WindowUtils.md#gtk_plug_new-linux) diff --git a/api/API-index.md b/api/API-index.md index 2ba9dc68..e7b7ce13 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -12,8 +12,6 @@ * [command_line_args_disabled](ApplicationSettings.md#command_line_args_disabled) * [context_menu](ApplicationSettings.md#context_menu) * [downloads_enabled](ApplicationSettings.md#downloads_enabled) - * [external_message_pump](ApplicationSettings.md#external_message_pump) - * [framework_dir_path](ApplicationSettings.md#framework_dir_path) * [ignore_certificate_errors](ApplicationSettings.md#ignore_certificate_errors) * [javascript_flags](ApplicationSettings.md#javascript_flags) * [locale](ApplicationSettings.md#locale) @@ -22,7 +20,6 @@ * [log_file](ApplicationSettings.md#log_file) * [log_severity](ApplicationSettings.md#log_severity) * [multi_threaded_message_loop](ApplicationSettings.md#multi_threaded_message_loop) - * [net_security_expiration_enabled](ApplicationSettings.md#net_security_expiration_enabled) * [pack_loading_disabled](ApplicationSettings.md#pack_loading_disabled) * [persist_session_cookies](ApplicationSettings.md#persist_session_cookies) * [persist_user_preferences](ApplicationSettings.md#persist_user_preferences) @@ -107,7 +104,6 @@ * [StopLoad](Browser.md#stopload) * [StopFinding](Browser.md#stopfinding) * [ToggleFullscreen](Browser.md#togglefullscreen) - * [TryCloseBrowser](Browser.md#tryclosebrowser) * [WasResized](Browser.md#wasresized) * [WasHidden](Browser.md#washidden) * [Browser settings](BrowserSettings.md#browser-settings) @@ -131,7 +127,6 @@ * [tab_to_links_disabled](BrowserSettings.md#tab_to_links_disabled) * [text_area_resize_disabled](BrowserSettings.md#text_area_resize_disabled) * [universal_access_from_file_urls_allowed](BrowserSettings.md#universal_access_from_file_urls_allowed) - * [user_style_sheet_location](BrowserSettings.md#user_style_sheet_location) * [web_security_disabled](BrowserSettings.md#web_security_disabled) * [webgl_disabled](BrowserSettings.md#webgl_disabled) * [windowless_frame_rate](BrowserSettings.md#windowless_frame_rate) @@ -283,6 +278,7 @@ * [_OnAfterCreated](LifespanHandler.md#_onaftercreated) * [DoClose](LifespanHandler.md#doclose) * [OnBeforeClose](LifespanHandler.md#onbeforeclose) + * [RunModal](LifespanHandler.md#runmodal) * [LoadHandler (interface)](LoadHandler.md#loadhandler-interface) * [OnLoadingStateChange](LoadHandler.md#onloadingstatechange) * [OnLoadStart](LoadHandler.md#onloadstart) @@ -441,6 +437,7 @@ * [OnEraseBackground ](WindowUtils.md#onerasebackground-win) * [SetTitle ](WindowUtils.md#settitle-win) * [SetIcon ](WindowUtils.md#seticon-win) + * [UpdateBrowserSize ](WindowUtils.md#updatebrowsersize-win) * [GetParentHandle](WindowUtils.md#getparenthandle) * [IsWindowHandle](WindowUtils.md#iswindowhandle) * [gtk_plug_new ](WindowUtils.md#gtk_plug_new-linux) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index 209e6f79..bad91959 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -16,8 +16,6 @@ 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) - * [framework_dir_path](#framework_dir_path) * [ignore_certificate_errors](#ignore_certificate_errors) * [javascript_flags](#javascript_flags) * [locale](#locale) @@ -26,7 +24,6 @@ Table of contents: * [log_file](#log_file) * [log_severity](#log_severity) * [multi_threaded_message_loop](#multi_threaded_message_loop) - * [net_security_expiration_enabled](#net_security_expiration_enabled) * [pack_loading_disabled](#pack_loading_disabled) * [persist_session_cookies](#persist_session_cookies) * [persist_user_preferences](#persist_user_preferences) @@ -175,44 +172,6 @@ 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: So far this was tested only on Linux and actually made app - significantly slower. Windows and Mac platforms were not - tested yet. Reported issue in upstream, see [Issue #246] - (https://github.com/cztomczak/cefpython/issues/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 -on Windows and Mac and resolves some bugs with missing keyboard events -on these platforms. See [Issue #246] -(https://github.com/cztomczak/cefpython/issues/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. - - -### framework_dir_path - -The path to the CEF framework directory on macOS. If this value is empty -then the framework must exist at "Contents/Frameworks/Chromium Embedded -Framework.framework" in the top-level app bundle. Also configurable using -the "framework-dir-path" command-line switch. - -See also [Issue #304](../../../issues/304). - - ### ignore_certificate_errors (bool) @@ -322,19 +281,6 @@ only supported on Windows. This option is not and cannot be supported on OS-X for architectural reasons. -### net_security_expiration_enabled - -(bool) -Set to true (1) to enable date-based expiration of built in network -security information (i.e. certificate transparency logs, HSTS preloading -and pinning information). Enabling this option improves network security -but may cause HTTPS load failures when using CEF binaries built more than -10 weeks in the past. See https://www.certificate-transparency.org/ and -https://www.chromium.org/hsts for details. Can be set globally using the -CefSettings.enable_net_security_expiration value. - - - ### pack_loading_disabled (bool) @@ -447,7 +393,7 @@ set `unique_request_context_per_browser` to True. In upstream CEF each request context may have separate settings like cache_path, persist_session_cookies, persist_user_preferences, -ignore_certificate_errors, enable_net_security_expiration, +ignore_certificate_errors, accept_language_list. Such functionality wasn't yet exposed in CEF Python. diff --git a/api/Browser.md b/api/Browser.md index 807b0cd1..61e48e14 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -86,21 +86,12 @@ Table of contents: * [StopLoad](#stopload) * [StopFinding](#stopfinding) * [ToggleFullscreen](#togglefullscreen) - * [TryCloseBrowser](#tryclosebrowser) * [WasResized](#wasresized) * [WasHidden](#washidden) ## Notes -Methods available in upstream CEF which were not yet exposed in CEF Python -(see src/include/cef_browser.h): - -* ImeSetComposition -* ImeCommitText -* ImeFinishComposingText -* ImeCancelComposition - There are some edge cases when after the OnBeforeClose event browser objects are no more globally referenced thus a new instance is created that wraps upstream CefBrowser object. Browser objects that were globally @@ -933,16 +924,6 @@ Switch between fullscreen mode / windowed mode. To check whether in fullscreen m This function is Windows-only. -### TryCloseBrowser - -Helper for closing a browser. Call this method from the top-level window -close handler. Internally this calls CloseBrowser(false) if the close has -not yet been initiated. This method returns false while the close is -pending and true after the close has completed. See CloseBrowser() and -CefLifeSpanHandler::DoClose() documentation for additional usage -information. This method must be called on the browser process UI thread. - - ### WasResized | | | diff --git a/api/BrowserSettings.md b/api/BrowserSettings.md index 0cc2816d..002ae795 100644 --- a/api/BrowserSettings.md +++ b/api/BrowserSettings.md @@ -27,7 +27,6 @@ Table of contents: * [tab_to_links_disabled](#tab_to_links_disabled) * [text_area_resize_disabled](#text_area_resize_disabled) * [universal_access_from_file_urls_allowed](#universal_access_from_file_urls_allowed) - * [user_style_sheet_location](#user_style_sheet_location) * [web_security_disabled](#web_security_disabled) * [webgl_disabled](#webgl_disabled) * [windowless_frame_rate](#windowless_frame_rate) @@ -168,13 +167,6 @@ switch. (bool) Controls whether file URLs will have access to all URLs. Also configurable using the --allow-universal-access-from-files switch. Other similar switches are --allow-file-access and --allow-file-access-from-files. -### user_style_sheet_location - -(string) Location of the user style sheet that will be used for all pages. This must be a data URL of the form `data:text/css;charset=utf-8;base64,content` where "content" is the base64 encoded contents of the CSS file. Also configurable using the "user-style-sheet-location" command-line switch. - -This setting was removed in Chrome 33. Soon it will be removed from cefpython as well. - - ### web_security_disabled (bool) Controls whether web security restrictions (same-origin policy) will be enforced. Disabling this setting is not recommend as it will allow risky security behavior such as cross-site scripting (XSS). Also configurable using the --disable-web-security switch. diff --git a/api/DpiAware.md b/api/DpiAware.md index a86d85d3..5967a1f0 100644 --- a/api/DpiAware.md +++ b/api/DpiAware.md @@ -24,7 +24,7 @@ Table of contents: By default if DPI awareness is not enabled in application, then OS performs display scaling. That causes text to look blurry on high DPI displays. To resolve this you have to call `cef.DpiAware.EnableHighDpiSupport` method. High DPI support is available only on Windows. -Enabling High DPI support in app can be done by embedding a DPI awareness xml manifest in both main executable and subprocess executable (see [Issue #112](../issues/112) comment #2), or by calling the `cef.DpiAware.EnableHighDpiSupport` method. +Enabling High DPI support in app can be done by embedding a DPI awareness xml manifest in both main executable and subprocess executable (see [Issue #112](../issues/112) comment #2), or by calling the `cef.DpiAware.EnableHighDpiSupport` method (Win7+). ## Static methods @@ -52,7 +52,7 @@ the `GetSystemDpi` method for that. | __Return__ | void | Calling this function will set current process and subprocesses -to be DPI aware. +to be DPI aware. This function supports only Windows 7 or newer. Description from upstream CEF: > Call during process startup to enable High-DPI support on Windows 7 or newer. @@ -101,8 +101,8 @@ On Win8 this will return True if DPI awareness is set to either "System DPI awar | --- | --- | | __Return__ | void | -Calling this method is deprecated, call instead `EnableHighDpiSupport()`. -See [Issue #358](../../../issues/358) for how the behavior changed in -latest CEF. This method now internally calls `EnableHighDpiSupport()`. - Enables DPI awareness for the running process. Embedding a DPI manifest in .exe is the prefered way, as it gives more reliable results, otherwise some display bugs may appear (discussed in the "Introduction" section on this page). + +This function only sets DPI awareness for the main process. +It's recommended to embed a DPI awareness manifest in both main process +and the subprocesses (the subprocess.exe executable). diff --git a/api/JavascriptDialogHandler.md b/api/JavascriptDialogHandler.md index ce905d63..12cc465d 100644 --- a/api/JavascriptDialogHandler.md +++ b/api/JavascriptDialogHandler.md @@ -43,6 +43,7 @@ pressed. The |user_input| value should be specified for prompt dialogs. | --- | --- | | browser | [Browser](Browser.md) | | origin_url | str | +| accept_lang | str | | dialog_type | int | | message_text | str | | default_prompt_text | str | diff --git a/api/LifespanHandler.md b/api/LifespanHandler.md index e4bac696..813af207 100644 --- a/api/LifespanHandler.md +++ b/api/LifespanHandler.md @@ -14,6 +14,7 @@ Table of contents: * [_OnAfterCreated](#_onaftercreated) * [DoClose](#doclose) * [OnBeforeClose](#onbeforeclose) + * [RunModal](#runmodal) ## Callbacks @@ -117,3 +118,15 @@ browser object and do not attempt to execute any methods on the browser object after this callback returns. This callback will be the last notification that references |browser|. See DoClose() documentation for additional usage information. + + +### RunModal + +| Parameter | Type | +| --- | --- | +| browser | [Browser](Browser.md) | +| __Return__ | bool | + +Called when a modal window is about to display and the modal loop should +begin running. Return false to use the default modal loop implementation or +true to use a custom implementation. diff --git a/api/RequestHandler.md b/api/RequestHandler.md index d8daf439..8738d217 100644 --- a/api/RequestHandler.md +++ b/api/RequestHandler.md @@ -103,7 +103,6 @@ The `GetResourceHandler` example can be found in the old v31 | old_url | string | | new_url_out | list[string] | | request | [Request](Request.md) | -| response | [Response](Response.md) | | __Return__ | void | Description from upstream CEF: @@ -244,7 +243,6 @@ this on Linux. | browser | [Browser](Browser.md) | | mime_type | string | | plugin_url | string | -| is_main_frame | bool | | top_origin_url | string | | plugin_info | [WebPluginInfo](WebPluginInfo.md) | | __Return__ | bool | diff --git a/api/WindowUtils.md b/api/WindowUtils.md index a21d05b9..5900ebc0 100644 --- a/api/WindowUtils.md +++ b/api/WindowUtils.md @@ -13,6 +13,7 @@ Table of contents: * [OnEraseBackground (Win)](#onerasebackground-win) * [SetTitle (Win)](#settitle-win) * [SetIcon (Win)](#seticon-win) + * [UpdateBrowserSize (Win)](#updatebrowsersize-win) * [GetParentHandle](#getparenthandle) * [IsWindowHandle](#iswindowhandle) * [gtk_plug_new (Linux)](#gtk_plug_new-linux) @@ -48,6 +49,11 @@ Windows-only. This method processes WM_SETFOCUS message which is sent to a windo Windows-only. This method processes WM_SIZE message which is sent to a window after its size has changed. +Do not call this function unless you know what you're doing, as it +can sometimes cause app hanging during window resize. You should +call instead the `UpdateBrowserSize` method with the help of +`cef.PostTask`. + ### OnEraseBackground (Win) @@ -84,6 +90,20 @@ Windows-only. Set the title for the main window or popup window. The default imp Windows-only. Set the icon for the popup window. The default implementation of [DisplayHandler](DisplayHandler.md).OnTitleChange() calls this method to set the icon for a window that wasn't created explicitily (for example a popup window), the icon is inherited from the parent window. Icon parameter accepts only "inherit", you cannot pass here a path to an icon (currently not implemented). +### UpdateBrowserSize (Win) + +| Parameter | Type | +| --- | --- | +| parent_window_handle | int | +| browser | [Browser](Browser.md) | +| redraw=True (optional) | bool | +| __Return__ | void | + +Windows-only. This function will resize browser using client coordinates +from the parent window in which browser is embedded. You should +call this function during a size event of your app window. + + ### GetParentHandle | Parameter | Type | diff --git a/api/cefpython.md b/api/cefpython.md index ea7c54cd..74d89430 100644 --- a/api/cefpython.md +++ b/api/cefpython.md @@ -217,10 +217,7 @@ Description from upstream CEF: > existing application message loop. Use of this function is not recommended > for most users; use either the CefRunMessageLoop() function or > CefSettings.multi_threaded_message_loop if possible. When using this function -> care must be taken to balance performance against excessive CPU usage. It is -> recommended to enable the CefSettings.external_message_pump option when using -> this function so that CefBrowserProcessHandler::OnScheduleMessagePumpWork() -> callbacks can facilitate the scheduling process. This function should only be +> care must be taken to balance performance against excessive CPU usage. This function should only be > called on the main application thread and only if CefInitialize() is called > with a CefSettings.multi_threaded_message_loop value of false. This function > will not block. diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 1b1811f2..56ee9a5e 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -1,6 +1,28 @@ +## Note on the `cefpython49-winxp` branch + +This branch was not tested for building CEF from sources. Build +instructions were copied from the `cefpython57` branch. To build +CEF Python you should use the prebuilt CEF binaries and libraries +tagged `v49-upstream` on the GitHub Releases page. + +To use the `automate.py --prebuilt-cef` tool you need to have +VS 2015 installed so that cefclient/cefsimple applications can +be built. If you want to use VS 2013 then edit `automate.py` +tool and replace "VS2015_VCVARS" with "VS2013_VCVARS" in the +`prepare_build_command` function. + +If you want to try to build CEF from sources then see the "Building +old unsupported versions of Chromium" section in master branch in +the Build-instructions.md document. Note that you will also have +to revert the upstream `automate-git.py` tool that resides in +cefpython's tools/ directory to an appropriate revision from the +times when CEF supported branch was 2623. + + # Build instructions Table of contents: +* [Note on the `cefpython49-winxp` branch](#note-on-the-cefpython49-winxp-branch) * [Preface](#preface) * [Quick build instructions for Windows](#quick-build-instructions-for-windows) * [Quick build instructions for Linux](#quick-build-instructions-for-linux) @@ -21,7 +43,7 @@ Table of contents: ## Preface -These instructions are for the new releases of CEF Python v50+. +These instructions are for the new releases of CEF Python v49+. For the old v31 release see the build instructions on Wiki pages. If you would like to quickly build cefpython then see the @@ -47,7 +69,7 @@ Before you can build CEF Python or CEF you must satisfy ## Quick build instructions for Windows -Complete steps for building CEF Python v50+ with Python 2.7 using +Complete steps for building CEF Python v49+ with Python 2.7 using prebuilt binaries and libraries from GitHub Releases: 1) Tested and works fine on Windows 7 64-bit diff --git a/docs/Knowledge-Base.md b/docs/Knowledge-Base.md index af38bd94..d74bc43f 100644 --- a/docs/Knowledge-Base.md +++ b/docs/Knowledge-Base.md @@ -4,7 +4,6 @@ Table of contents: * [Notifications about new releases / commits](#notifications-about-new-releases--commits) * [Changes in API after CEF updates](#changes-in-api-after-cef-updates) * [Differences between Python 2 and Python 3](#differences-between-python-2-and-python-3) -* [Location of CEF framework in Mac apps](#location-of-cef-framework-in-mac-apps) * [Flash support](#flash-support) * [Feature X works in Google Chrome, but doesn't work in CEF Python](#feature-x-works-in-google-chrome-but-doesnt-work-in-cef-python) * [How to capture Audio and Video in HTML5?](#how-to-capture-audio-and-video-in-html5) @@ -53,47 +52,6 @@ they are all unicode strings. Be aware of this when porting cefpython based apps to Python 3, as it may cause issues. -## Location of CEF framework in Mac apps - -This information here is for when creating apps for distribution -on Mac. - -By default CEF expects that CEF framework is located at -`Contents/Frameworks/Chromium Embedded Framework.framework` -in the top-level app bundle. If that is not the case then you have -to set ApplicationSettings.[framework_dir_path](../api/ApplicationSettings.md#framework_dir_path) -before calling cef.Initialize(). - -You may also need to change the structure and embedded paths in -CEF framework and in the cefpython module. Here are the default -settings: -``` -cefpython_package/ - cefpython_py27.so - rpath=@loader_path/ - load:@rpath/Chromium Embedded Framework.framework/Chromium Embedded Framework - Chromium Embedded Framework.framework/ - Chromium Embedded Framework - id:@rpath/Chromium Embedded Framework.framework/Chromium Embedded Framework -``` - -When creating Mac app for distribution you may want to change -directory structure, so you might have to change these settings -embedded in these libraries. You can do so with these commands: - -``` -install_name_tool -rpath old new -install_name_tool -change old new -install_name_tool -id name -``` - -To check whether it succeeded run these commands: -``` -otool -l file -otool -L file -``` - - ## Flash support See [Issue #235](../../../issues/235) ("Flash support in CEF 51+"). @@ -219,13 +177,10 @@ bt ## Windows XP support -CEF Python v31.2 was the last version to support Windows XP. This is -due to Chromium/CEF dropping XP support, last CEF version that -supported XP was v49. - -On XP you should disable GPU acceleration by using the --disable-gpu -and --disable-gpu-compositing switches. These switches must be passed -programmatically to cef.Initialize(), see [api/Command Line Switches](../api/CommandLineSwitches.md). +On XP you should disable GPU acceleration by setting the `--disable-gpu` +and `--disable-gpu-compositing` switches. These switches can +be passed programmatically to `cef.Initialize`, see +[api/Command Line Switches](../api/CommandLineSwitches.md). ## Mac 32-bit support diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index ae06f835..050a5206 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -15,31 +15,24 @@ in your application. Table of contents: -* [v50+ Distribution packages](#v50-distribution-packages) -* [v50+ Importing the cefpython3 package on Linux](#v50-importing-the-cefpython3-package-on-linux) -* [v50+ Install X11 error handlers on Linux](#v50-install-x11-error-handlers-on-linux) -* [v50+ Set window bounds on Linux](#v50-set-window-bounds-on-linux) -* [v50+ Notify CEF on move or resize events](#v50-notify-cef-on-move-or-resize-events) -* [v50+ Keyboard focus issues on Linux](#v50-keyboard-focus-issues-on-linux) -* [v50+ Windows XP and Vista are no more supported](#v50-windows-xp-and-vista-are-no-more-supported) -* [v50+ Mac 32-bit is no more supported](#v50-mac-32-bit-is-no-more-supported) -* [v50+ cefbuilds.com is deprected, use Spotify Automated CEF Builds](#v50-cefbuildscom-is-deprected-use-spotify-automated-cef-builds) -* [v50+ Build instructions and build tools](#v50-build-instructions-and-build-tools) -* [v51+ Off-screen-rendering: new option "windowless_rendering_enabled"](#v51-off-screen-rendering-new-option-windowless_rendering_enabled) -* [v51+ Remove LifespanHandler.RunModal](#v51-remove-lifespanhandlerrunmodal) -* [v51+ BrowserSettings options removed](#v51-browsersettings-options-removed) -* [v51+ cef.Request.Flags changed](#v51-cefrequestflags-changed) -* [v51+ Request.GetHeaderMap and SetHeaderMap change](#v51-requestgetheadermap-and-setheadermap-change) -* [v54+ GTK 3 example doesn't work anymore on Linux](#v54-gtk-3-example-doesnt-work-anymore-on-linux) -* [v54+ libcef.so library is stripped from symbols on Linux](#v54-libcefso-library-is-stripped-from-symbols-on-linux) -* [v55+ HTTPS cache problems on pages with certificate errors](#v55-https-cache-problems-on-pages-with-certificate-errors) -* [v55.3+ Handlers' callbacks and other interfaces](#v553-handlers-callbacks-and-other-interfaces) -* [v56+ MacOS 10.9+ required to run](#v56-macos-109-required-to-run) -* [v57.1+ High DPI support on Windows](#v571-high-dpi-support-on-windows) - - - -## v50+ Distribution packages +* [v49+ Distribution packages](#v49-distribution-packages) +* [v49+ cefbuilds.com is deprected, use Spotify Automated CEF Builds](#v49-cefbuildscom-is-deprected-use-spotify-automated-cef-builds) +* [v49+ Build instructions and build tools](#v49-build-instructions-and-build-tools) +* [v49: GPU acceleration should be disabled on Windows XP](#v49-gpu-acceleration-should-be-disabled-on-windows-xp) +* [v49+ Handlers' callbacks and other interfaces](#v49-handlers-callbacks-and-other-interfaces) +* [v49+ High DPI support on Windows](#v49-high-dpi-support-on-windows) +* [v49+ Do not call the 'WindowUtils.OnSize' function](#v49-do-not-call-the-windowutilsonsize-function) +* [v49+ Notify CEF on move or resize events](#v49-notify-cef-on-move-or-resize-events) +* [v49+ Flash support](#v49-flash-support) +* [v49+ Off-screen-rendering: new option "windowless_rendering_enabled"](#v49-off-screen-rendering-new-option-windowless_rendering_enabled) +* [v49+ BrowserSettings options removed](#v49-browsersettings-options-removed) +* [v49+ cef.Request.Flags changed](#v49-cefrequestflags-changed) +* [v49+ Request.GetHeaderMap and SetHeaderMap change](#v49-requestgetheadermap-and-setheadermap-change) +* [v49+ HTTPS cache problems on pages with certificate errors](#v49-https-cache-problems-on-pages-with-certificate-errors) + + + +## v49+ Distribution packages In latest CEF Python there is only one distribution package available: a wheel package. Wheel packages are distributed on @@ -67,120 +60,131 @@ in an automated manner, it might be reconsidered in the future to provide debian packages again. -## v50+ Importing the cefpython3 package on Linux +## v49+ cefbuilds.com is deprected, use Spotify Automated CEF Builds -In the past on Linux it was required for the cefpython3 package -to be imported before any other packages due to tcmalloc global -hook being loaded. This is not required anymore, tcmalloc is -disabled by default. +The cefbuilds.com site with CEF prebuilt binaries is now deprecated. +From now on download prebuilt CEF binaries from the Spotify Automated +CEF Builds: +http://opensource.spotify.com/cefbuilds/index.html -## v50+ Install X11 error handlers on Linux -It is required to install X11 error handlers on Linux, otherwise -you will see 'BadWindow' errors happening - sometimes randomly - -which will cause application to terminate. Since v56+ x11 error -handlers are installed automatically by default during the call -to cef.Initialize(). However sometimes that is not enough like -for example in the wxpython.py example which requires the x11 -error handlers to be installed manually after wx was initialized, -and that is because wx initialization had reset x11 error handlers -that were installed earlier during cef initialization (Issue [#334](../../../issues/334)). +## v49+ Build instructions and build tools -You can install X11 error handlers by calling: -``` -WindowUtils = cef.WindowUtils() -WindowUtils.InstallX11ErrorHandlers() -``` +There were many changes in regards to building CEF and CEF Python. +There are now new tools in the tools/ root directory that fully +automate building CEF and CEF Python. CEF Python now provides +upstream CEF prebuilt binaries and libraries on GitHub Releases +tagged eg. "v49-upstream". With these binaries you can build +CEF Python from sources in less than 10 minutes. See the new +[Build instructions](Build-instructions.md) document. -API ref: WindowUtils.[InstallX11ErrorHandlers()](../api/WindowUtils.md#installx11errorhandlers-linux) +## v49: GPU acceleration should be disabled on Windows XP -## v50+ Set window bounds on Linux +On XP you should disable GPU acceleration by setting the `--disable-gpu` +and `--disable-gpu-compositing` switches. These switches can +be passed programmatically to `cef.Initialize`, see +[api/Command Line Switches](../api/CommandLineSwitches.md). -It is now required to set window bounds during window "resize", -"move" and "configure" events on Linux. You can do so by calling: -``` -browser.SetBounds(x, y, width, height) -``` +## v49+ Handlers' callbacks and other interfaces -API ref: Browser.[SetBounds()](../api/Browser.md#setbounds) +Since v55.3 all handlers' callbacks and other interfaces such as +CookieVisitor, StringVisitor and WebRequestClient, are now called +using keyword arguments (Issue [#291](../../../issues/291)). +This will cause many of existing code to break. This is how you +should declare callbacks using the new style: +``` +def OnLoadStart(self, browser, **_): + pass -## v50+ Notify CEF on move or resize events +def OnLoadStart(self, **kwargs): + browser = kwargs["browser"] +``` -It is required to notify the browser on move or resize events -so that popup widgets (e.g. \) are displayed in the correct -location and dismissed when the window moves. Also so that -drag & drop areas are updated accordingly. +In the first declaration you see that only one argument is +declared, the browser, the others unused will be in the "_" +variable (the name of the variable is so that PyCharm doesn't +warn about unused variable). + +Even if you specify and use all arguments, always add the +unused kwargs (`**_`) at the end: ``` -browser.NotifyMoveOrResizeStarted() +def OnLoadStart(self, browser, frame, **_): + pass ``` -API ref: Browser.[NotifyMoveOrResizeStarted()](../api/Browser.md#notifymoveorresizestarted) - - -## v50+ Keyboard focus issues on Linux +This will be handy in the future, in a case when upstream CEF +adds a new argument to the API, your code won't break. When +an argument is removed in upstream CEF API, if it's possible +CEF Python will try to keep backward compatibility by +emulating behavior of the old argument. -There several keyboard focus issues on Linux since CEF library -replaced GTK library with X11 library. Most of these issues are -fixed in examples by calling SetFocus in LoadHandler.OnLoadStart -during initial app loading and/or by calling SetFocus in -FocusHandler.OnGotFocus. This keyboard focus issues need to be -fixed in usptream CEF. For more details see Issue [#284](../../../issues/284). +In case of OnLoadStart, when you've used "browser" and "frame" +names for the arguments, your code won't break. However in +many other callbacks, where you've used argument names that +differed from how they were named in API docs, your code will +break. Also argument names were changed from camelCase +to underscores. For example the OnLoadEnd callback has renamed +the `httpStatusCode` argument to `http_code`. So in this case +your code will definitely break, unless you've also used +"http_code" for argument name. -## v50+ Windows XP and Vista are no more supported +## v49+ High DPI support on Windows -CEF Python v31.2 was the last version to support Windows XP. -This is due to Chromium/CEF dropping XP support, last version -that supported XP was CEF v49. +It is recommended to embed a DPI awareness manifest in both the main +process and the subprocesses (the subprocess.exe executable) instead +of calling `DpiAware`.[SetProcessDpiAware](../api/DpiAware.md#setprocessdpiaware) +which sets DPI awareness only for the main process. +The `ApplicationSettings`.[auto_zooming](../api/ApplicationSettings.md#auto_zooming) +option has a default value of an empty string now. Previously the +default was "system_dpi". When enabling High DPI support you should +set it to "system_dpi" explicitilly. -## v50+ Mac 32-bit is no more supported +Note that `DpiAware`.[CalculateWindowSize](../api/DpiAware.md#calculatewindowsize) +does not handle all DPI settings (e.g. 132% on Windows 10). +In newer CEF Python there is available `DpiAware.Scale` which is more +reliable and can handle all DPI resolutions. You can copy see its +implementation in `src/dpi_aware_win.pyx`. -CEF Python v31.2 was the last version to support Mac 32-bit. -This is due to CEF/Chromium dropping 32-bit support, last version -that supported 32-bit was CEF v38. +## v49+ Do not call the 'WindowUtils.OnSize' function +This function can sometimes cause app hanging during window resize. +Call instead the new `WindowUtils`.[UpdateBrowserSize](../api/WindowUtils.md#updatebrowsersize) +function. -## v50+ cefbuilds.com is deprected, use Spotify Automated CEF Builds -The cefbuilds.com site with CEF prebuilt binaries is now deprecated. -From now on download prebuilt CEF binaries from the Spotify Automated -CEF Builds: +## v49+ Notify CEF on move or resize events -http://opensource.spotify.com/cefbuilds/index.html +It is required to notify the browser on move or resize events +so that popup widgets (e.g. \) are displayed in the correct +location and dismissed when the window moves. Also so that +drag & drop areas are updated accordingly. Call +Browser.[NotifyMoveOrResizeStarted()](../api/Browser.md#notifymoveorresizestarted) +during a move or resize event in your app window. -## v50+ Build instructions and build tools +## v49+ Flash support -There were many changes in regards to building CEF and CEF Python. -There are now new tools in the tools/ root directory that fully -automate building CEF and CEF Python. CEF Python now provides -upstream CEF prebuilt binaries and libraries on GitHub Releases -tagged eg. "v56-upstream". With these binaries you can build -CEF Python from sources in less than 10 minutes. See the new -[Build instructions](Build-instructions.md) document. +See [Issue #235](../../../issues/235) ("Flash support in CEF v49+") +for instructions on how to enable Flash. -## v51+ Off-screen-rendering: new option "windowless_rendering_enabled" +## v49+ Off-screen-rendering: new option "windowless_rendering_enabled" When using off-screen-rendering you must set the ApplicationSettings "windowless_rendering_enabled" option to True. This applies to -examples such as: Kivy, Panda3D and screenshot example. +examples such as: Kivy, Panda3D, PySDL2 and screenshot example. API ref: ApplicationSettings.[windowless_rendering_enabled](../api/ApplicationSettings.md#windowless_rendering_enabled) -## v51+ Remove LifespanHandler.RunModal - -LifespanHandler.RunModal callback is no more available. - - -## v51+ BrowserSettings options removed +## v49+ BrowserSettings options removed The following options were removed from BrowserSettings: - user_style_sheet_location @@ -189,7 +193,7 @@ The following options were removed from BrowserSettings: - author_and_user_styles_disabled -## v51+ cef.Request.Flags changed +## v49+ cef.Request.Flags changed The following flags were removed from cef.Request.Flags: - AllowCookies @@ -199,7 +203,7 @@ The following flags were removed from cef.Request.Flags: API ref: Request.[GetFlags](../api/Request.md#getflags) -## v51+ Request.GetHeaderMap and SetHeaderMap change +## v49+ Request.GetHeaderMap and SetHeaderMap change GetHeaderMap() will not include the Referer value if any and SetHeaderMap() will ignore the Referer value. @@ -207,91 +211,13 @@ and SetHeaderMap() will ignore the Referer value. API ref: Request.[GetHeaderMap](../api/Request.md#getheadermap) -## v54+ GTK 3 example doesn't work anymore on Linux - -Currently GTK 3 example is broken on Linux. You can either -downgrade to an old cefpython v53 (available on GitHub release -page) or use GTK 2 example. For more details on the problem see -Issue [#261](../../../issues/261). - - -## v54+ libcef.so library is stripped from symbols on Linux - -Symbols useful for debugging are no more available in libcef.so -shipped with distribution packages on Linux. This is explained -in details in Issue [#262](../../../issues/262). - - -## v55+ HTTPS cache problems on pages with certificate errors +## v49+ HTTPS cache problems on pages with certificate errors The fix for HTTPS cache problems on pages with certificate errors -is no more applied on Windows. +(and that includes self-signed certificates) is no more applied +on Windows. Soon this will fix also won't be applied on Linux anymore when cefpython starts using CEF prebuilt binaries from Spotify. See Issue [#125](../../../issues/125) for more details. - - -## v55.3+ Handlers' callbacks and other interfaces - -Since v55.3 all handlers' callbacks and other interfaces such as -CookieVisitor, StringVisitor and WebRequestClient, are now called -using keyword arguments (Issue [#291](../../../issues/291)). -This will cause many of existing code to break. This is how you -should declare callbacks using the new style: - -``` -def OnLoadStart(self, browser, **_): - pass - -def OnLoadStart(self, **kwargs): - browser = kwargs["browser"] -``` - -In the first declaration you see that only one argument is -declared, the browser, the others unused will be in the "_" -variable (the name of the variable is so that PyCharm doesn't -warn about unused variable). - -Even if you specify and use all arguments, always add the -unused kwargs (`**_`) at the end: - -``` -def OnLoadStart(self, browser, frame, **_): - pass -``` - -This will be handy in the future, in a case when upstream CEF -adds a new argument to the API, your code won't break. When -an argument is removed in upstream CEF API, if it's possible -CEF Python will try to keep backward compatibility by -emulating behavior of the old argument. - -In case of OnLoadStart, when you've used "browser" and "frame" -names for the arguments, your code won't break. However in -many other callbacks, where you've used argument names that -differed from how they were named in API docs, your code will -break. Also argument names were changed from camelCase -to underscores. For example the OnLoadEnd callback has renamed -the `httpStatusCode` argument to `http_code`. So in this case -your code will definitely break, unless you've also used -"http_code" for argument name. - - -## v56+ MacOS 10.9+ required to run - -CEF v55 was the last version to support MacOS 10.7. - - -## v57.1+ High DPI support on Windows - -The `cef.DpiAware.SetProcessDpiAware` function is now deprecated. -Use cef.DpiAware.[EnableHighDpiSupport](../api/DpiAware.md#enablehighdpisupport) -function instead. - -The ApplicationSettings.[auto_zooming](../api/ApplicationSettings.md#auto_zooming) -option should have its value set to an empty string (a default now) -for High DPI support. In previous versions the default value was -"system_dpi" and if you have set it explicitilly in your application, -then you should change it to an empty string now. diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 9c38279b..03ece79b 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -38,7 +38,7 @@ Run the commands below to install the cefpython3 package, clone the repository and run the Hello World example: ```commandline -pip install cefpython3==57.0 +pip install cefpython3==49.0 git clone https://github.com/cztomczak/cefpython.git cd cefpython/examples/ python hello_world.py @@ -181,13 +181,12 @@ Calling cef.MessageLoopWork() in a timer is not the best performant way to run CEF message loop, also there are known bugs on some platforms when calling message loop work in a timer. There are two options to increase performance depending on platform. On Windows -use a multi-threaded message loop for best performance. On Mac use -an external message pump for best performance. +use a multi-threaded message loop for best performance. **Windows: multi-threaded message loop** On Windows for best performance a multi-threaded message loop should -be used instead of cef.MessageLoopWork() or external message pump. To do +be used instead of cef.MessageLoopWork(). To do so, set ApplicationSettings.[multi_threaded_message_loop](../api/ApplicationSettings.md#multi_threaded_message_loop) to True and run a native message loop in your app. Don't call CEF's message loop. Create browser using `cef.PostTask(cef.TID_UI, cef.CreateBrowserSync, ...)`. @@ -198,24 +197,6 @@ may be called and in case of handlers' callbacks (and other interfaces) it is stated on which thread a callback will be called. See also [Issue #133](../../../issues/133). -**Mac: external message pump** - -CEF provides ApplicationSettings.[external_message_pump](../api/ApplicationSettings.md#external_message_pump) -option for running an external message pump that you should use for -best performance and to get rid of some bugs that appear when using -cef.MessageLoopWork() in a timer. - -This option is currently marked experimental as it wasn't yet fully -tested. This option should work good on Mac - in upstream CEF it was -tested mainly on Mac. If you've successfully used this option on Mac -please let us know on the Forum. - -**Linux** - -External message pump option is not recommended to use on Linux, -as during testing it actually made app x2 slower - it's a bug in -upstream CEF. See [Issue #246](../../../issues/246) for more details. - ## Settings diff --git a/examples/Examples-README.md b/examples/Examples-README.md index 89b1661d..464d89ec 100644 --- a/examples/Examples-README.md +++ b/examples/Examples-README.md @@ -3,6 +3,10 @@ Table of contents: * [Hello World!](#hello-world) * [Supported examples](#supported-examples) + * [Featured](#featured) + * [Snippets](#snippets) + * [GUI frameworks](#gui-frameworks) + * [Unit tests](#unit-tests) * [More examples](#more-examples) ## Hello World! @@ -11,7 +15,7 @@ Instructions to install the cefpython3 package, clone the repository and run the hello_world.py example: ``` -pip install cefpython3==57.0 +pip install cefpython3==49.0 git clone https://github.com/cztomczak/cefpython.git cd cefpython/examples/ python hello_world.py @@ -25,7 +29,7 @@ maintained. If there are any issues in examples read top comments in sources to see whether this is a known issue with available workarounds. -**Featured** +### Featured - [hello_world.py](hello_world.py) - Basic example, doesn't require any third party GUI framework to run @@ -35,7 +39,32 @@ workarounds. discussed in great details in Tutorial in the [Off-screen rendering](../docs/Tutorial.md#off-screen-rendering) section. -**Embedding using various GUI frameworks** + +### Snippets + +See small code snippets that show various CEF features in the +[examples/snippets/](snippets/) directory: + +- [javascript_bindings.py](snippets/javascript_bindings.py) - Communicate + between Python and Javascript asynchronously using + inter-process messaging with the use of Javascript Bindings. +- [javascript_errors.py](snippets/javascript_errors.py) - Two ways for + intercepting Javascript errors. +- [network_cookies.py](snippets/network_cookies.py) - Implement + interfaces to block or allow cookies over network requests. +- [onbeforeclose.py](snippets/onbeforeclose.py) - Implement interface + to execute custom code before browser window closes. +- [ondomready.py](snippets/ondomready.py) - Execute custom Python code + on a web page as soon as DOM is ready. +- [onpagecomplete.py](snippets/onpagecomplete.py) - Execute custom + Python code on a web page when page loading is complete. +- [window_size.py](snippets/window_size.py) - Set initial window size + without use of any third party GUI framework. + + +### GUI frameworks + +Examples of embedding using various GUI frameworks: - [gtk2.py](gtk2.py): example for [PyGTK](http://www.pygtk.org/) library (GTK 2) @@ -49,10 +78,13 @@ workarounds. - [wxpython.py](wxpython.py): example for [wxPython](https://wxpython.org/) toolkit -**Unit tests** + +### Unit tests There are also available unit tests and its usage of the API can -be of some use. See [main_test.py](../unittests/main_test.py). +be of some use. See: +- [main_test.py](../unittests/main_test.py) - windowed rendering general tests +- [osr_test.py](../unittests/osr_test.py) - off-screen rendering tests ## More examples diff --git a/examples/gtk2.py b/examples/gtk2.py index 981c422e..f3047cca 100644 --- a/examples/gtk2.py +++ b/examples/gtk2.py @@ -2,7 +2,7 @@ # Tested configurations: # - GTK 2.24 on Windows/Linux/Mac -# - CEF Python v55.3+ +# - CEF Python v49.0+ from cefpython3 import cefpython as cef import pygtk @@ -54,7 +54,7 @@ def check_versions(): ver=platform.python_version(), arch=platform.architecture()[0])) print("[gtk2.py] GTK {ver}".format(ver='.'.join( map(str, list(gtk.gtk_version))))) - assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" pygtk.require('2.0') @@ -161,7 +161,8 @@ def on_vbox_size_allocate(self, _, data): width = data.width height = data.height - self.menubar_height if WINDOWS: - WindowUtils.OnSize(self.get_window_handle(), 0, 0, 0) + WindowUtils.UpdateBrowserSize(self.get_window_handle(), + self.browser) elif LINUX: self.browser.SetBounds(x, y, width, height) diff --git a/examples/gtk3.py b/examples/gtk3.py index acc2ee07..fc9a8049 100644 --- a/examples/gtk3.py +++ b/examples/gtk3.py @@ -41,7 +41,7 @@ def main(): print("[gkt3.py] GTK {major}.{minor}".format( major=Gtk.get_major_version(), minor=Gtk.get_minor_version())) - assert cef.__version__ >= "53.1", "CEF Python v53.1+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" if not MAC: # On Mac exception hook doesn't work and is causing a strange error: # > Python[57738:d07] _createMenuRef called with existing principal @@ -126,7 +126,8 @@ def on_configure(self, *_): def on_size_allocate(self, _, data): if self.browser: if WINDOWS: - WindowUtils.OnSize(self.win32_handle, 0, 0, 0) + WindowUtils.UpdateBrowserSize(self.win32_handle, + self.browser) elif LINUX: self.browser.SetBounds(data.x, data.y, data.width, data.height) diff --git a/examples/hello_world.py b/examples/hello_world.py index 0157267f..04e6bd8a 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -1,5 +1,5 @@ # Hello world example. Doesn't depend on any third party GUI framework. -# Tested with CEF Python v55.3+. +# Tested with CEF Python v49.0+. from cefpython3 import cefpython as cef import platform @@ -20,7 +20,7 @@ def check_versions(): print("[hello_world.py] CEF Python {ver}".format(ver=cef.__version__)) print("[hello_world.py] Python {ver} {arch}".format( ver=platform.python_version(), arch=platform.architecture()[0])) - assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0 required to run this" if __name__ == '__main__': diff --git a/examples/qt.py b/examples/qt.py index 37478844..44d58d7a 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -6,7 +6,7 @@ # - PyQt 5.8.2 on Windows/Linux/Mac # - PyQt 4.11 (qt 4.8) on Windows/Linux # - PySide 1.2 (qt 4.8) on Windows/Linux/Mac -# - CEF Python v55.4+ +# - CEF Python v49.0+ # # Issues with PySide 1.2 on Mac: # - Keyboard focus issues when switching between controls (Issue #284) @@ -101,7 +101,7 @@ def check_versions(): print("[qt.py] PySide {v1} (qt {v2})".format( v1=PySide.__version__, v2=QtCore.__version__)) # CEF Python version requirement - assert cef.__version__ >= "55.4", "CEF Python v55.4+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" class MainWindow(QMainWindow): @@ -237,7 +237,7 @@ def moveEvent(self, _): self.y = 0 if self.browser: if WINDOWS: - WindowUtils.OnSize(self.getHandle(), 0, 0, 0) + WindowUtils.UpdateBrowserSize(self.getHandle(), self.browser) elif LINUX: self.browser.SetBounds(self.x, self.y, self.width(), self.height()) @@ -247,7 +247,7 @@ def resizeEvent(self, event): size = event.size() if self.browser: if WINDOWS: - WindowUtils.OnSize(self.getHandle(), 0, 0, 0) + WindowUtils.UpdateBrowserSize(self.getHandle(), self.browser) elif LINUX: self.browser.SetBounds(self.x, self.y, size.width(), size.height()) diff --git a/examples/screenshot.py b/examples/screenshot.py index 0f23b627..9e826938 100644 --- a/examples/screenshot.py +++ b/examples/screenshot.py @@ -22,7 +22,7 @@ python screenshot.py https://www.google.com/ncr 1024 768 Tested configurations: -- CEF Python v57.0+ +- CEF Python v49.0+ - Pillow 2.3.0 / 4.1.0 """ @@ -55,9 +55,26 @@ def main(): os.remove(SCREENSHOT_PATH) command_line_arguments() # Off-screen-rendering requires setting "windowless_rendering_enabled" - # option, so that RenderHandler callbacks are called. - cef.Initialize(settings={"windowless_rendering_enabled": True}) - create_browser() + # option. + settings = { + "windowless_rendering_enabled": True, + } + switches = { + # GPU acceleration is not supported in OSR mode, so must disable + # it using these Chromium switches (Issues #240 and #463) + "disable-gpu": "", + "disable-gpu-compositing": "", + # Tweaking OSR performance by setting the same Chromium flags + # as in upstream cefclient (Issue #240). + "enable-begin-frame-scheduling": "", + "disable-surfaces": "", # This is required for PDF ext to work + } + browser_settings = { + # Tweaking OSR performance (Issue #240) + "windowless_frame_rate": 30, # Default frame rate in CEF is 30 + } + cef.Initialize(settings=settings, switches=switches) + create_browser(browser_settings) cef.MessageLoop() cef.Shutdown() print("[screenshot.py] Opening screenshot with default application") @@ -69,7 +86,7 @@ def check_versions(): print("[screenshot.py] Python {ver} {arch}".format( ver=platform.python_version(), arch=platform.architecture()[0])) print("[screenshot.py] Pillow {ver}".format(ver=PILLOW_VERSION)) - assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" def command_line_arguments(): @@ -95,7 +112,7 @@ def command_line_arguments(): sys.exit(1) -def create_browser(): +def create_browser(settings): # Create browser in off-screen-rendering mode (windowless mode) # by calling SetAsOffscreen method. In such mode parent window # handle can be NULL (0). @@ -107,6 +124,7 @@ def create_browser(): print("[screenshot.py] Loading url: {url}" .format(url=URL)) browser = cef.CreateBrowserSync(window_info=window_info, + settings=settings, url=URL) browser.SetClientHandler(LoadHandler()) browser.SetClientHandler(RenderHandler()) diff --git a/examples/snippets/javascript_bindings.py b/examples/snippets/javascript_bindings.py new file mode 100644 index 00000000..63135463 --- /dev/null +++ b/examples/snippets/javascript_bindings.py @@ -0,0 +1,69 @@ +""" +Communicate between Python and Javascript asynchronously using +inter-process messaging with the use of Javascript Bindings. +""" + +from cefpython3 import cefpython as cef + +g_htmlcode = """ + + + + + + + +

Javascript Bindings

+
+ + +""" + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url=cef.GetDataUrl(g_htmlcode), + window_title="Javascript Bindings") + browser.SetClientHandler(LifespanHandler()) + bindings = cef.JavascriptBindings() + bindings.SetFunction("py_function", py_function) + bindings.SetFunction("py_callback", py_callback) + browser.SetJavascriptBindings(bindings) + cef.MessageLoop() + del browser + cef.Shutdown() + + +def py_function(value, js_callback): + print("Value sent from Javascript: "+value) + js_callback.Call("I am a Python string #2", py_callback) + + +def py_callback(value): + print("Value sent from Javascript: "+value) + + +class LifespanHandler(object): + def OnLoadEnd(self, browser, **_): + browser.ExecuteFunction("js_function", "I am a Python string #1") + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/javascript_errors.py b/examples/snippets/javascript_errors.py new file mode 100644 index 00000000..c33ef8d7 --- /dev/null +++ b/examples/snippets/javascript_errors.py @@ -0,0 +1,61 @@ +""" +Two ways for intercepting Javascript errors: +1. window.onerror event in Javascript +2. DisplayHandler.OnConsoleMessage in Python +""" + +from cefpython3 import cefpython as cef + +g_htmlcode = """ + + + + + + + +

Javascript Errors

+
+ + +""" + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url=cef.GetDataUrl(g_htmlcode), + window_title="Javascript Errors") + browser.SetClientHandler(DisplayHandler()) + cef.MessageLoop() + cef.Shutdown() + + +class DisplayHandler(object): + def OnConsoleMessage(self, browser, message, line, **_): + if "error" in message.lower() or "uncaught" in message.lower(): + logmsg = "[Py:OnConsoleMessage] {message} (line {line})" \ + .format(message=message, line=line) + print(logmsg) + browser.ExecuteFunction("print", logmsg) + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/onbeforeclose.py b/examples/snippets/onbeforeclose.py new file mode 100644 index 00000000..9b072e96 --- /dev/null +++ b/examples/snippets/onbeforeclose.py @@ -0,0 +1,26 @@ +""" +Implement LifespanHandler.OnBeforeClose to execute custom +code before browser window closes. +""" + +from cefpython3 import cefpython as cef + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_title="OnBeforeClose") + browser.SetClientHandler(LifespanHandler()) + cef.MessageLoop() + del browser + cef.Shutdown() + + +class LifespanHandler(object): + def OnBeforeClose(self, browser): + print("Browser ID: {}".format(browser.GetIdentifier())) + print("Browser will close and app will exit") + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/ondomready.py b/examples/snippets/ondomready.py new file mode 100644 index 00000000..8775129c --- /dev/null +++ b/examples/snippets/ondomready.py @@ -0,0 +1,50 @@ +""" +Execute custom Python code on a web page as soon as DOM is ready. +Implements a custom "_OnDomReady" event in the LoadHandler object. +""" + +from cefpython3 import cefpython as cef + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_title="_OnDomReady event") + load_handler = LoadHandler(browser) + browser.SetClientHandler(load_handler) + bindings = cef.JavascriptBindings() + bindings.SetFunction("LoadHandler_OnDomReady", + load_handler["_OnDomReady"]) + browser.SetJavascriptBindings(bindings) + cef.MessageLoop() + del load_handler + del browser + cef.Shutdown() + + +class LoadHandler(object): + def __init__(self, browser): + self.browser = browser + + def __getitem__(self, key): + return getattr(self, key) + + def OnLoadStart(self, browser, **_): + browser.ExecuteJavascript(""" + if (document.readyState === "complete") { + LoadHandler_OnDomReady(); + } else { + document.addEventListener("DOMContentLoaded", function() { + LoadHandler_OnDomReady(); + }); + } + """) + + def _OnDomReady(self): + print("DOM is ready!") + self.browser.ExecuteFunction("alert", + "Message from Python: DOM is ready!") + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/onpagecomplete.py b/examples/snippets/onpagecomplete.py new file mode 100644 index 00000000..e118e8e7 --- /dev/null +++ b/examples/snippets/onpagecomplete.py @@ -0,0 +1,35 @@ +""" +Execute custom Python code on a web page when page loading is complete. +Implements a custom "_OnPageComplete" event in the LoadHandler object. +""" + +from cefpython3 import cefpython as cef + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_title="_OnPageComplete event") + browser.SetClientHandler(LoadHandler()) + cef.MessageLoop() + del browser + cef.Shutdown() + + +class LoadHandler(object): + def OnLoadingStateChange(self, browser, is_loading, **_): + """For detecting if page loading has ended it is recommended + to use OnLoadingStateChange which is most reliable. The OnLoadEnd + callback also available in LoadHandler can sometimes fail in + some cases e.g. when image loading hangs.""" + if not is_loading: + self._OnPageComplete(browser) + + def _OnPageComplete(self, browser): + print("Page loading is complete!") + browser.ExecuteFunction("alert", "Message from Python: Page loading" + " is complete!") + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/setcookie.py b/examples/snippets/setcookie.py new file mode 100644 index 00000000..04a99508 --- /dev/null +++ b/examples/snippets/setcookie.py @@ -0,0 +1,36 @@ +""" +Shows how to set a cookie. +""" + +from cefpython3 import cefpython as cef +import datetime + + +def main(): + cef.Initialize() + cef.CreateBrowserSync( + url="http://www.html-kit.com/tools/cookietester/", + window_title="Set a cookie") + manager = cef.CookieManager.GetGlobalManager() + cookie = cef.Cookie() + cookie.Set({ + "name": "my_cookie", + "value": "my_value", + # Make sure domain is a valid value otherwise it crashes + # app (Issue #459) + "domain": "www.html-kit.com", + "path": "/", + "secure": False, + "httpOnly": False, + "creation": datetime.datetime(2018, 8, 22), + "lastAccess": datetime.datetime(2018, 8, 22), + "hasExpires": True, + "expires": datetime.datetime(2028, 12, 31, 23, 59, 59), + }) + manager.SetCookie("http://www.html-kit.com/", cookie) + cef.MessageLoop() + cef.Shutdown() + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/window_size.py b/examples/snippets/window_size.py new file mode 100644 index 00000000..5a7b04d0 --- /dev/null +++ b/examples/snippets/window_size.py @@ -0,0 +1,38 @@ +""" +Set initial window size to 900/640px without use of +any third party GUI framework. On Linux/Mac you can set +window size by calling WindowInfo.SetAsChild. On Windows +you can accomplish this by calling Windows native functions +using the ctypes module. +""" + +from cefpython3 import cefpython as cef +import ctypes +import platform + + +def main(): + cef.Initialize() + window_info = cef.WindowInfo() + parent_handle = 0 + # This call has effect only on Mac and Linux. + # All rect coordinates are applied including X and Y parameters. + window_info.SetAsChild(parent_handle, [0, 0, 900, 640]) + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_info=window_info, + window_title="Window size") + if platform.system() == "Windows": + window_handle = browser.GetOuterWindowHandle() + insert_after_handle = 0 + # X and Y parameters are ignored by setting the SWP_NOMOVE flag + SWP_NOMOVE = 0x0002 + # noinspection PyUnresolvedReferences + ctypes.windll.user32.SetWindowPos(window_handle, insert_after_handle, + 0, 0, 900, 640, SWP_NOMOVE) + cef.MessageLoop() + del browser + cef.Shutdown() + + +if __name__ == '__main__': + main() diff --git a/examples/tkinter_.py b/examples/tkinter_.py index 024e0feb..c6934c7e 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -8,7 +8,7 @@ # Tested configurations: # - Tk 8.5 on Windows/Mac # - Tk 8.6 on Linux -# - CEF Python v55.3+ +# - CEF Python v49.0+ # # Known issue on Linux: When typing url, mouse must be over url # entry widget otherwise keyboard focus is lost (Issue #255 @@ -50,7 +50,7 @@ def main(): logger.info("Python {ver} {arch}".format( ver=platform.python_version(), arch=platform.architecture()[0])) logger.info("Tk {ver}".format(ver=tk.Tcl().eval('info patchlevel'))) - assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error root = tk.Tk() app = MainFrame(root) @@ -201,7 +201,8 @@ def on_root_configure(self): def on_mainframe_configure(self, width, height): if self.browser: if WINDOWS: - WindowUtils.OnSize(self.get_window_handle(), 0, 0, 0) + WindowUtils.UpdateBrowserSize(self.get_window_handle(), + self.browser) elif LINUX: self.browser.SetBounds(0, 0, width, height) self.browser.NotifyMoveOrResizeStarted() diff --git a/examples/tutorial.py b/examples/tutorial.py index 025811b5..8409659f 100644 --- a/examples/tutorial.py +++ b/examples/tutorial.py @@ -1,5 +1,5 @@ # Tutorial example. Doesn't depend on any third party GUI framework. -# Tested with CEF Python v56.2+ +# Tested with CEF Python v49.0+ from cefpython3 import cefpython as cef import base64 @@ -74,7 +74,7 @@ def check_versions(): print("[tutorial.py] CEF Python {ver}".format(ver=cef.__version__)) print("[tutorial.py] Python {ver} {arch}".format( ver=platform.python_version(), arch=platform.architecture()[0])) - assert cef.__version__ >= "56.2", "CEF Python v56.2+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" def html_to_data_uri(html, js_callback=None): diff --git a/examples/wxpython.py b/examples/wxpython.py index b4c8546d..46a4dbdb 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -5,7 +5,7 @@ # - wxPython 4.0 on Windows/Mac/Linux # - wxPython 3.0 on Windows/Mac # - wxPython 2.8 on Linux -# - CEF Python v55.4+ +# - CEF Python v49.0+ import wx from cefpython3 import cefpython as cef @@ -33,9 +33,11 @@ def main(): check_versions() sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error settings = {} - if WINDOWS: - # noinspection PyUnresolvedReferences, PyArgumentList - cef.DpiAware.EnableHighDpiSupport() + # To enable High DPI support embed a DPI awareness manifest in + # both the main process and the subprocess.exe executable. Calling + # cef.DpiAware.SetProcessDpiAware sets DPI awareness only for + # the main process and thus is not recommended. Also you have to + # set ApplicationSettings.auto_zooming to "system_dpi". cef.Initialize(settings=settings) app = CefApp(False) app.MainLoop() @@ -51,7 +53,7 @@ def check_versions(): ver=platform.python_version(), arch=platform.architecture()[0])) print("[wxpython.py] wxPython {ver}".format(ver=wx.version())) # CEF Python version requirement - assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" + assert cef.__version__ >= "49.0", "CEF Python v49.0+ required to run this" class MainFrame(wx.Frame): @@ -133,8 +135,8 @@ def OnSize(self, _): if not self.browser: return if WINDOWS: - WindowUtils.OnSize(self.browser_panel.GetHandle(), - 0, 0, 0) + WindowUtils.UpdateBrowserSize(self.browser_panel.GetHandle(), + self.browser) elif LINUX: (x, y) = (0, 0) (width, height) = self.browser_panel.GetSize().Get() diff --git a/src/browser.pyx b/src/browser.pyx index 406a01c9..7a131ca2 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -18,6 +18,8 @@ MOUSEBUTTON_RIGHT = cef_types.MBT_RIGHT cdef dict g_pyBrowsers = {} +# See also g_browser_settings defined in cefpython.pyx + # Unreferenced browsers are added to this list in OnBeforeClose(). # Must keep a list of unreferenced browsers so that a new reference # is not created in GetPyBrowser() when browser was closed. @@ -106,9 +108,15 @@ cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser, openerHandle = pyBrowser.GetOpenerWindowHandle() for identifier, tempPyBrowser in g_pyBrowsers.items(): if tempPyBrowser.GetWindowHandle() == openerHandle: - clientCallbacks = tempPyBrowser.GetClientCallbacksDict() - if clientCallbacks: - pyBrowser.SetClientCallbacksDict(clientCallbacks) + # tempPyBrowser is a parent browser + if tempPyBrowser.GetSetting("inherit_client_handlers_for_popups"): + if pyBrowser.GetIdentifier() not in g_browser_settings: + g_browser_settings[pyBrowser.GetIdentifier()] = {} + g_browser_settings[pyBrowser.GetIdentifier()]["inherit_client_handlers_for_popups"] =\ + tempPyBrowser.GetSetting("inherit_client_handlers_for_popups") + clientCallbacks = tempPyBrowser.GetClientCallbacksDict() + if clientCallbacks: + pyBrowser.SetClientCallbacksDict(clientCallbacks) javascriptBindings = tempPyBrowser.GetJavascriptBindings() if javascriptBindings: if javascriptBindings.GetBindToPopups(): @@ -138,6 +146,14 @@ cpdef PyBrowser GetBrowserByWindowHandle(WindowHandle windowHandle): return pyBrowser return None +cpdef PyBrowser GetBrowserByIdentifier(int identifier): + cdef PyBrowser pyBrowser + for browserId in g_pyBrowsers: + pyBrowser = g_pyBrowsers[browserId] + if pyBrowser.GetIdentifier() == identifier: + return pyBrowser + return None + cdef public void PyBrowser_ShowDevTools(CefRefPtr[CefBrowser] cefBrowser ) except * with gil: # Called from ClientHandler::OnContextMenuCommand @@ -220,7 +236,7 @@ cdef class PyBrowser: # NOTE: OnAfterCreated not included as it must be set using # cefpython.SetGlobalClientCallback(). self.allowedClientCallbacks += ["OnBeforePopup", - "DoClose", "OnBeforeClose"] + "RunModal", "DoClose", "OnBeforeClose"] # RenderHandler self.allowedClientCallbacks += ["GetRootScreenRect", "GetViewRect", "GetScreenPoint", "GetScreenInfo", @@ -274,6 +290,14 @@ cdef class PyBrowser: cpdef JavascriptBindings GetJavascriptBindings(self): return self.javascriptBindings + cpdef object GetSetting(self, py_string key): + cdef int browser_id = self.GetIdentifier() + if browser_id in g_browser_settings: + if key in g_browser_settings[browser_id]: + return g_browser_settings[browser_id][key] + return None + + # -------------- # CEF API. # -------------- @@ -455,12 +479,14 @@ cdef class PyBrowser: # On Windows with empty window_info structure the devtools # window doesn't appear. window_info.SetAsPopup( - self.GetOpenerWindowHandle(), + self.GetWindowHandle(), PyToCefStringValue("DevTools")) cdef CefBrowserSettings settings cdef CefPoint inspect_element_at self.GetCefBrowserHost().get().ShowDevTools( - window_info, NULL, settings, + window_info, + new DevToolsClientHandler(), + settings, inspect_element_at) cpdef py_void StopLoad(self): @@ -625,7 +651,9 @@ cdef class PyBrowser: return self.GetCefBrowserHost().get().IsMouseCursorChangeDisabled() cpdef py_bool TryCloseBrowser(self): - return self.GetCefBrowserHost().get().TryCloseBrowser() + """For forward compatibility.""" + self.CloseBrowser() + return True cpdef py_void WasResized(self): self.GetCefBrowserHost().get().WasResized() diff --git a/src/cefpython.pyx b/src/cefpython.pyx index e67c3fa0..8b0590d1 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -278,6 +278,8 @@ import json import datetime # noinspection PyUnresolvedReferences import random +# noinspection PyUnresolvedReferences +import base64 if sys.version_info.major == 2: # noinspection PyUnresolvedReferences @@ -435,8 +437,6 @@ 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 * @@ -452,6 +452,7 @@ g_debug = False # The string_encoding key must be set early here and also in Initialize. g_applicationSettings = {"string_encoding": "utf-8"} g_commandLineSwitches = {} +g_browser_settings = {} # If ApplicationSettings.unique_request_context_per_browser is False # then a shared request context is used for all browsers. Otherwise @@ -459,8 +460,6 @@ g_commandLineSwitches = {} # 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 @@ -509,11 +508,6 @@ 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" @@ -675,21 +669,12 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): # Paths # ------------------------------------------------------------------------ cdef str module_dir = GetModuleDirectory() - if platform.system() == "Darwin": - if "framework_dir_path" not in application_settings: - application_settings["framework_dir_path"] = os.path.join( - module_dir, "Chromium Embedded Framework.framework") if "locales_dir_path" not in application_settings: if platform.system() != "Darwin": application_settings["locales_dir_path"] = os.path.join( module_dir, "locales") if "resources_dir_path" not in application_settings: application_settings["resources_dir_path"] = module_dir - if platform.system() == "Darwin": - # "framework_dir_path" will always be set, see code above. - application_settings["resources_dir_path"] = os.path.join( - application_settings["framework_dir_path"], - "Resources") if "browser_subprocess_path" not in application_settings: application_settings["browser_subprocess_path"] = os.path.join( module_dir, "subprocess") @@ -754,12 +739,6 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): cefApplicationSettings.no_sandbox = 1 SetApplicationSettings(application_settings, &cefApplicationSettings) - # 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: @@ -855,6 +834,10 @@ def CreateBrowserSync(windowInfo=None, if not browserSettings: browserSettings = {} + # CEF Python only settings + if "inherit_client_handlers_for_popups" not in browserSettings: + browserSettings["inherit_client_handlers_for_popups"] = True + cdef CefBrowserSettings cefBrowserSettings SetBrowserSettings(browserSettings, &cefBrowserSettings) @@ -1068,11 +1051,6 @@ def Shutdown(): 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) @@ -1081,12 +1059,31 @@ def SetOsModalLoop(py_bool modalLoop): cpdef py_void SetGlobalClientCallback(py_string name, object callback): global g_globalClientCallbacks + # Global callbacks are prefixed with "_" in documentation. + # Accept both with and without a prefix. + if name.startswith("_"): + name = name[1:] if name in ["OnCertificateError", "OnBeforePluginLoad", "OnAfterCreated"]: g_globalClientCallbacks[name] = callback else: raise Exception("SetGlobalClientCallback() failed: "\ "invalid callback name = %s" % name) +cpdef py_void SetGlobalClientHandler(object clientHandler): + if not hasattr(clientHandler, "__class__"): + raise Exception("SetGlobalClientHandler() failed: __class__ " + "attribute missing") + cdef dict methods = {} + cdef py_string key + cdef object method + cdef tuple value + for value in inspect.getmembers(clientHandler, + predicate=inspect.ismethod): + key = value[0] + method = value[1] + if key and key[0:2] != '__': + SetGlobalClientCallback(key, method) + cpdef object GetGlobalClientCallback(py_string name): global g_globalClientCallbacks if name in g_globalClientCallbacks: @@ -1112,3 +1109,9 @@ cpdef dict GetVersion(): cef_commit_hash=__cef_commit_hash__, cef_commit_number=__cef_commit_number__, ) + +cpdef GetDataUrl(data, mediatype="html"): + html = data.encode("utf-8", "replace") + b64 = base64.b64encode(html).decode("utf-8", "replace") + ret = "data:text/html;base64,{data}".format(data=b64) + return ret diff --git a/src/client_handler/client_handler.h b/src/client_handler/client_handler.h index 3e9e3917..31f0de3b 100644 --- a/src/client_handler/client_handler.h +++ b/src/client_handler/client_handler.h @@ -23,6 +23,16 @@ #include "request_handler.h" +class DevToolsClientHandler : public CefClient +{ +public: + DevToolsClientHandler(){} + virtual ~DevToolsClientHandler(){} +private: + IMPLEMENT_REFCOUNTING(DevToolsClientHandler); +}; + + class ClientHandler : public CefClient, public ContextMenuHandler, public DialogHandler, diff --git a/src/client_handler/js_dialog_handler.cpp b/src/client_handler/js_dialog_handler.cpp index 15dfc848..c450b746 100644 --- a/src/client_handler/js_dialog_handler.cpp +++ b/src/client_handler/js_dialog_handler.cpp @@ -15,6 +15,7 @@ JSDialogHandler::JSDialogHandler() bool JSDialogHandler::OnJSDialog(CefRefPtr browser, const CefString& origin_url, + const CefString& accept_lang, JSDialogType dialog_type, const CefString& message_text, const CefString& default_prompt_text, @@ -23,7 +24,7 @@ bool JSDialogHandler::OnJSDialog(CefRefPtr browser, { REQUIRE_UI_THREAD(); bool ret = JavascriptDialogHandler_OnJavascriptDialog( - browser, origin_url, + browser, origin_url, accept_lang, dialog_type, message_text, default_prompt_text, callback, suppress_message); diff --git a/src/client_handler/js_dialog_handler.h b/src/client_handler/js_dialog_handler.h index 43fdf251..525d74c5 100644 --- a/src/client_handler/js_dialog_handler.h +++ b/src/client_handler/js_dialog_handler.h @@ -20,6 +20,7 @@ class JSDialogHandler : public CefJSDialogHandler bool OnJSDialog(CefRefPtr browser, const CefString& origin_url, + const CefString& accept_lang, JSDialogType dialog_type, const CefString& message_text, const CefString& default_prompt_text, diff --git a/src/client_handler/lifespan_handler.cpp b/src/client_handler/lifespan_handler.cpp index d54f5f0a..26212117 100644 --- a/src/client_handler/lifespan_handler.cpp +++ b/src/client_handler/lifespan_handler.cpp @@ -59,3 +59,8 @@ void LifespanHandler::OnBeforeClose(CefRefPtr browser) REQUIRE_UI_THREAD(); LifespanHandler_OnBeforeClose(browser); } + +bool LifespanHandler::RunModal(CefRefPtr browser) { + REQUIRE_UI_THREAD(); + return LifespanHandler_RunModal(browser); +} diff --git a/src/client_handler/lifespan_handler.h b/src/client_handler/lifespan_handler.h index 91244eff..a796d4fd 100644 --- a/src/client_handler/lifespan_handler.h +++ b/src/client_handler/lifespan_handler.h @@ -28,6 +28,7 @@ class LifespanHandler : public CefLifeSpanHandler void OnAfterCreated(CefRefPtr browser) override; bool DoClose(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override; + bool RunModal(CefRefPtr browser) override; private: IMPLEMENT_REFCOUNTING(LifespanHandler); diff --git a/src/client_handler/load_handler.cpp b/src/client_handler/load_handler.cpp index 20a6d341..ec780878 100644 --- a/src/client_handler/load_handler.cpp +++ b/src/client_handler/load_handler.cpp @@ -17,8 +17,7 @@ void LoadHandler::OnLoadingStateChange(CefRefPtr browser, void LoadHandler::OnLoadStart(CefRefPtr browser, - CefRefPtr frame, - TransitionType transition_type) + CefRefPtr frame) { REQUIRE_UI_THREAD(); LoadHandler_OnLoadStart(browser, frame); diff --git a/src/client_handler/load_handler.h b/src/client_handler/load_handler.h index fd203d7d..45d38c62 100644 --- a/src/client_handler/load_handler.h +++ b/src/client_handler/load_handler.h @@ -12,16 +12,13 @@ class LoadHandler : public CefLoadHandler LoadHandler(){} virtual ~LoadHandler(){} - typedef cef_transition_type_t TransitionType; - void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) override; void OnLoadStart(CefRefPtr browser, - CefRefPtr frame, - TransitionType transition_type) override; + CefRefPtr frame) override; void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, diff --git a/src/client_handler/request_context_handler.cpp b/src/client_handler/request_context_handler.cpp index bf816cf5..fbe18f9a 100644 --- a/src/client_handler/request_context_handler.cpp +++ b/src/client_handler/request_context_handler.cpp @@ -24,7 +24,6 @@ CefRefPtr RequestContextHandler::GetCookieManager() { bool RequestContextHandler::OnBeforePluginLoad( const CefString& mime_type, const CefString& plugin_url, - bool is_main_frame, const CefString& top_origin_url, CefRefPtr plugin_info, PluginPolicy* plugin_policy) { @@ -32,7 +31,6 @@ bool RequestContextHandler::OnBeforePluginLoad( return RequestHandler_OnBeforePluginLoad(browser_, mime_type, plugin_url, - is_main_frame, top_origin_url, plugin_info, plugin_policy); diff --git a/src/client_handler/request_context_handler.h b/src/client_handler/request_context_handler.h index b8bf25f3..404bdacf 100644 --- a/src/client_handler/request_context_handler.h +++ b/src/client_handler/request_context_handler.h @@ -32,7 +32,6 @@ class RequestContextHandler : virtual CefRefPtr GetCookieManager() OVERRIDE; virtual bool OnBeforePluginLoad(const CefString& mime_type, const CefString& plugin_url, - bool is_main_frame, const CefString& top_origin_url, CefRefPtr plugin_info, PluginPolicy* plugin_policy) OVERRIDE; diff --git a/src/client_handler/request_handler.cpp b/src/client_handler/request_handler.cpp index c06e098d..98e2f949 100644 --- a/src/client_handler/request_handler.cpp +++ b/src/client_handler/request_handler.cpp @@ -45,12 +45,11 @@ CefRefPtr RequestHandler::GetResourceHandler( void RequestHandler::OnResourceRedirect(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, - CefRefPtr response, CefString& new_url) { REQUIRE_IO_THREAD(); RequestHandler_OnResourceRedirect(browser, frame, request->GetURL(), - new_url, request, response); + new_url, request); } diff --git a/src/client_handler/request_handler.h b/src/client_handler/request_handler.h index afaf3f83..288d2301 100644 --- a/src/client_handler/request_handler.h +++ b/src/client_handler/request_handler.h @@ -33,7 +33,6 @@ class RequestHandler : public CefRequestHandler void OnResourceRedirect(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, - CefRefPtr response, CefString& new_url) override; bool GetAuthCredentials(CefRefPtr browser, diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h index 0fbd60a5..82a6af78 100644 --- a/src/common/cefpython_public_api.h +++ b/src/common/cefpython_public_api.h @@ -40,10 +40,6 @@ #elif PY_MAJOR_VERSION == 3 #if PY_MINOR_VERSION == 4 #include "../../build/build_cefpython/cefpython_py34_fixed.h" -#elif PY_MINOR_VERSION == 5 -#include "../../build/build_cefpython/cefpython_py35_fixed.h" -#elif PY_MINOR_VERSION == 6 -#include "../../build/build_cefpython/cefpython_py36_fixed.h" #endif // PY_MINOR_VERSION #endif // PY_MAJOR_VERSION diff --git a/src/dpi_aware_win.pyx b/src/dpi_aware_win.pyx index 4a02184c..21048f50 100644 --- a/src/dpi_aware_win.pyx +++ b/src/dpi_aware_win.pyx @@ -23,13 +23,13 @@ class DpiAware: cdef int dpix = 0 cdef int dpiy = 0 GetSystemDpi(&dpix, &dpiy) - return tuple(dpix, dpiy) + return dpix, dpiy @staticmethod def CalculateWindowSize(int width, int height): # Calculation for DPI < 96 is not yet supported. GetDpiAwareWindowSize(&width, &height) - return tuple(width, height) + return width, height @staticmethod def IsProcessDpiAware(): @@ -37,11 +37,10 @@ class DpiAware: @staticmethod def SetProcessDpiAware(): - """Deprecated.""" - DpiAware.EnableHighDpiSupport() + SetProcessDpiAware() @staticmethod def EnableHighDpiSupport(): # This CEF function sets process to be DPI aware. This - # CEF func is also called in subprocesses. + # CEF func is also called in subprocesses. Only for Win7+. CefEnableHighDPISupport() diff --git a/src/extern/cef/cef_browser.pxd b/src/extern/cef/cef_browser.pxd index 50a70393..4a597757 100644 --- a/src/extern/cef/cef_browser.pxd +++ b/src/extern/cef/cef_browser.pxd @@ -66,7 +66,6 @@ cdef extern from "include/cef_browser.h": cpp_bool matchCase, cpp_bool findNext) void StopFinding(cpp_bool clearSelection) void Print() - cpp_bool TryCloseBrowser() # Drag & drop OSR void DragTargetDragEnter(CefRefPtr[CefDragData] drag_data, diff --git a/src/extern/cef/cef_drag_data.pxd b/src/extern/cef/cef_drag_data.pxd index a011df10..246252ac 100644 --- a/src/extern/cef/cef_drag_data.pxd +++ b/src/extern/cef/cef_drag_data.pxd @@ -5,7 +5,6 @@ from libcpp cimport bool as cpp_bool from cef_string cimport CefString from cef_ptr cimport CefRefPtr -from cef_image cimport CefImage cdef extern from "include/cef_drag_data.h": cdef cppclass CefDragData: @@ -18,8 +17,6 @@ cdef extern from "include/cef_drag_data.h": void SetFragmentText(const CefString& text) void SetFragmentHtml(const CefString& html) void SetFragmentBaseURL(const CefString& base_url) - cpp_bool HasImage() - CefRefPtr[CefImage] GetImage() cdef CefRefPtr[CefDragData] CefDragData_Create "CefDragData::Create"() diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index 60ace4f3..e3a447b7 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -38,7 +38,6 @@ cdef extern from "include/internal/cef_types.h": cef_string_t browser_subprocess_path int command_line_args_disabled cef_string_t cache_path - int enable_net_security_expiration int persist_session_cookies cef_string_t user_agent cef_string_t product_version @@ -59,8 +58,6 @@ 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 - cef_string_t framework_dir_path ctypedef struct CefBrowserSettings: cef_string_t accept_language_list diff --git a/src/extern/cef/cef_views.pxd b/src/extern/cef/cef_views.pxd index 09763a07..1bfa89ef 100644 --- a/src/extern/cef/cef_views.pxd +++ b/src/extern/cef/cef_views.pxd @@ -37,6 +37,9 @@ cdef extern from "include/internal/cef_types.h": int minimum_cross_axis_size; int default_flex; +""" +# CEF Views + cdef extern from "include/views/cef_box_layout.h": cdef cppclass CefBoxLayout: void SetFlexForView(CefRefPtr[CefWindow] view, int flex) @@ -82,3 +85,4 @@ cdef extern from "include/views/cef_window.h": size_t GetChildViewCount() void AddChildView(CefRefPtr[CefPanel] view) void ReorderChildView(CefRefPtr[CefPanel] view, int index) +""" diff --git a/src/extern/client_handler.pxd b/src/extern/client_handler.pxd index 2150bc93..0d27ec05 100644 --- a/src/extern/client_handler.pxd +++ b/src/extern/client_handler.pxd @@ -7,3 +7,5 @@ cdef extern from "client_handler/client_handler.h": cdef cppclass ClientHandler: pass + cdef cppclass DevToolsClientHandler: + pass diff --git a/src/extern/main_message_loop.pxd b/src/extern/main_message_loop.pxd deleted file mode 100644 index 486f309a..00000000 --- a/src/extern/main_message_loop.pxd +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2016 CEF Python, see the Authors file. -# All rights reserved. Licensed under BSD 3-clause license. -# Project website: https://github.com/cztomczak/cefpython - -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() diff --git a/src/extern/windows.pxd b/src/extern/windows.pxd index 82355a60..68aff624 100644 --- a/src/extern/windows.pxd +++ b/src/extern/windows.pxd @@ -50,6 +50,7 @@ cdef extern from "Windows.h" nogil: ctypedef void* HDWP cdef int SWP_NOZORDER + cdef int SWP_NOREDRAW cdef HDWP BeginDeferWindowPos(int nNumWindows) cdef HDWP DeferWindowPos( HDWP hWinPosInfo, HWND hWnd, HWND hWndInsertAfter, diff --git a/src/handlers/javascript_dialog_handler.pyx b/src/handlers/javascript_dialog_handler.pyx index 283aad54..f1af7150 100644 --- a/src/handlers/javascript_dialog_handler.pyx +++ b/src/handlers/javascript_dialog_handler.pyx @@ -33,6 +33,7 @@ cdef class PyJavascriptDialogCallback: cdef public cpp_bool JavascriptDialogHandler_OnJavascriptDialog( CefRefPtr[CefBrowser] cefBrowser, const CefString& origin_url, + const CefString& accept_lang, cef_types.cef_jsdialog_type_t dialog_type, const CefString& message_text, const CefString& default_prompt_text, @@ -41,6 +42,7 @@ cdef public cpp_bool JavascriptDialogHandler_OnJavascriptDialog( ) except * with gil: cdef PyBrowser pyBrowser cdef py_string pyOriginUrl + cdef py_string pyAcceptLang cdef py_string pyMessageText cdef py_string pyDefaultPromptText cdef PyJavascriptDialogCallback pyCallback @@ -51,6 +53,7 @@ cdef public cpp_bool JavascriptDialogHandler_OnJavascriptDialog( try: pyBrowser = GetPyBrowser(cefBrowser, "OnJavascriptDialog") pyOriginUrl = CefToPyString(origin_url) + pyAcceptLang = CefToPyString(accept_lang) pyMessageText = CefToPyString(message_text) pyDefaultPromptText = CefToPyString(default_prompt_text) pyCallback = CreatePyJavascriptDialogCallback(callback) @@ -61,6 +64,7 @@ cdef public cpp_bool JavascriptDialogHandler_OnJavascriptDialog( returnValue = clientCallback( browser=pyBrowser, origin_url=pyOriginUrl, + accept_lang=pyAcceptLang, dialog_type=dialog_type, message_text=pyMessageText, default_prompt_text=pyDefaultPromptText, diff --git a/src/handlers/lifespan_handler.pyx b/src/handlers/lifespan_handler.pyx index 32a3f0c4..bc558b45 100644 --- a/src/handlers/lifespan_handler.pyx +++ b/src/handlers/lifespan_handler.pyx @@ -137,3 +137,17 @@ cdef public void LifespanHandler_OnBeforeClose( except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) + +cdef public cpp_bool LifespanHandler_RunModal( + CefRefPtr[CefBrowser] cefBrowser + ) except * with gil: + cdef PyBrowser pyBrowser + try: + pyBrowser = GetPyBrowser(cefBrowser) + callback = pyBrowser.GetClientCallback("RunModal") + if callback: + return bool(callback(pyBrowser)) + return False + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) diff --git a/src/handlers/request_handler.pyx b/src/handlers/request_handler.pyx index 0df1d867..6df83a14 100644 --- a/src/handlers/request_handler.pyx +++ b/src/handlers/request_handler.pyx @@ -151,15 +151,13 @@ cdef public void RequestHandler_OnResourceRedirect( CefRefPtr[CefFrame] cefFrame, const CefString& cefOldUrl, CefString& cefNewUrl, - CefRefPtr[CefRequest] cefRequest, - CefRefPtr[CefResponse] cefResponse + CefRefPtr[CefRequest] cefRequest ) except * with gil: cdef PyBrowser pyBrowser cdef PyFrame pyFrame cdef str pyOldUrl cdef list pyNewUrlOut cdef PyRequest pyRequest - cdef PyResponse pyResponse cdef object clientCallback try: pyBrowser = GetPyBrowser(cefBrowser, "OnResourceRedirect") @@ -167,7 +165,6 @@ cdef public void RequestHandler_OnResourceRedirect( pyOldUrl = CefToPyString(cefOldUrl) pyNewUrlOut = [CefToPyString(cefNewUrl)] pyRequest = CreatePyRequest(cefRequest) - pyResponse = CreatePyResponse(cefResponse) clientCallback = pyBrowser.GetClientCallback("OnResourceRedirect") if clientCallback: clientCallback( @@ -175,8 +172,7 @@ cdef public void RequestHandler_OnResourceRedirect( frame=pyFrame, old_url=pyOldUrl, new_url_out=pyNewUrlOut, - request=pyRequest, - response=pyResponse) + request=pyRequest) if pyNewUrlOut[0]: PyToCefString(pyNewUrlOut[0], cefNewUrl) except: @@ -348,7 +344,6 @@ cdef public cpp_bool RequestHandler_OnBeforePluginLoad( CefRefPtr[CefBrowser] browser, const CefString& mime_type, const CefString& plugin_url, - cpp_bool is_main_frame, const CefString& top_origin_url, CefRefPtr[CefWebPluginInfo] plugin_info, cef_types.cef_plugin_policy_t* plugin_policy @@ -375,7 +370,6 @@ cdef public cpp_bool RequestHandler_OnBeforePluginLoad( browser=py_browser, mime_type=CefToPyString(mime_type), plugin_url=CefToPyString(plugin_url), - is_main_frame=bool(is_main_frame), top_origin_url=CefToPyString(top_origin_url), plugin_info=py_plugin_info) if returnValue: diff --git a/src/paint_buffer.pyx b/src/paint_buffer.pyx index 2d746bd3..04772dca 100644 --- a/src/paint_buffer.pyx +++ b/src/paint_buffer.pyx @@ -14,9 +14,9 @@ cdef PaintBuffer CreatePaintBuffer(const void* buffer_, int width, int height): cdef class PaintBuffer: cdef const void* buffer - cdef int width - cdef int height - cdef Py_ssize_t length + cdef public int width + cdef public int height + cdef public Py_ssize_t length cpdef uintptr_t GetPointer(self) except *: # BEFORE MODIFYING CODE: diff --git a/src/settings.pyx b/src/settings.pyx index be8667ae..8117c4d1 100644 --- a/src/settings.pyx +++ b/src/settings.pyx @@ -70,9 +70,6 @@ cdef void SetApplicationSettings( cefAppSettings.log_severity = int(appSettings[key]) elif key == "multi_threaded_message_loop": cefAppSettings.multi_threaded_message_loop = int(appSettings[key]) - elif key == "net_security_expiration_enabled": - cefAppSettings.enable_net_security_expiration =\ - int(appSettings[key]) elif key == "release_dcheck_enabled": # Keep for BC, just log info - no error Debug("DEPRECATED: 'release_dcheck_enabled' setting") @@ -117,13 +114,6 @@ 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]) - elif key == "framework_dir_path": - cefString = new CefString(&cefAppSettings.framework_dir_path) - PyToCefStringPointer(appSettings[key], cefString) - del cefString else: raise Exception("Invalid appSettings key: %s" % key) @@ -134,7 +124,10 @@ cdef void SetBrowserSettings( cdef CefString* cefString for key in browserSettings: - if key == "accept_language_list": + if key == "inherit_client_handlers_for_popups": + # CEF Python only options. These are not to be found in CEF. + continue + elif key == "accept_language_list": cefString = new CefString(&cefBrowserSettings.accept_language_list) PyToCefStringPointer(browserSettings[key], cefString) del cefString diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index e811a32f..ababc0c9 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -12,17 +12,13 @@ UNAME_S = $(shell uname -s) CCFLAGS = -fPIC -DBROWSER_PROCESS $(CEF_CCFLAGS) ifeq ($(UNAME_S), Linux) - SRC_MORE = print_handler_gtk.cpp \ - main_message_loop/main_message_loop_external_pump_linux.cpp + SRC_MORE = print_handler_gtk.cpp else ifeq ($(UNAME_S), Darwin) - SRC_MORE = main_message_loop/main_message_loop_external_pump_mac.mm + SRC_MORE = endif SRC = cefpython_app.cpp v8function_handler.cpp v8utils.cpp \ 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 \ $(SRC_MORE) OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o)) diff --git a/src/subprocess/cefpython_app.cpp b/src/subprocess/cefpython_app.cpp index e2c2ed64..8299d24c 100644 --- a/src/subprocess/cefpython_app.cpp +++ b/src/subprocess/cefpython_app.cpp @@ -36,10 +36,6 @@ #include "javascript_callback.h" #include "v8function_handler.h" -#ifdef BROWSER_PROCESS -#include "main_message_loop/main_message_loop_external_pump.h" -#endif - // GLOBALS bool g_debug = false; @@ -144,7 +140,7 @@ void CefPythonApp::OnBeforeCommandLineProcessing( } void CefPythonApp::OnRegisterCustomSchemes( - CefRawPtr registrar) { + CefRefPtr registrar) { } CefRefPtr CefPythonApp::GetResourceBundleHandler() { @@ -229,16 +225,6 @@ CefRefPtr 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 -} - // ----------------------------------------------------------------------------- // CefRenderProcessHandler // ----------------------------------------------------------------------------- @@ -665,7 +651,7 @@ void CefPythonApp::DoJavascriptBindingsForFrame(CefRefPtr browser, for (std::vector::iterator it = objectsVector.begin(); \ it != objectsVector.end(); ++it) { CefString objectName = *it; - CefRefPtr v8Object = CefV8Value::CreateObject(NULL, NULL); + CefRefPtr v8Object = CefV8Value::CreateObject(NULL); v8Window->SetValue(objectName, v8Object, V8_PROPERTY_ATTRIBUTE_NONE); // METHODS. if (!(objects->GetType(objectName) == VTYPE_DICTIONARY)) { diff --git a/src/subprocess/cefpython_app.h b/src/subprocess/cefpython_app.h index e12fd570..0b0af5e1 100644 --- a/src/subprocess/cefpython_app.h +++ b/src/subprocess/cefpython_app.h @@ -30,7 +30,7 @@ class CefPythonApp : CefRefPtr command_line) override; void OnRegisterCustomSchemes( - CefRawPtr registrar) override; + CefRefPtr registrar) override; CefRefPtr GetResourceBundleHandler() override; @@ -55,9 +55,6 @@ class CefPythonApp : CefRefPtr GetPrintHandler() override; - void OnScheduleMessagePumpWork(int64 delay_ms) override; - - // --------------------------------------------------------------------------- // CefRenderProcessHandler // --------------------------------------------------------------------------- diff --git a/src/subprocess/main_message_loop/main_message_loop.cpp b/src/subprocess/main_message_loop/main_message_loop.cpp deleted file mode 100644 index dfad2a19..00000000 --- a/src/subprocess/main_message_loop/main_message_loop.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copied from upstream cefclient with minor modifications. - -// Copyright (c) 2015 The Chromium Embedded Framework 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 "main_message_loop.h" - -#include "include/cef_task.h" -#include "include/wrapper/cef_closure_task.h" - -namespace { - -MainMessageLoop* g_main_message_loop = NULL; - -} // namespace - -MainMessageLoop::MainMessageLoop() { - DCHECK(!g_main_message_loop); - g_main_message_loop = this; -} - -MainMessageLoop::~MainMessageLoop() { - g_main_message_loop = NULL; -} - -// static -MainMessageLoop* MainMessageLoop::Get() { - DCHECK(g_main_message_loop); - return g_main_message_loop; -} - -void MainMessageLoop::PostClosure(const base::Closure& closure) { - PostTask(CefCreateClosureTask(closure)); -} diff --git a/src/subprocess/main_message_loop/main_message_loop.h b/src/subprocess/main_message_loop/main_message_loop.h deleted file mode 100644 index 50ef9f7b..00000000 --- a/src/subprocess/main_message_loop/main_message_loop.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#ifndef CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_H_ -#define CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_H_ -#pragma once - -#include "include/base/cef_bind.h" -#include "include/base/cef_scoped_ptr.h" -#include "include/cef_task.h" - -#if defined(OS_WIN) -#include -#endif - -// Represents the message loop running on the main application thread in the -// browser process. This will be the same as the CEF UI thread on Linux, OS X -// and Windows when not using multi-threaded message loop mode. The methods of -// this class are thread-safe unless otherwise indicated. -class MainMessageLoop { - public: - // Returns the singleton instance of this object. - static MainMessageLoop* Get(); - - // Run the message loop. The thread that this method is called on will be - // considered the main thread. This blocks until Quit() is called. - virtual int Run() = 0; - - // Quit the message loop. - virtual void Quit() = 0; - - // Post a task for execution on the main message loop. - virtual void PostTask(CefRefPtr task) = 0; - - // Returns true if this message loop runs tasks on the current thread. - virtual bool RunsTasksOnCurrentThread() const = 0; - -#if defined(OS_WIN) - // Set the current modeless dialog on Windows for proper delivery of dialog - // messages when using multi-threaded message loop mode. This method must be - // called from the main thread. See http://support.microsoft.com/kb/71450 for - // background. - virtual void SetCurrentModelessDialog(HWND hWndDialog) = 0; -#endif - - // Post a closure for execution on the main message loop. - void PostClosure(const base::Closure& closure); - - protected: - // Only allow deletion via scoped_ptr. - friend struct base::DefaultDeleter; - - MainMessageLoop(); - virtual ~MainMessageLoop(); - - private: - DISALLOW_COPY_AND_ASSIGN(MainMessageLoop); -}; - -#define CURRENTLY_ON_MAIN_THREAD() \ - MainMessageLoop::Get()->RunsTasksOnCurrentThread() - -#define REQUIRE_MAIN_THREAD() DCHECK(CURRENTLY_ON_MAIN_THREAD()) - -#define MAIN_POST_TASK(task) \ - MainMessageLoop::Get()->PostTask(task) - -#define MAIN_POST_CLOSURE(closure) \ - MainMessageLoop::Get()->PostClosure(closure) - -// Use this struct in conjuction with RefCountedThreadSafe to ensure that an -// object is deleted on the main thread. For example: -// -// class Foo : public base::RefCountedThreadSafe { -// public: -// Foo(); -// void DoSomething(); -// -// private: -// // Allow deletion via scoped_refptr only. -// friend struct DeleteOnMainThread; -// friend class base::RefCountedThreadSafe; -// -// virtual ~Foo() {} -// }; -// -// base::scoped_refptr foo = new Foo(); -// foo->DoSomething(); -// foo = NULL; // Deletion of |foo| will occur on the main thread. -// -struct DeleteOnMainThread { - template - static void Destruct(const T* x) { - if (CURRENTLY_ON_MAIN_THREAD()) { - delete x; - } else { - MainMessageLoop::Get()->PostClosure( - base::Bind(&DeleteOnMainThread::Destruct, x)); - } - } -}; - -#endif // CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_H_ diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp b/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp deleted file mode 100644 index abd81a84..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copied from upstream cefclient with minor modifications. - -// Copyright (c) 2016 The Chromium Embedded Framework 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 "main_message_loop_external_pump.h" - -#include - -#include "include/cef_app.h" -#include "include/wrapper/cef_helpers.h" -#include "main_message_loop.h" - -namespace { - -// Special timer delay placeholder value. Intentionally 32-bit for Windows and -// OS X platform API compatibility. -const int32 kTimerDelayPlaceholder = INT_MAX; - -// The maximum number of milliseconds we're willing to wait between calls to -// DoWork(). -const int64 kMaxTimerDelay = 1000 / 30; // 30fps - -MainMessageLoopExternalPump* g_external_message_pump = NULL; - -} // namespace - -MainMessageLoopExternalPump::MainMessageLoopExternalPump() - : is_active_(false), - reentrancy_detected_(false) { - DCHECK(!g_external_message_pump); - g_external_message_pump = this; -} - -MainMessageLoopExternalPump::~MainMessageLoopExternalPump() { - g_external_message_pump = NULL; -} - -MainMessageLoopExternalPump* MainMessageLoopExternalPump::Get() { - return g_external_message_pump; -} - -void MainMessageLoopExternalPump::OnScheduleWork(int64 delay_ms) { - REQUIRE_MAIN_THREAD(); - - if (delay_ms == kTimerDelayPlaceholder && IsTimerPending()) { - // Don't set the maximum timer requested from DoWork() if a timer event is - // currently pending. - return; - } - - KillTimer(); - - if (delay_ms <= 0) { - // Execute the work immediately. - DoWork(); - } else { - // Never wait longer than the maximum allowed time. - if (delay_ms > kMaxTimerDelay) - delay_ms = kMaxTimerDelay; - - // Results in call to OnTimerTimeout() after the specified delay. - SetTimer(delay_ms); - } -} - -void MainMessageLoopExternalPump::OnTimerTimeout() { - REQUIRE_MAIN_THREAD(); - - KillTimer(); - DoWork(); -} - -void MainMessageLoopExternalPump::DoWork() { - const bool was_reentrant = PerformMessageLoopWork(); - if (was_reentrant) { - // Execute the remaining work as soon as possible. - OnScheduleMessagePumpWork(0); - } else if (!IsTimerPending()) { - // Schedule a timer event at the maximum allowed time. This may be dropped - // in OnScheduleWork() if another timer event is already in-flight. - OnScheduleMessagePumpWork(kTimerDelayPlaceholder); - } -} - -bool MainMessageLoopExternalPump::PerformMessageLoopWork() { - if (is_active_) { - // When CefDoMessageLoopWork() is called there may be various callbacks - // (such as paint and IPC messages) that result in additional calls to this - // method. If re-entrancy is detected we must repost a request again to the - // owner thread to ensure that the discarded call is executed in the future. - reentrancy_detected_ = true; - return false; - } - - reentrancy_detected_ = false; - - is_active_ = true; - CefDoMessageLoopWork(); - is_active_ = false; - - // |reentrancy_detected_| may have changed due to re-entrant calls to this - // method. - return reentrancy_detected_; -} diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump.h b/src/subprocess/main_message_loop/main_message_loop_external_pump.h deleted file mode 100644 index d94bb037..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#ifndef CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_EXTERNAL_PUMP_H_ -#define CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_EXTERNAL_PUMP_H_ -#pragma once - -#include "main_message_loop_std.h" - -// This MessageLoop implementation simulates the embedding of CEF into an -// existing host application that runs its own message loop. The scheduling -// implementation provided by this class is very simplistic and does not handle -// all cases (for example, nested message loops on Windows will not function -// correctly). See comments in Chromium's platform-specific -// base/message_loop/message_pump_* source files for additional guidance when -// implementing CefBrowserProcessHandler::OnScheduleMessagePumpWork() in your -// application. Run cefclient or ceftests with the -// "--external-message-pump" command-line flag to test this mode. -class MainMessageLoopExternalPump : public MainMessageLoopStd { - public: - // Creates the singleton instance of this object. Must be called on the main - // application thread. - static scoped_ptr Create(); - - // Returns the singleton instance of this object. Safe to call from any - // thread. - static MainMessageLoopExternalPump* Get(); - - // Called from CefBrowserProcessHandler::OnScheduleMessagePumpWork() on any - // thread. The platform subclass must implement this method and schedule a - // call to OnScheduleWork() on the main application thread. - virtual void OnScheduleMessagePumpWork(int64 delay_ms) = 0; - - protected: - // Only allow deletion via scoped_ptr. - friend struct base::DefaultDeleter; - - // Construct and destruct this object on the main application thread. - MainMessageLoopExternalPump(); - ~MainMessageLoopExternalPump(); - - // The platform subclass calls this method on the main application thread in - // response to the OnScheduleMessagePumpWork() call. - void OnScheduleWork(int64 delay_ms); - - // The platform subclass calls this method on the main application thread when - // the pending work timer times out. - void OnTimerTimeout(); - - // Control the pending work timer in the platform subclass. Only called on - // the main application thread. - virtual void SetTimer(int64 delay_ms) = 0; - virtual void KillTimer() = 0; - virtual bool IsTimerPending() = 0; - - private: - // Handle work processing. - void DoWork(); - bool PerformMessageLoopWork(); - - bool is_active_; - bool reentrancy_detected_; -}; - -#endif // CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_EXTERNAL_PUMP_H_ diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump_linux.cpp b/src/subprocess/main_message_loop/main_message_loop_external_pump_linux.cpp deleted file mode 100644 index 773149f6..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump_linux.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Copied from upstream cefclient with minor modifications. - -// Copyright (c) 2016 The Chromium Embedded Framework 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 "main_message_loop_external_pump.h" - -#include -#include -#include - -#include - -#include "include/base/cef_logging.h" -#include "include/cef_app.h" - -// From base/posix/eintr_wrapper.h. -// This provides a wrapper around system calls which may be interrupted by a -// signal and return EINTR. See man 7 signal. -// To prevent long-lasting loops (which would likely be a bug, such as a signal -// that should be masked) to go unnoticed, there is a limit after which the -// caller will nonetheless see an EINTR in Debug builds. -#if !defined(HANDLE_EINTR) -#if !DCHECK_IS_ON() - -#define HANDLE_EINTR(x) ({ \ - decltype(x) eintr_wrapper_result; \ - do { \ - eintr_wrapper_result = (x); \ - } while (eintr_wrapper_result == -1 && errno == EINTR); \ - eintr_wrapper_result; \ -}) - -#else - -#define HANDLE_EINTR(x) ({ \ - int eintr_wrapper_counter = 0; \ - decltype(x) eintr_wrapper_result; \ - do { \ - eintr_wrapper_result = (x); \ - } while (eintr_wrapper_result == -1 && errno == EINTR && \ - eintr_wrapper_counter++ < 100); \ - eintr_wrapper_result; \ -}) - -#endif // !DCHECK_IS_ON() -#endif // !defined(HANDLE_EINTR) - -namespace { - -class MainMessageLoopExternalPumpLinux : public MainMessageLoopExternalPump { - public: - MainMessageLoopExternalPumpLinux(); - ~MainMessageLoopExternalPumpLinux(); - - // MainMessageLoopStd methods: - void Quit() OVERRIDE; - int Run() OVERRIDE; - - // MainMessageLoopExternalPump methods: - void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE; - - // Internal methods used for processing the pump callbacks. They are public - // for simplicity but should not be used directly. HandlePrepare is called - // during the prepare step of glib, and returns a timeout that will be passed - // to the poll. HandleCheck is called after the poll has completed, and - // returns whether or not HandleDispatch should be called. HandleDispatch is - // called if HandleCheck returned true. - int HandlePrepare(); - bool HandleCheck(); - void HandleDispatch(); - - protected: - // MainMessageLoopExternalPump methods: - void SetTimer(int64 delay_ms) OVERRIDE; - void KillTimer() OVERRIDE; - bool IsTimerPending() OVERRIDE; - - private: - // Used to flag that the Run() invocation should return ASAP. - bool should_quit_; - - // A GLib structure that we can add event sources to. We use the default GLib - // context, which is the one to which all GTK events are dispatched. - GMainContext* context_; - - // The work source. It is destroyed when the message pump is destroyed. - GSource* work_source_; - - // The time when we need to do delayed work. - CefTime delayed_work_time_; - - // We use a wakeup pipe to make sure we'll get out of the glib polling phase - // when another thread has scheduled us to do some work. There is a glib - // mechanism g_main_context_wakeup, but this won't guarantee that our event's - // Dispatch() will be called. - int wakeup_pipe_read_; - int wakeup_pipe_write_; - - // Use a scoped_ptr to avoid needing the definition of GPollFD in the header. - scoped_ptr wakeup_gpollfd_; -}; - -// Return a timeout suitable for the glib loop, -1 to block forever, -// 0 to return right away, or a timeout in milliseconds from now. -int GetTimeIntervalMilliseconds(const CefTime& from) { - if (from.GetDoubleT() == 0.0) - return -1; - - CefTime now; - now.Now(); - - // Be careful here. CefTime has a precision of microseconds, but we want a - // value in milliseconds. If there are 5.5ms left, should the delay be 5 or - // 6? It should be 6 to avoid executing delayed work too early. - int delay = static_cast( - ceil((from.GetDoubleT() - now.GetDoubleT()) * 1000.0)); - - // If this value is negative, then we need to run delayed work soon. - return delay < 0 ? 0 : delay; -} - -struct WorkSource : public GSource { - MainMessageLoopExternalPumpLinux* pump; -}; - -gboolean WorkSourcePrepare(GSource* source, - gint* timeout_ms) { - *timeout_ms = static_cast(source)->pump->HandlePrepare(); - // We always return FALSE, so that our timeout is honored. If we were - // to return TRUE, the timeout would be considered to be 0 and the poll - // would never block. Once the poll is finished, Check will be called. - return FALSE; -} - -gboolean WorkSourceCheck(GSource* source) { - // Only return TRUE if Dispatch should be called. - return static_cast(source)->pump->HandleCheck(); -} - -gboolean WorkSourceDispatch(GSource* source, - GSourceFunc unused_func, - gpointer unused_data) { - - static_cast(source)->pump->HandleDispatch(); - // Always return TRUE so our source stays registered. - return TRUE; -} - -// I wish these could be const, but g_source_new wants non-const. -GSourceFuncs WorkSourceFuncs = { - WorkSourcePrepare, - WorkSourceCheck, - WorkSourceDispatch, - NULL -}; - -MainMessageLoopExternalPumpLinux::MainMessageLoopExternalPumpLinux() - : should_quit_(false), - context_(g_main_context_default()), - wakeup_gpollfd_(new GPollFD) { - // Create our wakeup pipe, which is used to flag when work was scheduled. - int fds[2]; - int ret = pipe(fds); - DCHECK_EQ(ret, 0); - (void)ret; // Prevent warning in release mode. - - wakeup_pipe_read_ = fds[0]; - wakeup_pipe_write_ = fds[1]; - wakeup_gpollfd_->fd = wakeup_pipe_read_; - wakeup_gpollfd_->events = G_IO_IN; - - work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); - static_cast(work_source_)->pump = this; - g_source_add_poll(work_source_, wakeup_gpollfd_.get()); - // Use a low priority so that we let other events in the queue go first. - g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); - // This is needed to allow Run calls inside Dispatch. - g_source_set_can_recurse(work_source_, TRUE); - g_source_attach(work_source_, context_); -} - -MainMessageLoopExternalPumpLinux::~MainMessageLoopExternalPumpLinux() { - g_source_destroy(work_source_); - g_source_unref(work_source_); - close(wakeup_pipe_read_); - close(wakeup_pipe_write_); -} - -void MainMessageLoopExternalPumpLinux::Quit() { - should_quit_ = true; -} - -int MainMessageLoopExternalPumpLinux::Run() { - // We really only do a single task for each iteration of the loop. If we - // have done something, assume there is likely something more to do. This - // will mean that we don't block on the message pump until there was nothing - // more to do. We also set this to true to make sure not to block on the - // first iteration of the loop. - bool more_work_is_plausible = true; - - // We run our own loop instead of using g_main_loop_quit in one of the - // callbacks. This is so we only quit our own loops, and we don't quit - // nested loops run by others. - for (;;) { - // Don't block if we think we have more work to do. - bool block = !more_work_is_plausible; - - more_work_is_plausible = g_main_context_iteration(context_, block); - if (should_quit_) - break; - } - - // We need to run the message pump until it is idle. However we don't have - // that information here so we run the message loop "for a while". - for (int i = 0; i < 10; ++i) { - // Do some work. - CefDoMessageLoopWork(); - - // Sleep to allow the CEF proc to do work. - usleep(50000); - } - - return 0; -} - -void MainMessageLoopExternalPumpLinux::OnScheduleMessagePumpWork( - int64 delay_ms) { - // This can be called on any thread, so we don't want to touch any state - // variables as we would then need locks all over. This ensures that if we - // are sleeping in a poll that we will wake up. - if (HANDLE_EINTR(write(wakeup_pipe_write_, &delay_ms, sizeof(int64))) != - sizeof(int64)) { - NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; - } -} - -// Return the timeout we want passed to poll. -int MainMessageLoopExternalPumpLinux::HandlePrepare() { - // We don't think we have work to do, but make sure not to block longer than - // the next time we need to run delayed work. - return GetTimeIntervalMilliseconds(delayed_work_time_); -} - -bool MainMessageLoopExternalPumpLinux::HandleCheck() { - // We usually have a single message on the wakeup pipe, since we are only - // signaled when the queue went from empty to non-empty, but there can be - // two messages if a task posted a task, hence we read at most two bytes. - // The glib poll will tell us whether there was data, so this read shouldn't - // block. - if (wakeup_gpollfd_->revents & G_IO_IN) { - int64 delay_ms[2]; - const size_t num_bytes = - HANDLE_EINTR(read(wakeup_pipe_read_, delay_ms, sizeof(int64) * 2)); - if (num_bytes < sizeof(int64)) { - NOTREACHED() << "Error reading from the wakeup pipe."; - } - if (num_bytes == sizeof(int64)) - OnScheduleWork(delay_ms[0]); - if (num_bytes == sizeof(int64) * 2) - OnScheduleWork(delay_ms[1]); - } - - if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { - // The timer has expired. That condition will stay true until we process - // that delayed work, so we don't need to record this differently. - return true; - } - - return false; -} - -void MainMessageLoopExternalPumpLinux::HandleDispatch() { - OnTimerTimeout(); -} - -void MainMessageLoopExternalPumpLinux::SetTimer(int64 delay_ms) { - DCHECK_GT(delay_ms, 0); - - CefTime now; - now.Now(); - - delayed_work_time_ = - CefTime(now.GetDoubleT() + static_cast(delay_ms) / 1000.0); -} - -void MainMessageLoopExternalPumpLinux::KillTimer() { - delayed_work_time_ = CefTime(); -} - -bool MainMessageLoopExternalPumpLinux::IsTimerPending() { - return GetTimeIntervalMilliseconds(delayed_work_time_) > 0; -} - -} // namespace - -// static -scoped_ptr -MainMessageLoopExternalPump::Create() { - return scoped_ptr( - new MainMessageLoopExternalPumpLinux()); -} diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm b/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm deleted file mode 100644 index f77c4d27..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2016 The Chromium Embedded Framework 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 "main_message_loop_external_pump.h" - -#import -#import - -#include "include/cef_app.h" - -@class EventHandler; - -class MainMessageLoopExternalPumpMac : public MainMessageLoopExternalPump { - public: - MainMessageLoopExternalPumpMac(); - ~MainMessageLoopExternalPumpMac(); - - // MainMessageLoopStd methods: - void Quit() OVERRIDE; - int Run() OVERRIDE; - - // MainMessageLoopExternalPump methods: - void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE; - - // Internal methods used for processing the event callbacks. They are public - // for simplicity but should not be used directly. - void HandleScheduleWork(int64 delay_ms); - void HandleTimerTimeout(); - - protected: - // MainMessageLoopExternalPump methods: - void SetTimer(int64 delay_ms) OVERRIDE; - void KillTimer() OVERRIDE; - bool IsTimerPending() OVERRIDE { return timer_ != nil; } - - private: - // Owner thread that will run events. - NSThread* owner_thread_; - - // Pending work timer. - NSTimer* timer_; - - // Used to handle event callbacks on the owner thread. - EventHandler* event_handler_; -}; - -// Object that handles event callbacks on the owner thread. -@interface EventHandler : NSObject { - @private - MainMessageLoopExternalPumpMac* pump_; -} - -- (id)initWithPump:(MainMessageLoopExternalPumpMac*)pump; -- (void)scheduleWork:(NSNumber*)delay_ms; -- (void)timerTimeout:(id)obj; -@end - -@implementation EventHandler - -- (id)initWithPump:(MainMessageLoopExternalPumpMac*)pump { - if (self = [super init]) { - pump_ = pump; - } - return self; -} - -- (void)scheduleWork:(NSNumber*)delay_ms { - pump_->HandleScheduleWork([delay_ms integerValue]); -} - -- (void)timerTimeout:(id)obj { - pump_->HandleTimerTimeout(); -} - -@end - -MainMessageLoopExternalPumpMac::MainMessageLoopExternalPumpMac() - : owner_thread_([[NSThread currentThread] retain]), - timer_(nil) { - event_handler_ = [[[EventHandler alloc] initWithPump:this] retain]; -} - -MainMessageLoopExternalPumpMac::~MainMessageLoopExternalPumpMac() { - KillTimer(); - [owner_thread_ release]; - [event_handler_ release]; -} - -void MainMessageLoopExternalPumpMac::Quit() { - [NSApp stop:nil]; -} - -int MainMessageLoopExternalPumpMac::Run() { - // Run the message loop. - [NSApp run]; - - KillTimer(); - - // We need to run the message pump until it is idle. However we don't have - // that information here so we run the message loop "for a while". - for (int i = 0; i < 10; ++i) { - // Let default runloop observers run. - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, 1); - - // Do some work. - CefDoMessageLoopWork(); - - // Sleep to allow the CEF proc to do work. - [NSThread sleepForTimeInterval: 0.05]; - } - - return 0; -} - -void MainMessageLoopExternalPumpMac::OnScheduleMessagePumpWork(int64 delay_ms) { - // This method may be called on any thread. - NSNumber* number = [NSNumber numberWithInt:static_cast(delay_ms)]; - [event_handler_ performSelector:@selector(scheduleWork:) - onThread:owner_thread_ - withObject:number - waitUntilDone:NO]; -} - -void MainMessageLoopExternalPumpMac::HandleScheduleWork(int64 delay_ms) { - OnScheduleWork(delay_ms); -} - -void MainMessageLoopExternalPumpMac::HandleTimerTimeout() { - OnTimerTimeout(); -} - -void MainMessageLoopExternalPumpMac::SetTimer(int64 delay_ms) { - DCHECK_GT(delay_ms, 0); - DCHECK(!timer_); - - const double delay_s = static_cast(delay_ms) / 1000.0; - timer_ = [[NSTimer timerWithTimeInterval: delay_s - target: event_handler_ - selector: @selector(timerTimeout:) - userInfo: nil - repeats: NO] retain]; - - // Add the timer to default and tracking runloop modes. - NSRunLoop* owner_runloop = [NSRunLoop currentRunLoop]; - [owner_runloop addTimer: timer_ forMode: NSRunLoopCommonModes]; - [owner_runloop addTimer: timer_ forMode: NSEventTrackingRunLoopMode]; -} - -void MainMessageLoopExternalPumpMac::KillTimer() { - if (timer_ != nil) { - [timer_ invalidate]; - [timer_ release]; - timer_ = nil; - } -} - -// static -scoped_ptr -MainMessageLoopExternalPump::Create() { - return scoped_ptr( - new MainMessageLoopExternalPumpMac()); -} diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp b/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp deleted file mode 100644 index 4ba8575b..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// Copied from upstream cefclient with minor modifications. -// Windows UNICODE API calls were converted to ANSI or commented out. - -// Copyright (c) 2016 The Chromium Embedded Framework 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 "main_message_loop_external_pump.h" - -#include - -#include "include/cef_app.h" -#include "util_win.h" - -namespace { - -// Message sent to get an additional time slice for pumping (processing) another -// task (a series of such messages creates a continuous task pump). -static const int kMsgHaveWork = WM_USER + 1; - -class MainMessageLoopExternalPumpWin : public MainMessageLoopExternalPump { - public: - MainMessageLoopExternalPumpWin(); - ~MainMessageLoopExternalPumpWin(); - - // MainMessageLoopStd methods: - void Quit() OVERRIDE; - int Run() OVERRIDE; - - // MainMessageLoopExternalPump methods: - void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE; - - protected: - // MainMessageLoopExternalPump methods: - void SetTimer(int64 delay_ms) OVERRIDE; - void KillTimer() OVERRIDE; - bool IsTimerPending() OVERRIDE { return timer_pending_; } - - private: - static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, - LPARAM lparam); - - // True if a timer event is currently pending. - bool timer_pending_; - - // HWND owned by the thread that CefDoMessageLoopWork should be invoked on. - HWND main_thread_target_; -}; - -MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin() - : timer_pending_(false), - main_thread_target_(NULL) { - HINSTANCE hInstance = GetModuleHandle(NULL); - const char* const kClassName = "CEFMainTargetHWND"; - - WNDCLASSEX wcex = {}; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.lpfnWndProc = WndProc; - wcex.hInstance = hInstance; - wcex.lpszClassName = kClassName; - RegisterClassEx(&wcex); - - // Create the message handling window. - main_thread_target_ = CreateWindowA(kClassName, NULL, WS_OVERLAPPEDWINDOW, - 0, 0, 0, 0, HWND_MESSAGE , NULL, hInstance, NULL); - DCHECK(main_thread_target_); - SetUserDataPtr(main_thread_target_, this); -} - -MainMessageLoopExternalPumpWin::~MainMessageLoopExternalPumpWin() { - KillTimer(); - if (main_thread_target_) - DestroyWindow(main_thread_target_); -} - -void MainMessageLoopExternalPumpWin::Quit() { - PostMessage(NULL, WM_QUIT, 0, 0); -} - -int MainMessageLoopExternalPumpWin::Run() { - // Run the message loop. - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - KillTimer(); - - // We need to run the message pump until it is idle. However we don't have - // that information here so we run the message loop "for a while". - for (int i = 0; i < 10; ++i) { - // Do some work. - CefDoMessageLoopWork(); - - // Sleep to allow the CEF proc to do work. - Sleep(50); - } - - return 0; -} - -void MainMessageLoopExternalPumpWin::OnScheduleMessagePumpWork(int64 delay_ms) { - // This method may be called on any thread. - PostMessage(main_thread_target_, kMsgHaveWork, 0, - static_cast(delay_ms)); -} - -void MainMessageLoopExternalPumpWin::SetTimer(int64 delay_ms) { - DCHECK(!timer_pending_); - DCHECK_GT(delay_ms, 0); - timer_pending_ = true; - ::SetTimer(main_thread_target_, 1, static_cast(delay_ms), NULL); -} - -void MainMessageLoopExternalPumpWin::KillTimer() { - if (timer_pending_) { - ::KillTimer(main_thread_target_, 1); - timer_pending_ = false; - } -} - -// static -LRESULT CALLBACK MainMessageLoopExternalPumpWin::WndProc( - HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - if (msg == WM_TIMER || msg == kMsgHaveWork) { - MainMessageLoopExternalPumpWin* message_loop = - GetUserDataPtr(hwnd); - if (msg == kMsgHaveWork) { - // OnScheduleMessagePumpWork() request. - const int64 delay_ms = static_cast(lparam); - message_loop->OnScheduleWork(delay_ms); - } else { - // Timer timed out. - message_loop->OnTimerTimeout(); - } - } - return DefWindowProc(hwnd, msg, wparam, lparam); -} - -} // namespace - -// static -scoped_ptr -MainMessageLoopExternalPump::Create() { - return scoped_ptr( - new MainMessageLoopExternalPumpWin()); -} diff --git a/src/subprocess/main_message_loop/main_message_loop_std.cpp b/src/subprocess/main_message_loop/main_message_loop_std.cpp deleted file mode 100644 index cdb7988d..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_std.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copied from upstream cefclient with minor modifications. - -// Copyright (c) 2015 The Chromium Embedded Framework 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 "main_message_loop_std.h" - -#include "include/cef_app.h" - -MainMessageLoopStd::MainMessageLoopStd() { -} - -int MainMessageLoopStd::Run() { - CefRunMessageLoop(); - return 0; -} - -void MainMessageLoopStd::Quit() { - CefQuitMessageLoop(); -} - -void MainMessageLoopStd::PostTask(CefRefPtr task) { - CefPostTask(TID_UI, task); -} - -bool MainMessageLoopStd::RunsTasksOnCurrentThread() const { - return CefCurrentlyOn(TID_UI); -} - -#if defined(OS_WIN) -void MainMessageLoopStd::SetCurrentModelessDialog(HWND hWndDialog) { - // Nothing to do here. The Chromium message loop implementation will - // internally route dialog messages. -} -#endif diff --git a/src/subprocess/main_message_loop/main_message_loop_std.h b/src/subprocess/main_message_loop/main_message_loop_std.h deleted file mode 100644 index b10b2581..00000000 --- a/src/subprocess/main_message_loop/main_message_loop_std.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copied from upstream cefclient with minor modifications. - -// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#ifndef CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_STD_H_ -#define CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_STD_H_ -#pragma once - -#include "main_message_loop.h" - -// Represents the main message loop in the browser process. This implementation -// is a light-weight wrapper around the Chromium UI thread. -class MainMessageLoopStd : public MainMessageLoop { - public: - MainMessageLoopStd(); - - // MainMessageLoop methods. - int Run() OVERRIDE; - void Quit() OVERRIDE; - void PostTask(CefRefPtr task) OVERRIDE; - bool RunsTasksOnCurrentThread() const OVERRIDE; - -#if defined(OS_WIN) - void SetCurrentModelessDialog(HWND hWndDialog) OVERRIDE; -#endif - - private: - DISALLOW_COPY_AND_ASSIGN(MainMessageLoopStd); -}; - -#endif // CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_STD_H_ diff --git a/src/subprocess/main_message_loop/util_win.cpp b/src/subprocess/main_message_loop/util_win.cpp deleted file mode 100644 index bc1f0965..00000000 --- a/src/subprocess/main_message_loop/util_win.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copied from upstream cefclient with minor modifications. -// Windows UNICODE API calls were converted to ANSI or commented out. - -// Copyright (c) 2015 The Chromium Embedded Framework 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 "util_win.h" - -#include "include/base/cef_logging.h" - -void SetUserDataPtr(HWND hWnd, void* ptr) { - SetLastError(ERROR_SUCCESS); - LONG_PTR result = ::SetWindowLongPtr( - hWnd, GWLP_USERDATA, reinterpret_cast(ptr)); - CHECK(result != 0 || GetLastError() == ERROR_SUCCESS); -} - -WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc) { - WNDPROC old = - reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_WNDPROC)); - CHECK(old != NULL); - LONG_PTR result = ::SetWindowLongPtr( - hWnd, GWLP_WNDPROC, reinterpret_cast(wndProc)); - CHECK(result != 0 || GetLastError() == ERROR_SUCCESS); - return old; -} - -//std::wstring GetResourceString(UINT id) { -// #define MAX_LOADSTRING 100 -// TCHAR buff[MAX_LOADSTRING] = {0}; -// LoadString(::GetModuleHandle(NULL), id, buff, MAX_LOADSTRING); -// return buff; -//} - -int GetCefMouseModifiers(WPARAM wparam) { - int modifiers = 0; - if (wparam & MK_CONTROL) - modifiers |= EVENTFLAG_CONTROL_DOWN; - if (wparam & MK_SHIFT) - modifiers |= EVENTFLAG_SHIFT_DOWN; - if (IsKeyDown(VK_MENU)) - modifiers |= EVENTFLAG_ALT_DOWN; - if (wparam & MK_LBUTTON) - modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; - if (wparam & MK_MBUTTON) - modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; - if (wparam & MK_RBUTTON) - modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; - - // Low bit set from GetKeyState indicates "toggled". - if (::GetKeyState(VK_NUMLOCK) & 1) - modifiers |= EVENTFLAG_NUM_LOCK_ON; - if (::GetKeyState(VK_CAPITAL) & 1) - modifiers |= EVENTFLAG_CAPS_LOCK_ON; - return modifiers; -} - -int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam) { - int modifiers = 0; - if (IsKeyDown(VK_SHIFT)) - modifiers |= EVENTFLAG_SHIFT_DOWN; - if (IsKeyDown(VK_CONTROL)) - modifiers |= EVENTFLAG_CONTROL_DOWN; - if (IsKeyDown(VK_MENU)) - modifiers |= EVENTFLAG_ALT_DOWN; - - // Low bit set from GetKeyState indicates "toggled". - if (::GetKeyState(VK_NUMLOCK) & 1) - modifiers |= EVENTFLAG_NUM_LOCK_ON; - if (::GetKeyState(VK_CAPITAL) & 1) - modifiers |= EVENTFLAG_CAPS_LOCK_ON; - - switch (wparam) { - case VK_RETURN: - if ((lparam >> 16) & KF_EXTENDED) - modifiers |= EVENTFLAG_IS_KEY_PAD; - break; - case VK_INSERT: - case VK_DELETE: - case VK_HOME: - case VK_END: - case VK_PRIOR: - case VK_NEXT: - case VK_UP: - case VK_DOWN: - case VK_LEFT: - case VK_RIGHT: - if (!((lparam >> 16) & KF_EXTENDED)) - modifiers |= EVENTFLAG_IS_KEY_PAD; - break; - case VK_NUMLOCK: - case VK_NUMPAD0: - case VK_NUMPAD1: - case VK_NUMPAD2: - case VK_NUMPAD3: - case VK_NUMPAD4: - case VK_NUMPAD5: - case VK_NUMPAD6: - case VK_NUMPAD7: - case VK_NUMPAD8: - case VK_NUMPAD9: - case VK_DIVIDE: - case VK_MULTIPLY: - case VK_SUBTRACT: - case VK_ADD: - case VK_DECIMAL: - case VK_CLEAR: - modifiers |= EVENTFLAG_IS_KEY_PAD; - break; - case VK_SHIFT: - if (IsKeyDown(VK_LSHIFT)) - modifiers |= EVENTFLAG_IS_LEFT; - else if (IsKeyDown(VK_RSHIFT)) - modifiers |= EVENTFLAG_IS_RIGHT; - break; - case VK_CONTROL: - if (IsKeyDown(VK_LCONTROL)) - modifiers |= EVENTFLAG_IS_LEFT; - else if (IsKeyDown(VK_RCONTROL)) - modifiers |= EVENTFLAG_IS_RIGHT; - break; - case VK_MENU: - if (IsKeyDown(VK_LMENU)) - modifiers |= EVENTFLAG_IS_LEFT; - else if (IsKeyDown(VK_RMENU)) - modifiers |= EVENTFLAG_IS_RIGHT; - break; - case VK_LWIN: - modifiers |= EVENTFLAG_IS_LEFT; - break; - case VK_RWIN: - modifiers |= EVENTFLAG_IS_RIGHT; - break; - } - return modifiers; -} - -bool IsKeyDown(WPARAM wparam) { - return (GetKeyState(wparam) & 0x8000) != 0; -} - -float GetDeviceScaleFactor() { - static float scale_factor = 1.0; - static bool initialized = false; - - if (!initialized) { - // This value is safe to cache for the life time of the app since the user - // must logout to change the DPI setting. This value also applies to all - // screens. - HDC screen_dc = ::GetDC(NULL); - int dpi_x = GetDeviceCaps(screen_dc, LOGPIXELSX); - scale_factor = static_cast(dpi_x) / 96.0f; - ::ReleaseDC(NULL, screen_dc); - initialized = true; - } - - return scale_factor; -} diff --git a/src/subprocess/main_message_loop/util_win.h b/src/subprocess/main_message_loop/util_win.h deleted file mode 100644 index 39870204..00000000 --- a/src/subprocess/main_message_loop/util_win.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copied from upstream cefclient with minor modifications. -// Windows UNICODE API calls were converted to ANSI or commented out. - -// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#ifndef CEF_TESTS_CEFCLIENT_BROWSER_UTIL_WIN_H_ -#define CEF_TESTS_CEFCLIENT_BROWSER_UTIL_WIN_H_ -#pragma once - -#include -#include - -#include "include/internal/cef_types_wrappers.h" - -// Set the window's user data pointer. -void SetUserDataPtr(HWND hWnd, void* ptr); - -// Return the window's user data pointer. -template -T GetUserDataPtr(HWND hWnd) { - return reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); -} - -// Set the window's window procedure pointer and return the old value. -WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc); - -// Return the resource string with the specified id. -//std::wstring GetResourceString(UINT id); - -int GetCefMouseModifiers(WPARAM wparam); -int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam); -bool IsKeyDown(WPARAM wparam); - -// Returns the device scale factor. For example, 200% display scaling will -// return 2.0. -float GetDeviceScaleFactor(); - -#endif // CEF_TESTS_CEFCLIENT_BROWSER_UTIL_WIN_H_ diff --git a/src/subprocess/v8utils.cpp b/src/subprocess/v8utils.cpp index 684f8c4e..030112b2 100644 --- a/src/subprocess/v8utils.cpp +++ b/src/subprocess/v8utils.cpp @@ -325,7 +325,7 @@ CefRefPtr CefDictionaryValueToV8Value( " dictValue->GetKeys() failed"; return CefV8Value::CreateNull(); } - CefRefPtr ret = CefV8Value::CreateObject(NULL, NULL); + CefRefPtr ret = CefV8Value::CreateObject(NULL); CefRefPtr binaryValue; PythonCallback pyCallback; CefRefPtr v8FunctionHandler; diff --git a/src/utils.pyx b/src/utils.pyx index e253e9f7..d0803c71 100644 --- a/src/utils.pyx +++ b/src/utils.pyx @@ -122,6 +122,7 @@ cpdef py_bool IsFunctionOrMethod(object valueType): if (valueType == types.FunctionType or valueType == types.MethodType or valueType == types.BuiltinFunctionType - or valueType == types.BuiltinMethodType): + or valueType == types.BuiltinMethodType + or valueType.__name__ == "cython_function_or_method"): return True return False diff --git a/src/version/cef_version_linux.h b/src/version/cef_version_linux.h deleted file mode 100644 index 11454273..00000000 --- a/src/version/cef_version_linux.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// --------------------------------------------------------------------------- -// -// This file is generated by the make_version_header.py tool. -// - -#ifndef CEF_INCLUDE_CEF_VERSION_H_ -#define CEF_INCLUDE_CEF_VERSION_H_ - -#define CEF_VERSION "3.2987.1601.gf035232" -#define CEF_VERSION_MAJOR 3 -#define CEF_COMMIT_NUMBER 1601 -#define CEF_COMMIT_HASH "f035232c082f837d2b85bd7821a93a54fc742775" -#define COPYRIGHT_YEAR 2017 - -#define CHROME_VERSION_MAJOR 57 -#define CHROME_VERSION_MINOR 0 -#define CHROME_VERSION_BUILD 2987 -#define CHROME_VERSION_PATCH 133 - -#define DO_MAKE_STRING(p) #p -#define MAKE_STRING(p) DO_MAKE_STRING(p) - -#ifndef APSTUDIO_HIDDEN_SYMBOLS - -#include "include/internal/cef_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// The API hash is created by analyzing CEF header files for C API type -// definitions. The hash value will change when header files are modified -// in a way that may cause binary incompatibility with other builds. The -// universal hash value will change if any platform is affected whereas the -// platform hash values will change only if that particular platform is -// affected. -#define CEF_API_HASH_UNIVERSAL "b0a24e3e202f3d8b72f2fbc1ebc5864f96ec16ae" -#if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "1c6a27f840ac87c8c971350c907edbe2c5fa0387" -#elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "1567db600ee83cc2a59bb8c17ca416d11a7c9b8a" -#elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "1f9f9e15bf7cf13de2557ddd411dfc9f694503b0" -#endif - -// Returns CEF version information for the libcef library. The |entry| -// parameter describes which version component will be returned: -// 0 - CEF_VERSION_MAJOR -// 1 - CEF_COMMIT_NUMBER -// 2 - CHROME_VERSION_MAJOR -// 3 - CHROME_VERSION_MINOR -// 4 - CHROME_VERSION_BUILD -// 5 - CHROME_VERSION_PATCH -/// -CEF_EXPORT int cef_version_info(int entry); - -/// -// Returns CEF API hashes for the libcef library. The returned string is owned -// by the library and should not be freed. The |entry| parameter describes which -// hash value will be returned: -// 0 - CEF_API_HASH_PLATFORM -// 1 - CEF_API_HASH_UNIVERSAL -// 2 - CEF_COMMIT_HASH -/// -CEF_EXPORT const char* cef_api_hash(int entry); - -#ifdef __cplusplus -} -#endif - -#endif // APSTUDIO_HIDDEN_SYMBOLS - -#endif // CEF_INCLUDE_CEF_VERSION_H_ diff --git a/src/version/cef_version_mac.h b/src/version/cef_version_mac.h deleted file mode 100644 index 11454273..00000000 --- a/src/version/cef_version_mac.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// --------------------------------------------------------------------------- -// -// This file is generated by the make_version_header.py tool. -// - -#ifndef CEF_INCLUDE_CEF_VERSION_H_ -#define CEF_INCLUDE_CEF_VERSION_H_ - -#define CEF_VERSION "3.2987.1601.gf035232" -#define CEF_VERSION_MAJOR 3 -#define CEF_COMMIT_NUMBER 1601 -#define CEF_COMMIT_HASH "f035232c082f837d2b85bd7821a93a54fc742775" -#define COPYRIGHT_YEAR 2017 - -#define CHROME_VERSION_MAJOR 57 -#define CHROME_VERSION_MINOR 0 -#define CHROME_VERSION_BUILD 2987 -#define CHROME_VERSION_PATCH 133 - -#define DO_MAKE_STRING(p) #p -#define MAKE_STRING(p) DO_MAKE_STRING(p) - -#ifndef APSTUDIO_HIDDEN_SYMBOLS - -#include "include/internal/cef_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// The API hash is created by analyzing CEF header files for C API type -// definitions. The hash value will change when header files are modified -// in a way that may cause binary incompatibility with other builds. The -// universal hash value will change if any platform is affected whereas the -// platform hash values will change only if that particular platform is -// affected. -#define CEF_API_HASH_UNIVERSAL "b0a24e3e202f3d8b72f2fbc1ebc5864f96ec16ae" -#if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "1c6a27f840ac87c8c971350c907edbe2c5fa0387" -#elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "1567db600ee83cc2a59bb8c17ca416d11a7c9b8a" -#elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "1f9f9e15bf7cf13de2557ddd411dfc9f694503b0" -#endif - -// Returns CEF version information for the libcef library. The |entry| -// parameter describes which version component will be returned: -// 0 - CEF_VERSION_MAJOR -// 1 - CEF_COMMIT_NUMBER -// 2 - CHROME_VERSION_MAJOR -// 3 - CHROME_VERSION_MINOR -// 4 - CHROME_VERSION_BUILD -// 5 - CHROME_VERSION_PATCH -/// -CEF_EXPORT int cef_version_info(int entry); - -/// -// Returns CEF API hashes for the libcef library. The returned string is owned -// by the library and should not be freed. The |entry| parameter describes which -// hash value will be returned: -// 0 - CEF_API_HASH_PLATFORM -// 1 - CEF_API_HASH_UNIVERSAL -// 2 - CEF_COMMIT_HASH -/// -CEF_EXPORT const char* cef_api_hash(int entry); - -#ifdef __cplusplus -} -#endif - -#endif // APSTUDIO_HIDDEN_SYMBOLS - -#endif // CEF_INCLUDE_CEF_VERSION_H_ diff --git a/src/version/cef_version_win.h b/src/version/cef_version_win.h index 11454273..3c29e987 100644 --- a/src/version/cef_version_win.h +++ b/src/version/cef_version_win.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. +// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -35,16 +35,16 @@ #ifndef CEF_INCLUDE_CEF_VERSION_H_ #define CEF_INCLUDE_CEF_VERSION_H_ -#define CEF_VERSION "3.2987.1601.gf035232" +#define CEF_VERSION "3.2623.1401.gb90a3be" #define CEF_VERSION_MAJOR 3 -#define CEF_COMMIT_NUMBER 1601 -#define CEF_COMMIT_HASH "f035232c082f837d2b85bd7821a93a54fc742775" -#define COPYRIGHT_YEAR 2017 +#define CEF_COMMIT_NUMBER 1401 +#define CEF_COMMIT_HASH "b90a3be1860b0647e8a62c218ff7c054390365b1" +#define COPYRIGHT_YEAR 2016 -#define CHROME_VERSION_MAJOR 57 +#define CHROME_VERSION_MAJOR 49 #define CHROME_VERSION_MINOR 0 -#define CHROME_VERSION_BUILD 2987 -#define CHROME_VERSION_PATCH 133 +#define CHROME_VERSION_BUILD 2623 +#define CHROME_VERSION_PATCH 110 #define DO_MAKE_STRING(p) #p #define MAKE_STRING(p) DO_MAKE_STRING(p) @@ -63,13 +63,13 @@ extern "C" { // universal hash value will change if any platform is affected whereas the // platform hash values will change only if that particular platform is // affected. -#define CEF_API_HASH_UNIVERSAL "b0a24e3e202f3d8b72f2fbc1ebc5864f96ec16ae" +#define CEF_API_HASH_UNIVERSAL "32c1d3523da124f2dea7b80b92c53c4d4a463c65" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "1c6a27f840ac87c8c971350c907edbe2c5fa0387" +#define CEF_API_HASH_PLATFORM "64b27477b82b44b51ce817522f744fca6768cbbb" #elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "1567db600ee83cc2a59bb8c17ca416d11a7c9b8a" +#define CEF_API_HASH_PLATFORM "e3b9c36454ae5ae4fb3509e17fb6a7d2877c847d" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "1f9f9e15bf7cf13de2557ddd411dfc9f694503b0" +#define CEF_API_HASH_PLATFORM "87a195efc055fb9f39c84f5ce8199cc8766290e3" #endif // Returns CEF version information for the libcef library. The |entry| diff --git a/src/window_utils_win.pyx b/src/window_utils_win.pyx index 0dd9ee70..bdd0d9ea 100644 --- a/src/window_utils_win.pyx +++ b/src/window_utils_win.pyx @@ -21,18 +21,44 @@ class WindowUtils(object): return DefWindowProc(windowHandle, msg, wparam, lparam) cdef HWND innerHwnd = pyBrowser.GetWindowHandle() + if not innerHwnd: + return DefWindowProc(windowHandle, msg, wparam, lparam) + cdef RECT rect2 - GetClientRect(windowHandle, &rect2) + cdef BOOL result = GetClientRect(windowHandle, &rect2) + + cdef HDWP hdwp + if result != 0: + hdwp = BeginDeferWindowPos(1) + if hdwp: + hdwp = DeferWindowPos(hdwp, innerHwnd, NULL, + rect2.left, rect2.top, + rect2.right - rect2.left, + rect2.bottom - rect2.top, + SWP_NOZORDER) + if hdwp: + EndDeferWindowPos(hdwp) - cdef HDWP hdwp = BeginDeferWindowPos(1) - hdwp = DeferWindowPos(hdwp, innerHwnd, NULL, + return DefWindowProc(windowHandle, msg, wparam, lparam) + + @staticmethod + def UpdateBrowserSize(WindowHandle parent_window_handle, + PyBrowser browser, + py_bool redraw=True): + cdef HWND innerHwnd = browser.GetWindowHandle() + if not innerHwnd: + return + cdef RECT rect2 + cdef BOOL result = GetClientRect(parent_window_handle, &rect2) + cdef UINT flags = SWP_NOZORDER + if not redraw: + flags = SWP_NOZORDER | SWP_NOREDRAW + if result != 0: + SetWindowPos(innerHwnd, NULL, rect2.left, rect2.top, rect2.right - rect2.left, rect2.bottom - rect2.top, - SWP_NOZORDER) - EndDeferWindowPos(hdwp) - - return DefWindowProc(windowHandle, msg, wparam, lparam) + flags) @staticmethod def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, diff --git a/tools/apidocs.py b/tools/apidocs.py index 8bd8da31..b872e925 100644 --- a/tools/apidocs.py +++ b/tools/apidocs.py @@ -17,10 +17,31 @@ import glob import os import re +import subprocess def main(): """Main entry point.""" + # Call toc.py for docs/ and api/ directories and for README.md in root dir + print("Running toc.py in {}/ dir".format(os.path.basename(API_DIR))) + retcode = subprocess.call([sys.executable, + os.path.join(TOOLS_DIR, "toc.py"), + API_DIR]) + assert retcode == 0, "Executing toc.py for API_DIR failed" + + print("Running toc.py in {}/ dir".format(os.path.basename(DOCS_DIR))) + retcode = subprocess.call([sys.executable, + os.path.join(TOOLS_DIR, "toc.py"), + DOCS_DIR]) + assert retcode == 0, "Executing toc.py for DOCS_DIR failed" + + print("Running toc.py for root/README.md") + retcode = subprocess.call([sys.executable, + os.path.join(TOOLS_DIR, "toc.py"), + os.path.join(ROOT_DIR, "README.md")]) + assert retcode == 0, "Executing toc.py for root/README.md failed" + + # Generate API reference in api/ dir and in root/README.md api_links = generate_api_links() update_api_index_file(api_links) update_readme_file(api_links) @@ -69,8 +90,8 @@ def update_readme_file(api_links): assert re.search(re_find, readme_contents), ("API categories not found" " in README") contents = re.sub(re_find, - ("### API categories\r\n\r\n{categories_contents}" - "\r\n### API index" + (u"### API categories\r\n\r\n{categories_contents}" + u"\r\n### API index" .format(categories_contents=categories_contents)), contents) @@ -80,7 +101,7 @@ def update_readme_file(api_links): assert re.search(re_find, readme_contents), ("API index not found" " in README") contents = re.sub(re_find, - ("### API index\r\n\r\n{api_links}" + (u"### API index\r\n\r\n{api_links}" .format(api_links=api_links)), contents) @@ -103,7 +124,7 @@ def generate_api_links(): continue with open(file_, "rb") as fo: md_contents = fo.read().decode("utf-8") - md_contents = re.sub(r"```[\s\S]+?```", "", md_contents) + md_contents = re.sub(u"```[\\s\\S]+?```", u"", md_contents) matches = re.findall(r"^(#|###)\s+(.*)", md_contents, re.MULTILINE) for match in matches: diff --git a/tools/automate.py b/tools/automate.py index 45a592d2..658a2417 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -156,7 +156,9 @@ def setup_options(docopt_args): if int(Options.cef_branch) >= 2704: Options.gyp_msvs_version = "2015" else: - Options.gyp_msvs_version = "2013" + # Branch 2623 supports building with VS 2013, so you can + # change it to 2013 below. + Options.gyp_msvs_version = "2015" # --build-dir if Options.build_dir: @@ -232,7 +234,7 @@ def build_cef(): " --prebuilt-cef on Linux 32-bit.") sys.exit(0) else: - # Build cefclient, cefsimple, ceftests, libcef_dll_wrapper + # Build cefclient, cefsimple, libcef_dll_wrapper build_cef_projects() create_prebuilt_binaries() @@ -337,13 +339,14 @@ def update_cef_patches(): def build_cef_projects(): - """Build cefclient, cefsimple, ceftests, libcef_dll_wrapper.""" + """Build cefclient, cefsimple, libcef_dll_wrapper.""" print("[automate.py] Build cef projects...") if WINDOWS: - fix_cmake_variables_permanently_windows() + # fix_cmake_variables_permanently_windows() + pass - fix_cef_include_files() + # fix_cef_include_files() # Find cef_binary directory. # Might already be set if --prebuilt-cef flag was passed. @@ -373,8 +376,7 @@ def build_cef_projects(): # Set build directory build_cefclient_dir = os.path.join(Options.cef_binary, "build_cefclient") - cefclient_exe = os.path.join(build_cefclient_dir, "tests", "cefclient", - Options.build_type, + cefclient_exe = os.path.join(build_cefclient_dir, "cefclient", "cefclient" + APP_EXT) # Check whether already built @@ -391,11 +393,11 @@ def build_cef_projects(): print("[automate.py] Create build_cefclient/ dir in cef_binary*/ dir") os.makedirs(build_cefclient_dir) - # Build cefclient, cefsimple, ceftests + # Build cefclient, cefsimple if already_built: - print("[automate.py] Already built: cefclient, cefsimple, ceftests") + print("[automate.py] Already built: cefclient, cefsimple") else: - print("[automate.py] Build cefclient, cefsimple, ceftests") + print("[automate.py] Build cefclient, cefsimple") # Cmake command = prepare_build_command() command.extend(["cmake", "-G", "Ninja"]) @@ -415,7 +417,7 @@ def build_cef_projects(): "cefsimple"]) else: command.extend(["ninja", "-j", Options.ninja_jobs, - "cefclient", "cefsimple", "ceftests"]) + "cefclient", "cefsimple"]) run_command(command, build_cefclient_dir) print("[automate.py] OK") assert os.path.exists(cefclient_exe) @@ -460,7 +462,7 @@ def build_wrapper_library_windows(runtime_library, msvs, vcvars): Options.cef_binary, "build_wrapper_{runtime_library}_VS{msvs}" .format(runtime_library=runtime_library, msvs=msvs)) - wrapper_lib = os.path.join(build_wrapper_dir, "libcef_dll_wrapper", + wrapper_lib = os.path.join(build_wrapper_dir, "libcef_dll", "libcef_dll_wrapper{ext}".format(ext=LIB_EXT)) # Check whether library is already built @@ -559,14 +561,14 @@ def fix_cmake_variables_for_MD_library(undo=False, try_undo=False): # This replacements must be unique for the undo operation # to be reliable. - mt_find = r"/MT " - mt_replace = r"/MD /wd4275 " + mt_find = u'"/MT ' + mt_replace = u'"/MD /wd4275 ' - mtd_find = r"/MTd " - mtd_replace = r"/MDd /wd4275 " + mtd_find = u'"/MTd ' + mtd_replace = u'"/MDd /wd4275 ' - cmake_variables = os.path.join(Options.cef_binary, "cmake", - "cef_variables.cmake") + cmake_variables = os.path.join(Options.cef_binary, + "CMakeLists.txt") with open(cmake_variables, "rb") as fp: contents = fp.read().decode("utf-8") @@ -581,14 +583,14 @@ def fix_cmake_variables_for_MD_library(undo=False, try_undo=False): if undo: (contents, count) = re.subn(re.escape(mt_replace), mt_find, contents) - assert count == 2 + assert count == 1 (contents, count) = re.subn(re.escape(mtd_replace), mtd_find, contents) assert count == 1 else: (contents, count) = re.subn(re.escape(mt_find), mt_replace, contents) - assert count == 2 + assert count == 1 (contents, count) = re.subn(re.escape(mtd_find), mtd_replace, contents) assert count == 1 @@ -659,7 +661,9 @@ def prepare_build_command(build_lib=False, vcvars=None): if int(Options.cef_branch) >= 2704: command.append(VS2015_VCVARS) else: - command.append(VS2013_VCVARS) + # Branch 2623 supports building with VS 2013, so you can + # change it to 2013 below. + command.append(VS2015_VCVARS) command.append(VS_PLATFORM_ARG) command.append("&&") return command @@ -716,45 +720,27 @@ def create_prebuilt_binaries(): run_command(["install_name_tool", "-id", new_id, cef_library], working_dir=cef_framework_dir) - # Copy cefclient, cefsimple, ceftests + # Copy cefclient, cefsimple # cefclient cefclient = os.path.join( src, - "build_cefclient", "tests", "cefclient", - Options.build_type, + "build_cefclient", "cefclient", "cefclient" + APP_EXT) if LINUX and os.path.exists(cefclient): # On Windows resources/*.html files are embedded inside exe cefclient_files = os.path.join( src, - "build_cefclient", "tests", "cefclient", - Options.build_type, + "build_cefclient", "cefclient", "cefclient_files") cpdir(cefclient_files, os.path.join(bindir, "cefclient_files")) # cefsimple cefsimple = os.path.join( src, - "build_cefclient", "tests", "cefsimple", - Options.build_type, + "build_cefclient", "cefsimple", "cefsimple" + APP_EXT) - # ceftests - ceftests = os.path.join( - src, - "build_cefclient", "tests", "ceftests", - Options.build_type, - "ceftests" + APP_EXT) - if LINUX and os.path.exists(ceftests): - # On Windows resources/*.html files are embedded inside exe - ceftests_files = os.path.join( - src, - "build_cefclient", "tests", "ceftests", - Options.build_type, - "ceftests_files") - cpdir(ceftests_files, os.path.join(bindir, "ceftests_files")) - def copy_app(app): if os.path.exists(app): if os.path.isdir(app): @@ -769,9 +755,8 @@ def copy_app(app): # Currently do not copy apps on Mac copy_app(cefclient) copy_app(cefsimple) - copy_app(ceftests) - # END: Copy cefclient, cefsimple, ceftests + # END: Copy cefclient, cefsimple # Copy libraries if platform.system() == "Windows": @@ -784,13 +769,13 @@ def copy_app(app): # MT library libsrc = os.path.join( src, "build_wrapper_MT_VS{msvs}".format(msvs=msvs), - "libcef_dll_wrapper", "libcef_dll_wrapper.lib") + "libcef_dll", "libcef_dll_wrapper.lib") libdst = os.path.join(vs_subdir, "libcef_dll_wrapper_MT.lib") shutil.copy(libsrc, libdst) # MD library libsrc = os.path.join( src, "build_wrapper_MD_VS{msvs}".format(msvs=msvs), - "libcef_dll_wrapper", "libcef_dll_wrapper.lib") + "libcef_dll", "libcef_dll_wrapper.lib") libdst = os.path.join(vs_subdir, "libcef_dll_wrapper_MD.lib") shutil.copy(libsrc, libdst) elif platform.system() == "Darwin": @@ -819,6 +804,12 @@ def copy_app(app): shutil.copy(os.path.join(src, "README.txt"), dst) shutil.copy(os.path.join(src, "LICENSE.txt"), dst) + # Copy cef_version_{os}.h + cef_version_file = os.path.join(dst, "cef_version_{os_postfix}.h".format( + os_postfix=OS_POSTFIX)) + shutil.copy(os.path.join(src, "include", "cef_version.h"), + cef_version_file) + print("[automate.py] OK prebuilt binaries created in '%s/'" % dst) @@ -826,7 +817,6 @@ def get_available_python_compilers(): all_python_compilers = OrderedDict([ ("2008", VS2008_VCVARS), ("2010", VS2010_VCVARS), - ("2015", VS2015_VCVARS), ]) ret_compilers = OrderedDict() for msvs in all_python_compilers: diff --git a/tools/build.py b/tools/build.py index 1ae47b73..f1f39ef0 100644 --- a/tools/build.py +++ b/tools/build.py @@ -475,7 +475,6 @@ def clean_cpp_projects_unix(): delete_files_by_pattern("{0}/*.o".format(SUBPROCESS_DIR)) delete_files_by_pattern("{0}/*.a".format(SUBPROCESS_DIR)) delete_files_by_pattern("{0}/subprocess".format(SUBPROCESS_DIR)) - delete_files_by_pattern("{0}/main_message_loop/*.o".format(SUBPROCESS_DIR)) delete_files_by_pattern("{0}/*.o".format(CPP_UTILS_DIR)) delete_files_by_pattern("{0}/*.a".format(CPP_UTILS_DIR)) diff --git a/tools/build_cpp_projects.py b/tools/build_cpp_projects.py index b662c76f..5ad3547b 100644 --- a/tools/build_cpp_projects.py +++ b/tools/build_cpp_projects.py @@ -151,8 +151,6 @@ def build_library(lib_name, macros, output_dir, def build_cefpython_app_library(): sources = get_sources(SUBPROCESS_DIR, exclude_names=["main.cpp"]) - main_message_loop_dir = os.path.join(SUBPROCESS_DIR, "main_message_loop") - sources.extend(get_sources(main_message_loop_dir)) build_library(lib_name="cefpython_app", macros=cefpython_app_MACROS, sources=sources, diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 3c2abef8..9ce6b878 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -75,7 +75,7 @@ NO_AUTOMATE = False # Python versions -SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6)] +SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4)] # Python search paths. It will use first Python found for specific version. # Supports replacement of one environment variable in path eg.: %ENV_KEY%. @@ -140,9 +140,9 @@ def main(): build_cefpython_modules(pythons_32bit, "32bit") build_cefpython_modules(pythons_64bit, "64bit") if pythons_32bit: - make_packages(pythons_32bit[0], "32bit") + make_packages(pythons_32bit[0], "32bit", pythons_32bit) if pythons_64bit: - make_packages(pythons_64bit[0], "64bit") + make_packages(pythons_64bit[0], "64bit", pythons_64bit) test_wheel_packages(pythons_32bit + pythons_64bit) show_summary(pythons_32bit, pythons_64bit) @@ -192,6 +192,28 @@ def clean_build_directories(): # Delete cef binaries and libraries dirs if not NO_AUTOMATE: + # Delete cef binlib dir only if cef_binary dir exists, + # otherwise you will end up with cef binlib directory + # deleted and script failing further when calling + # automate.py --prebuilt-cef. + version = get_cefpython_version() + # 32-bit + if not MAC: + postfix2 = get_cef_postfix2_for_arch("32bit") + cef_binary_dir = "cef_binary_{cef_version}_{postfix2}"\ + .format(cef_version=version["CEF_VERSION"], + postfix2=postfix2) + if len(glob.glob(cef_binary_dir)) != 1: + raise Exception("Directory not found: "+cef_binary_dir) + # 64-bit + postfix2 = get_cef_postfix2_for_arch("64bit") + cef_binary_dir = "cef_binary_{cef_version}_windows64"\ + .format(cef_version=version["CEF_VERSION"], + postfix2=postfix2) + if len(glob.glob(cef_binary_dir)) != 1: + raise Exception("Directory not found: "+cef_binary_dir) + + # Delete delete_cef_binaries_libraries_dir("32bit") delete_cef_binaries_libraries_dir("64bit") @@ -524,7 +546,7 @@ def restore_subprocess_executable_issue342(arch): shutil.copy(src, dst) -def make_packages(python, arch): +def make_packages(python, arch, all_pythons): """Make setup and wheel packages.""" print("[build_distrib.py] Make setup package for {arch}..." .format(arch=arch)) @@ -547,6 +569,7 @@ def make_packages(python, arch): setup_basename = get_setup_installer_basename( VERSION, get_os_postfix2_for_arch(arch)) setup_dir = os.path.join(BUILD_DIR, setup_basename) + check_cpp_extension_dependencies_issue359(setup_dir, all_pythons) archive = pack_directory(setup_dir, BUILD_DIR) shutil.move(archive, DISTRIB_DIR) @@ -574,6 +597,26 @@ def make_packages(python, arch): shutil.rmtree(setup_dir) +def check_cpp_extension_dependencies_issue359(setup_dir, all_pythons): + """Windows only: check if msvcpXX.dll exist for all Python versions. + Issue #359.""" + if not WINDOWS: + return + checked_any = False + for python in all_pythons: + if python["version2"] == (3, 4): + checked_any = True + if not os.path.exists(os.path.join(setup_dir, "cefpython3", + "msvcp100.dll")): + raise Exception("C++ ext dependency missing: msvcp100.dll") + elif python["version2"] == (2, 7): + if not os.path.exists(os.path.join(setup_dir, "cefpython3", + "msvcp90.dll")): + raise Exception("C++ ext dependency missing: msvcp90.dll") + checked_any = True + assert checked_any + + def test_wheel_packages(pythons): """Test wheel packages installation and run unit tests.""" uninstall_cefpython3_packages(pythons) diff --git a/tools/common.py b/tools/common.py index 4ca34d73..1517ece2 100644 --- a/tools/common.py +++ b/tools/common.py @@ -26,6 +26,10 @@ assert platform.architecture()[0] == "64bit" ARCH_STR = platform.architecture()[0] +# Operating system architecture +SYSTEM64 = platform.machine().endswith('64') +SYSTEM32 = not SYSTEM64 + # OS_POSTFIX is for directories/files names in cefpython sources # and doesn't include architecture type, just OS name. @@ -121,6 +125,7 @@ # API reference API_DIR = os.path.join(ROOT_DIR, "api") +DOCS_DIR = os.path.join(ROOT_DIR, "docs") # Build directories BUILD_DIR = os.path.join(ROOT_DIR, "build") @@ -151,6 +156,7 @@ # -- end build directories EXAMPLES_DIR = os.path.join(ROOT_DIR, "examples") +SNIPPETS_DIR = os.path.join(EXAMPLES_DIR, "snippets") SRC_DIR = os.path.join(ROOT_DIR, "src") # Subdirectories in src/ @@ -420,10 +426,6 @@ def get_msvs_for_python(vs_prefix=False): return "VS2008" if vs_prefix else "2008" elif sys.version_info[:2] == (3, 4): return "VS2010" if vs_prefix else "2010" - elif sys.version_info[:2] == (3, 5): - return "VS2015" if vs_prefix else "2015" - elif sys.version_info[:2] == (3, 6): - return "VS2015" if vs_prefix else "2015" else: print("ERROR: This version of Python is not supported") sys.exit(1) diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index d7463248..22e628f0 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -51,11 +51,5 @@ elif sys.version_info[:2] == (3, 4): # noinspection PyUnresolvedReferences from . import cefpython_py34 as cefpython -elif sys.version_info[:2] == (3, 5): - # noinspection PyUnresolvedReferences - from . import cefpython_py35 as cefpython -elif sys.version_info[:2] == (3, 6): - # noinspection PyUnresolvedReferences - from . import cefpython_py36 as cefpython else: raise Exception("Python version not supported: " + sys.version) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 08065ebd..6814cbb7 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -129,7 +129,7 @@ def main(): "https://github.com/cztomczak/cefpython", license="BSD 3-clause", author="Czarek Tomczak", - author_email="czarek.tomczak@@gmail.com", + author_email="czarek.tomczak@gmail.com", url="https://github.com/cztomczak/cefpython", download_url="https://github.com/cztomczak/cefpython/releases", platforms=["{{SYSCONFIG_PLATFORM}}"], @@ -147,6 +147,7 @@ def main(): "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Topic :: Desktop Environment", "Topic :: Internet", "Topic :: Internet :: WWW/HTTP", diff --git a/tools/make_installer.py b/tools/make_installer.py index 4220710f..ddef6ddd 100644 --- a/tools/make_installer.py +++ b/tools/make_installer.py @@ -102,6 +102,8 @@ def main(): create_empty_log_file(os.path.join(PKG_DIR, "debug.log")) create_empty_log_file(os.path.join(PKG_DIR, "examples/debug.log")) + copy_cpp_extension_dependencies_issue359(PKG_DIR) + print("[make_installer.py] Done. Installer package created: {setup_dir}" .format(setup_dir=SETUP_DIR)) @@ -184,7 +186,7 @@ def replace_template_vars(string, dictionary): string = string.replace("{{"+key+"}}", value) if string == orig_string: raise Exception("Nothing to format") - if re.search(r"\{\{[a-zA-Z0-9_]+\}\}", string): + if re.search(r"{{[a-zA-Z0-9_]+}}", string): raise Exception("Not all strings were formatted") return string @@ -334,6 +336,73 @@ def create_empty_log_file(log_file): subprocess.check_call(command, shell=True) +def copy_cpp_extension_dependencies_issue359(pkg_dir): + """CEF Python module is written in Cython and is a Python C++ + extension and depends on msvcpXX.dll. For Python 3.4 + msvcp100.dll is required. See Issue #359. For Python 2.7 + msvcp90.dll is required. Etc. These dependencies are not included + with Python binaries from Python.org.""" + if not WINDOWS: + return + + windows_dir = os.environ["SYSTEMROOT"] + if SYSTEM64: + system32 = os.path.join(windows_dir, "SysWOW64") + system64 = os.path.join(windows_dir, "System32") + else: + system32 = os.path.join(windows_dir, "") + system64 = None + if ARCH64: + system = system64 + else: + system = system32 + + root_search_paths = [] + + # Need to check for .pyd files for all Python version, because + # the builder/installer work in a way that previous cefpython + # module builds for other Python versions are also included + # in the package. Thus if included, msvcpxx.dll dependency is + # required as well. + + # Python 3.4 + if os.path.exists(os.path.join(pkg_dir, "cefpython_py34.pyd")): + search_paths = [ + # 10.00.40219.325 installs here on my system. + os.path.join(system, "msvcp100.dll"), + ] + root_search_paths.append(search_paths) + + # Python 2.7 + if os.path.exists(os.path.join(pkg_dir, "cefpython_py27.pyd")): + if ARCH32: + search_paths = [ + # This runtime version is shipped with Python 2.7.14 + r"c:\Windows\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b" + r"_9.0.30729.1_none_e163563597edeada\msvcp90.dll", + ] + else: + search_paths = [ + # This runtime version is shipped with Python 2.7.14 + r"c:\Windows\winsxs\amd64_microsoft.vc90.crt_1fc8b3b9a1e18e3b" + r"_9.0.30729.1_none_99b61f5e8371c1d4\msvcp90.dll", + ] + root_search_paths.append(search_paths) + + assert len(root_search_paths) + + for search_paths in root_search_paths: + found = False + for path in search_paths: + if os.path.exists(path): + shutil.copy(path, pkg_dir) + found = True + if not found: + raise Exception("C++ extension dll dependency not found." + " Search paths: {0}" + .format(", ".join(search_paths))) + + def short_src_path(path): # Very long: \build\cef55_3.2883.1553.g80bd606_win32\ find = os.path.basename(CEF_BINARIES_LIBRARIES) diff --git a/tools/requirements.py b/tools/requirements.py new file mode 100644 index 00000000..e1637904 --- /dev/null +++ b/tools/requirements.py @@ -0,0 +1,22 @@ +""" +Installs Python dependencies using the pip tool. +See the requirements.txt file. +pip install --upgrade -r ../tools/requirements.txt +""" + +from common import * +import subprocess + + +def main(): + args = [] + if sys.executable.startswith("/usr/"): + args.append("sudo") + requirements = os.path.join(TOOLS_DIR, "requirements.txt") + args.extend(["pip", "install", "--upgrade", "-r", requirements]) + retcode = subprocess.call(args) + sys.exit(retcode) + + +if __name__ == "__main__": + main() diff --git a/tools/run_examples.py b/tools/run_examples.py index ea28df33..bf70f045 100644 --- a/tools/run_examples.py +++ b/tools/run_examples.py @@ -3,7 +3,8 @@ # Project website: https://github.com/cztomczak/cefpython """ -Run all examples that can be run on current configuration. +Run all examples that can be run on current configuration +and display a summary at the end. Note on GTK 2 / GTK 3 on Windows: Installing both PyGTK and PyGI on Windows will cause errors. @@ -14,6 +15,7 @@ import importlib import os +import subprocess import sys @@ -59,11 +61,14 @@ def main(): passed.append("gtk2.py") # gtk3 + """ if LINUX: # Broken on Linux (Issue #261) print("[run_examples.py] PASS: gtk3.py (Issue #261)") passed.append("gtk3.py (Issue #261)") - elif MAC: + """ + + if MAC: # Crashes on Mac (Issue #310) print("[run_examples.py] PASS: gtk3.py (Issue #310)") passed.append("gtk3.py (Issue #310)") @@ -74,7 +79,10 @@ def main(): passed.append("gtk3.py") # pyqt4 - if packages["PyQt4"]: + if LINUX: + print("[run_examples.py] PASS: qt.py pyqt4 (Issue #452)") + passed.append("qt.py pyqt4 (Issue #452)") + elif packages["PyQt4"]: examples.append("qt.py pyqt4") else: print("[run_examples.py] PASS: qt.py pyqt4 (PyQt4 not installed)") @@ -88,7 +96,10 @@ def main(): passed.append("qt.py pyqt5") # pyside - if packages["PySide"]: + if LINUX: + print("[run_examples.py] PASS: qt.py pyside (Issue #452)") + passed.append("qt.py pyside (Issue #452)") + elif packages["PySide"]: examples.append("qt.py pyside") else: print("[run_examples.py] PASS: qt.py pyside (PySide not installed)") @@ -99,6 +110,9 @@ def main(): # This example often crashes on Mac (Issue #309) print("[run_examples.py] PASS: tkinter_.py (Issue #309)") passed.append("tkinter_.py (Issue #309)") + elif WINDOWS and sys.version_info.major == 2: + print("[run_examples.py] PASS: tkinter_.py (Issue #441)") + passed.append("tkinter_.py (Issue #441)") elif packages["tkinter"] or packages["Tkinter"]: examples.append("tkinter_.py") else: @@ -165,7 +179,6 @@ def main(): def check_installed_packages(): packages = { "gtk": False, - "gi": False, "kivy": False, "PyQt4": False, "PyQt5": False, @@ -176,12 +189,27 @@ def check_installed_packages(): } for package in packages: try: - importlib.import_module(package) - packages[package] = True + if package == "PyQt5": + # Strange issue on Mac, PyQt5 is an empty built-in module + from PyQt5 import QtGui + else: + importlib.import_module(package) + packages[package] = True except ImportError: packages[package] = False + packages["gi"] = check_gi_installed() return packages +def check_gi_installed(): + # Cannot import both gtk and gi in the same script, thus + # need another way of checking if gi package is installed. + code = subprocess.call([sys.executable, "-c", "import gi"]) + if code != 0: + print("[run_examples.py] gi module not found (PyGI / GTK 3).") + print(" Import error above can be safely ignored.") + return True if code == 0 else False + + if __name__ == "__main__": main() diff --git a/tools/run_snippets.py b/tools/run_snippets.py new file mode 100644 index 00000000..193d9558 --- /dev/null +++ b/tools/run_snippets.py @@ -0,0 +1,55 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +""" +Run all snippets from the examples/snippets/ directory +and display a summary at the end. +""" + +from common import * + +import glob +import os +import subprocess +import sys + + +def main(): + # Iterate over all snippets + snippets_iter = glob.glob(os.path.join(SNIPPETS_DIR, "*.py")) + succeeded = [] + failed = [] + for snippet in snippets_iter: + print("[run_snippets.py] Running '{snippet}'..." + .format(snippet=os.path.basename(snippet))) + retcode = subprocess.call([sys.executable, snippet]) + if retcode == 0: + succeeded.append(os.path.basename(snippet)) + else: + print("[run_snippets.py] ERROR while running snippet: {snippet}" + .format(snippet=snippet)) + failed.append(os.path.basename(snippet)) + + # Print summary + summary = "" + for snippet in succeeded: + summary += " OK {snippet}{nl}"\ + .format(snippet=snippet, nl=os.linesep) + for snippet in failed: + summary += " ERROR {snippet}{nl}"\ + .format(snippet=snippet, nl=os.linesep) + summary = summary[:-(len(os.linesep))] + print("[run_snippets.py] SUMMARY:") + print(summary.format()) + if len(failed): + print("[run_snippets.py] ERRORS ({failed}) while running snippets" + .format(failed=len(failed))) + sys.exit(1) + else: + print("[run_snippets.py] OK ({succeeded})" + .format(succeeded=len(succeeded))) + + +if __name__ == "__main__": + main() diff --git a/unittests/_common.py b/unittests/_common.py new file mode 100644 index 00000000..f58b0b83 --- /dev/null +++ b/unittests/_common.py @@ -0,0 +1,244 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +from cefpython3 import cefpython as cef + +import base64 +import os +import platform +import sys +import time + +# Platforms +SYSTEM = platform.system().upper() +if SYSTEM == "DARWIN": + SYSTEM = "MAC" +WINDOWS = SYSTEM if SYSTEM == "WINDOWS" else False +LINUX = SYSTEM if SYSTEM == "LINUX" else False +MAC = SYSTEM if SYSTEM == "MAC" else False + +# To show the window for an extended period of time increase this number. +MESSAGE_LOOP_RANGE = 200 # each iteration is 0.01 sec + +MAIN_BROWSER_ID = 1 +POPUP_BROWSER_ID = 2 + +g_subtests_ran = 0 +g_js_code_completed = False +g_on_load_end_callbacks = [] + + +def subtest_message(message): + global g_subtests_ran + g_subtests_ran += 1 + print(str(g_subtests_ran) + ". " + message) + sys.stdout.flush() + + +def show_test_summary(pyfile): + print("\nRan " + str(g_subtests_ran) + " sub-tests in " + + os.path.basename(pyfile)) + + +def run_message_loop(): + # Run message loop for some time. + # noinspection PyTypeChecker + for i in range(MESSAGE_LOOP_RANGE): + cef.MessageLoopWork() + time.sleep(0.01) + subtest_message("cef.MessageLoopWork() ok") + + +def do_message_loop_work(work_loops): + # noinspection PyTypeChecker + for i in range(work_loops): + cef.MessageLoopWork() + time.sleep(0.01) + + +def on_load_end(callback, *args): + global g_on_load_end_callbacks + g_on_load_end_callbacks.append([callback, args]) + + +def js_code_completed(): + """Sometimes window.onload can execute before javascript bindings + are ready if the document loads very fast. When setting javascript + bindings it can take some time, because these bindings are sent + via IPC messaging to the Renderer process.""" + global g_js_code_completed + assert not g_js_code_completed + g_js_code_completed = True + subtest_message("js_code_completed() ok") + + +def check_auto_asserts(test_case, objects): + # Check if js code completed + test_case.assertTrue(g_js_code_completed) + + # Automatic check of asserts in handlers and in external + for obj in objects: + test_for_True = False # Test whether asserts are working correctly + for key, value in obj.__dict__.items(): + if key == "test_for_True": + test_for_True = True + continue + if "_True" in key: + test_case.assertTrue(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_True", "") + + " ok") + elif "_False" in key: + test_case.assertFalse(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_False", "") + + " ok") + test_case.assertTrue(test_for_True) + + +class DisplayHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.javascript_errors_False = False + self.OnConsoleMessage_True = False + + def OnConsoleMessage(self, message, **_): + if "error" in message.lower() or "uncaught" in message.lower(): + self.javascript_errors_False = True + raise Exception("Javascript error: " + message) + else: + # Check whether messages from javascript are coming + self.OnConsoleMessage_True = True + subtest_message(message) + + +def close_popup(global_handler, browser): + browser.CloseBrowser() + global_handler.PopupClosed_True = True + + # Test developer tools popup + main_browser = cef.GetBrowserByIdentifier(MAIN_BROWSER_ID) + main_browser.ShowDevTools() + cef.PostDelayedTask(cef.TID_UI, 1500, close_devtools, global_handler) + cef.PostDelayedTask(cef.TID_UI, 500, main_browser.SetFocus, True) + + +def close_devtools(global_handler): + main_browser = cef.GetBrowserByIdentifier(MAIN_BROWSER_ID) + main_browser.CloseDevTools() + subtest_message("DevTools popup ok") + + +class GlobalHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnAfterCreatedMain_True = False + + if "main_test" in test_case.id(): + self.OnAfterCreatedPopup_True = False + self.PopupClosed_True = False + + def _OnAfterCreated(self, browser, **_): + # For asserts that are checked automatically before shutdown its + # values should be set first, so that when other asserts fail + # (the ones called through the test_case member) they are reported + # correctly. + if not self.OnAfterCreatedMain_True: + # First call for main browser. + # browser.GetUrl() returns empty at this moment. + self.OnAfterCreatedMain_True = True + self.test_case.assertEqual(browser.GetIdentifier(), + MAIN_BROWSER_ID) + self.test_case.assertFalse(browser.IsPopup()) + else: + # Second call for implicit popup browser opened via js. + # Should execute only for main test. + # Should not execute for DevTools window. + assert "main_test" in self.test_case.id() + assert not self.OnAfterCreatedPopup_True + self.OnAfterCreatedPopup_True = True + self.test_case.assertEqual(browser.GetIdentifier(), + POPUP_BROWSER_ID) + # browser.GetUrl() returns empty at this moment. + self.test_case.assertTrue(browser.IsPopup()) + if WINDOWS: + cef.WindowUtils.SetTitle(browser, "Popup test") + # Close the popup browser after 250 ms + cef.PostDelayedTask(cef.TID_UI, 250, close_popup, self, browser) + + +class LoadHandler(object): + def __init__(self, test_case, datauri): + self.test_case = test_case + self.datauri = datauri + self.frame_source_visitor = None + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnLoadStart_True = False + self.OnLoadEnd_True = False + self.FrameSourceVisitor_True = False + # self.OnLoadingStateChange_Start_True = False # FAILS + self.OnLoadingStateChange_End_True = False + + def OnLoadStart(self, browser, frame, **_): + self.test_case.assertFalse(self.OnLoadStart_True) + self.OnLoadStart_True = True + self.test_case.assertEqual(browser.GetUrl(), frame.GetUrl()) + self.test_case.assertEqual(browser.GetUrl(), self.datauri) + + def OnLoadEnd(self, browser, frame, http_code, **_): + # OnLoadEnd should be called only once + self.test_case.assertFalse(self.OnLoadEnd_True) + self.OnLoadEnd_True = True + self.test_case.assertEqual(http_code, 200) + self.frame_source_visitor = FrameSourceVisitor(self, self.test_case) + frame.GetSource(self.frame_source_visitor) + browser.ExecuteJavascript("print('LoadHandler.OnLoadEnd() ok')") + + subtest_message("Executing callbacks registered with on_load_end()") + global g_on_load_end_callbacks + for callback_data in g_on_load_end_callbacks: + callback_data[0](*callback_data[1]) + del g_on_load_end_callbacks + + def OnLoadingStateChange(self, browser, is_loading, can_go_back, + can_go_forward, **_): + if is_loading: + # TODO: this test fails, looks like OnLoadingStaetChange with + # is_loading=False is being called very fast, before + # OnLoadStart and before client handler is set by calling + # browser.SetClientHandler(). + # SOLUTION: allow to set OnLoadingStateChange through + # SetGlobalClientCallback similarly to _OnAfterCreated(). + # self.test_case.assertFalse(self.OnLoadingStateChange_Start_True) + # self.OnLoadingStateChange_Start_True = True + pass + else: + self.test_case.assertFalse(self.OnLoadingStateChange_End_True) + self.OnLoadingStateChange_End_True = True + self.test_case.assertEqual(browser.CanGoBack(), can_go_back) + self.test_case.assertEqual(browser.CanGoForward(), can_go_forward) + + +class FrameSourceVisitor(object): + """Visitor for Frame.GetSource().""" + + def __init__(self, load_handler, test_case): + self.load_handler = load_handler + self.test_case = test_case + + def Visit(self, value): + self.test_case.assertFalse(self.load_handler.FrameSourceVisitor_True) + self.load_handler.FrameSourceVisitor_True = True + self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee", + value) diff --git a/unittests/main_test.py b/unittests/main_test.py index d7a2fbf9..52079e95 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -7,14 +7,14 @@ import unittest # noinspection PyUnresolvedReferences import _test_runner -from os.path import basename +from _common import * + from cefpython3 import cefpython as cef -import time -import base64 + +import glob +import os import sys -# To show the window for an extended period of time increase this number. -MESSAGE_LOOP_RANGE = 200 # each iteration is 0.01 sec g_datauri_data = """ @@ -25,6 +25,10 @@ font-family: Arial; font-size: 11pt; } + body { + width: 810px; + heiht: 610px; + } @@ -86,25 +112,14 @@ """ -g_datauri = "data:text/html;base64,"+base64.b64encode(g_datauri_data.encode( - "utf-8", "replace")).decode("utf-8", "replace") - -g_subtests_ran = 0 - - -def subtest_message(message): - global g_subtests_ran - g_subtests_ran += 1 - print(str(g_subtests_ran) + ". " + message) - sys.stdout.flush() +g_datauri = cef.GetDataUrl(g_datauri_data) class MainTest_IsolatedTest(unittest.TestCase): - def test_main(self): - """Main entry point.""" - # All this code must run inside one single test, otherwise strange - # things happen. + """Main entry point. All the code must run inside one + single test, otherwise strange things happen.""" + print("") print("CEF Python {ver}".format(ver=cef.__version__)) print("Python {ver}".format(ver=sys.version[:6])) @@ -115,187 +130,130 @@ def test_main(self): "log_severity": cef.LOGSEVERITY_ERROR, "log_file": "", } + if not LINUX: + # On Linux you get a lot of "X error received" messages + # from Chromium's "x11_util.cc", so do not show them. + settings["log_severity"] = cef.LOGSEVERITY_WARNING if "--debug" in sys.argv: settings["debug"] = True settings["log_severity"] = cef.LOGSEVERITY_INFO + if "--debug-warning" in sys.argv: + settings["debug"] = True + settings["log_severity"] = cef.LOGSEVERITY_WARNING cef.Initialize(settings) subtest_message("cef.Initialize() ok") - # Test global handler + # High DPI on Windows + if WINDOWS: + self.assertIsInstance(cef.DpiAware.GetSystemDpi(), tuple) + window_size = cef.DpiAware.CalculateWindowSize(800, 600) + self.assertIsInstance(window_size, tuple) + self.assertGreater(window_size[0], 0) + # It seems that in v49 Chromium sets process DPI awareness + # by default, so IsProcessDpiAware returns True. + # self.assertFalse(cef.DpiAware.IsProcessDpiAware()) + subtest_message("cef.DpiAware ok") + + # Global handler global_handler = GlobalHandler(self) cef.SetGlobalClientCallback("OnAfterCreated", global_handler._OnAfterCreated) subtest_message("cef.SetGlobalClientCallback() ok") - # Test creation of browser - browser = cef.CreateBrowserSync(url=g_datauri) + # Create browser + browser_settings = { + "inherit_client_handlers_for_popups": False, + } + browser = cef.CreateBrowserSync(url=g_datauri, + settings=browser_settings) self.assertIsNotNone(browser, "Browser object") + browser.SetFocus(True) subtest_message("cef.CreateBrowserSync() ok") - # Test other handlers: LoadHandler, DisplayHandler etc. - client_handlers = [LoadHandler(self), DisplayHandler(self)] + # Client handlers + client_handlers = [LoadHandler(self, g_datauri), + DisplayHandler(self)] for handler in client_handlers: browser.SetClientHandler(handler) subtest_message("browser.SetClientHandler() ok") - # Test javascript bindings + # Javascript bindings external = External(self) bindings = cef.JavascriptBindings( bindToFrames=False, bindToPopups=False) + bindings.SetFunction("js_code_completed", js_code_completed) bindings.SetFunction("test_function", external.test_function) bindings.SetProperty("test_property1", external.test_property1) bindings.SetProperty("test_property2", external.test_property2) + # Property with a function value can also be bound. CEF Python + # supports passing functions as callbacks when called from + # javascript, and as a side effect any value and in this case + # a property can also be a function. + bindings.SetProperty("test_property3_function", + external.test_property3_function) bindings.SetProperty("cefpython_version", cef.GetVersion()) bindings.SetObject("external", external) browser.SetJavascriptBindings(bindings) subtest_message("browser.SetJavascriptBindings() ok") - # Run message loop for some time. - # noinspection PyTypeChecker - for i in range(MESSAGE_LOOP_RANGE): - cef.MessageLoopWork() - time.sleep(0.01) - subtest_message("cef.MessageLoopWork() ok") - - # Test browser closing. Remember to clean reference. + # Cookie manager + self.assertIsInstance(cef.CookieManager.CreateManager(path=""), + cef.PyCookieManager) + self.assertIsInstance(cef.CookieManager.GetGlobalManager(), + cef.PyCookieManager) + subtest_message("cef.CookieManager ok") + + # Window Utils + if WINDOWS: + hwnd = 1 # When using 0 getting issues with OnautoResize + self.assertFalse(cef.WindowUtils.IsWindowHandle(hwnd)) + cef.WindowUtils.OnSetFocus(hwnd, 0, 0, 0) + cef.WindowUtils.OnSize(hwnd, 0, 0, 0) + cef.WindowUtils.OnEraseBackground(hwnd, 0, 0, 0) + cef.WindowUtils.GetParentHandle(hwnd) + cef.WindowUtils.SetTitle(browser, "Main test") + subtest_message("cef.WindowUtils ok") + elif LINUX: + cef.WindowUtils.InstallX11ErrorHandlers() + subtest_message("cef.WindowUtils ok") + elif MAC: + hwnd = 0 + cef.WindowUtils.GetParentHandle(hwnd) + cef.WindowUtils.IsWindowHandle(hwnd) + subtest_message("cef.WindowUtils ok") + + # Run message loop + run_message_loop() + + # Make sure popup browser was destroyed + self.assertIsInstance(cef.GetBrowserByIdentifier(MAIN_BROWSER_ID), + cef.PyBrowser) + self.assertIsNone(cef.GetBrowserByIdentifier(POPUP_BROWSER_ID)) + subtest_message("cef.GetBrowserByIdentifier() ok") + + # Close browser and clean reference browser.CloseBrowser(True) del browser subtest_message("browser.CloseBrowser() ok") - # Give it some time to close before calling shutdown. + # Give it some time to close before checking asserts + # and calling shutdown. + do_message_loop_work(25) + # noinspection PyTypeChecker - for i in range(25): - cef.MessageLoopWork() - time.sleep(0.01) - - # Automatic check of asserts in handlers and in external - for obj in [] + client_handlers + [global_handler, external]: - test_for_True = False # Test whether asserts are working correctly - for key, value in obj.__dict__.items(): - if key == "test_for_True": - test_for_True = True - continue - if "_True" in key: - self.assertTrue(value, "Check assert: " + - obj.__class__.__name__ + "." + key) - subtest_message(obj.__class__.__name__ + "." + - key.replace("_True", "") + - " ok") - elif "_False" in key: - self.assertFalse(value, "Check assert: " + - obj.__class__.__name__ + "." + key) - subtest_message(obj.__class__.__name__ + "." + - key.replace("_False", "") + - " ok") - self.assertTrue(test_for_True) + check_auto_asserts(self, [] + client_handlers + + [global_handler, + external]) # Test shutdown of CEF cef.Shutdown() subtest_message("cef.Shutdown() ok") - # Display real number of tests there were run - print("\nRan " + str(g_subtests_ran) + " sub-tests in test_main") + # Display summary + show_test_summary(__file__) sys.stdout.flush() -class GlobalHandler(object): - def __init__(self, test_case): - self.test_case = test_case - - # Asserts for True/False will be checked just before shutdown - self.test_for_True = True # Test whether asserts are working correctly - self.OnAfterCreated_True = False - - def _OnAfterCreated(self, browser, **_): - # For asserts that are checked automatically before shutdown its - # values should be set first, so that when other asserts fail - # (the ones called through the test_case member) they are reported - # correctly. - self.test_case.assertFalse(self.OnAfterCreated_True) - self.OnAfterCreated_True = True - self.test_case.assertEqual(browser.GetIdentifier(), 1) - - -class LoadHandler(object): - def __init__(self, test_case): - self.test_case = test_case - self.frame_source_visitor = None - - # Asserts for True/False will be checked just before shutdown - self.test_for_True = True # Test whether asserts are working correctly - self.OnLoadStart_True = False - self.OnLoadEnd_True = False - self.FrameSourceVisitor_True = False - # self.OnLoadingStateChange_Start_True = False # FAILS - self.OnLoadingStateChange_End_True = False - - def OnLoadStart(self, browser, frame, **_): - self.test_case.assertFalse(self.OnLoadStart_True) - self.OnLoadStart_True = True - self.test_case.assertEqual(browser.GetUrl(), frame.GetUrl()) - self.test_case.assertEqual(browser.GetUrl(), g_datauri) - - def OnLoadEnd(self, browser, frame, http_code, **_): - # OnLoadEnd should be called only once - self.test_case.assertFalse(self.OnLoadEnd_True) - self.OnLoadEnd_True = True - self.test_case.assertEqual(http_code, 200) - self.frame_source_visitor = FrameSourceVisitor(self, self.test_case) - frame.GetSource(self.frame_source_visitor) - browser.ExecuteJavascript("print('LoadHandler.OnLoadEnd() ok')") - - def OnLoadingStateChange(self, browser, is_loading, can_go_back, - can_go_forward, **_): - if is_loading: - # TODO: this test fails, looks like OnLoadingStaetChange with - # is_loading=False is being called very fast, before - # OnLoadStart and before client handler is set by calling - # browser.SetClientHandler(). - # SOLUTION: allow to set OnLoadingStateChange through - # SetGlobalClientCallback similarly to _OnAfterCreated(). - # self.test_case.assertFalse(self.OnLoadingStateChange_Start_True) - # self.OnLoadingStateChange_Start_True = True - pass - else: - self.test_case.assertFalse(self.OnLoadingStateChange_End_True) - self.OnLoadingStateChange_End_True = True - self.test_case.assertEqual(browser.CanGoBack(), can_go_back) - self.test_case.assertEqual(browser.CanGoForward(), can_go_forward) - - -class DisplayHandler(object): - def __init__(self, test_case): - self.test_case = test_case - - # Asserts for True/False will be checked just before shutdown - self.test_for_True = True # Test whether asserts are working correctly - self.javascript_errors_False = False - self.OnConsoleMessage_True = False - - def OnConsoleMessage(self, message, **_): - if "error" in message.lower() or "uncaught" in message.lower(): - self.javascript_errors_False = True - raise Exception(message) - else: - # Check whether messages from javascript are coming - self.OnConsoleMessage_True = True - subtest_message(message) - - -class FrameSourceVisitor(object): - """Visitor for Frame.GetSource().""" - - def __init__(self, load_handler, test_case): - self.load_handler = load_handler - self.test_case = test_case - - def Visit(self, **_): - self.test_case.assertFalse(self.load_handler.FrameSourceVisitor_True) - self.load_handler.FrameSourceVisitor_True = True - self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee", - g_datauri_data) - - class External(object): """Javascript 'window.external' object.""" @@ -310,6 +268,7 @@ def __init__(self, test_case): # Asserts for True/False will be checked just before shutdown self.test_for_True = True # Test whether asserts are working correctly self.test_function_True = False + self.test_property3_function_True = False self.test_callbacks_True = False self.py_callback_True = False @@ -317,6 +276,10 @@ def test_function(self): """Test binding function to the 'window' object.""" self.test_function_True = True + def test_property3_function(self): + """Test binding function to the 'window' object.""" + self.test_property3_function_True = True + def test_callbacks(self, js_callback): """Test both javascript and python callbacks.""" def py_callback(msg_from_js): @@ -328,4 +291,4 @@ def py_callback(msg_from_js): if __name__ == "__main__": - _test_runner.main(basename(__file__)) + _test_runner.main(os.path.basename(__file__)) diff --git a/unittests/main_test_old.py b/unittests/main_test_old.py new file mode 100644 index 00000000..254e1f5f --- /dev/null +++ b/unittests/main_test_old.py @@ -0,0 +1,331 @@ +# Copyright (c) 2016 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +"""General testing of CEF Python.""" + +import unittest +# noinspection PyUnresolvedReferences +import _test_runner +from os.path import basename +from cefpython3 import cefpython as cef +import time +import base64 +import sys + +# To show the window for an extended period of time increase this number. +MESSAGE_LOOP_RANGE = 200 # each iteration is 0.01 sec + +g_datauri_data = """ + + + + + + + + +

Main test (old)

+
+ + +""" +g_datauri = "data:text/html;base64,"+base64.b64encode(g_datauri_data.encode( + "utf-8", "replace")).decode("utf-8", "replace") + +g_subtests_ran = 0 + + +def subtest_message(message): + global g_subtests_ran + g_subtests_ran += 1 + print(str(g_subtests_ran) + ". " + message) + sys.stdout.flush() + + +class MainTest_IsolatedTest(unittest.TestCase): + + def test_main(self): + """Main entry point.""" + # All this code must run inside one single test, otherwise strange + # things happen. + print("") + print("CEF Python {ver}".format(ver=cef.__version__)) + print("Python {ver}".format(ver=sys.version[:6])) + + # Test initialization of CEF + settings = { + "debug": False, + "log_severity": cef.LOGSEVERITY_ERROR, + "log_file": "", + } + if "--debug" in sys.argv: + settings["debug"] = True + settings["log_severity"] = cef.LOGSEVERITY_INFO + cef.Initialize(settings) + subtest_message("cef.Initialize() ok") + + # Test global handler + global_handler = GlobalHandler(self) + cef.SetGlobalClientCallback("OnAfterCreated", + global_handler._OnAfterCreated) + subtest_message("cef.SetGlobalClientCallback() ok") + + # Test creation of browser + browser = cef.CreateBrowserSync(url=g_datauri) + self.assertIsNotNone(browser, "Browser object") + subtest_message("cef.CreateBrowserSync() ok") + + # Test other handlers: LoadHandler, DisplayHandler etc. + client_handlers = [LoadHandler(self), DisplayHandler(self)] + for handler in client_handlers: + browser.SetClientHandler(handler) + subtest_message("browser.SetClientHandler() ok") + + # Test javascript bindings + external = External(self) + bindings = cef.JavascriptBindings( + bindToFrames=False, bindToPopups=False) + bindings.SetFunction("test_function", external.test_function) + bindings.SetProperty("test_property1", external.test_property1) + bindings.SetProperty("test_property2", external.test_property2) + bindings.SetProperty("cefpython_version", cef.GetVersion()) + bindings.SetObject("external", external) + browser.SetJavascriptBindings(bindings) + subtest_message("browser.SetJavascriptBindings() ok") + + # Run message loop for some time. + # noinspection PyTypeChecker + for i in range(MESSAGE_LOOP_RANGE): + cef.MessageLoopWork() + time.sleep(0.01) + subtest_message("cef.MessageLoopWork() ok") + + # Test browser closing. Remember to clean reference. + browser.CloseBrowser(True) + del browser + subtest_message("browser.CloseBrowser() ok") + + # Give it some time to close before calling shutdown. + # noinspection PyTypeChecker + for i in range(25): + cef.MessageLoopWork() + time.sleep(0.01) + + # Automatic check of asserts in handlers and in external + for obj in [] + client_handlers + [global_handler, external]: + test_for_True = False # Test whether asserts are working correctly + for key, value in obj.__dict__.items(): + if key == "test_for_True": + test_for_True = True + continue + if "_True" in key: + self.assertTrue(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_True", "") + + " ok") + elif "_False" in key: + self.assertFalse(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_False", "") + + " ok") + self.assertTrue(test_for_True) + + # Test shutdown of CEF + cef.Shutdown() + subtest_message("cef.Shutdown() ok") + + # Display real number of tests there were run + print("\nRan " + str(g_subtests_ran) + " sub-tests in test_main") + sys.stdout.flush() + + +class GlobalHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnAfterCreated_True = False + + def _OnAfterCreated(self, browser, **_): + # For asserts that are checked automatically before shutdown its + # values should be set first, so that when other asserts fail + # (the ones called through the test_case member) they are reported + # correctly. + self.test_case.assertFalse(self.OnAfterCreated_True) + self.OnAfterCreated_True = True + self.test_case.assertEqual(browser.GetIdentifier(), 1) + + +class LoadHandler(object): + def __init__(self, test_case): + self.test_case = test_case + self.frame_source_visitor = None + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnLoadStart_True = False + self.OnLoadEnd_True = False + self.FrameSourceVisitor_True = False + # self.OnLoadingStateChange_Start_True = False # FAILS + self.OnLoadingStateChange_End_True = False + + def OnLoadStart(self, browser, frame, **_): + self.test_case.assertFalse(self.OnLoadStart_True) + self.OnLoadStart_True = True + self.test_case.assertEqual(browser.GetUrl(), frame.GetUrl()) + self.test_case.assertEqual(browser.GetUrl(), g_datauri) + + def OnLoadEnd(self, browser, frame, http_code, **_): + # OnLoadEnd should be called only once + self.test_case.assertFalse(self.OnLoadEnd_True) + self.OnLoadEnd_True = True + self.test_case.assertEqual(http_code, 200) + self.frame_source_visitor = FrameSourceVisitor(self, self.test_case) + frame.GetSource(self.frame_source_visitor) + browser.ExecuteJavascript("print('LoadHandler.OnLoadEnd() ok')") + + def OnLoadingStateChange(self, browser, is_loading, can_go_back, + can_go_forward, **_): + if is_loading: + # TODO: this test fails, looks like OnLoadingStaetChange with + # is_loading=False is being called very fast, before + # OnLoadStart and before client handler is set by calling + # browser.SetClientHandler(). + # SOLUTION: allow to set OnLoadingStateChange through + # SetGlobalClientCallback similarly to _OnAfterCreated(). + # self.test_case.assertFalse(self.OnLoadingStateChange_Start_True) + # self.OnLoadingStateChange_Start_True = True + pass + else: + self.test_case.assertFalse(self.OnLoadingStateChange_End_True) + self.OnLoadingStateChange_End_True = True + self.test_case.assertEqual(browser.CanGoBack(), can_go_back) + self.test_case.assertEqual(browser.CanGoForward(), can_go_forward) + + +class DisplayHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.javascript_errors_False = False + self.OnConsoleMessage_True = False + + def OnConsoleMessage(self, message, **_): + if "error" in message.lower() or "uncaught" in message.lower(): + self.javascript_errors_False = True + raise Exception(message) + else: + # Check whether messages from javascript are coming + self.OnConsoleMessage_True = True + subtest_message(message) + + +class FrameSourceVisitor(object): + """Visitor for Frame.GetSource().""" + + def __init__(self, load_handler, test_case): + self.load_handler = load_handler + self.test_case = test_case + + def Visit(self, **_): + self.test_case.assertFalse(self.load_handler.FrameSourceVisitor_True) + self.load_handler.FrameSourceVisitor_True = True + self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee", + g_datauri_data) + + +class External(object): + """Javascript 'window.external' object.""" + + def __init__(self, test_case): + self.test_case = test_case + + # Test binding properties to the 'window' object. + self.test_property1 = "Test binding property to the 'window' object" + self.test_property2 = {"key1": self.test_property1, + "key2": ["Inside list", 1, 2]} + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.test_function_True = False + self.test_callbacks_True = False + self.py_callback_True = False + + def test_function(self): + """Test binding function to the 'window' object.""" + self.test_function_True = True + + def test_callbacks(self, js_callback): + """Test both javascript and python callbacks.""" + def py_callback(msg_from_js): + self.py_callback_True = True + self.test_case.assertEqual(msg_from_js, + "String sent from Javascript") + self.test_callbacks_True = True + js_callback.Call("String sent from Python", py_callback) + + +if __name__ == "__main__": + _test_runner.main(basename(__file__)) diff --git a/unittests/osr_test.py b/unittests/osr_test.py new file mode 100644 index 00000000..4ca00581 --- /dev/null +++ b/unittests/osr_test.py @@ -0,0 +1,222 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +"""Off-screen rendering tests.""" + +import unittest +# noinspection PyUnresolvedReferences +import _test_runner +from _common import * + +from cefpython3 import cefpython as cef + +import sys + +g_datauri_data = """ + + + + + + + + + +

Off-screen rendering test

+
+
Test selection.
+ + +""" +g_datauri = cef.GetDataUrl(g_datauri_data) + + +class OsrTest_IsolatedTest(unittest.TestCase): + def test_osr(self): + """Main entry point. All the code must run inside one + single test, otherwise strange things happen.""" + + print("") + print("CEF Python {ver}".format(ver=cef.__version__)) + print("Python {ver}".format(ver=sys.version[:6])) + + # Application settings + settings = { + "debug": False, + "log_severity": cef.LOGSEVERITY_ERROR, + "log_file": "", + "windowless_rendering_enabled": True + } + if not LINUX: + # On Linux you get a lot of "X error received" messages + # from Chromium's "x11_util.cc", so do not show them. + settings["log_severity"] = cef.LOGSEVERITY_WARNING + if "--debug" in sys.argv: + settings["debug"] = True + settings["log_severity"] = cef.LOGSEVERITY_INFO + if "--debug-warning" in sys.argv: + settings["debug"] = True + settings["log_severity"] = cef.LOGSEVERITY_WARNING + + # All settings + settings = { + "windowless_rendering_enabled": True, + } + switches = { + # GPU acceleration is not supported in OSR mode, so must disable + # it using these Chromium switches (Issues #240 and #463) + "disable-gpu": "", + "disable-gpu-compositing": "", + # Tweaking OSR performance by setting the same Chromium flags + # as in upstream cefclient (Issue #240). + "enable-begin-frame-scheduling": "", + "disable-surfaces": "", # This is required for PDF ext to work + } + browser_settings = { + # Tweaking OSR performance (Issue #240) + "windowless_frame_rate": 30, # Default frame rate in CEF is 30 + } + + # Initialize + cef.Initialize(settings, switches=switches) + subtest_message("cef.Initialize() ok") + + # Global handler + global_handler = GlobalHandler(self) + cef.SetGlobalClientCallback("OnAfterCreated", + global_handler._OnAfterCreated) + subtest_message("cef.SetGlobalClientCallback() ok") + + # Create browser + window_info = cef.WindowInfo() + window_info.SetAsOffscreen(0) + browser = cef.CreateBrowserSync(window_info=window_info, + settings=browser_settings, + url=g_datauri) + + # Javascript bindings + bindings = cef.JavascriptBindings( + bindToFrames=False, bindToPopups=False) + bindings.SetFunction("js_code_completed", js_code_completed) + bindings.SetProperty("cefpython_version", cef.GetVersion()) + browser.SetJavascriptBindings(bindings) + subtest_message("browser.SetJavascriptBindings() ok") + + # Client handlers + client_handlers = [LoadHandler(self, g_datauri), + DisplayHandler(self), + RenderHandler(self)] + for handler in client_handlers: + browser.SetClientHandler(handler) + + # Initiate OSR rendering + browser.SendFocusEvent(True) + browser.WasResized() + + # Test selection + on_load_end(select_h1_text, browser) + + # Message loop + run_message_loop() + + # Close browser and clean reference + browser.CloseBrowser(True) + del browser + subtest_message("browser.CloseBrowser() ok") + + # Give it some time to close before checking asserts + # and calling shutdown. + do_message_loop_work(25) + + # Asserts before shutdown + # noinspection PyTypeChecker + check_auto_asserts(self, [] + client_handlers + + [global_handler]) + + # Test shutdown of CEF + cef.Shutdown() + subtest_message("cef.Shutdown() ok") + + # Display summary + show_test_summary(__file__) + sys.stdout.flush() + + +def select_h1_text(browser): + browser.SendMouseClickEvent(0, 0, cef.MOUSEBUTTON_LEFT, + mouseUp=False, clickCount=1) + browser.SendMouseMoveEvent(400, 20, mouseLeave=False, + modifiers=cef.EVENTFLAG_LEFT_MOUSE_BUTTON) + browser.SendMouseClickEvent(400, 20, cef.MOUSEBUTTON_LEFT, + mouseUp=True, clickCount=1) + subtest_message("select_h1_text() ok") + + +class RenderHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown. + # Test whether asserts are working correctly. + self.test_for_True = True + + self.GetViewRect_True = False + self.OnPaint_True = False + + def GetViewRect(self, rect_out, **_): + """Called to retrieve the view rectangle which is relative + to screen coordinates. Return True if the rectangle was + provided.""" + # rect_out --> [x, y, width, height] + self.GetViewRect_True = True + rect_out.extend([0, 0, 800, 600]) + return True + + def OnPaint(self, element_type, paint_buffer, **_): + """Called when an element should be painted.""" + if element_type == cef.PET_VIEW: + self.test_case.assertEqual(paint_buffer.width, 800) + self.test_case.assertEqual(paint_buffer.height, 600) + if not self.OnPaint_True: + self.OnPaint_True = True + subtest_message("RenderHandler.OnPaint: viewport ok") + else: + raise Exception("Unsupported element_type in OnPaint") + + +if __name__ == "__main__": + _test_runner.main(os.path.basename(__file__))