From e452fdcb8d3c2cafba0760c6b77df9b3b85e924a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 24 Jan 2017 08:45:26 +0100 Subject: [PATCH 01/16] Update Tutorial --- docs/Tutorial.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 2ed26cae..04ecf74e 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -91,3 +91,91 @@ API docs (the api/ directory in GitHub's repository): ## Handling Python exceptions ... + + +## Message loop + +Message loop is a programming construct that waits for and +dispatches events or messages in a program. All desktop GUI +programs must run some kind of message loop. +The hello_world.py example doesn't depend on any third party GUI +framework and thus can run CEF message loop directly by calling call +cef.MessageLoop(). However in most of other examples that show how +to embed CEF Python browser inside GUI frameworks such as +Qt/wxPython/Tkinter, you can't call cef.MessageLoop(), because these +frameworks run a message loop of its own. For such cases CEF provides +cef.MessageLoopWork() which is for integrating CEF message loop into +existing application message loop. Usually cef.MessageLoopWork() is +called in a 10ms timer. + +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. 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. However this option is +still experimental, as during testing on Linux it actually made app +x2 slower - it's a bug in upstream CEF that was reported. See +[Issue #246](../../../issues/246) for more details. On Windows/Mac +external message pump should work good, but it wasn't yet tested +with CEF Python. + +On Windows for best performance a multi-threaded message loop should +be used, instead of cef.MessageLoopWork / external message pump. To do +so, set +ApplicationSettings.[multi_threaded_message_loop](../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, ...). +Note that when using multi-threaded message loop, CEF's UI thread +is no more application's main thread, and that makes it a bit harder +to correctly use CEF API. API docs explain on which threads a function +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). + + +## Settings + +ApplicationSettings, BrowserSettings, CommandLineSwitches, Chromium +Preferences (not implemented yet) ... + + +## Handlers + +... + + +## Javascript integration + +... + + +## Plugins + +... + + +## Helper functions + +GetApplicationPath... +GetModulePath... +others... + +## Build executable + +Examples for building an executable are yet to be created: + +* On Windows use py2exe ([#35](../../../issues/35)) + or pyinstaller ([#135](../../../issues/135)) +* On Mac use py2app or pyinstaller +* On Linux use pyinstaller or cx_freeze + + +## What's next? + +See more examples in the examples/ directory +See API docs in the api/ directory +Example usage of most of API is available in the unittests/ directory +See the Knowledge base document +Ask questions and report problems on the Forum From e6424fe5285685da90eb3a1ee532bac9eff09ddc Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 4 Feb 2017 13:54:55 +0100 Subject: [PATCH 02/16] Update to Chromium 55.0.2883.87 and CEF 3.2883.1554.gf984155 (v55.3). Changes: * Add is_main_frame param in RequestHandler._OnBeforePluginLoad * Crash reporting (crashpad) configurable using crash_reporter.cfg file, see src/include/cef_crash_util.h and upstream cef/tools/crash_server.py for more details. Upstream [Issue #1995](https://bitbucket.org/chromiumembedded/cef/issues/1995/migrate-from-breakpad-to-crashpad). * Fix CefRequestHandler::OnCertificateError callback trigger * Fix potential OSR crash on renderer process recreation --- api/RequestHandler.md | 27 ++-- api/WebPluginInfo.md | 2 +- patches/issue125.patch | 4 +- patches/issue231.patch | 10 +- .../request_context_handler.cpp | 9 +- src/client_handler/request_context_handler.h | 1 + src/handlers/request_handler.pyx | 2 + src/include/cef_context_menu_handler.h | 7 + src/include/cef_crash_util.h | 135 ++++++++++++++++++ src/include/cef_request_context_handler.h | 26 ++-- src/linux/setup/cefpython.h | 2 +- src/version/cef_version_linux.h | 20 +-- 12 files changed, 200 insertions(+), 45 deletions(-) create mode 100644 src/include/cef_crash_util.h diff --git a/api/RequestHandler.md b/api/RequestHandler.md index 667f3169..d8daf439 100644 --- a/api/RequestHandler.md +++ b/api/RequestHandler.md @@ -244,6 +244,7 @@ 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 | @@ -252,18 +253,20 @@ Description from upstream CEF: > Called on multiple browser process threads before a plugin instance is > loaded. |mime_type| is the mime type of the plugin that will be loaded. > |plugin_url| is the content URL that the plugin will load and may be empty. -> |top_origin_url| is the URL for the top-level frame that contains the -> plugin when loading a specific plugin instance or empty when building the -> initial list of enabled plugins for 'navigator.plugins' JavaScript state. -> |plugin_info| includes additional information about the plugin that will be -> loaded. |plugin_policy| is the recommended policy. Modify |plugin_policy| -> and return true to change the policy. Return false to use the recommended -> policy. The default plugin policy can be set at runtime using the -> `--plugin-policy=[allow|detect|block]` command-line flag. Decisions to mark -> a plugin as disabled by setting |plugin_policy| to PLUGIN_POLICY_DISABLED -> may be cached when |top_origin_url| is empty. To purge the plugin list -> cache and potentially trigger new calls to this method call -> CefRequestContext::PurgePluginListCache. +> |is_main_frame| will be true if the plugin is being loaded in the main +> (top-level) frame, |top_origin_url| is the URL for the top-level frame that +> contains the plugin when loading a specific plugin instance or empty when +> building the initial list of enabled plugins for 'navigator.plugins' +> JavaScript state. |plugin_info| includes additional information about the +> plugin that will be loaded. |plugin_policy| is the recommended policy. +> Modify |plugin_policy| and return true to change the policy. Return false +> to use the recommended policy. The default plugin policy can be set at +> runtime using the `--plugin-policy=[allow|detect|block]` command-line flag. +> Decisions to mark a plugin as disabled by setting |plugin_policy| to +> PLUGIN_POLICY_DISABLED may be cached when |top_origin_url| is empty. To +> purge the plugin list cache and potentially trigger new calls to this +> method call CefRequestContext::PurgePluginListCache. + Return True to block loading of the plugin. diff --git a/api/WebPluginInfo.md b/api/WebPluginInfo.md index b0038980..ad4396d3 100644 --- a/api/WebPluginInfo.md +++ b/api/WebPluginInfo.md @@ -3,7 +3,7 @@ # WebPluginInfo (object) -See [RequestHandler](RequestHandler.md).OnBeforePluginLoad(). +See also [RequestHandler](RequestHandler.md)._OnBeforePluginLoad(). Web Plugin API available in upstream CEF, but not yet exposed in CEF Python (see src/include/cef_web_plugin.h): diff --git a/patches/issue125.patch b/patches/issue125.patch index bd07a5ff..0f332367 100644 --- a/patches/issue125.patch +++ b/patches/issue125.patch @@ -1,8 +1,8 @@ diff --git http_cache_transaction.cc http_cache_transaction.cc -index 370862d..e63aecf 100644 +index 45f3db6aed3c..373b1e3c5b45 100644 --- http_cache_transaction.cc +++ http_cache_transaction.cc -@@ -2548,7 +2548,8 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { +@@ -2552,7 +2552,8 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { // blocking page is shown. An alternative would be to reverse-map the cert // status to a net error and replay the net error. if ((response_.headers->HasHeaderValue("cache-control", "no-store")) || diff --git a/patches/issue231.patch b/patches/issue231.patch index ac6f062e..80175a2f 100644 --- a/patches/issue231.patch +++ b/patches/issue231.patch @@ -1,5 +1,5 @@ diff --git include/capi/cef_path_util_capi.h include/capi/cef_path_util_capi.h -index 23befacc..de31b3d6 100644 +index de99ebd8..dd2f21c5 100644 --- include/capi/cef_path_util_capi.h +++ include/capi/cef_path_util_capi.h @@ -51,6 +51,16 @@ extern "C" { @@ -83,10 +83,10 @@ index 6a759309..ad620d7f 100644 + return PathService::Override(pref_key, file_path); +} diff --git libcef_dll/libcef_dll.cc libcef_dll/libcef_dll.cc -index 2e353f5b..f18e89b5 100644 +index cc1aafe5..bde00c09 100644 --- libcef_dll/libcef_dll.cc +++ libcef_dll/libcef_dll.cc -@@ -899,6 +899,23 @@ CEF_EXPORT int cef_get_path(cef_path_key_t key, cef_string_t* path) { +@@ -930,6 +930,23 @@ CEF_EXPORT int cef_get_path(cef_path_key_t key, cef_string_t* path) { return _retval; } @@ -111,10 +111,10 @@ index 2e353f5b..f18e89b5 100644 // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git libcef_dll/wrapper/libcef_dll_wrapper.cc libcef_dll/wrapper/libcef_dll_wrapper.cc -index 5e6ba876..be6e5af6 100644 +index 08ddbd83..42e7dca1 100644 --- libcef_dll/wrapper/libcef_dll_wrapper.cc +++ libcef_dll/wrapper/libcef_dll_wrapper.cc -@@ -820,6 +820,23 @@ CEF_GLOBAL bool CefGetPath(PathKey key, CefString& path) { +@@ -851,6 +851,23 @@ CEF_GLOBAL bool CefGetPath(PathKey key, CefString& path) { return _retval?true:false; } diff --git a/src/client_handler/request_context_handler.cpp b/src/client_handler/request_context_handler.cpp index f4a5dfaf..116d1fab 100644 --- a/src/client_handler/request_context_handler.cpp +++ b/src/client_handler/request_context_handler.cpp @@ -25,11 +25,16 @@ 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) { // Called on multiple threads - return RequestHandler_OnBeforePluginLoad(browser_, mime_type, plugin_url, - top_origin_url, plugin_info, + 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 b5aef99f..0b4837ec 100644 --- a/src/client_handler/request_context_handler.h +++ b/src/client_handler/request_context_handler.h @@ -32,6 +32,7 @@ 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/handlers/request_handler.pyx b/src/handlers/request_handler.pyx index 82af2ebb..74b3c23d 100644 --- a/src/handlers/request_handler.pyx +++ b/src/handlers/request_handler.pyx @@ -347,6 +347,7 @@ 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 @@ -364,6 +365,7 @@ 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/include/cef_context_menu_handler.h b/src/include/cef_context_menu_handler.h index ab450357..2bad463b 100644 --- a/src/include/cef_context_menu_handler.h +++ b/src/include/cef_context_menu_handler.h @@ -195,6 +195,13 @@ class CefContextMenuParams : public virtual CefBase { /*--cef()--*/ virtual bool HasImageContents() =0; + /// + // Returns the title text or the alt text if the context menu was invoked on + // an image. + /// + /*--cef()--*/ + virtual CefString GetTitleText() = 0; + /// // Returns the URL of the top level page that the context menu was invoked on. /// diff --git a/src/include/cef_crash_util.h b/src/include/cef_crash_util.h new file mode 100644 index 00000000..d74e792b --- /dev/null +++ b/src/include/cef_crash_util.h @@ -0,0 +1,135 @@ +// 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 +// 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. +// +// --------------------------------------------------------------------------- +// +// The contents of this file must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// + +#ifndef CEF_INCLUDE_CEF_CRASH_UTIL_H_ +#define CEF_INCLUDE_CEF_CRASH_UTIL_H_ +#pragma once + +/// +// Crash reporting is configured using an INI-style config file named +// "crash_reporter.cfg". On Windows and Linux this file must be placed next to +// the main application executable. On macOS this file must be placed in the +// top-level app bundle Resources directory (e.g. +// ".app/Contents/Resources"). File contents are as follows: +// +// # Comments start with a hash character and must be on their own line. +// +// [Config] +// ProductName= +// ProductVersion= +// AppName= +// ExternalHandler= +// ServerURL= +// RateLimitEnabled= +// MaxUploadsPerDay= +// MaxDatabaseSizeInMb= +// MaxDatabaseAgeInDays= +// +// [CrashKeys] +// my_key1= +// my_key2= +// +// Config section: +// +// If "ProductName" and/or "ProductVersion" are set then the specified values +// will be included in the crash dump metadata. On macOS if these values are set +// to empty then they will be retrieved from the Info.plist file using the +// "CFBundleName" and "CFBundleShortVersionString" keys respectively. +// +// If "AppName" is set on Windows then crash report information (metrics, +// database and dumps) will be stored locally on disk under the +// "C:\Users\[CurrentUser]\AppData\Local\[AppName]\User Data" folder. On other +// platforms the CefSettings.user_data_path value will be used. +// +// If "ExternalHandler" is set on Windows then the specified exe will be +// launched as the crashpad-handler instead of re-launching the main process +// exe. The value can be an absolute path or a path relative to the main exe +// directory. On Linux the CefSettings.browser_subprocess_path value will be +// used. On macOS the existing subprocess app bundle will be used. +// +// If "ServerURL" is set then crashes will be uploaded as a multi-part POST +// request to the specified URL. Otherwise, reports will only be stored locally +// on disk. +// +// If "RateLimitEnabled" is set to true then crash report uploads will be rate +// limited as follows: +// 1. If "MaxUploadsPerDay" is set to a positive value then at most the +// specified number of crashes will be uploaded in each 24 hour period. +// 2. If crash upload fails due to a network or server error then an +// incremental backoff delay up to a maximum of 24 hours will be applied for +// retries. +// 3. If a backoff delay is applied and "MaxUploadsPerDay" is > 1 then the +// "MaxUploadsPerDay" value will be reduced to 1 until the client is +// restarted. This helps to avoid an upload flood when the network or +// server error is resolved. +// Rate limiting is not supported on Linux. +// +// If "MaxDatabaseSizeInMb" is set to a positive value then crash report storage +// on disk will be limited to that size in megabytes. For example, on Windows +// each dump is about 600KB so a "MaxDatabaseSizeInMb" value of 20 equates to +// about 34 crash reports stored on disk. Not supported on Linux. +// +// If "MaxDatabaseAgeInDays" is set to a positive value then crash reports older +// than the specified age in days will be deleted. Not supported on Linux. +// +// CrashKeys section: +// +// Any number of crash keys can be specified for use by the application. Crash +// key values will be truncated based on the specified size (small = 63 bytes, +// medium = 252 bytes, large = 1008 bytes). The value of crash keys can be set +// from any thread or process using the CefSetCrashKeyValue function. These +// key/value pairs will be sent to the crash server along with the crash dump +// file. Medium and large values will be chunked for submission. For example, +// if your key is named "mykey" then the value will be broken into ordered +// chunks and submitted using keys named "mykey-1", "mykey-2", etc. +/// +/*--cef()--*/ +bool CefCrashReportingEnabled(); + +#include "include/cef_base.h" + +/// +// Sets or clears a specific key-value pair from the crash metadata. +/// +/*--cef()--*/ +void CefSetCrashKeyValue(const CefString& key, const CefString& value); + +#endif // CEF_INCLUDE_CEF_CRASH_UTIL_H_ diff --git a/src/include/cef_request_context_handler.h b/src/include/cef_request_context_handler.h index c2d3c7af..6996d9ed 100644 --- a/src/include/cef_request_context_handler.h +++ b/src/include/cef_request_context_handler.h @@ -64,22 +64,24 @@ class CefRequestContextHandler : public virtual CefBase { // Called on multiple browser process threads before a plugin instance is // loaded. |mime_type| is the mime type of the plugin that will be loaded. // |plugin_url| is the content URL that the plugin will load and may be empty. - // |top_origin_url| is the URL for the top-level frame that contains the - // plugin when loading a specific plugin instance or empty when building the - // initial list of enabled plugins for 'navigator.plugins' JavaScript state. - // |plugin_info| includes additional information about the plugin that will be - // loaded. |plugin_policy| is the recommended policy. Modify |plugin_policy| - // and return true to change the policy. Return false to use the recommended - // policy. The default plugin policy can be set at runtime using the - // `--plugin-policy=[allow|detect|block]` command-line flag. Decisions to mark - // a plugin as disabled by setting |plugin_policy| to PLUGIN_POLICY_DISABLED - // may be cached when |top_origin_url| is empty. To purge the plugin list - // cache and potentially trigger new calls to this method call - // CefRequestContext::PurgePluginListCache. + // |is_main_frame| will be true if the plugin is being loaded in the main + // (top-level) frame, |top_origin_url| is the URL for the top-level frame that + // contains the plugin when loading a specific plugin instance or empty when + // building the initial list of enabled plugins for 'navigator.plugins' + // JavaScript state. |plugin_info| includes additional information about the + // plugin that will be loaded. |plugin_policy| is the recommended policy. + // Modify |plugin_policy| and return true to change the policy. Return false + // to use the recommended policy. The default plugin policy can be set at + // runtime using the `--plugin-policy=[allow|detect|block]` command-line flag. + // Decisions to mark a plugin as disabled by setting |plugin_policy| to + // PLUGIN_POLICY_DISABLED may be cached when |top_origin_url| is empty. To + // purge the plugin list cache and potentially trigger new calls to this + // method call CefRequestContext::PurgePluginListCache. /// /*--cef(optional_param=plugin_url,optional_param=top_origin_url)--*/ 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) { diff --git a/src/linux/setup/cefpython.h b/src/linux/setup/cefpython.h index cb13a910..2bdae432 100644 --- a/src/linux/setup/cefpython.h +++ b/src/linux/setup/cefpython.h @@ -79,7 +79,7 @@ __PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_GetAuthCredentials(CefRefPtr , CefString const &, int64, CefRefPtr ); __PYX_EXTERN_C DL_IMPORT(CefRefPtr ) RequestHandler_GetCookieManager(CefRefPtr , CefString const &); __PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnProtocolExecution(CefRefPtr , CefString const &, bool &); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforePluginLoad(CefRefPtr , CefString const &, CefString const &, CefString const &, CefRefPtr , cef_plugin_policy_t *); +__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforePluginLoad(CefRefPtr , CefString const &, CefString const &, bool, CefString const &, CefRefPtr , cef_plugin_policy_t *); __PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnCertificateError(int, CefString const &, CefRefPtr ); __PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnRendererProcessTerminated(CefRefPtr , cef_termination_status_t); __PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnPluginCrashed(CefRefPtr , CefString const &); diff --git a/src/version/cef_version_linux.h b/src/version/cef_version_linux.h index b1cc4dc7..39126c00 100644 --- a/src/version/cef_version_linux.h +++ b/src/version/cef_version_linux.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved. +// 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 @@ -35,16 +35,16 @@ #ifndef CEF_INCLUDE_CEF_VERSION_H_ #define CEF_INCLUDE_CEF_VERSION_H_ -#define CEF_VERSION "3.2883.1539.gd7f087e" +#define CEF_VERSION "3.2883.1554.gf984155" #define CEF_VERSION_MAJOR 3 -#define CEF_COMMIT_NUMBER 1539 -#define CEF_COMMIT_HASH "d7f087e76634af8ceffdef28c794b7310bb507e5" -#define COPYRIGHT_YEAR 2016 +#define CEF_COMMIT_NUMBER 1554 +#define CEF_COMMIT_HASH "f984155b3f0ad80833742d17cdab2065f3ec75ce" +#define COPYRIGHT_YEAR 2017 #define CHROME_VERSION_MAJOR 55 #define CHROME_VERSION_MINOR 0 #define CHROME_VERSION_BUILD 2883 -#define CHROME_VERSION_PATCH 59 +#define CHROME_VERSION_PATCH 87 #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 "95aeb3fed6b2c813abe66fda168b5176cc520f6e" +#define CEF_API_HASH_UNIVERSAL "87b7eefcb86c87b28f86bfd7919f7d7a6cffc0d8" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "7ece9a94eb706c0814b34b3bb4dc227edf46fdec" +#define CEF_API_HASH_PLATFORM "00823905486d7b7222da5654fe35d2d15f65543a" #elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "6361eb5f8e0440c4e103f1c95373eef83c16f242" +#define CEF_API_HASH_PLATFORM "f0180f006643782254250f34e858b98110a40e6e" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "37b7bf683bd6d8d858acbaba00f30796ca530b2a" +#define CEF_API_HASH_PLATFORM "14b19454a4231fa10a77b8955954dc95f073af6b" #endif // Returns CEF version information for the libcef library. The |entry| From d4217b23c932f4f69e8ec0069d8316287938d644 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 4 Feb 2017 19:13:18 +0100 Subject: [PATCH 03/16] Fix context menu items when navigation option is set to False (#222) --- src/client_handler/context_menu_handler.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client_handler/context_menu_handler.cpp b/src/client_handler/context_menu_handler.cpp index 085fe1af..4d5a24c8 100644 --- a/src/client_handler/context_menu_handler.cpp +++ b/src/client_handler/context_menu_handler.cpp @@ -35,11 +35,14 @@ void ContextMenuHandler::OnBeforeContextMenu( model->Clear(); return; } - if (!navigation) { + if (!navigation && model->IsVisible(MENU_ID_BACK) + && model->IsVisible(MENU_ID_FORWARD)) { model->Remove(MENU_ID_BACK); model->Remove(MENU_ID_FORWARD); // Remove separator - model->RemoveAt(0); + if (model->GetTypeAt(0) == MENUITEMTYPE_SEPARATOR) { + model->RemoveAt(0); + } } if (!print) { model->Remove(MENU_ID_PRINT); From 5fefeeca95c3b650e011ba4e33e13754ad532420 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 11 Feb 2017 20:34:31 +0100 Subject: [PATCH 04/16] Rename wx subpackage dir, so that "import wx" works correctly in PyCharm --- src/{wx => cefpython3.wx}/README.txt | 0 src/{wx => cefpython3.wx}/__init__.py | 0 src/{wx => cefpython3.wx}/chromectrl.py | 0 src/{wx => cefpython3.wx}/examples/back.png | Bin src/{wx => cefpython3.wx}/examples/forward.png | Bin src/{wx => cefpython3.wx}/examples/reload_page.png | Bin src/{wx => cefpython3.wx}/examples/sample1.html | 0 src/{wx => cefpython3.wx}/examples/sample1.py | 0 src/{wx => cefpython3.wx}/examples/sample2.py | 0 src/{wx => cefpython3.wx}/examples/sample3.py | 0 src/{wx => cefpython3.wx}/images/back.png | Bin src/{wx => cefpython3.wx}/images/forward.png | Bin src/{wx => cefpython3.wx}/images/reload_page.png | Bin src/{wx => cefpython3.wx}/utils.py | 0 src/linux/installer/make-setup.py | 3 ++- 15 files changed, 2 insertions(+), 1 deletion(-) rename src/{wx => cefpython3.wx}/README.txt (100%) rename src/{wx => cefpython3.wx}/__init__.py (100%) rename src/{wx => cefpython3.wx}/chromectrl.py (100%) rename src/{wx => cefpython3.wx}/examples/back.png (100%) rename src/{wx => cefpython3.wx}/examples/forward.png (100%) rename src/{wx => cefpython3.wx}/examples/reload_page.png (100%) rename src/{wx => cefpython3.wx}/examples/sample1.html (100%) rename src/{wx => cefpython3.wx}/examples/sample1.py (100%) rename src/{wx => cefpython3.wx}/examples/sample2.py (100%) rename src/{wx => cefpython3.wx}/examples/sample3.py (100%) rename src/{wx => cefpython3.wx}/images/back.png (100%) rename src/{wx => cefpython3.wx}/images/forward.png (100%) rename src/{wx => cefpython3.wx}/images/reload_page.png (100%) rename src/{wx => cefpython3.wx}/utils.py (100%) diff --git a/src/wx/README.txt b/src/cefpython3.wx/README.txt similarity index 100% rename from src/wx/README.txt rename to src/cefpython3.wx/README.txt diff --git a/src/wx/__init__.py b/src/cefpython3.wx/__init__.py similarity index 100% rename from src/wx/__init__.py rename to src/cefpython3.wx/__init__.py diff --git a/src/wx/chromectrl.py b/src/cefpython3.wx/chromectrl.py similarity index 100% rename from src/wx/chromectrl.py rename to src/cefpython3.wx/chromectrl.py diff --git a/src/wx/examples/back.png b/src/cefpython3.wx/examples/back.png similarity index 100% rename from src/wx/examples/back.png rename to src/cefpython3.wx/examples/back.png diff --git a/src/wx/examples/forward.png b/src/cefpython3.wx/examples/forward.png similarity index 100% rename from src/wx/examples/forward.png rename to src/cefpython3.wx/examples/forward.png diff --git a/src/wx/examples/reload_page.png b/src/cefpython3.wx/examples/reload_page.png similarity index 100% rename from src/wx/examples/reload_page.png rename to src/cefpython3.wx/examples/reload_page.png diff --git a/src/wx/examples/sample1.html b/src/cefpython3.wx/examples/sample1.html similarity index 100% rename from src/wx/examples/sample1.html rename to src/cefpython3.wx/examples/sample1.html diff --git a/src/wx/examples/sample1.py b/src/cefpython3.wx/examples/sample1.py similarity index 100% rename from src/wx/examples/sample1.py rename to src/cefpython3.wx/examples/sample1.py diff --git a/src/wx/examples/sample2.py b/src/cefpython3.wx/examples/sample2.py similarity index 100% rename from src/wx/examples/sample2.py rename to src/cefpython3.wx/examples/sample2.py diff --git a/src/wx/examples/sample3.py b/src/cefpython3.wx/examples/sample3.py similarity index 100% rename from src/wx/examples/sample3.py rename to src/cefpython3.wx/examples/sample3.py diff --git a/src/wx/images/back.png b/src/cefpython3.wx/images/back.png similarity index 100% rename from src/wx/images/back.png rename to src/cefpython3.wx/images/back.png diff --git a/src/wx/images/forward.png b/src/cefpython3.wx/images/forward.png similarity index 100% rename from src/wx/images/forward.png rename to src/cefpython3.wx/images/forward.png diff --git a/src/wx/images/reload_page.png b/src/cefpython3.wx/images/reload_page.png similarity index 100% rename from src/wx/images/reload_page.png rename to src/cefpython3.wx/images/reload_page.png diff --git a/src/wx/utils.py b/src/cefpython3.wx/utils.py similarity index 100% rename from src/wx/utils.py rename to src/cefpython3.wx/utils.py diff --git a/src/linux/installer/make-setup.py b/src/linux/installer/make-setup.py index a7dd2e65..f2700e57 100644 --- a/src/linux/installer/make-setup.py +++ b/src/linux/installer/make-setup.py @@ -190,7 +190,8 @@ def main(): print("Copying wx/ to package dir") wx_subpackage_dir = os.path.abspath(INSTALLER_DIR+"/../../wx/") - ret = os.system("cp -rf "+wx_subpackage_dir+"/* "+package_dir+"/wx/") + ret = os.system("cp -rf " + wx_subpackage_dir + "/* " + package_dir + + "/cefpython3.wx/") assert ret == 0 # print("Moving wx examples from wx/examples to examples/wx") From a6c0a0a12cb1c59cfe2757e59b0a68768700e2b2 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 11 Feb 2017 22:39:42 +0100 Subject: [PATCH 05/16] Create wxpython.py example (#269) --- examples/resources/wxpython.png | Bin 0 -> 11528 bytes examples/wxpython.py | 183 ++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 examples/resources/wxpython.png create mode 100644 examples/wxpython.py diff --git a/examples/resources/wxpython.png b/examples/resources/wxpython.png new file mode 100644 index 0000000000000000000000000000000000000000..73aa3e93582aef341e3e1dac9a61bb2a2fad4482 GIT binary patch literal 11528 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 literal 0 HcmV?d00001 diff --git a/examples/wxpython.py b/examples/wxpython.py new file mode 100644 index 00000000..042b3a78 --- /dev/null +++ b/examples/wxpython.py @@ -0,0 +1,183 @@ +# Example of embedding CEF Python browser using wxPython library. +# This example has a top menu and a browser widget without navigation bar. + +# To install wxPython on Linux type "sudo apt-get install python-wxtools". + +# Tested with wxPython 2.8 on Linux, wxPython 3.0 on Windows/Mac +# and CEF Python v55.3. + +import wx +from cefpython3 import cefpython as cef +import platform +import sys +import os + +# Constants +LINUX = (platform.system() == "Linux") +WINDOWS = (platform.system() == "Windows") +WIDTH = 800 +HEIGHT = 600 + + +def main(): + check_versions() + sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error + cef.Initialize() + app = CefApp(False) + app.MainLoop() + del app # Must destroy before calling Shutdown + cef.Shutdown() + + +def check_versions(): + print("[wxpython.py] CEF Python {ver}".format(ver=cef.__version__)) + print("[wxpython.py] Python {ver}".format(ver=sys.version[:6])) + print("[wxpython.py] wx {ver}".format(ver=wx.version())) + # CEF Python version requirement + assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" + + +class MainFrame(wx.Frame): + + def __init__(self): + wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, + title='wxPython example', size=(WIDTH, HEIGHT)) + self.browser = None + + self.setup_icon() + self.create_menu() + self.Bind(wx.EVT_CLOSE, self.OnClose) + + # Set wx.WANTS_CHARS style for the keyboard to work. + # This style also needs to be set for all parent controls. + self.browser_panel = wx.Panel(self, style=wx.WANTS_CHARS) + self.browser_panel.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) + self.browser_panel.Bind(wx.EVT_SIZE, self.OnSize) + + # Must show so that handle is available when embedding browser + self.Show() + self.embed_browser() + + def setup_icon(self): + icon_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), + "resources", "wxpython.png") + if os.path.exists(icon_file): + icon = wx.IconFromBitmap(wx.Bitmap(icon_file, wx.BITMAP_TYPE_PNG)) + self.SetIcon(icon) + + def create_menu(self): + filemenu = wx.Menu() + filemenu.Append(1, "Open") + exit_ = filemenu.Append(2, "Exit") + self.Bind(wx.EVT_MENU, self.OnClose, exit_) + aboutmenu = wx.Menu() + aboutmenu.Append(1, "CEF Python") + menubar = wx.MenuBar() + menubar.Append(filemenu, "&File") + menubar.Append(aboutmenu, "&About") + self.SetMenuBar(menubar) + + def embed_browser(self): + window_info = cef.WindowInfo() + window_info.SetAsChild(self.browser_panel.GetHandle()) + self.browser = cef.CreateBrowserSync(window_info, + url="https://www.google.com/") + self.browser.SetClientHandler(FocusHandler()) + + def OnSetFocus(self, _): + if not self.brower: + return + if WINDOWS: + # noinspection PyUnresolvedReferences + cef.WindowUtils.OnSetFocus(self.GetHandleForBrowser(), 0, 0, 0) + self.browser.SetFocus(True) + + def OnSize(self, _): + if not self.browser: + return + if WINDOWS: + # noinspection PyUnresolvedReferences + cef.WindowUtils.OnSize(self.GetHandleForBrowser(), 0, 0, 0) + elif LINUX: + (x, y) = (0, 0) + (width, height) = self.browser_panel.GetSizeTuple() + # noinspection PyUnresolvedReferences + self.browser.SetBounds(x, y, width, height) + + def OnClose(self, event): + # In cefpython3.wx.chromectrl example calling browser.CloseBrowser() + # and/or self.Destroy() in OnClose is causing crashes when + # embedding multiple browser tabs. The solution is to call only + # browser.ParentWindowWillClose. Behavior of this example + # seems different as it extends wx.Frame, while ChromeWindow + # from chromectrl extends wx.Window. Calling CloseBrowser + # and Destroy does not cause crashes, but is not recommended. + # Call ParentWindowWillClose and event.Skip() instead. See + # also Issue #107: https://github.com/cztomczak/cefpython/issues/107 + self.browser.ParentWindowWillClose() + event.Skip() + + # Clear all browser references for CEF to shutdown cleanly + del self.browser + + +class FocusHandler(object): + + def __init__(self): + pass + + def OnTakeFocus(self, **kwargs): + # print("[wxpython.py] FocusHandler.OnTakeFocus, next={next}" + # .format(next=kwargs["next_component"]])) + pass + + def OnSetFocus(self, **kwargs): + # source_enum = {cef.FOCUS_SOURCE_NAVIGATION: "navigation", + # cef.FOCUS_SOURCE_SYSTEM: "system"} + # print("[wxpython.py] FocusHandler.OnSetFocus, source={source}" + # .format(source=source_enum[kwargs["source"]])) + # return False + pass + + def OnGotFocus(self, browser, **_): + # Temporary fix for focus issues on Linux (Issue #284). + # If this is not applied then when switching to another + # window (alt+tab) and then back to this example, keyboard + # focus becomes broken, you can't type anything, even + # though a type cursor blinks in web view. + print("[wxpython.py] FocusHandler.OnGotFocus:" + " keyboard focus fix (#284)") + browser.SetFocus(True) + + +class CefApp(wx.App): + + def __init__(self, redirect): + self.timer = None + self.timer_id = 1 + super(CefApp, self).__init__(redirect=redirect) + + def OnInit(self): + self.create_timer() + frame = MainFrame() + self.SetTopWindow(frame) + frame.Show() + return True + + def create_timer(self): + # See also "Making a render loop": + # http://wiki.wxwidgets.org/Making_a_render_loop + # Another way would be to use EVT_IDLE in MainFrame. + self.timer = wx.Timer(self, self.timer_id) + self.timer.Start(10) # 10ms + wx.EVT_TIMER(self, self.timer_id, self.on_timer) + + def on_timer(self, _): + cef.MessageLoopWork() + + def OnExit(self): + self.timer.Stop() + + +if __name__ == '__main__': + main() From be10dba412335aa2f73e83080d0d871e28bd831f Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 11 Feb 2017 22:49:02 +0100 Subject: [PATCH 06/16] Add high dpi support in wxpython example (#269) --- examples/wxpython.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/wxpython.py b/examples/wxpython.py index 042b3a78..d4a0a5e1 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -4,7 +4,7 @@ # To install wxPython on Linux type "sudo apt-get install python-wxtools". # Tested with wxPython 2.8 on Linux, wxPython 3.0 on Windows/Mac -# and CEF Python v55.3. +# and CEF Python v55.3+. import wx from cefpython3 import cefpython as cef @@ -22,7 +22,15 @@ def main(): check_versions() sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error - cef.Initialize() + settings = {} + if WINDOWS: + # High DPI support + settings["auto_zooming"] = "system_dpi" + # Embed DPI awareness xml manifest inside .exe (recommended, + # most reliable) or call the SetProcessDpiAware function. + # noinspection PyUnresolvedReferences + cef.DpiAware.SetProcessDpiAware() + cef.Initialize(settings=settings) app = CefApp(False) app.MainLoop() del app # Must destroy before calling Shutdown From 81a85587af589d591983cfc29f580c8094fd6b6e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sun, 12 Feb 2017 09:08:01 +0100 Subject: [PATCH 07/16] Expose spell checking support API in the Browser object (#274). See also Issue #274 to see command line switches related to spell checking support. Run wxpython example after compile. Fix linux make-setup.py. Update tools/toc.py. --- api/API-index.md | 2 ++ api/Browser.md | 23 +++++++++++++++++++++++ src/browser.pyx | 10 ++++++++++ src/extern/cef/cef_browser.pxd | 4 ++++ src/linux/compile.py | 1 + src/linux/installer/make-setup.py | 4 ++-- tools/toc.py | 4 ++++ 7 files changed, 46 insertions(+), 2 deletions(-) diff --git a/api/API-index.md b/api/API-index.md index ee8882a4..94b887a9 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -35,6 +35,7 @@ * [user_data_path](ApplicationSettings.md#user_data_path) * [windowless_rendering_enabled](ApplicationSettings.md#windowless_rendering_enabled) * [Browser (object)](Browser.md) + * [AddWordToDictionary](Browser.md#addwordtodictionary) * [CanGoBack](Browser.md#cangoback) * [CanGoForward](Browser.md#cangoforward) * [CloseBrowser](Browser.md#closebrowser) @@ -84,6 +85,7 @@ * [ParentWindowWillClose](Browser.md#parentwindowwillclose) * [Reload](Browser.md#reload) * [ReloadIgnoreCache](Browser.md#reloadignorecache) + * [ReplaceMisspelling](Browser.md#replacemisspelling) * [SetBounds](Browser.md#setbounds) * [SendKeyEvent](Browser.md#sendkeyevent) * [SendMouseClickEvent](Browser.md#sendmouseclickevent) diff --git a/api/Browser.md b/api/Browser.md index 2f02c60f..46135cc0 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -12,6 +12,7 @@ just assign a None value to a "browser" variable. Table of contents: * [Notes](#notes) * [Methods](#methods) + * [AddWordToDictionary](#addwordtodictionary) * [CanGoBack](#cangoback) * [CanGoForward](#cangoforward) * [CloseBrowser](#closebrowser) @@ -61,6 +62,7 @@ Table of contents: * [ParentWindowWillClose](#parentwindowwillclose) * [Reload](#reload) * [ReloadIgnoreCache](#reloadignorecache) + * [ReplaceMisspelling](#replacemisspelling) * [SetBounds](#setbounds) * [SendKeyEvent](#sendkeyevent) * [SendMouseClickEvent](#sendmouseclickevent) @@ -99,6 +101,16 @@ Methods available in upstream CEF which were not yet exposed in CEF Python ## Methods +### AddWordToDictionary + +| Parameter | Type | +| --- | --- | +| word | string | +| __Return__ | void | + +Add the specified |word| to the spelling dictionary. + + ### CanGoBack | | | @@ -658,6 +670,17 @@ Reload the current page. Reload the current page ignoring any cached data. +### ReplaceMisspelling + +| Parameter | Type | +| --- | --- | +| word | string | +| __Return__ | void | + +If a misspelled word is currently selected in an editable node calling +this method will replace it with the specified |word|. + + ### SetBounds | Parameter | Type | diff --git a/src/browser.pyx b/src/browser.pyx index dba50285..83b0d417 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -251,6 +251,11 @@ cdef class PyBrowser: # CEF API. # -------------- + cpdef py_void AddWordToDictionary(self, py_string word): + cdef CefString cef_word + PyToCefString(word, cef_word) + self.GetCefBrowserHost().get().AddWordToDictionary(cef_word) + cpdef py_bool CanGoBack(self): return self.GetCefBrowser().get().CanGoBack() @@ -404,6 +409,11 @@ cdef class PyBrowser: cpdef py_void ReloadIgnoreCache(self): self.GetCefBrowser().get().ReloadIgnoreCache() + cpdef py_void ReplaceMisspelling(self, py_string word): + cdef CefString cef_word + PyToCefString(word, cef_word) + self.GetCefBrowserHost().get().ReplaceMisspelling(cef_word) + cpdef py_void SetBounds(self, int x, int y, int width, int height): if platform.system() == "Linux": x11.SetX11WindowBounds(self.GetCefBrowser(), x, y, width, height) diff --git a/src/extern/cef/cef_browser.pxd b/src/extern/cef/cef_browser.pxd index 26b2b314..663fcb2b 100644 --- a/src/extern/cef/cef_browser.pxd +++ b/src/extern/cef/cef_browser.pxd @@ -79,6 +79,10 @@ cdef extern from "include/cef_browser.h": void DragSourceEndedAt(int x, int y, cef_types.cef_drag_operations_mask_t op) void DragSourceSystemDragEnded() + # Spell checking + void ReplaceMisspelling(const CefString& word) + void AddWordToDictionary(const CefString& word) + cdef cppclass CefBrowser: diff --git a/src/linux/compile.py b/src/linux/compile.py index 125c5dda..623c3b07 100644 --- a/src/linux/compile.py +++ b/src/linux/compile.py @@ -303,6 +303,7 @@ def check_cython_version(): run_examples = " && {python} ../src/linux/binaries_64bit/kivy_.py" else: run_examples = (" && {python} hello_world.py" + " && {python} wxpython.py" " && {python} gtk2.py" " && {python} gtk2.py --message-loop-timer" # " && {python} gtk3.py" diff --git a/src/linux/installer/make-setup.py b/src/linux/installer/make-setup.py index f2700e57..bd016cb1 100644 --- a/src/linux/installer/make-setup.py +++ b/src/linux/installer/make-setup.py @@ -189,9 +189,9 @@ def main(): # assert ret == 0 print("Copying wx/ to package dir") - wx_subpackage_dir = os.path.abspath(INSTALLER_DIR+"/../../wx/") + wx_subpackage_dir = os.path.abspath(INSTALLER_DIR+"/../../cefpython3.wx/") ret = os.system("cp -rf " + wx_subpackage_dir + "/* " + package_dir - + "/cefpython3.wx/") + + "/wx/") assert ret == 0 # print("Moving wx examples from wx/examples to examples/wx") diff --git a/tools/toc.py b/tools/toc.py index a0791e0b..3e4d698c 100644 --- a/tools/toc.py +++ b/tools/toc.py @@ -14,9 +14,13 @@ import re import glob +API_DIR = os.path.join(os.path.dirname(__file__), "..", "api") + def main(): """Main entry point.""" + if len(sys.argv) == 1: + sys.argv.append(API_DIR) if (len(sys.argv) == 1 or "-h" in sys.argv or "--help" in sys.argv or From 749157670c68bceee01f115f39dbd522cf08f987 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Feb 2017 20:41:21 +0100 Subject: [PATCH 08/16] Build latest v55 on Windows (#294)... There is still issue with building vcproj files to be resolved. Currently supported on Windows is only Python 2.7 32-bit. Remove patch deps on Windows and Mac platforms to facilitate building (#300, #231, #251). Create tools/build.py and tools/build_module.py (#210). Cleanup in directories due to new build tools. Update tools/automate.py. DragData methods become Linux-only: GetImage(), HasImage(). This functionality will be available on all platforms when patch is merged into upstream (#251). --- api/DragData.md | 4 + patches/patch.py | 25 +- src/browser.pyx | 10 +- src/cefpython.pyx | 19 +- .../client_handler_py27_32bit.vcproj | 168 - .../client_handler_py27_win32.vcproj | 110 + ...precated_client_handler_py27_64bit.vcproj} | 0 ...precated_client_handler_py34_32bit.vcproj} | 0 src/common/cefpython_public_api.h | 9 +- src/compile_time_constants.pxi | 2 +- src/cpp_utils/cpp_utils_32bit.vcproj | 114 - src/cpp_utils/cpp_utils_win32.vcproj | 75 + ...proj => deprecated_cpp_utils_64bit.vcproj} | 0 src/drag_data.pyx | 22 +- src/extern/cef/cef_platform.pxd | 3 +- src/image.pyx | 3 +- src/include/internal/cef_types_wrappers.h | 2 +- src/linux/.gitignore | 12 - src/linux/CEF-GTK-patch.txt | 56 - src/linux/binaries_32bit/README.txt | 131 - src/linux/binaries_32bit/example.html | 58 - .../kivy-select-boxes/kivy-selectBox.css | 186 - .../kivy-select-boxes/kivy-selectBox.js | 10420 ---------------- .../kivy-select-boxes/kivy-test.html | 131 - .../kivy-select-boxes/readme.md | 156 - src/linux/binaries_32bit/kivy_.py | 677 - src/linux/binaries_32bit/pygtk_.py | 199 - src/linux/binaries_32bit/pyqt.py | 238 - src/linux/binaries_32bit/wxpython-response.py | 452 - src/linux/binaries_32bit/wxpython.html | 707 -- src/linux/binaries_32bit/wxpython.py | 782 -- src/linux/compile.py | 4 +- src/linux/rebuild.sh | 1 - src/linux/wxpython.sh | 2 - src/mac/.gitignore | 5 - src/mac/binaries_64bit/prism.css | 130 - src/mac/binaries_64bit/prism.js | 5 - src/mac/binaries_64bit/wxpython.html | 708 -- src/mac/binaries_64bit/wxpython.py | 851 -- src/mac/compile.py | 181 - .../deprecated_64bit}/prism.css | 0 .../deprecated_64bit}/prism.js | 0 .../wxpython.html | 0 .../wxpython.py | 0 src/mac/installer/make-setup.py | 4 +- src/mac/mac32.sh | 14 - src/mac/setup/.gitignore | 6 - src/mac/setup/cefpython.h | 92 - src/mac/setup/fix_pyx_files.py | 109 - src/mac/setup/lib_32bit/README | 1 - src/mac/setup/lib_64bit/README | 1 - src/mac/setup/setup.py | 110 - ...recated_libcefpythonapp_py27_64bit.vcproj} | 0 ...recated_libcefpythonapp_py34_32bit.vcproj} | 0 ...roj => deprecated_subprocess_64bit.vcproj} | 0 .../libcefpythonapp_py27_32bit.vcproj | 108 +- src/subprocess/subprocess_32bit.vcproj | 261 +- src/version/cef_version_win.h | 102 + src/window_info.pyx | 2 +- src/window_utils_win.pyx | 2 +- src/windows/.gitignore | 8 - src/windows/binaries_32bit/prism.css | 130 - src/windows/binaries_32bit/prism.js | 5 - src/windows/binaries_64bit/LICENSE.txt | 39 - src/windows/binaries_64bit/README.txt | 119 - src/windows/binaries_64bit/cefwindow.py | 205 - src/windows/binaries_64bit/example.html | 58 - src/windows/binaries_64bit/icon.ico | Bin 198275 -> 0 bytes src/windows/binaries_64bit/prism.css | 130 - src/windows/binaries_64bit/prism.js | 5 - src/windows/binaries_64bit/pygtk_.py | 196 - src/windows/binaries_64bit/pyqt.py | 190 - src/windows/binaries_64bit/pyside.py | 188 - src/windows/binaries_64bit/pywin32.py | 151 - src/windows/binaries_64bit/smoke.css | 110 - src/windows/binaries_64bit/smoke.min.js | 1 - src/windows/binaries_64bit/wxpython.html | 743 -- src/windows/binaries_64bit/wxpython.py | 906 -- src/windows/cefpython.rc | 31 - src/windows/compile.cmd | 1 - .../LICENSE.txt | 0 .../README.txt | 0 .../cefwindow.py | 0 .../example.html | 0 .../icon.ico | Bin .../deprecated_32bit}/prism.css | 0 .../deprecated_32bit}/prism.js | 0 .../pygtk_.py | 0 .../pyqt.py | 0 .../pyside.py | 0 .../pywin32.py | 0 .../smoke.css | 0 .../smoke.min.js | 0 .../wxpython.html | 0 .../wxpython.py | 0 src/windows/setup/.gitignore | 3 - src/windows/setup/cefpython.h | 94 - src/windows/setup/compile_rc.py | 91 - src/windows/setup/delete_pyx_files.bat | 2 - src/windows/setup/fix_cefpython_h.py | 29 - src/windows/setup/fix_pyx_files.py | 107 - src/windows/setup/lib_32bit/README | 1 - src/windows/setup/lib_64bit/README | 1 - src/windows/setup/setup.py | 115 - src/windows/wxpython.bat | 3 - tools/automate.py | 360 +- tools/build.py | 638 + tools/build_module.py | 299 + tools/common.py | 91 + 109 files changed, 1711 insertions(+), 20809 deletions(-) delete mode 100644 src/client_handler/client_handler_py27_32bit.vcproj create mode 100644 src/client_handler/client_handler_py27_win32.vcproj rename src/client_handler/{client_handler_py27_64bit.vcproj => deprecated_client_handler_py27_64bit.vcproj} (100%) rename src/client_handler/{client_handler_py34_32bit.vcproj => deprecated_client_handler_py34_32bit.vcproj} (100%) delete mode 100644 src/cpp_utils/cpp_utils_32bit.vcproj create mode 100644 src/cpp_utils/cpp_utils_win32.vcproj rename src/cpp_utils/{cpp_utils_64bit.vcproj => deprecated_cpp_utils_64bit.vcproj} (100%) delete mode 100644 src/linux/.gitignore delete mode 100644 src/linux/CEF-GTK-patch.txt delete mode 100644 src/linux/binaries_32bit/README.txt delete mode 100644 src/linux/binaries_32bit/example.html delete mode 100644 src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.css delete mode 100644 src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.js delete mode 100644 src/linux/binaries_32bit/kivy-select-boxes/kivy-test.html delete mode 100644 src/linux/binaries_32bit/kivy-select-boxes/readme.md delete mode 100644 src/linux/binaries_32bit/kivy_.py delete mode 100644 src/linux/binaries_32bit/pygtk_.py delete mode 100644 src/linux/binaries_32bit/pyqt.py delete mode 100644 src/linux/binaries_32bit/wxpython-response.py delete mode 100644 src/linux/binaries_32bit/wxpython.html delete mode 100644 src/linux/binaries_32bit/wxpython.py delete mode 100644 src/linux/rebuild.sh delete mode 100644 src/linux/wxpython.sh delete mode 100644 src/mac/.gitignore delete mode 100644 src/mac/binaries_64bit/prism.css delete mode 100644 src/mac/binaries_64bit/prism.js delete mode 100644 src/mac/binaries_64bit/wxpython.html delete mode 100644 src/mac/binaries_64bit/wxpython.py delete mode 100644 src/mac/compile.py rename src/{linux/binaries_32bit => mac/deprecated_64bit}/prism.css (100%) rename src/{linux/binaries_32bit => mac/deprecated_64bit}/prism.js (100%) rename src/mac/{binaries_32bit => deprecated_64bit}/wxpython.html (100%) rename src/mac/{binaries_32bit => deprecated_64bit}/wxpython.py (100%) delete mode 100644 src/mac/mac32.sh delete mode 100644 src/mac/setup/.gitignore delete mode 100644 src/mac/setup/cefpython.h delete mode 100644 src/mac/setup/fix_pyx_files.py delete mode 100644 src/mac/setup/lib_32bit/README delete mode 100644 src/mac/setup/lib_64bit/README delete mode 100644 src/mac/setup/setup.py rename src/subprocess/{libcefpythonapp_py27_64bit.vcproj => deprecated_libcefpythonapp_py27_64bit.vcproj} (100%) rename src/subprocess/{libcefpythonapp_py34_32bit.vcproj => deprecated_libcefpythonapp_py34_32bit.vcproj} (100%) rename src/subprocess/{subprocess_64bit.vcproj => deprecated_subprocess_64bit.vcproj} (100%) create mode 100644 src/version/cef_version_win.h delete mode 100644 src/windows/.gitignore delete mode 100644 src/windows/binaries_32bit/prism.css delete mode 100644 src/windows/binaries_32bit/prism.js delete mode 100644 src/windows/binaries_64bit/LICENSE.txt delete mode 100644 src/windows/binaries_64bit/README.txt delete mode 100644 src/windows/binaries_64bit/cefwindow.py delete mode 100644 src/windows/binaries_64bit/example.html delete mode 100644 src/windows/binaries_64bit/icon.ico delete mode 100644 src/windows/binaries_64bit/prism.css delete mode 100644 src/windows/binaries_64bit/prism.js delete mode 100644 src/windows/binaries_64bit/pygtk_.py delete mode 100644 src/windows/binaries_64bit/pyqt.py delete mode 100644 src/windows/binaries_64bit/pyside.py delete mode 100644 src/windows/binaries_64bit/pywin32.py delete mode 100644 src/windows/binaries_64bit/smoke.css delete mode 100644 src/windows/binaries_64bit/smoke.min.js delete mode 100644 src/windows/binaries_64bit/wxpython.html delete mode 100644 src/windows/binaries_64bit/wxpython.py delete mode 100644 src/windows/cefpython.rc delete mode 100644 src/windows/compile.cmd rename src/windows/{binaries_32bit => deprecated_32bit}/LICENSE.txt (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/README.txt (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/cefwindow.py (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/example.html (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/icon.ico (100%) rename src/{mac/binaries_32bit => windows/deprecated_32bit}/prism.css (100%) rename src/{mac/binaries_32bit => windows/deprecated_32bit}/prism.js (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/pygtk_.py (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/pyqt.py (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/pyside.py (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/pywin32.py (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/smoke.css (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/smoke.min.js (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/wxpython.html (100%) rename src/windows/{binaries_32bit => deprecated_32bit}/wxpython.py (100%) delete mode 100644 src/windows/setup/.gitignore delete mode 100644 src/windows/setup/cefpython.h delete mode 100644 src/windows/setup/compile_rc.py delete mode 100644 src/windows/setup/delete_pyx_files.bat delete mode 100644 src/windows/setup/fix_cefpython_h.py delete mode 100644 src/windows/setup/fix_pyx_files.py delete mode 100644 src/windows/setup/lib_32bit/README delete mode 100644 src/windows/setup/lib_64bit/README delete mode 100644 src/windows/setup/setup.py delete mode 100644 src/windows/wxpython.bat create mode 100644 tools/build.py create mode 100644 tools/build_module.py create mode 100644 tools/common.py diff --git a/api/DragData.md b/api/DragData.md index 96503fb0..4dca7ae5 100644 --- a/api/DragData.md +++ b/api/DragData.md @@ -80,6 +80,8 @@ Return the text/html fragment that is being dragged. | --- | --- | | __Return__ | [Image](Image.md) | +Linux-only currently (#251). + Get image representation of drag data. Check with HasImage() first, otherwise if there is no image an exception is thrown. @@ -90,5 +92,7 @@ otherwise if there is no image an exception is thrown. | --- | --- | | __Return__ | bool | +Linux-only currently (#251). + Whether image representation of drag data is available. diff --git a/patches/patch.py b/patches/patch.py index 71a28274..80a33921 100644 --- a/patches/patch.py +++ b/patches/patch.py @@ -17,19 +17,20 @@ 'name': 'issue125', 'path': '../net/http/' }, - { - # Discovery of the "icudtl.dat" file fails on Linux. - # Apply patch for all platforms just in case. - 'name': 'issue231', - 'path': './' - }, - { - # Adds drag-image representation during drag & drop in OSR mode. - 'name': 'issue251', - 'path': './' - }, ]) # LINUX if OS_POSTFIX == "linux": - pass + # noinspection PyUnresolvedReferences + patches.extend([ + { + # Discovery of the "icudtl.dat" file fails on Linux. + 'name': 'issue231', + 'path': './' + }, + { + # Adds drag-image representation during drag & drop in OSR mode. + 'name': 'issue251', + 'path': './' + }, + ]) diff --git a/src/browser.pyx b/src/browser.pyx index 83b0d417..5907dd72 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -415,10 +415,10 @@ cdef class PyBrowser: self.GetCefBrowserHost().get().ReplaceMisspelling(cef_word) cpdef py_void SetBounds(self, int x, int y, int width, int height): - if platform.system() == "Linux": + IF UNAME_SYSNAME == "Linux": x11.SetX11WindowBounds(self.GetCefBrowser(), x, y, width, height) - else: - raise Exception("SetBounds() not impplemented on this platform") + ELSE: + raise Exception("SetBounds() not implemented on this platform") cpdef py_void SetFocus(self, enable): self.GetCefBrowserHost().get().SetFocus(bool(enable)) @@ -543,7 +543,7 @@ cdef class PyBrowser: if "native_key_code" in pyEvent: cefEvent.native_key_code = int(pyEvent["native_key_code"]) if "is_system_key" in pyEvent: - cefEvent.is_system_key = int(pyEvent["is_system_key"]) + cefEvent.is_system_key = int(bool(pyEvent["is_system_key"])) if "character" in pyEvent: cefEvent.character = int(pyEvent["character"]) if "unmodified_character" in pyEvent: @@ -551,7 +551,7 @@ cdef class PyBrowser: int(pyEvent["unmodified_character"]) if "focus_on_editable_field" in pyEvent: cefEvent.focus_on_editable_field = \ - int(pyEvent["focus_on_editable_field"]) + int(bool(pyEvent["focus_on_editable_field"])) self.GetCefBrowserHost().get().SendKeyEvent(cefEvent) cpdef py_void SendMouseClickEvent(self, int x, int y, diff --git a/src/cefpython.pyx b/src/cefpython.pyx index c44b7fc5..634a9930 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -580,15 +580,16 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): if "switches" in kwargs: commandLineSwitches = kwargs["switches"] - # Fix Issue #231 - Discovery of the "icudtl.dat" file fails on Linux. - # Apply patch for all platforms just in case. - cdef str py_module_dir = GetModuleDirectory() - cdef CefString cef_module_dir - PyToCefString(py_module_dir, cef_module_dir) - CefOverridePath(PK_DIR_EXE, cef_module_dir)\ - or Debug("ERROR: CefOverridePath failed") - CefOverridePath(PK_DIR_MODULE, cef_module_dir)\ - or Debug("ERROR: CefOverridePath failed") + IF UNAME_SYSNAME == "Linux": + # Fix Issue #231 - Discovery of the "icudtl.dat" file fails on Linux. + cdef str py_module_dir = GetModuleDirectory() + cdef CefString cef_module_dir + PyToCefString(py_module_dir, cef_module_dir) + CefOverridePath(PK_DIR_EXE, cef_module_dir)\ + or Debug("ERROR: CefOverridePath failed") + CefOverridePath(PK_DIR_MODULE, cef_module_dir)\ + or Debug("ERROR: CefOverridePath failed") + # END IF UNAME_SYSNAME == "Linux": if not applicationSettings: applicationSettings = {} diff --git a/src/client_handler/client_handler_py27_32bit.vcproj b/src/client_handler/client_handler_py27_32bit.vcproj deleted file mode 100644 index c8d8051b..00000000 --- a/src/client_handler/client_handler_py27_32bit.vcproj +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/client_handler/client_handler_py27_win32.vcproj b/src/client_handler/client_handler_py27_win32.vcproj new file mode 100644 index 00000000..51670b21 --- /dev/null +++ b/src/client_handler/client_handler_py27_win32.vcproj @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/client_handler/client_handler_py27_64bit.vcproj b/src/client_handler/deprecated_client_handler_py27_64bit.vcproj similarity index 100% rename from src/client_handler/client_handler_py27_64bit.vcproj rename to src/client_handler/deprecated_client_handler_py27_64bit.vcproj diff --git a/src/client_handler/client_handler_py34_32bit.vcproj b/src/client_handler/deprecated_client_handler_py34_32bit.vcproj similarity index 100% rename from src/client_handler/client_handler_py34_32bit.vcproj rename to src/client_handler/deprecated_client_handler_py34_32bit.vcproj diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h index 4401355c..e2b6b150 100644 --- a/src/common/cefpython_public_api.h +++ b/src/common/cefpython_public_api.h @@ -26,13 +26,6 @@ #include "include/cef_command_line.h" #include "util.h" -// cefpython.h include depending on platform -#if defined(OS_WIN) -#include "windows/setup/cefpython.h" -#elif defined(OS_LINUX) -#include "linux/setup/cefpython.h" -#elif defined(OS_MACOSX) -#include "mac/setup/cefpython.h" -#endif +#include "../../build/build_cefpython/cefpython.h" #endif // CEFPYTHON_PUBLIC_API_H diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 35f85002..10ec798a 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -1,3 +1,3 @@ # This file was generated by setup.py -DEF UNAME_SYSNAME = "Linux" +DEF UNAME_SYSNAME = "Windows" DEF PY_MAJOR_VERSION = 2 diff --git a/src/cpp_utils/cpp_utils_32bit.vcproj b/src/cpp_utils/cpp_utils_32bit.vcproj deleted file mode 100644 index bf3ca53f..00000000 --- a/src/cpp_utils/cpp_utils_32bit.vcproj +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/cpp_utils/cpp_utils_win32.vcproj b/src/cpp_utils/cpp_utils_win32.vcproj new file mode 100644 index 00000000..cc2349da --- /dev/null +++ b/src/cpp_utils/cpp_utils_win32.vcproj @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cpp_utils/cpp_utils_64bit.vcproj b/src/cpp_utils/deprecated_cpp_utils_64bit.vcproj similarity index 100% rename from src/cpp_utils/cpp_utils_64bit.vcproj rename to src/cpp_utils/deprecated_cpp_utils_64bit.vcproj diff --git a/src/drag_data.pyx b/src/drag_data.pyx index bdc7f46c..0f2b4237 100644 --- a/src/drag_data.pyx +++ b/src/drag_data.pyx @@ -34,12 +34,16 @@ cdef class DragData: cpdef py_string GetFragmentHtml(self): return CefToPyString(self.cef_drag_data.get().GetFragmentHtml()) - cpdef PyImage GetImage(self): - cdef CefRefPtr[CefImage] cef_image =\ - self.cef_drag_data.get().GetImage() - if not cef_image.get(): - raise Exception("Image is not available") - return PyImage_Init(cef_image) - - cpdef py_bool HasImage(self): - return self.cef_drag_data.get().HasImage() + IF UNAME_SYSNAME == "Linux": + + cpdef PyImage GetImage(self): + cdef CefRefPtr[CefImage] cef_image =\ + self.cef_drag_data.get().GetImage() + if not cef_image.get(): + raise Exception("Image is not available") + return PyImage_Init(cef_image) + + cpdef py_bool HasImage(self): + return self.cef_drag_data.get().HasImage() + + # END IF UNAME_SYSNAME == "Linux": diff --git a/src/extern/cef/cef_platform.pxd b/src/extern/cef/cef_platform.pxd index 1a4d42f7..3ee19a1e 100644 --- a/src/extern/cef/cef_platform.pxd +++ b/src/extern/cef/cef_platform.pxd @@ -6,8 +6,7 @@ include "compile_time_constants.pxi" IF UNAME_SYSNAME == "Windows": # noinspection PyUnresolvedReferences - from cef_win cimport CefWindowHandle, CefCursorHandle, CefKeyInfo,\ - CefWindowInfo + from cef_win cimport * ELIF UNAME_SYSNAME == "Darwin": from cef_mac cimport * ELIF UNAME_SYSNAME == "Linux": diff --git a/src/image.pyx b/src/image.pyx index cfdb8f8f..7164bbd6 100644 --- a/src/image.pyx +++ b/src/image.pyx @@ -48,7 +48,8 @@ cdef class PyImage: if not self.cef_image.get(): raise Exception("cef_image is NULL") cdef CefRefPtr[CefBinaryValue] binary_value =\ - self.cef_image.get().GetAsPNG(scale_factor, with_transparency, + self.cef_image.get().GetAsPNG(scale_factor, + bool(with_transparency), pixel_width, pixel_height) cdef size_t size = binary_value.get().GetSize() cdef void* abuffer = malloc(size) diff --git a/src/include/internal/cef_types_wrappers.h b/src/include/internal/cef_types_wrappers.h index bbe348df..e78771ec 100644 --- a/src/include/internal/cef_types_wrappers.h +++ b/src/include/internal/cef_types_wrappers.h @@ -983,7 +983,7 @@ struct CefCompositionUnderlineTraits { typedef cef_composition_underline_t struct_type; static inline void init(struct_type* s) { - s->range = {0, 0}; + s->range.from = 0; s->range.to = 0; s->color = 0; s->background_color = 0; s->thick = 0; diff --git a/src/linux/.gitignore b/src/linux/.gitignore deleted file mode 100644 index 7d787251..00000000 --- a/src/linux/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.pak -cefclient -cefsimple -chrome-sandbox -*.so -subprocess -*.a -*.o -*.cc.o.d -files/ -*.dat -*.bin diff --git a/src/linux/CEF-GTK-patch.txt b/src/linux/CEF-GTK-patch.txt deleted file mode 100644 index f9658215..00000000 --- a/src/linux/CEF-GTK-patch.txt +++ /dev/null @@ -1,56 +0,0 @@ -See this topic on the CEF C++ forum for more details: -http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10641 - -Do the following changes in the CEF C++ sources: - -1. In `chromium/src/cef/libcef/browser/browser_host_impl_gtk.cc`: - - At the end of the CefBrowserHostImpl::PlatformCreateWindow() function, - before the return statement add the following code: - - gtk_widget_show_all(GTK_WIDGET(window_info_.widget)); - - In the same function replace this code: - - gtk_container_add(GTK_CONTAINER(window_info_.parent_widget), - window_info_.widget); - - With the following code: - - if (GTK_IS_BOX(window_info_.parent_widget)) { - gtk_box_pack_start(GTK_BOX(window_info_.parent_widget), - window_info_.widget, TRUE, TRUE, 0); - } else { - // Parent view shouldn't contain any children, but in wxWidgets library - // there will be GtkPizza widget for Panel or any other control. - GList *children, *iter; - children = gtk_container_get_children(GTK_CONTAINER( - window_info_.parent_widget)); - GtkWidget* child = NULL; - GtkWidget* vbox = gtk_vbox_new(FALSE, 0); - for (iter = children; iter != NULL; iter = g_list_next(iter)) { - child = GTK_WIDGET(iter->data); - // We will have to keep a reference to that child that we remove, - // otherwise we will get lots of warnings like "invalid unclassed - // pointer in cast to `GtkPizza'". First we increase a reference, - // we need to do this for a moment before we add this child to the - // vbox, then we will decrease that reference. - g_object_ref(G_OBJECT(child)); - gtk_container_remove(GTK_CONTAINER(window_info_.parent_widget), child); - } - g_list_free(children); - gtk_box_pack_start(GTK_BOX(vbox), window_info_.widget, TRUE, TRUE, 0); - if (child != NULL) { - // This child is packed to the box only so that its reference lives, - // as it might be referenced from other code thus resulting in errors. - gtk_box_pack_end(GTK_BOX(vbox), child, FALSE, FALSE, 0); - gtk_widget_hide(GTK_WIDGET(child)); - g_object_unref(G_OBJECT(child)); - } - gtk_widget_show(GTK_WIDGET(vbox)); - if (GTK_IS_SCROLLED_WINDOW(window_info_.parent_widget)) - gtk_scrolled_window_add_with_viewport( - GTK_SCROLLED_WINDOW(window_info_.parent_widget), vbox); - else - gtk_container_add(GTK_CONTAINER(window_info_.parent_widget), vbox); - } diff --git a/src/linux/binaries_32bit/README.txt b/src/linux/binaries_32bit/README.txt deleted file mode 100644 index 19c3a6c9..00000000 --- a/src/linux/binaries_32bit/README.txt +++ /dev/null @@ -1,131 +0,0 @@ -Chromium Embedded Framework (CEF) Standard Binary Distribution for Linux -------------------------------------------------------------------------------- - -Date: September 08, 2016 - -CEF Version: 3.2785.1475.gaead085 -CEF URL: https://bitbucket.org/chromiumembedded/cef.git - @aead0852b31b5f9acfa5e9c1216f6dd76d73506f - -Chromium Verison: 53.0.2785.89 -Chromium URL: https://chromium.googlesource.com/chromium/src.git - @49dccdea6645f6759fc6e779e12e81930ede2d4a - -This distribution contains all components necessary to build and distribute an -application using CEF on the Linux platform. Please see the LICENSING -section of this document for licensing terms and conditions. - - -CONTENTS --------- - -cefclient Contains the cefclient sample application configured to build - using the files in this distribution. This application demonstrates - a wide range of CEF functionalities. - -cefsimple Contains the cefsimple sample application configured to build - using the files in this distribution. This application demonstrates - the minimal functionality required to create a browser window. - -cmake Contains CMake configuration files shared by all targets. - -Debug Contains libcef.so and other components required to run the debug - version of CEF-based applications. By default these files should be - placed in the same directory as the executable and will be copied - there as part of the build process. - -include Contains all required CEF header files. - -libcef_dll Contains the source code for the libcef_dll_wrapper static library - that all applications using the CEF C++ API must link against. - -Release Contains libcef.so and other components required to run the release - version of CEF-based applications. By default these files should be - placed in the same directory as the executable and will be copied - there as part of the build process. - -Resources Contains resources required by libcef.so. By default these files - should be placed in the same directory as libcef.so and will be - copied there as part of the build process. - - -USAGE ------ - -Building using CMake: - CMake can be used to generate project files in many different formats. See - usage instructions at the top of the CMakeLists.txt file. - -Please visit the CEF Website for additional usage information. - -https://bitbucket.org/chromiumembedded/cef/ - - -REDISTRIBUTION --------------- - -This binary distribution contains the below components. - -Required components: - -The following components are required. CEF will not function without them. - -* CEF core library. - * libcef.so - -* Unicode support data. - * icudtl.dat - -* V8 snapshot data. - * natives_blob.bin - * snapshot_blob.bin - -Optional components: - -The following components are optional. If they are missing CEF will continue to -run but any related functionality may become broken or disabled. - -* Localized resources. - Locale file loading can be disabled completely using - CefSettings.pack_loading_disabled. The locales directory path can be - customized using CefSettings.locales_dir_path. - - * locales/ - Directory containing localized resources used by CEF, Chromium and Blink. A - .pak file is loaded from this directory based on the value of environment - variables which are read with the following precedence order: LANGUAGE, - LC_ALL, LC_MESSAGES and LANG. Only configured locales need to be - distributed. If no locale is configured the default locale of "en-US" will - be used. Without these files arbitrary Web components may display - incorrectly. - -* Other resources. - Pack file loading can be disabled completely using - CefSettings.pack_loading_disabled. The resources directory path can be - customized using CefSettings.resources_dir_path. - - * cef.pak - * cef_100_percent.pak - * cef_200_percent.pak - These files contain non-localized resources used by CEF, Chromium and Blink. - Without these files arbitrary Web components may display incorrectly. - - * cef_extensions.pak - This file contains non-localized resources required for extension loading. - Pass the `--disable-extensions` command-line flag to disable use of this - file. Without this file components that depend on the extension system, - such as the PDF viewer, will not function. - - * devtools_resources.pak - This file contains non-localized resources required for Chrome Developer - Tools. Without this file Chrome Developer Tools will not function. - - -LICENSING ---------- - -The CEF project is BSD licensed. Please read the LICENSE.txt file included with -this binary distribution for licensing terms and conditions. Other software -included in this distribution is provided under other licenses. Please visit -"about:credits" in a CEF-based application for complete Chromium and third-party -licensing information. diff --git a/src/linux/binaries_32bit/example.html b/src/linux/binaries_32bit/example.html deleted file mode 100644 index 891bc5e8..00000000 --- a/src/linux/binaries_32bit/example.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CEF Python 3 example (utf-8: ąś) - - - - - - -

Use mouse context menu to go Back/Forward in history navigation.

- - - -

Google Search

-https://www.google.com/ - - - -

User agent

- - - - -

Popup

- - window.open('example.html') - - - -

HTML5 video and accelerated content

- -HTML 5 video
- -Accelerated canvas
- -Accelerated layers
- - - -

Advanced example

- -See the wxpython.py script for an advanced usage of CEF Python 3 -features - including javascript bindings, js and python callbacks, -client handlers and others. - - -



-



-



-



- - - diff --git a/src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.css b/src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.css deleted file mode 100644 index 46ce5e40..00000000 --- a/src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.css +++ /dev/null @@ -1,186 +0,0 @@ -/* - IMPORTANT: - 1. DO NOT USE DOUBLE QUOTES HERE! - This file contents is included on page using document.write: - | (DQ = double quote) - | document.write(DQDQ); - 2. Each attribute must end with a semicolon, as newlines are removed - from this file. -*/ - -/* Dropdown control */ -.__kivy__selectBox-dropdown { - min-width: 150px; - position: relative; - border: solid 1px #BBB; - line-height: 1.5; - text-decoration: none; - text-align: left; - color: #000; - outline: none; - vertical-align: middle; - background: #F2F2F2; - background: -moz-linear-gradient(top, #F8F8F8 1%, #E1E1E1 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(1%, #F8F8F8), color-stop(100%, #E1E1E1)); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F8F8F8', endColorstr='#E1E1E1', GradientType=0); - -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, .75); - -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, .75); - box-shadow: 0 1px 0 rgba(255, 255, 255, .75); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - display: inline-block; - cursor: default; -} - -.__kivy__selectBox-dropdown:focus, -.__kivy__selectBox-dropdown:focus .__kivy__selectBox-arrow { - border-color: #666; -} - -.__kivy__selectBox-dropdown.__kivy__selectBox-menuShowing-bottom { - -moz-border-radius-bottomleft: 0; - -moz-border-radius-bottomright: 0; - -webkit-border-bottom-left-radius: 0; - -webkit-border-bottom-right-radius: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.__kivy__selectBox-dropdown.__kivy__selectBox-menuShowing-top { - -moz-border-radius-topleft: 0; - -moz-border-radius-topright: 0; - -webkit-border-top-left-radius: 0; - -webkit-border-top-right-radius: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.__kivy__selectBox-dropdown .__kivy__selectBox-label { - padding: 2px 8px; - display: inline-block; - white-space: nowrap; - overflow: hidden; -} - -.__kivy__selectBox-dropdown .__kivy__selectBox-arrow { - position: absolute; - top: 0; - right: 0; - width: 23px; - height: 100%; - background: url(data:image/gif;base64,R0lGODlhCQAOAIABAAAAAP///yH5BAEAAAEALAAAAAAJAA4AAAIXjAOnwIrcDJxvQoez3tUmn0jUEjmhUQAAOw==) 50% center no-repeat; - border-left: solid 1px #BBB; -} - -/* Dropdown menu */ -.__kivy__selectBox-dropdown-menu { - position: absolute; - z-index: 99999; - max-height: 200px; - min-height: 1em; - border: solid 1px #BBB; /* should be the same border width as .__kivy__selectBox-dropdown */ - background: #FFF; - -moz-box-shadow: 0 2px 6px rgba(0, 0, 0, .2); - -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, .2); - box-shadow: 0 2px 6px rgba(0, 0, 0, .2); - overflow: auto; - -webkit-overflow-scrolling: touch; -} - -/* Inline control */ -.__kivy__selectBox-inline { - min-width: 150px; - outline: none; - border: solid 1px #BBB; - background: #FFF; - display: inline-block; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - overflow: auto; -} - -.__kivy__selectBox-inline:focus { - border-color: #666; -} - -/* Options */ -.__kivy__selectBox-options, -.__kivy__selectBox-options LI, -.__kivy__selectBox-options LI A { - list-style: none; - display: block; - cursor: default; - padding: 0; - margin: 0; -} - -.__kivy__selectBox-options.__kivy__selectBox-options-top{ - border-bottom:none; - margin-top:1px; - -moz-border-radius-topleft: 5px; - -moz-border-radius-topright: 5px; - -webkit-border-top-left-radius: 5px; - -webkit-border-top-right-radius: 5px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -.__kivy__selectBox-options.__kivy__selectBox-options-bottom{ - border-top:none; - -moz-border-radius-bottomleft: 5px; - -moz-border-radius-bottomright: 5px; - -webkit-border-bottom-left-radius: 5px; - -webkit-border-bottom-right-radius: 5px; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; -} - -.__kivy__selectBox-options LI A { - line-height: 1.5; - padding: 0 .5em; - white-space: nowrap; - overflow: hidden; - background: 6px center no-repeat; -} - -.__kivy__selectBox-options LI.__kivy__selectBox-hover A { - background-color: #EEE; -} - -.__kivy__selectBox-options LI.__kivy__selectBox-disabled A { - color: #888; - background-color: transparent; -} - -.__kivy__selectBox-options LI.__kivy__selectBox-selected A { - background-color: #C8DEF4; -} - -.__kivy__selectBox-options .__kivy__selectBox-optgroup { - color: #666; - background: #EEE; - font-weight: bold; - line-height: 1.5; - padding: 0 .3em; - white-space: nowrap; -} - -/* Disabled state */ -.__kivy__selectBox.__kivy__selectBox-disabled { - color: #888 !important; -} - -.__kivy__selectBox-dropdown.__kivy__selectBox-disabled .__kivy__selectBox-arrow { - opacity: .5; - filter: alpha(opacity=50); - border-color: #666; -} - -.__kivy__selectBox-inline.__kivy__selectBox-disabled { - color: #888 !important; -} - -.__kivy__selectBox-inline.__kivy__selectBox-disabled .__kivy__selectBox-options A { - background-color: transparent !important; -} diff --git a/src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.js b/src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.js deleted file mode 100644 index a7d3d229..00000000 --- a/src/linux/binaries_32bit/kivy-select-boxes/kivy-selectBox.js +++ /dev/null @@ -1,10420 +0,0 @@ -/*! - * __kivy__jQuery JavaScript Library v1.7.1 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Mon Nov 21 21:11:03 2011 -0500 - */ -(function( window, undefined ) { - -// Use the correct document accordingly with window argument (sandbox) -var document = window.document, - navigator = window.navigator, - location = window.location; -var __kivy__jQuery = (function() { - -// Define a local copy of __kivy__jQuery -var __kivy__jQuery = function( selector, context ) { - // The __kivy__jQuery object is actually just the init constructor 'enhanced' - return new __kivy__jQuery.fn.init( selector, context, root__kivy__jQuery ); - }, - - // Map over __kivy__jQuery in case of overwrite - ___kivy__jQuery = window.__kivy__jQuery, - - // Map over the $ in case of overwrite - __kivy___$ = window.__kivy__$, - - // A central reference to the root __kivy__jQuery(document) - root__kivy__jQuery, - - // A simple way to check for HTML strings or ID strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - trimLeft = /^\s+/, - trimRight = /\s+$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - - // Useragent RegExp - rwebkit = /(webkit)[ \/]([\w.]+)/, - ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, - rmsie = /(msie) ([\w.]+)/, - rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, - - // Matches dashed string for camelizing - rdashAlpha = /-([a-z]|[0-9])/ig, - rmsPrefix = /^-ms-/, - - // Used by __kivy__jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, - - // Keep a UserAgent string for use with __kivy__jQuery.browser - userAgent = navigator.userAgent, - - // For matching the engine and version of the browser - browserMatch, - - // The deferred used on DOM ready - readyList, - - // The ready event handler - DOMContentLoaded, - - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - trim = String.prototype.trim, - indexOf = Array.prototype.indexOf, - - // [[Class]] -> type pairs - class2type = {}; - -__kivy__jQuery.fn = __kivy__jQuery.prototype = { - constructor: __kivy__jQuery, - init: function( selector, context, root__kivy__jQuery ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // The body element only exists once, optimize finding it - if ( selector === "body" && !context && document.body ) { - this.context = document; - this[0] = document.body; - this.selector = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = quickExpr.exec( selector ); - } - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof __kivy__jQuery ? context[0] : context; - doc = ( context ? context.ownerDocument || context : document ); - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); - - if ( ret ) { - if ( __kivy__jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - __kivy__jQuery.fn.attr.call( selector, context, true ); - - } else { - selector = [ doc.createElement( ret[1] ) ]; - } - - } else { - ret = __kivy__jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = ( ret.cacheable ? __kivy__jQuery.clone(ret.fragment) : ret.fragment ).childNodes; - } - - return __kivy__jQuery.merge( this, selector ); - - // HANDLE: $("#id") - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return root__kivy__jQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the __kivy__jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.__kivy__jquery ) { - return ( context || root__kivy__jQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( __kivy__jQuery.isFunction( selector ) ) { - return root__kivy__jQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return __kivy__jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of __kivy__jQuery being used - __kivy__jquery: "1.7.1", - - // The default length of a __kivy__jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return slice.call( this, 0 ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new __kivy__jQuery matched element set - var ret = this.constructor(); - - if ( __kivy__jQuery.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - __kivy__jQuery.merge( ret, elems ); - } - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return __kivy__jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Attach the listeners - __kivy__jQuery.bindReady(); - - // Add the callback - readyList.add( fn ); - - return this; - }, - - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( __kivy__jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a __kivy__jQuery method. - push: push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the __kivy__jQuery prototype for later instantiation -__kivy__jQuery.fn.init.prototype = __kivy__jQuery.fn; - -__kivy__jQuery.extend = __kivy__jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !__kivy__jQuery.isFunction(target) ) { - target = {}; - } - - // extend __kivy__jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( __kivy__jQuery.isPlainObject(copy) || (copyIsArray = __kivy__jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && __kivy__jQuery.isArray(src) ? src : []; - - } else { - clone = src && __kivy__jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = __kivy__jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -__kivy__jQuery.extend({ - noConflict: function( deep ) { - if ( window.__kivy__$ === __kivy__jQuery ) { - window.__kivy__$ = __kivy___$; - } - - if ( deep && window.__kivy__jQuery === __kivy__jQuery ) { - window.__kivy__jQuery = ___kivy__jQuery; - } - - return __kivy__jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - __kivy__jQuery.readyWait++; - } else { - __kivy__jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - // Either a released hold or an DOMready/load event and not yet ready - if ( (wait === true && !--__kivy__jQuery.readyWait) || (wait !== true && !__kivy__jQuery.isReady) ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( __kivy__jQuery.ready, 1 ); - } - - // Remember that the DOM is ready - __kivy__jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --__kivy__jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.fireWith( document, [ __kivy__jQuery ] ); - - // Trigger any bound ready events - if ( __kivy__jQuery.fn.trigger ) { - __kivy__jQuery( document ).trigger( "ready" ).off( "ready" ); - } - } - }, - - bindReady: function() { - if ( readyList ) { - return; - } - - readyList = __kivy__jQuery.Callbacks( "once memory" ); - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - return setTimeout( __kivy__jQuery.ready, 1 ); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", __kivy__jQuery.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", __kivy__jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return __kivy__jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return __kivy__jQuery.type(obj) === "array"; - }, - - // A crude way of determining if an object is a window - isWindow: function( obj ) { - return obj && typeof obj === "object" && "setInterval" in obj; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ toString.call(obj) ] || "object"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || __kivy__jQuery.type(obj) !== "object" || obj.nodeType || __kivy__jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = __kivy__jQuery.trim( data ); - - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return ( new Function( "return " + data ) )(); - - } - __kivy__jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - __kivy__jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than __kivy__jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || __kivy__jQuery.isFunction( object ); - - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { - break; - } - } - } - } - - return object; - }, - - // Use native String.trim function wherever possible - trim: trim ? - function( text ) { - return text == null ? - "" : - trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); - }, - - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - var type = __kivy__jQuery.type( array ); - - if ( array.length == null || type === "string" || type === "function" || type === "regexp" || __kivy__jQuery.isWindow( array ) ) { - push.call( ret, array ); - } else { - __kivy__jQuery.merge( ret, array ); - } - } - - return ret; - }, - - inArray: function( elem, array, i ) { - var len; - - if ( array ) { - if ( indexOf ) { - return indexOf.call( array, elem, i ); - } - - len = array.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in array && array[ i ] === elem ) { - return i; - } - } - } - - return -1; - }, - - merge: function( first, second ) { - var i = first.length, - j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var ret = [], retVal; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof __kivy__jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || __kivy__jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - if ( typeof context === "string" ) { - var tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !__kivy__jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - var args = slice.call( arguments, 2 ), - proxy = function() { - return fn.apply( context, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || __kivy__jQuery.guid++; - - return proxy; - }, - - // Mutifunctional method to get and set values to a collection - // The value/s can optionally be executed if it's a function - access: function( elems, key, value, exec, fn, pass ) { - var length = elems.length; - - // Setting many attributes - if ( typeof key === "object" ) { - for ( var k in key ) { - __kivy__jQuery.access( elems, k, key[k], exec, fn, value ); - } - return elems; - } - - // Setting one attribute - if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = !pass && exec && __kivy__jQuery.isFunction(value); - - for ( var i = 0; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - - return elems; - } - - // Getting an attribute - return length ? fn( elems[0], key ) : undefined; - }, - - now: function() { - return ( new Date() ).getTime(); - }, - - // Use of __kivy__jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/__kivy__jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); - - var match = rwebkit.exec( ua ) || - ropera.exec( ua ) || - rmsie.exec( ua ) || - ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || - []; - - return { browser: match[1] || "", version: match[2] || "0" }; - }, - - sub: function() { - function __kivy__jQuerySub( selector, context ) { - return new __kivy__jQuerySub.fn.init( selector, context ); - } - __kivy__jQuery.extend( true, __kivy__jQuerySub, this ); - __kivy__jQuerySub.superclass = this; - __kivy__jQuerySub.fn = __kivy__jQuerySub.prototype = this(); - __kivy__jQuerySub.fn.constructor = __kivy__jQuerySub; - __kivy__jQuerySub.sub = this.sub; - __kivy__jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof __kivy__jQuery && !(context instanceof __kivy__jQuerySub) ) { - context = __kivy__jQuerySub( context ); - } - - return __kivy__jQuery.fn.init.call( this, selector, context, root__kivy__jQuerySub ); - }; - __kivy__jQuerySub.fn.init.prototype = __kivy__jQuerySub.fn; - var root__kivy__jQuerySub = __kivy__jQuerySub(document); - return __kivy__jQuerySub; - }, - - browser: {} -}); - -// Populate the class2type map -__kivy__jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -browserMatch = __kivy__jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - __kivy__jQuery.browser[ browserMatch.browser ] = true; - __kivy__jQuery.browser.version = browserMatch.version; -} - -// Deprecated, use __kivy__jQuery.browser.webkit instead -if ( __kivy__jQuery.browser.webkit ) { - __kivy__jQuery.browser.safari = true; -} - -// IE doesn't match non-breaking spaces with \s -if ( rnotwhite.test( "\xA0" ) ) { - trimLeft = /^[\s\xA0]+/; - trimRight = /[\s\xA0]+$/; -} - -// All __kivy__jQuery objects should point back to these -root__kivy__jQuery = __kivy__jQuery(document); - -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - __kivy__jQuery.ready(); - }; - -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - __kivy__jQuery.ready(); - } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( __kivy__jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch(e) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - __kivy__jQuery.ready(); -} - -return __kivy__jQuery; - -})(); - - -// String to Object flags format cache -var flagsCache = {}; - -// Convert String-formatted flags into Object-formatted ones and store in cache -function createFlags( flags ) { - var object = flagsCache[ flags ] = {}, - i, length; - flags = flags.split( /\s+/ ); - for ( i = 0, length = flags.length; i < length; i++ ) { - object[ flags[i] ] = true; - } - return object; -} - -/* - * Create a callback list using the following parameters: - * - * flags: an optional list of space-separated flags that will change how - * the callback list behaves - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible flags: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -__kivy__jQuery.Callbacks = function( flags ) { - - // Convert flags from String-formatted to Object-formatted - // (we check in cache first) - flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; - - var // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = [], - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Add one or several callbacks to the list - add = function( args ) { - var i, - length, - elem, - type, - actual; - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = __kivy__jQuery.type( elem ); - if ( type === "array" ) { - // Inspect recursively - add( elem ); - } else if ( type === "function" ) { - // Add if not in unique mode and callback is not in - if ( !flags.unique || !self.has( elem ) ) { - list.push( elem ); - } - } - } - }, - // Fire callbacks - fire = function( context, args ) { - args = args || []; - memory = !flags.memory || [ context, args ]; - firing = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { - memory = true; // Mark as halted - break; - } - } - firing = false; - if ( list ) { - if ( !flags.once ) { - if ( stack && stack.length ) { - memory = stack.shift(); - self.fireWith( memory[ 0 ], memory[ 1 ] ); - } - } else if ( memory === true ) { - self.disable(); - } else { - list = []; - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - var length = list.length; - add( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away, unless previous - // firing was halted (stopOnFalse) - } else if ( memory && memory !== true ) { - firingStart = length; - fire( memory[ 0 ], memory[ 1 ] ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - var args = arguments, - argIndex = 0, - argLength = args.length; - for ( ; argIndex < argLength ; argIndex++ ) { - for ( var i = 0; i < list.length; i++ ) { - if ( args[ argIndex ] === list[ i ] ) { - // Handle firingIndex and firingLength - if ( firing ) { - if ( i <= firingLength ) { - firingLength--; - if ( i <= firingIndex ) { - firingIndex--; - } - } - } - // Remove the element - list.splice( i--, 1 ); - // If we have some unicity property then - // we only need to do this once - if ( flags.unique ) { - break; - } - } - } - } - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - if ( list ) { - var i = 0, - length = list.length; - for ( ; i < length; i++ ) { - if ( fn === list[ i ] ) { - return true; - } - } - } - return false; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory || memory === true ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( stack ) { - if ( firing ) { - if ( !flags.once ) { - stack.push( [ context, args ] ); - } - } else if ( !( flags.once && memory ) ) { - fire( context, args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!memory; - } - }; - - return self; -}; - - - - -var // Static reference to slice - sliceDeferred = [].slice; - -__kivy__jQuery.extend({ - - Deferred: function( func ) { - var doneList = __kivy__jQuery.Callbacks( "once memory" ), - failList = __kivy__jQuery.Callbacks( "once memory" ), - progressList = __kivy__jQuery.Callbacks( "memory" ), - state = "pending", - lists = { - resolve: doneList, - reject: failList, - notify: progressList - }, - promise = { - done: doneList.add, - fail: failList.add, - progress: progressList.add, - - state: function() { - return state; - }, - - // Deprecated - isResolved: doneList.fired, - isRejected: failList.fired, - - then: function( doneCallbacks, failCallbacks, progressCallbacks ) { - deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); - return this; - }, - always: function() { - deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); - return this; - }, - pipe: function( fnDone, fnFail, fnProgress ) { - return __kivy__jQuery.Deferred(function( newDefer ) { - __kivy__jQuery.each( { - done: [ fnDone, "resolve" ], - fail: [ fnFail, "reject" ], - progress: [ fnProgress, "notify" ] - }, function( handler, data ) { - var fn = data[ 0 ], - action = data[ 1 ], - returned; - if ( __kivy__jQuery.isFunction( fn ) ) { - deferred[ handler ](function() { - returned = fn.apply( this, arguments ); - if ( returned && __kivy__jQuery.isFunction( returned.promise ) ) { - returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - }); - } else { - deferred[ handler ]( newDefer[ action ] ); - } - }); - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - if ( obj == null ) { - obj = promise; - } else { - for ( var key in promise ) { - obj[ key ] = promise[ key ]; - } - } - return obj; - } - }, - deferred = promise.promise({}), - key; - - for ( key in lists ) { - deferred[ key ] = lists[ key ].fire; - deferred[ key + "With" ] = lists[ key ].fireWith; - } - - // Handle state - deferred.done( function() { - state = "resolved"; - }, failList.disable, progressList.lock ).fail( function() { - state = "rejected"; - }, doneList.disable, progressList.lock ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( firstParam ) { - var args = sliceDeferred.call( arguments, 0 ), - i = 0, - length = args.length, - pValues = new Array( length ), - count = length, - pCount = length, - deferred = length <= 1 && firstParam && __kivy__jQuery.isFunction( firstParam.promise ) ? - firstParam : - __kivy__jQuery.Deferred(), - promise = deferred.promise(); - function resolveFunc( i ) { - return function( value ) { - args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - if ( !( --count ) ) { - deferred.resolveWith( deferred, args ); - } - }; - } - function progressFunc( i ) { - return function( value ) { - pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - deferred.notifyWith( promise, pValues ); - }; - } - if ( length > 1 ) { - for ( ; i < length; i++ ) { - if ( args[ i ] && args[ i ].promise && __kivy__jQuery.isFunction( args[ i ].promise ) ) { - args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); - } else { - --count; - } - } - if ( !count ) { - deferred.resolveWith( deferred, args ); - } - } else if ( deferred !== firstParam ) { - deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); - } - return promise; - } -}); - - - - -__kivy__jQuery.support = (function() { - - var support, - all, - a, - select, - opt, - input, - marginDiv, - fragment, - tds, - events, - eventName, - i, - isSupported, - div = document.createElement( "div" ), - documentElement = document.documentElement; - - // Preliminary tests - div.setAttribute("className", "t"); - div.innerHTML = "
a"; - - all = div.getElementsByTagName( "*" ); - a = div.getElementsByTagName( "a" )[ 0 ]; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return {}; - } - - // First batch of supports tests - select = document.createElement( "select" ); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName( "input" )[ 0 ]; - - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute("href") === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Tests for enctype support on a form(#6743) - enctype: !!document.createElement("form").enctype, - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true - }; - - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent( "onclick" ); - } - - // Check if a radio maintains its value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute("type", "radio"); - support.radioValue = input.value === "t"; - - input.setAttribute("checked", "checked"); - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.lastChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - fragment.removeChild( input ); - fragment.appendChild( div ); - - div.innerHTML = ""; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - if ( window.getComputedStyle ) { - marginDiv = document.createElement( "div" ); - marginDiv.style.width = "0"; - marginDiv.style.marginRight = "0"; - div.style.width = "2px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; - } - - // Technique from Juriy Zaytsev - // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for( i in { - submit: 1, - change: 1, - focusin: 1 - }) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - fragment.removeChild( div ); - - // Null elements to avoid leaks in IE - fragment = select = opt = marginDiv = div = input = null; - - // Run tests that need a body at doc ready - __kivy__jQuery(function() { - var container, outer, inner, table, td, offsetSupport, - conMarginTop, ptlm, vb, style, html, - body = document.getElementsByTagName("body")[0]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - conMarginTop = 1; - ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;"; - vb = "visibility:hidden;border:0;"; - style = "style='" + ptlm + "border:5px solid #000;padding:0;'"; - html = "
" + - "" + - "
"; - - container = document.createElement("div"); - container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; - body.insertBefore( container, body.firstChild ); - - // Construct the test element - div = document.createElement("div"); - container.appendChild( div ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - div.innerHTML = "
t
"; - tds = div.getElementsByTagName( "td" ); - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE <= 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - - // Figure out if the W3C box model works as expected - div.innerHTML = ""; - div.style.width = div.style.paddingLeft = "1px"; - __kivy__jQuery.boxModel = support.boxModel = div.offsetWidth === 2; - - if ( typeof div.style.zoom !== "undefined" ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.style.display = "inline"; - div.style.zoom = 1; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = ""; - div.innerHTML = "
"; - support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); - } - - div.style.cssText = ptlm + vb; - div.innerHTML = html; - - outer = div.firstChild; - inner = outer.firstChild; - td = outer.nextSibling.firstChild.firstChild; - - offsetSupport = { - doesNotAddBorder: ( inner.offsetTop !== 5 ), - doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) - }; - - inner.style.position = "fixed"; - inner.style.top = "20px"; - - // safari subtracts parent border width here which is 5px - offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); - inner.style.position = inner.style.top = ""; - - outer.style.overflow = "hidden"; - outer.style.position = "relative"; - - offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); - offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); - - body.removeChild( container ); - div = container = null; - - __kivy__jQuery.extend( support, offsetSupport ); - }); - - return support; -})(); - - - - -var rbrace = /^(?:\{.*\}|\[.*\])$/, - rmultiDash = /([A-Z])/g; - -__kivy__jQuery.extend({ - cache: {}, - - // Please use with caution - uuid: 0, - - // Unique for each copy of __kivy__jQuery on the page - // Non-digits removed to match rinline__kivy__jQuery - expando: "__kivy__jQuery" + ( __kivy__jQuery.fn.__kivy__jquery + Math.random() ).replace( /\D/g, "" ), - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - - hasData: function( elem ) { - elem = elem.nodeType ? __kivy__jQuery.cache[ elem[__kivy__jQuery.expando] ] : elem[ __kivy__jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !__kivy__jQuery.acceptData( elem ) ) { - return; - } - - var privateCache, thisCache, ret, - internalKey = __kivy__jQuery.expando, - getByName = typeof name === "string", - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global __kivy__jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? __kivy__jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, - isEvents = name === "events"; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ internalKey ] = id = ++__kivy__jQuery.uuid; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - cache[ id ] = {}; - - // Avoids exposing __kivy__jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = __kivy__jQuery.noop; - } - } - - // An object can be passed to __kivy__jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = __kivy__jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = __kivy__jQuery.extend( cache[ id ].data, name ); - } - } - - privateCache = thisCache = cache[ id ]; - - // __kivy__jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ __kivy__jQuery.camelCase( name ) ] = data; - } - - // Users should not attempt to inspect the internal events object using __kivy__jQuery.data, - // it is undocumented and subject to change. But does anyone listen? No. - if ( isEvents && !thisCache[ name ] ) { - return privateCache.events; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ __kivy__jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; - }, - - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !__kivy__jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, l, - - // Reference to internal data cache key - internalKey = __kivy__jQuery.expando, - - isNode = elem.nodeType, - - // See __kivy__jQuery.data for more information - cache = isNode ? __kivy__jQuery.cache : elem, - - // See __kivy__jQuery.data for more information - id = isNode ? elem[ internalKey ] : internalKey; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !__kivy__jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = __kivy__jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split( " " ); - } - } - } - - for ( i = 0, l = name.length; i < l; i++ ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !( pvt ? isEmptyDataObject : __kivy__jQuery.isEmptyObject )( thisCache ) ) { - return; - } - } - } - - // See __kivy__jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject(cache[ id ]) ) { - return; - } - } - - // Browsers that fail expando deletion also refuse to delete expandos on - // the window, but it will allow it on all other JS objects; other browsers - // don't care - // Ensure that `cache` is not a window object #10080 - if ( __kivy__jQuery.support.deleteExpando || !cache.setInterval ) { - delete cache[ id ]; - } else { - cache[ id ] = null; - } - - // We destroyed the cache and need to eliminate the expando on the node to avoid - // false lookups in the cache for entries that no longer exist - if ( isNode ) { - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( __kivy__jQuery.support.deleteExpando ) { - delete elem[ internalKey ]; - } else if ( elem.removeAttribute ) { - elem.removeAttribute( internalKey ); - } else { - elem[ internalKey ] = null; - } - } - }, - - // For internal use only. - _data: function( elem, name, data ) { - return __kivy__jQuery.data( elem, name, data, true ); - }, - - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - if ( elem.nodeName ) { - var match = __kivy__jQuery.noData[ elem.nodeName.toLowerCase() ]; - - if ( match ) { - return !(match === true || elem.getAttribute("classid") !== match); - } - } - - return true; - } -}); - -__kivy__jQuery.fn.extend({ - data: function( key, value ) { - var parts, attr, name, - data = null; - - if ( typeof key === "undefined" ) { - if ( this.length ) { - data = __kivy__jQuery.data( this[0] ); - - if ( this[0].nodeType === 1 && !__kivy__jQuery._data( this[0], "parsedAttrs" ) ) { - attr = this[0].attributes; - for ( var i = 0, l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = __kivy__jQuery.camelCase( name.substring(5) ); - - dataAttr( this[0], name, data[ name ] ); - } - } - __kivy__jQuery._data( this[0], "parsedAttrs", true ); - } - } - - return data; - - } else if ( typeof key === "object" ) { - return this.each(function() { - __kivy__jQuery.data( this, key ); - }); - } - - parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - // Try to fetch any internally stored data first - if ( data === undefined && this.length ) { - data = __kivy__jQuery.data( this[0], key ); - data = dataAttr( this[0], key, data ); - } - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - - } else { - return this.each(function() { - var self = __kivy__jQuery( this ), - args = [ parts[0], value ]; - - self.triggerHandler( "setData" + parts[1] + "!", args ); - __kivy__jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + parts[1] + "!", args ); - }); - } - }, - - removeData: function( key ) { - return this.each(function() { - __kivy__jQuery.removeData( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - __kivy__jQuery.isNumeric( data ) ? parseFloat( data ) : - rbrace.test( data ) ? __kivy__jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - __kivy__jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - for ( var name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && __kivy__jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - - - - -function handleQueueMarkDefer( elem, type, src ) { - var deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - defer = __kivy__jQuery._data( elem, deferDataKey ); - if ( defer && - ( src === "queue" || !__kivy__jQuery._data(elem, queueDataKey) ) && - ( src === "mark" || !__kivy__jQuery._data(elem, markDataKey) ) ) { - // Give room for hard-coded callbacks to fire first - // and eventually mark/queue something else on the element - setTimeout( function() { - if ( !__kivy__jQuery._data( elem, queueDataKey ) && - !__kivy__jQuery._data( elem, markDataKey ) ) { - __kivy__jQuery.removeData( elem, deferDataKey, true ); - defer.fire(); - } - }, 0 ); - } -} - -__kivy__jQuery.extend({ - - _mark: function( elem, type ) { - if ( elem ) { - type = ( type || "fx" ) + "mark"; - __kivy__jQuery._data( elem, type, (__kivy__jQuery._data( elem, type ) || 0) + 1 ); - } - }, - - _unmark: function( force, elem, type ) { - if ( force !== true ) { - type = elem; - elem = force; - force = false; - } - if ( elem ) { - type = type || "fx"; - var key = type + "mark", - count = force ? 0 : ( (__kivy__jQuery._data( elem, key ) || 1) - 1 ); - if ( count ) { - __kivy__jQuery._data( elem, key, count ); - } else { - __kivy__jQuery.removeData( elem, key, true ); - handleQueueMarkDefer( elem, type, "mark" ); - } - } - }, - - queue: function( elem, type, data ) { - var q; - if ( elem ) { - type = ( type || "fx" ) + "queue"; - q = __kivy__jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !q || __kivy__jQuery.isArray(data) ) { - q = __kivy__jQuery._data( elem, type, __kivy__jQuery.makeArray(data) ); - } else { - q.push( data ); - } - } - return q || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = __kivy__jQuery.queue( elem, type ), - fn = queue.shift(), - hooks = {}; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - } - - if ( fn ) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - __kivy__jQuery._data( elem, type + ".run", hooks ); - fn.call( elem, function() { - __kivy__jQuery.dequeue( elem, type ); - }, hooks ); - } - - if ( !queue.length ) { - __kivy__jQuery.removeData( elem, type + "queue " + type + ".run", true ); - handleQueueMarkDefer( elem, type, "queue" ); - } - } -}); - -__kivy__jQuery.fn.extend({ - queue: function( type, data ) { - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - } - - if ( data === undefined ) { - return __kivy__jQuery.queue( this[0], type ); - } - return this.each(function() { - var queue = __kivy__jQuery.queue( this, type, data ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - __kivy__jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - __kivy__jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = __kivy__jQuery.fx ? __kivy__jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, object ) { - if ( typeof type !== "string" ) { - object = type; - type = undefined; - } - type = type || "fx"; - var defer = __kivy__jQuery.Deferred(), - elements = this, - i = elements.length, - count = 1, - deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - tmp; - function resolve() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - } - while( i-- ) { - if (( tmp = __kivy__jQuery.data( elements[ i ], deferDataKey, undefined, true ) || - ( __kivy__jQuery.data( elements[ i ], queueDataKey, undefined, true ) || - __kivy__jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && - __kivy__jQuery.data( elements[ i ], deferDataKey, __kivy__jQuery.Callbacks( "once memory" ), true ) )) { - count++; - tmp.add( resolve ); - } - } - resolve(); - return defer.promise(); - } -}); - - - - -var rclass = /[\n\t\r]/g, - rspace = /\s+/, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea)?$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - getSetAttribute = __kivy__jQuery.support.getSetAttribute, - nodeHook, boolHook, fixSpecified; - -__kivy__jQuery.fn.extend({ - attr: function( name, value ) { - return __kivy__jQuery.access( this, name, value, true, __kivy__jQuery.attr ); - }, - - removeAttr: function( name ) { - return this.each(function() { - __kivy__jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return __kivy__jQuery.access( this, name, value, true, __kivy__jQuery.prop ); - }, - - removeProp: function( name ) { - name = __kivy__jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classNames, i, l, elem, - setClass, c, cl; - - if ( __kivy__jQuery.isFunction( value ) ) { - return this.each(function( j ) { - __kivy__jQuery( this ).addClass( value.call(this, j, this.className) ); - }); - } - - if ( value && typeof value === "string" ) { - classNames = value.split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setClass = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { - setClass += classNames[ c ] + " "; - } - } - elem.className = __kivy__jQuery.trim( setClass ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classNames, i, l, elem, className, c, cl; - - if ( __kivy__jQuery.isFunction( value ) ) { - return this.each(function( j ) { - __kivy__jQuery( this ).removeClass( value.call(this, j, this.className) ); - }); - } - - if ( (value && typeof value === "string") || value === undefined ) { - classNames = ( value || "" ).split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - className = (" " + elem.className + " ").replace( rclass, " " ); - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[ c ] + " ", " "); - } - elem.className = __kivy__jQuery.trim( className ); - - } else { - elem.className = ""; - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( __kivy__jQuery.isFunction( value ) ) { - return this.each(function( i ) { - __kivy__jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = __kivy__jQuery( this ), - state = stateVal, - classNames = value.split( rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space seperated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - __kivy__jQuery._data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : __kivy__jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = __kivy__jQuery.valHooks[ elem.nodeName.toLowerCase() ] || __kivy__jQuery.valHooks[ elem.type ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = __kivy__jQuery.isFunction( value ); - - return this.each(function( i ) { - var self = __kivy__jQuery(this), val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( __kivy__jQuery.isArray( val ) ) { - val = __kivy__jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = __kivy__jQuery.valHooks[ this.nodeName.toLowerCase() ] || __kivy__jQuery.valHooks[ this.type ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -__kivy__jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (__kivy__jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !__kivy__jQuery.nodeName( option.parentNode, "optgroup" )) ) { - - // Get the specific value for the option - value = __kivy__jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return __kivy__jQuery( options[ index ] ).val(); - } - - return values; - }, - - set: function( elem, value ) { - var values = __kivy__jQuery.makeArray( value ); - - __kivy__jQuery(elem).find("option").each(function() { - this.selected = __kivy__jQuery.inArray( __kivy__jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attrFn: { - val: true, - css: true, - html: true, - text: true, - data: true, - width: true, - height: true, - offset: true - }, - - attr: function( elem, name, value, pass ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( pass && name in __kivy__jQuery.attrFn ) { - return __kivy__jQuery( elem )[ name ]( value ); - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return __kivy__jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !__kivy__jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = __kivy__jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - __kivy__jQuery.removeAttr( elem, name ); - return; - - } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, "" + value ); - return value; - } - - } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var propName, attrNames, name, l, - i = 0; - - if ( value && elem.nodeType === 1 ) { - attrNames = value.toLowerCase().split( rspace ); - l = attrNames.length; - - for ( ; i < l; i++ ) { - name = attrNames[ i ]; - - if ( name ) { - propName = __kivy__jQuery.propFix[ name ] || name; - - // See #9699 for explanation of this approach (setting first, then removal) - __kivy__jQuery.attr( elem, name, "" ); - elem.removeAttribute( getSetAttribute ? name : propName ); - - // Set corresponding property to false for boolean attributes - if ( rboolean.test( name ) && propName in elem ) { - elem[ propName ] = false; - } - } - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( rtype.test( elem.nodeName ) && elem.parentNode ) { - __kivy__jQuery.error( "type property can't be changed" ); - } else if ( !__kivy__jQuery.support.radioValue && value === "radio" && __kivy__jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to it's default in case type is set after value - // This is for element creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - }, - // Use the value property for back compat - // Use the nodeHook for button elements in IE6/7 (#1954) - value: { - get: function( elem, name ) { - if ( nodeHook && __kivy__jQuery.nodeName( elem, "button" ) ) { - return nodeHook.get( elem, name ); - } - return name in elem ? - elem.value : - null; - }, - set: function( elem, value, name ) { - if ( nodeHook && __kivy__jQuery.nodeName( elem, "button" ) ) { - return nodeHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !__kivy__jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = __kivy__jQuery.propFix[ name ] || name; - hooks = __kivy__jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) -__kivy__jQuery.attrHooks.tabindex = __kivy__jQuery.propHooks.tabIndex; - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - // Align boolean attributes with corresponding properties - // Fall back to attribute presence where some booleans are not supported - var attrNode, - property = __kivy__jQuery.prop( elem, name ); - return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - var propName; - if ( value === false ) { - // Remove boolean attributes when set to false - __kivy__jQuery.removeAttr( elem, name ); - } else { - // value is true since we know at this point it's type boolean and not false - // Set boolean attributes to the same name and set the DOM property - propName = __kivy__jQuery.propFix[ name ] || name; - if ( propName in elem ) { - // Only set the IDL specifically if it already exists on the element - elem[ propName ] = true; - } - - elem.setAttribute( name, name.toLowerCase() ); - } - return name; - } -}; - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !getSetAttribute ) { - - fixSpecified = { - name: true, - id: true - }; - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = __kivy__jQuery.valHooks.button = { - get: function( elem, name ) { - var ret; - ret = elem.getAttributeNode( name ); - return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? - ret.nodeValue : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - ret = document.createAttribute( name ); - elem.setAttributeNode( ret ); - } - return ( ret.nodeValue = value + "" ); - } - }; - - // Apply the nodeHook to tabindex - __kivy__jQuery.attrHooks.tabindex.set = nodeHook.set; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - __kivy__jQuery.each([ "width", "height" ], function( i, name ) { - __kivy__jQuery.attrHooks[ name ] = __kivy__jQuery.extend( __kivy__jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); - - // Set contenteditable to false on removals(#10429) - // Setting to empty string throws an error as an invalid value - __kivy__jQuery.attrHooks.contenteditable = { - get: nodeHook.get, - set: function( elem, value, name ) { - if ( value === "" ) { - value = "false"; - } - nodeHook.set( elem, value, name ); - } - }; -} - - -// Some attributes require a special call on IE -if ( !__kivy__jQuery.support.hrefNormalized ) { - __kivy__jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - __kivy__jQuery.attrHooks[ name ] = __kivy__jQuery.extend( __kivy__jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret === null ? undefined : ret; - } - }); - }); -} - -if ( !__kivy__jQuery.support.style ) { - __kivy__jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Normalize to lowercase since IE uppercases css property names - return elem.style.cssText.toLowerCase() || undefined; - }, - set: function( elem, value ) { - return ( elem.style.cssText = "" + value ); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !__kivy__jQuery.support.optSelected ) { - __kivy__jQuery.propHooks.selected = __kivy__jQuery.extend( __kivy__jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// IE6/7 call enctype encoding -if ( !__kivy__jQuery.support.enctype ) { - __kivy__jQuery.propFix.enctype = "encoding"; -} - -// Radios and checkboxes getter/setter -if ( !__kivy__jQuery.support.checkOn ) { - __kivy__jQuery.each([ "radio", "checkbox" ], function() { - __kivy__jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -__kivy__jQuery.each([ "radio", "checkbox" ], function() { - __kivy__jQuery.valHooks[ this ] = __kivy__jQuery.extend( __kivy__jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( __kivy__jQuery.isArray( value ) ) { - return ( elem.checked = __kivy__jQuery.inArray( __kivy__jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); - - - - -var rformElems = /^(?:textarea|input|select)$/i, - rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, - rhoverHack = /\bhover(\.\S+)?\b/, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, - quickParse = function( selector ) { - var quick = rquickIs.exec( selector ); - if ( quick ) { - // 0 1 2 3 - // [ _, tag, id, class ] - quick[1] = ( quick[1] || "" ).toLowerCase(); - quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); - } - return quick; - }, - quickIs = function( elem, m ) { - var attrs = elem.attributes || {}; - return ( - (!m[1] || elem.nodeName.toLowerCase() === m[1]) && - (!m[2] || (attrs.id || {}).value === m[2]) && - (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) - ); - }, - hoverHack = function( events ) { - return __kivy__jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); - }; - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -__kivy__jQuery.event = { - - add: function( elem, types, handler, data, selector ) { - - var elemData, eventHandle, events, - t, tns, type, namespaces, handleObj, - handleObjIn, quick, handlers, special; - - // Don't attach events to noData or text/comment nodes (allow plain objects tho) - if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = __kivy__jQuery._data( elem )) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = __kivy__jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - events = elemData.events; - if ( !events ) { - elemData.events = events = {}; - } - eventHandle = elemData.handle; - if ( !eventHandle ) { - elemData.handle = eventHandle = function( e ) { - // Discard the second event of a __kivy__jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof __kivy__jQuery !== "undefined" && (!e || __kivy__jQuery.event.triggered !== e.type) ? - __kivy__jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - // __kivy__jQuery(...).bind("mouseover mouseout", fn); - types = __kivy__jQuery.trim( hoverHack(types) ).split( " " ); - for ( t = 0; t < types.length; t++ ) { - - tns = rtypenamespace.exec( types[t] ) || []; - type = tns[1]; - namespaces = ( tns[2] || "" ).split( "." ).sort(); - - // If event changes its type, use the special event handlers for the changed type - special = __kivy__jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = __kivy__jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = __kivy__jQuery.extend({ - type: type, - origType: tns[1], - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - quick: quickParse( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - handlers = events[ type ]; - if ( !handlers ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - __kivy__jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var elemData = __kivy__jQuery.hasData( elem ) && __kivy__jQuery._data( elem ), - t, tns, type, origType, namespaces, origCount, - j, events, special, handle, eventType, handleObj; - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = __kivy__jQuery.trim( hoverHack( types || "" ) ).split(" "); - for ( t = 0; t < types.length; t++ ) { - tns = rtypenamespace.exec( types[t] ) || []; - type = origType = tns[1]; - namespaces = tns[2]; - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - __kivy__jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = __kivy__jQuery.event.special[ type ] || {}; - type = ( selector? special.delegateType : special.bindType ) || type; - eventType = events[ type ] || []; - origCount = eventType.length; - namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - - // Remove matching events - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !namespaces || namespaces.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - eventType.splice( j--, 1 ); - - if ( handleObj.selector ) { - eventType.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( eventType.length === 0 && origCount !== eventType.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { - __kivy__jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( __kivy__jQuery.isEmptyObject( events ) ) { - handle = elemData.handle; - if ( handle ) { - handle.elem = null; - } - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - __kivy__jQuery.removeData( elem, [ "events", "handle" ], true ); - } - }, - - // Events that are safe to short-circuit if no handlers are attached. - // Native DOM events should not be added, they may have inline handlers. - customEvent: { - "getData": true, - "setData": true, - "changeData": true - }, - - trigger: function( event, data, elem, onlyHandlers ) { - // Don't do events on text and comment nodes - if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { - return; - } - - // Event object or event type - var type = event.type || event, - namespaces = [], - cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + __kivy__jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "!" ) >= 0 ) { - // Exclusive events trigger only for the exact event (no namespaces) - type = type.slice(0, -1); - exclusive = true; - } - - if ( type.indexOf( "." ) >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - - if ( (!elem || __kivy__jQuery.event.customEvent[ type ]) && !__kivy__jQuery.event.global[ type ] ) { - // No __kivy__jQuery handlers for this event type, and it can't have inline handlers - return; - } - - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // __kivy__jQuery.Event object - event[ __kivy__jQuery.expando ] ? event : - // Object literal - new __kivy__jQuery.Event( type, event ) : - // Just the event type (string) - new __kivy__jQuery.Event( type ); - - event.type = type; - event.isTrigger = true; - event.exclusive = exclusive; - event.namespace = namespaces.join( "." ); - event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; - - // Handle a global trigger - if ( !elem ) { - - // TODO: Stop taunting the data cache; remove global events and always attach to document - cache = __kivy__jQuery.cache; - for ( i in cache ) { - if ( cache[ i ].events && cache[ i ].events[ type ] ) { - __kivy__jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); - } - } - return; - } - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data != null ? __kivy__jQuery.makeArray( data ) : []; - data.unshift( event ); - - // Allow special events to draw outside the lines - special = __kivy__jQuery.event.special[ type ] || {}; - if ( special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - eventPath = [[ elem, special.bindType || type ]]; - if ( !onlyHandlers && !special.noBubble && !__kivy__jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; - old = null; - for ( ; cur; cur = cur.parentNode ) { - eventPath.push([ cur, bubbleType ]); - old = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( old && old === elem.ownerDocument ) { - eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); - } - } - - // Fire handlers on the event path - for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { - - cur = eventPath[i][0]; - event.type = eventPath[i][1]; - - handle = ( __kivy__jQuery._data( cur, "events" ) || {} )[ event.type ] && __kivy__jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - // Note that this is a bare JS function and not a __kivy__jQuery handler - handle = ontype && cur[ ontype ]; - if ( handle && __kivy__jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { - event.preventDefault(); - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && - !(type === "click" && __kivy__jQuery.nodeName( elem, "a" )) && __kivy__jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - // IE<9 dies on focus/blur to hidden element (#1486) - if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !__kivy__jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; - - if ( old ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - __kivy__jQuery.event.triggered = type; - elem[ type ](); - __kivy__jQuery.event.triggered = undefined; - - if ( old ) { - elem[ ontype ] = old; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable __kivy__jQuery.Event from the native event object - event = __kivy__jQuery.event.fix( event || window.event ); - - var handlers = ( (__kivy__jQuery._data( this, "events" ) || {} )[ event.type ] || []), - delegateCount = handlers.delegateCount, - args = [].slice.call( arguments, 0 ), - run_all = !event.exclusive && !event.namespace, - handlerQueue = [], - i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; - - // Use the fix-ed __kivy__jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Determine handlers that should run if there are delegated events - // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { - - // Pregenerate a single __kivy__jQuery object for reuse with .is() - jqcur = __kivy__jQuery(this); - jqcur.context = this.ownerDocument || this; - - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - selMatch = {}; - matches = []; - jqcur[0] = cur; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - sel = handleObj.selector; - - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = ( - handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) - ); - } - if ( selMatch[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); - } - } - } - - // Add the remaining (directly-bound) handlers - if ( handlers.length > delegateCount ) { - handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); - } - - // Run delegates first; they may want to stop propagation beneath us - for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { - matched = handlerQueue[ i ]; - event.currentTarget = matched.elem; - - for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { - handleObj = matched.matches[ j ]; - - // Triggered event must either 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { - - event.data = handleObj.data; - event.handleObj = handleObj; - - ret = ( (__kivy__jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - return event.result; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** - props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ __kivy__jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, - originalEvent = event, - fixHook = __kivy__jQuery.event.fixHooks[ event.type ] || {}, - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = __kivy__jQuery.Event( originalEvent ); - - for ( i = copy.length; i; ) { - prop = copy[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Target should not be a text node (#504, Safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) - if ( event.metaKey === undefined ) { - event.metaKey = event.ctrlKey; - } - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - ready: { - // Make sure the ready event is setup - setup: __kivy__jQuery.bindReady - }, - - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - - focus: { - delegateType: "focusin" - }, - blur: { - delegateType: "focusout" - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( __kivy__jQuery.isWindow( this ) ) { - this.onbeforeunload = eventHandle; - } - }, - - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = __kivy__jQuery.extend( - new __kivy__jQuery.Event(), - event, - { type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - __kivy__jQuery.event.trigger( e, null, elem ); - } else { - __kivy__jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -// Some plugins are using, but it's undocumented/deprecated and will be removed. -// The 1.7 special event interface should provide all the hooks needed now. -__kivy__jQuery.event.handle = __kivy__jQuery.event.dispatch; - -__kivy__jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - if ( elem.detachEvent ) { - elem.detachEvent( "on" + type, handle ); - } - }; - -__kivy__jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof __kivy__jQuery.Event) ) { - return new __kivy__jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - __kivy__jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || __kivy__jQuery.now(); - - // Mark it as fixed - this[ __kivy__jQuery.expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// __kivy__jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -__kivy__jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // otherwise set the returnValue property of the original event to false (IE) - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -__kivy__jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - __kivy__jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var target = this, - related = event.relatedTarget, - handleObj = event.handleObj, - selector = handleObj.selector, - ret; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !__kivy__jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// IE submit delegation -if ( !__kivy__jQuery.support.submitBubbles ) { - - __kivy__jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( __kivy__jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - __kivy__jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = __kivy__jQuery.nodeName( elem, "input" ) || __kivy__jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !form._submit_attached ) { - __kivy__jQuery.event.add( form, "submit._submit", function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( this.parentNode && !event.isTrigger ) { - __kivy__jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - }); - form._submit_attached = true; - } - }); - // return undefined since we don't need an event listener - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( __kivy__jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - __kivy__jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !__kivy__jQuery.support.changeBubbles ) { - - __kivy__jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - __kivy__jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - __kivy__jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - __kivy__jQuery.event.simulate( "change", this, event, true ); - } - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - __kivy__jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { - __kivy__jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - __kivy__jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - elem._change_attached = true; - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - __kivy__jQuery.event.remove( this, "._change" ); - - return rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !__kivy__jQuery.support.focusinBubbles ) { - __kivy__jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - __kivy__jQuery.event.simulate( fix, event.target, __kivy__jQuery.event.fix( event ), true ); - }; - - __kivy__jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -__kivy__jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - __kivy__jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = __kivy__jQuery.guid++ ); - } - return this.each( function() { - __kivy__jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on.call( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched __kivy__jQuery.Event - var handleObj = types.handleObj; - __kivy__jQuery( types.delegateTarget ).off( - handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( var type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - __kivy__jQuery.event.remove( this, types, fn, selector ); - }); - }, - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - live: function( types, data, fn ) { - __kivy__jQuery( this.context ).on( types, this.selector, data, fn ); - return this; - }, - die: function( types, fn ) { - __kivy__jQuery( this.context ).off( types, this.selector || "**", fn ); - return this; - }, - - delegate: function( selector, types, data, fn ) { - return this.on( types, selector, data, fn ); - }, - undelegate: function( selector, types, fn ) { - // ( namespace ) or ( selector, types [, fn] ) - return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); - }, - - trigger: function( type, data ) { - return this.each(function() { - __kivy__jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - if ( this[0] ) { - return __kivy__jQuery.event.trigger( type, data, this[0], true ); - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, - guid = fn.guid || __kivy__jQuery.guid++, - i = 0, - toggler = function( event ) { - // Figure out which function to execute - var lastToggle = ( __kivy__jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; - __kivy__jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - }; - - // link all the functions, so any of them can unbind this click handler - toggler.guid = guid; - while ( i < args.length ) { - args[ i++ ].guid = guid; - } - - return this.click( toggler ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -__kivy__jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { - - // Handle event binding - __kivy__jQuery.fn[ name ] = function( data, fn ) { - if ( fn == null ) { - fn = data; - data = null; - } - - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - - if ( __kivy__jQuery.attrFn ) { - __kivy__jQuery.attrFn[ name ] = true; - } - - if ( rkeyEvent.test( name ) ) { - __kivy__jQuery.event.fixHooks[ name ] = __kivy__jQuery.event.keyHooks; - } - - if ( rmouseEvent.test( name ) ) { - __kivy__jQuery.event.fixHooks[ name ] = __kivy__jQuery.event.mouseHooks; - } -}); - - - -/*! - * Sizzle CSS Selector Engine - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){ - -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - expando = "sizcache" + (Math.random() + '').replace('.', ''), - done = 0, - toString = Object.prototype.toString, - hasDuplicate = false, - baseHasDuplicate = true, - rBackslash = /\\/g, - rReturn = /\r\n/g, - rNonWord = /\W/; - -// Here we check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function() { - baseHasDuplicate = false; - return 0; -}); - -var Sizzle = function( selector, context, results, seed ) { - results = results || []; - context = context || document; - - var origContext = context; - - if ( context.nodeType !== 1 && context.nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - var m, set, checkSet, extra, ret, cur, pop, i, - prune = true, - contextXML = Sizzle.isXML( context ), - parts = [], - soFar = selector; - - // Reset the position of the chunker regexp (start from head) - do { - chunker.exec( "" ); - m = chunker.exec( soFar ); - - if ( m ) { - soFar = m[3]; - - parts.push( m[1] ); - - if ( m[2] ) { - extra = m[3]; - break; - } - } - } while ( m ); - - if ( parts.length > 1 && origPOS.exec( selector ) ) { - - if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context, seed ); - - } else { - set = Expr.relative[ parts[0] ] ? - [ context ] : - Sizzle( parts.shift(), context ); - - while ( parts.length ) { - selector = parts.shift(); - - if ( Expr.relative[ selector ] ) { - selector += parts.shift(); - } - - set = posProcess( selector, set, seed ); - } - } - - } else { - // Take a shortcut and set the context if the root selector is an ID - // (but not if it'll be faster if the inner selector is an ID) - if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && - Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { - - ret = Sizzle.find( parts.shift(), context, contextXML ); - context = ret.expr ? - Sizzle.filter( ret.expr, ret.set )[0] : - ret.set[0]; - } - - if ( context ) { - ret = seed ? - { expr: parts.pop(), set: makeArray(seed) } : - Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); - - set = ret.expr ? - Sizzle.filter( ret.expr, ret.set ) : - ret.set; - - if ( parts.length > 0 ) { - checkSet = makeArray( set ); - - } else { - prune = false; - } - - while ( parts.length ) { - cur = parts.pop(); - pop = cur; - - if ( !Expr.relative[ cur ] ) { - cur = ""; - } else { - pop = parts.pop(); - } - - if ( pop == null ) { - pop = context; - } - - Expr.relative[ cur ]( checkSet, pop, contextXML ); - } - - } else { - checkSet = parts = []; - } - } - - if ( !checkSet ) { - checkSet = set; - } - - if ( !checkSet ) { - Sizzle.error( cur || selector ); - } - - if ( toString.call(checkSet) === "[object Array]" ) { - if ( !prune ) { - results.push.apply( results, checkSet ); - - } else if ( context && context.nodeType === 1 ) { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { - results.push( set[i] ); - } - } - - } else { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && checkSet[i].nodeType === 1 ) { - results.push( set[i] ); - } - } - } - - } else { - makeArray( checkSet, results ); - } - - if ( extra ) { - Sizzle( extra, origContext, results, seed ); - Sizzle.uniqueSort( results ); - } - - return results; -}; - -Sizzle.uniqueSort = function( results ) { - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( var i = 1; i < results.length; i++ ) { - if ( results[i] === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - } - - return results; -}; - -Sizzle.matches = function( expr, set ) { - return Sizzle( expr, null, null, set ); -}; - -Sizzle.matchesSelector = function( node, expr ) { - return Sizzle( expr, null, null, [node] ).length > 0; -}; - -Sizzle.find = function( expr, context, isXML ) { - var set, i, len, match, type, left; - - if ( !expr ) { - return []; - } - - for ( i = 0, len = Expr.order.length; i < len; i++ ) { - type = Expr.order[i]; - - if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - left = match[1]; - match.splice( 1, 1 ); - - if ( left.substr( left.length - 1 ) !== "\\" ) { - match[1] = (match[1] || "").replace( rBackslash, "" ); - set = Expr.find[ type ]( match, context, isXML ); - - if ( set != null ) { - expr = expr.replace( Expr.match[ type ], "" ); - break; - } - } - } - } - - if ( !set ) { - set = typeof context.getElementsByTagName !== "undefined" ? - context.getElementsByTagName( "*" ) : - []; - } - - return { set: set, expr: expr }; -}; - -Sizzle.filter = function( expr, set, inplace, not ) { - var match, anyFound, - type, found, item, filter, left, - i, pass, - old = expr, - result = [], - curLoop = set, - isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); - - while ( expr && set.length ) { - for ( type in Expr.filter ) { - if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - filter = Expr.filter[ type ]; - left = match[1]; - - anyFound = false; - - match.splice(1,1); - - if ( left.substr( left.length - 1 ) === "\\" ) { - continue; - } - - if ( curLoop === result ) { - result = []; - } - - if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); - - if ( !match ) { - anyFound = found = true; - - } else if ( match === true ) { - continue; - } - } - - if ( match ) { - for ( i = 0; (item = curLoop[i]) != null; i++ ) { - if ( item ) { - found = filter( item, match, i, curLoop ); - pass = not ^ found; - - if ( inplace && found != null ) { - if ( pass ) { - anyFound = true; - - } else { - curLoop[i] = false; - } - - } else if ( pass ) { - result.push( item ); - anyFound = true; - } - } - } - } - - if ( found !== undefined ) { - if ( !inplace ) { - curLoop = result; - } - - expr = expr.replace( Expr.match[ type ], "" ); - - if ( !anyFound ) { - return []; - } - - break; - } - } - } - - // Improper expression - if ( expr === old ) { - if ( anyFound == null ) { - Sizzle.error( expr ); - - } else { - break; - } - } - - old = expr; - } - - return curLoop; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Utility function for retreiving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -var getText = Sizzle.getText = function( elem ) { - var i, node, - nodeType = elem.nodeType, - ret = ""; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 ) { - // Use textContent || innerText for elements - if ( typeof elem.textContent === 'string' ) { - return elem.textContent; - } else if ( typeof elem.innerText === 'string' ) { - // Replace IE's carriage returns - return elem.innerText.replace( rReturn, '' ); - } else { - // Traverse it's children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - } else { - - // If no nodeType, this is expected to be an array - for ( i = 0; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - if ( node.nodeType !== 8 ) { - ret += getText( node ); - } - } - } - return ret; -}; - -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - - match: { - ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - - leftMatch: {}, - - attrMap: { - "class": "className", - "for": "htmlFor" - }, - - attrHandle: { - href: function( elem ) { - return elem.getAttribute( "href" ); - }, - type: function( elem ) { - return elem.getAttribute( "type" ); - } - }, - - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !rNonWord.test( part ), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} - - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } - - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); - } - }, - - ">": function( checkSet, part ) { - var elem, - isPartStr = typeof part === "string", - i = 0, - l = checkSet.length; - - if ( isPartStr && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; - } - } - - } else { - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; - } - } - - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, - - "": function(checkSet, part, isXML){ - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); - }, - - "~": function( checkSet, part, isXML ) { - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); - } - }, - - find: { - ID: function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }, - - NAME: function( match, context ) { - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], - results = context.getElementsByName( match[1] ); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } - } - - return ret.length === 0 ? null : ret; - } - }, - - TAG: function( match, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( match[1] ); - } - } - }, - preFilter: { - CLASS: function( match, curLoop, inplace, result, not, isXML ) { - match = " " + match[1].replace( rBackslash, "" ) + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); - } - - } else if ( inplace ) { - curLoop[i] = false; - } - } - } - - return false; - }, - - ID: function( match ) { - return match[1].replace( rBackslash, "" ); - }, - - TAG: function( match, curLoop ) { - return match[1].replace( rBackslash, "" ).toLowerCase(); - }, - - CHILD: function( match ) { - if ( match[1] === "nth" ) { - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - match[2] = match[2].replace(/^\+|\s*/g, ''); - - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - - ATTR: function( match, curLoop, inplace, result, not, isXML ) { - var name = match[1] = match[1].replace( rBackslash, "" ); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - // Handle if an un-quoted value was used - match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - - PSEUDO: function( match, curLoop, inplace, result, not ) { - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - - if ( !inplace ) { - result.push.apply( result, ret ); - } - - return false; - } - - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - - POS: function( match ) { - match.unshift( true ); - - return match; - } - }, - - filters: { - enabled: function( elem ) { - return elem.disabled === false && elem.type !== "hidden"; - }, - - disabled: function( elem ) { - return elem.disabled === true; - }, - - checked: function( elem ) { - return elem.checked === true; - }, - - selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - parent: function( elem ) { - return !!elem.firstChild; - }, - - empty: function( elem ) { - return !elem.firstChild; - }, - - has: function( elem, i, match ) { - return !!Sizzle( match[3], elem ).length; - }, - - header: function( elem ) { - return (/h\d/i).test( elem.nodeName ); - }, - - text: function( elem ) { - var attr = elem.getAttribute( "type" ), type = elem.type; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); - }, - - radio: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; - }, - - checkbox: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; - }, - - file: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; - }, - - password: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; - }, - - submit: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "submit" === elem.type; - }, - - image: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; - }, - - reset: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "reset" === elem.type; - }, - - button: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && "button" === elem.type || name === "button"; - }, - - input: function( elem ) { - return (/input|select|textarea|button/i).test( elem.nodeName ); - }, - - focus: function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - setFilters: { - first: function( elem, i ) { - return i === 0; - }, - - last: function( elem, i, match, array ) { - return i === array.length - 1; - }, - - even: function( elem, i ) { - return i % 2 === 0; - }, - - odd: function( elem, i ) { - return i % 2 === 1; - }, - - lt: function( elem, i, match ) { - return i < match[3] - 0; - }, - - gt: function( elem, i, match ) { - return i > match[3] - 0; - }, - - nth: function( elem, i, match ) { - return match[3] - 0 === i; - }, - - eq: function( elem, i, match ) { - return match[3] - 0 === i; - } - }, - filter: { - PSEUDO: function( elem, match, i, array ) { - var name = match[1], - filter = Expr.filters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - - } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; - - } else if ( name === "not" ) { - var not = match[3]; - - for ( var j = 0, l = not.length; j < l; j++ ) { - if ( not[j] === elem ) { - return false; - } - } - - return true; - - } else { - Sizzle.error( name ); - } - }, - - CHILD: function( elem, match ) { - var first, last, - doneName, parent, cache, - count, diff, - type = match[1], - node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - - case "nth": - first = match[2]; - last = match[3]; - - if ( first === 1 && last === 0 ) { - return true; - } - - doneName = match[0]; - parent = elem.parentNode; - - if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { - count = 0; - - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.nodeIndex = ++count; - } - } - - parent[ expando ] = doneName; - } - - diff = elem.nodeIndex - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - } - }, - - ID: function( elem, match ) { - return elem.nodeType === 1 && elem.getAttribute("id") === match; - }, - - TAG: function( elem, match ) { - return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; - }, - - CLASS: function( elem, match ) { - return (" " + (elem.className || elem.getAttribute("class")) + " ") - .indexOf( match ) > -1; - }, - - ATTR: function( elem, match ) { - var name = match[1], - result = Sizzle.attr ? - Sizzle.attr( elem, name ) : - Expr.attrHandle[ name ] ? - Expr.attrHandle[ name ]( elem ) : - elem[ name ] != null ? - elem[ name ] : - elem.getAttribute( name ), - value = result + "", - type = match[2], - check = match[4]; - - return result == null ? - type === "!=" : - !type && Sizzle.attr ? - result != null : - type === "=" ? - value === check : - type === "*=" ? - value.indexOf(check) >= 0 : - type === "~=" ? - (" " + value + " ").indexOf(check) >= 0 : - !check ? - value && result !== false : - type === "!=" ? - value !== check : - type === "^=" ? - value.indexOf(check) === 0 : - type === "$=" ? - value.substr(value.length - check.length) === check : - type === "|=" ? - value === check || value.substr(0, check.length + 1) === check + "-" : - false; - }, - - POS: function( elem, match, i, array ) { - var name = match[2], - filter = Expr.setFilters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } - } - } -}; - -var origPOS = Expr.match.POS, - fescape = function(all, num){ - return "\\" + (num - 0 + 1); - }; - -for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); - Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); -} - -var makeArray = function( array, results ) { - array = Array.prototype.slice.call( array, 0 ); - - if ( results ) { - results.push.apply( results, array ); - return results; - } - - return array; -}; - -// Perform a simple check to determine if the browser is capable of -// converting a NodeList to an array using builtin methods. -// Also verifies that the returned array holds DOM nodes -// (which is not the case in the Blackberry browser) -try { - Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; - -// Provide a fallback method if it does not work -} catch( e ) { - makeArray = function( array, results ) { - var i = 0, - ret = results || []; - - if ( toString.call(array) === "[object Array]" ) { - Array.prototype.push.apply( ret, array ); - - } else { - if ( typeof array.length === "number" ) { - for ( var l = array.length; i < l; i++ ) { - ret.push( array[i] ); - } - - } else { - for ( ; array[i]; i++ ) { - ret.push( array[i] ); - } - } - } - - return ret; - }; -} - -var sortOrder, siblingCheck; - -if ( document.documentElement.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { - return a.compareDocumentPosition ? -1 : 1; - } - - return a.compareDocumentPosition(b) & 4 ? -1 : 1; - }; - -} else { - sortOrder = function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - - siblingCheck = function( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; - }; -} - -// Check to see if the browser returns elements by name when -// querying by getElementById (and provide a workaround) -(function(){ - // We're going to inject a fake input element with a specified name - var form = document.createElement("div"), - id = "script" + (new Date()).getTime(), - root = document.documentElement; - - form.innerHTML = ""; - - // Inject it into the root element, check its status, and remove it quickly - root.insertBefore( form, root.firstChild ); - - // The workaround has to do additional checks after a getElementById - // Which slows things down for other browsers (hence the branching) - if ( document.getElementById( id ) ) { - Expr.find.ID = function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - - return m ? - m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? - [m] : - undefined : - []; - } - }; - - Expr.filter.ID = function( elem, match ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - - return elem.nodeType === 1 && node && node.nodeValue === match; - }; - } - - root.removeChild( form ); - - // release memory in IE - root = form = null; -})(); - -(function(){ - // Check to see if the browser returns only elements - // when doing getElementsByTagName("*") - - // Create a fake element - var div = document.createElement("div"); - div.appendChild( document.createComment("") ); - - // Make sure no comments are found - if ( div.getElementsByTagName("*").length > 0 ) { - Expr.find.TAG = function( match, context ) { - var results = context.getElementsByTagName( match[1] ); - - // Filter out possible comments - if ( match[1] === "*" ) { - var tmp = []; - - for ( var i = 0; results[i]; i++ ) { - if ( results[i].nodeType === 1 ) { - tmp.push( results[i] ); - } - } - - results = tmp; - } - - return results; - }; - } - - // Check to see if an attribute returns normalized href attributes - div.innerHTML = ""; - - if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && - div.firstChild.getAttribute("href") !== "#" ) { - - Expr.attrHandle.href = function( elem ) { - return elem.getAttribute( "href", 2 ); - }; - } - - // release memory in IE - div = null; -})(); - -if ( document.querySelectorAll ) { - (function(){ - var oldSizzle = Sizzle, - div = document.createElement("div"), - id = "__sizzle__"; - - div.innerHTML = "

"; - - // Safari can't handle uppercase or unicode characters when - // in quirks mode. - if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { - return; - } - - Sizzle = function( query, context, extra, seed ) { - context = context || document; - - // Only use querySelectorAll on non-XML documents - // (ID selectors don't work in non-HTML documents) - if ( !seed && !Sizzle.isXML(context) ) { - // See if we find a selector to speed up - var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); - - if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { - // Speed-up: Sizzle("TAG") - if ( match[1] ) { - return makeArray( context.getElementsByTagName( query ), extra ); - - // Speed-up: Sizzle(".CLASS") - } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { - return makeArray( context.getElementsByClassName( match[2] ), extra ); - } - } - - if ( context.nodeType === 9 ) { - // Speed-up: Sizzle("body") - // The body element only exists once, optimize finding it - if ( query === "body" && context.body ) { - return makeArray( [ context.body ], extra ); - - // Speed-up: Sizzle("#ID") - } else if ( match && match[3] ) { - var elem = context.getElementById( match[3] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id === match[3] ) { - return makeArray( [ elem ], extra ); - } - - } else { - return makeArray( [], extra ); - } - } - - try { - return makeArray( context.querySelectorAll(query), extra ); - } catch(qsaError) {} - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var oldContext = context, - old = context.getAttribute( "id" ), - nid = old || id, - hasParent = context.parentNode, - relativeHierarchySelector = /^\s*[+~]/.test( query ); - - if ( !old ) { - context.setAttribute( "id", nid ); - } else { - nid = nid.replace( /'/g, "\\$&" ); - } - if ( relativeHierarchySelector && hasParent ) { - context = context.parentNode; - } - - try { - if ( !relativeHierarchySelector || hasParent ) { - return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); - } - - } catch(pseudoError) { - } finally { - if ( !old ) { - oldContext.removeAttribute( "id" ); - } - } - } - } - - return oldSizzle(query, context, extra, seed); - }; - - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } - - // release memory in IE - div = null; - })(); -} - -(function(){ - var html = document.documentElement, - matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; - - if ( matches ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9 fails this) - var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), - pseudoWorks = false; - - try { - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( document.documentElement, "[test!='']:sizzle" ); - - } catch( pseudoError ) { - pseudoWorks = true; - } - - Sizzle.matchesSelector = function( node, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); - - if ( !Sizzle.isXML( node ) ) { - try { - if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { - var ret = matches.call( node, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || !disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9, so check for that - node.document && node.document.nodeType !== 11 ) { - return ret; - } - } - } catch(e) {} - } - - return Sizzle(expr, null, null, [node]).length > 0; - }; - } -})(); - -(function(){ - var div = document.createElement("div"); - - div.innerHTML = "
"; - - // Opera can't find a second classname (in 9.6) - // Also, make sure that getElementsByClassName actually exists - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - - if ( div.getElementsByClassName("e").length === 1 ) { - return; - } - - Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function( match, context, isXML ) { - if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { - return context.getElementsByClassName(match[1]); - } - }; - - // release memory in IE - div = null; -})(); - -function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem[ expando ] === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 && !isXML ){ - elem[ expando ] = doneName; - elem.sizset = i; - } - - if ( elem.nodeName.toLowerCase() === cur ) { - match = elem; - break; - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem[ expando ] === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 ) { - if ( !isXML ) { - elem[ expando ] = doneName; - elem.sizset = i; - } - - if ( typeof cur !== "string" ) { - if ( elem === cur ) { - match = true; - break; - } - - } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { - match = elem; - break; - } - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -if ( document.documentElement.contains ) { - Sizzle.contains = function( a, b ) { - return a !== b && (a.contains ? a.contains(b) : true); - }; - -} else if ( document.documentElement.compareDocumentPosition ) { - Sizzle.contains = function( a, b ) { - return !!(a.compareDocumentPosition(b) & 16); - }; - -} else { - Sizzle.contains = function() { - return false; - }; -} - -Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; - - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -var posProcess = function( selector, context, seed ) { - var match, - tmpSet = [], - later = "", - root = context.nodeType ? [context] : context; - - // Position selectors must be done after the filter - // And so must :not(positional) so we move all PSEUDOs to the end - while ( (match = Expr.match.PSEUDO.exec( selector )) ) { - later += match[0]; - selector = selector.replace( Expr.match.PSEUDO, "" ); - } - - selector = Expr.relative[selector] ? selector + "*" : selector; - - for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet, seed ); - } - - return Sizzle.filter( later, tmpSet ); -}; - -// EXPOSE -// Override sizzle attribute retrieval -Sizzle.attr = __kivy__jQuery.attr; -Sizzle.selectors.attrMap = {}; -__kivy__jQuery.find = Sizzle; -__kivy__jQuery.expr = Sizzle.selectors; -__kivy__jQuery.expr[":"] = __kivy__jQuery.expr.filters; -__kivy__jQuery.unique = Sizzle.uniqueSort; -__kivy__jQuery.text = Sizzle.getText; -__kivy__jQuery.isXMLDoc = Sizzle.isXML; -__kivy__jQuery.contains = Sizzle.contains; - - -})(); - - -var runtil = /Until$/, - rparentsprev = /^(?:parents|prevUntil|prevAll)/, - // Note: This RegExp should be improved, or likely pulled from Sizzle - rmultiselector = /,/, - isSimple = /^.[^:#\[\.,]*$/, - slice = Array.prototype.slice, - POS = __kivy__jQuery.expr.match.POS, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -__kivy__jQuery.fn.extend({ - find: function( selector ) { - var self = this, - i, l; - - if ( typeof selector !== "string" ) { - return __kivy__jQuery( selector ).filter(function() { - for ( i = 0, l = self.length; i < l; i++ ) { - if ( __kivy__jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }); - } - - var ret = this.pushStack( "", "find", selector ), - length, n, r; - - for ( i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - __kivy__jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( n = length; n < ret.length; n++ ) { - for ( r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var targets = __kivy__jQuery( target ); - return this.filter(function() { - for ( var i = 0, l = targets.length; i < l; i++ ) { - if ( __kivy__jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && ( - typeof selector === "string" ? - // If this is a positional selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - POS.test( selector ) ? - __kivy__jQuery( selector, this.context ).index( this[0] ) >= 0 : - __kivy__jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var ret = [], i, l, cur = this[0]; - - // Array (deprecated as of __kivy__jQuery 1.7) - if ( __kivy__jQuery.isArray( selectors ) ) { - var level = 1; - - while ( cur && cur.ownerDocument && cur !== context ) { - for ( i = 0; i < selectors.length; i++ ) { - - if ( __kivy__jQuery( cur ).is( selectors[ i ] ) ) { - ret.push({ selector: selectors[ i ], elem: cur, level: level }); - } - } - - cur = cur.parentNode; - level++; - } - - return ret; - } - - // String - var pos = POS.test( selectors ) || typeof selectors !== "string" ? - __kivy__jQuery( selectors, context || this.context ) : - 0; - - for ( i = 0, l = this.length; i < l; i++ ) { - cur = this[i]; - - while ( cur ) { - if ( pos ? pos.index(cur) > -1 : __kivy__jQuery.find.matchesSelector(cur, selectors) ) { - ret.push( cur ); - break; - - } else { - cur = cur.parentNode; - if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { - break; - } - } - } - } - - ret = ret.length > 1 ? __kivy__jQuery.unique( ret ) : ret; - - return this.pushStack( ret, "closest", selectors ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return __kivy__jQuery.inArray( this[0], __kivy__jQuery( elem ) ); - } - - // Locate the position of the desired element - return __kivy__jQuery.inArray( - // If it receives a __kivy__jQuery object, the first element is used - elem.__kivy__jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - __kivy__jQuery( selector, context ) : - __kivy__jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = __kivy__jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - __kivy__jQuery.unique( all ) ); - }, - - andSelf: function() { - return this.add( this.prevObject ); - } -}); - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -__kivy__jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return __kivy__jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return __kivy__jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return __kivy__jQuery.nth( elem, 2, "nextSibling" ); - }, - prev: function( elem ) { - return __kivy__jQuery.nth( elem, 2, "previousSibling" ); - }, - nextAll: function( elem ) { - return __kivy__jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return __kivy__jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return __kivy__jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return __kivy__jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return __kivy__jQuery.sibling( elem.parentNode.firstChild, elem ); - }, - children: function( elem ) { - return __kivy__jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return __kivy__jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - __kivy__jQuery.makeArray( elem.childNodes ); - } -}, function( name, fn ) { - __kivy__jQuery.fn[ name ] = function( until, selector ) { - var ret = __kivy__jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = __kivy__jQuery.filter( selector, ret ); - } - - ret = this.length > 1 && !guaranteedUnique[ name ] ? __kivy__jQuery.unique( ret ) : ret; - - if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, slice.call( arguments ).join(",") ); - }; -}); - -__kivy__jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - __kivy__jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : - __kivy__jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !__kivy__jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - nth: function( cur, result, dir, elem ) { - result = result || 1; - var num = 0; - - for ( ; cur; cur = cur[dir] ) { - if ( cur.nodeType === 1 && ++num === result ) { - break; - } - } - - return cur; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - if ( __kivy__jQuery.isFunction( qualifier ) ) { - return __kivy__jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - - } else if ( qualifier.nodeType ) { - return __kivy__jQuery.grep(elements, function( elem, i ) { - return ( elem === qualifier ) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = __kivy__jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return __kivy__jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = __kivy__jQuery.filter( qualifier, filtered ); - } - } - - return __kivy__jQuery.grep(elements, function( elem, i ) { - return ( __kivy__jQuery.inArray( elem, qualifier ) >= 0 ) === keep; - }); -} - - - - -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinline__kivy__jQuery = / __kivy__jQuery\d+="(?:\d+|null)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, - rtagName = /<([\w:]+)/, - rtbody = /", "" ], - legend: [ 1, "
", "
" ], - thead: [ 1, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - col: [ 2, "", "
" ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }, - safeFragment = createSafeFragment( document ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize and - - - - -Some <select> element from JIRA:
-(click submit to test the post data sent)

- - -
- - - - - -
- -

With Fixed as "selected":

- - - -

Add new <select> element dynamically on a page:
- (select boxes are checked every second on a page,
- you should see a normal select element transforming
- into js select box a moment after you click the button)

- - - - - -
- -

- - - -

Test interaction with other webpages that already use some other
- javascript boxes library. -

- -

These links need to be opened in the kivy_.py example:

- - - -

Some libraries that are not well-designed will display
- the select element twice, take a look at this for example:

- - - http://adam.co/lab/jquery/customselect/ (open in kivy_.py example) - -

-Although, it will still be functional, just with a display glitch. -

- -

The following code was added to the - SelectBox library to transform <select> element into - js box only if it's visible on a webpage:

- -
-    // This should fix interaction with other js boxes libraries,
-    // so that the select element does not appear twice.
-    if (!$(select).is(":visible")) {
-        return;
-    }
-
- -Also this code gives one second to other select box libraries
-to do their stuff, before we start ours: - -
-    __kivy__jQuery(document).ready(function() {
-    // Transform select elements only after a second,
-    // this will give time other select boxes libraries
-    // included on a webpage to their stuff first.
-    window.setInterval(function(){
-        __kivy__jQuery(document).ready(function() { 
-            __kivy__jQuery("select").__kivy__selectBox(); 
-        });
-    }, 1000);
-});
-
- - - - \ No newline at end of file diff --git a/src/linux/binaries_32bit/kivy-select-boxes/readme.md b/src/linux/binaries_32bit/kivy-select-boxes/readme.md deleted file mode 100644 index ad45aa7f..00000000 --- a/src/linux/binaries_32bit/kivy-select-boxes/readme.md +++ /dev/null @@ -1,156 +0,0 @@ -# jQuery selectBox: A styleable replacement for SELECT elements - -_Licensed under the MIT license: http://opensource.org/licenses/MIT_ - -## Features - -* Supports OPTGROUPS -* Supports standard dropdown controls -* Supports multi-select controls (i.e. multiple="multiple") -* Supports inline controls (i.e. size="5") -* Fully accessible via keyboard -* Shift + click (or shift + enter) to select a range of options in multi-select controls -* Type to search when the control has focus -* Auto-height based on the size attribute (to use, omit the height property in your CSS!) -* Tested in IE7-IE9, Firefox 3-4, recent WebKit browsers, and Opera - - -## Usage - -Link to the JS file: - -```html - -``` - -Add the CSS file (or append contents to your own stylesheet): - -```html - -``` - -To initialize: - -```javascript -// default -$('select').selectBox(); - -// or with custom settings -$('select').selectBox({ - mobile: true, - menuSpeed: 'fast' -}); -``` - -## Settings - -| Key | Default | Values | Description | -| ---------------|:-------------:|---------------------------:|-------------------------------------------------:| -| mobile | `false` | Boolean | Disables the widget for mobile devices | -| menuTransition | `default` | `default`, `slide`, `fade` | The show/hide transition for dropdown menus | -| menuSpeed | `normal` | `slow`, `normal`, `fast` | The show/hide transition speed | -| loopOptions | `false` | Boolean | Flag to allow arrow keys to loop through options | - - -To specify settings after the init, use this syntax: - -```javascript -$('select').selectBox('settings', {settingName: value, ... }); -``` - -## Methods - -To call a method use this syntax: - -```javascript -$('select').selectBox('methodName', [option]); -``` - -### Available methods - - -| Key | Description | -| ---------------|-----------------------------------------------------------------------------------------------| -| create | Creates the control (default) | -| destroy | Destroys the selectBox control and reverts back to the original form control | -| disable | Disables the control (i.e. disabled="disabled") | -| enable | Enables the control | -| value | If passed with a value, sets the control to that value; otherwise returns the current value | -| options | If passed either a string of HTML or a JSON object, replaces the existing options; otherwise Returns the options container element as a jQuery object | -| control | Returns the selectBox control element (an anchor tag) for working with directly | -| refresh | Updates the selectBox control's options based on the original controls options | -| instance | Returns the SelectBox instance, where you have more methods available (only in v1.2.0-dev | - | available) as in the `SelectBox` class below. | - -## API `SelectBox` - -You can instantiate the selectBox also through a classic OOP way: - -```javascript -var selectBox = new SelectBox($('#mySelectBox'), settings = {}); -selectBox.showMenu(); -``` - -The public methods are: - -```javascript -refresh() -destroy() -disable() -enable() - -getLabelClass() -getLabelText() -getSelectElement() -getOptions(String type = 'inline'|'dropdown') - -hideMenus() -showMenu() - -setLabel() -setOptions(Object options) -setValue(String value) - -removeHover(HTMLElement li) -addHover(HTMLElement li) - -disableSelection(HTMLElement selector) -generateOptions(jQuery self, jQuery options) -handleKeyDown(event) -handleKeyPress(event) -init(options) -keepOptionInView(jQuery li, Boolean center) -refresh() -removeHover(HTMLElement li) -selectOption(HTMLElement li, event) -``` - -## Events - -Events are fired on the original select element. You can bind events like this: - -```javascript -$('select').selectBox().change(function () { - alert($(this).val()); -}); -``` - -### Available events - -| Key | Description | -| ---------------|-----------------------------------------------------------------------------------------------| -| focus | Fired when the control gains focus | -| blur | Fired when the control loses focus | -| change | Fired when the value of a control changes | -| beforeopen | Fired before a dropdown menu opens (cancelable) | -| open | Fired after a dropdown menu opens (not cancelable) | -| beforeclose | Fired before a dropdown menu closes (cancelable) | -| close | Fired after a dropdown menu closes (not cancelable) | - -### Known Issues - -* The blur and focus callbacks are not very reliable in IE7. The change callback works fine. - -## Credits - -Original plugin by Cory LaViska of A Beautiful Site, LLC. (http://www.abeautifulsite.net/) \ No newline at end of file diff --git a/src/linux/binaries_32bit/kivy_.py b/src/linux/binaries_32bit/kivy_.py deleted file mode 100644 index 258e0969..00000000 --- a/src/linux/binaries_32bit/kivy_.py +++ /dev/null @@ -1,677 +0,0 @@ -# An example of embedding CEF browser in the Kivy framework. -# The browser is embedded using off-screen rendering mode. - -# Tested using Kivy 1.7.2 stable on Ubuntu 12.04 64-bit. - -# In this example kivy-lang is used to declare the layout which -# contains two buttons (back, forward) and the browser view. - -# The official CEF Python binaries come with tcmalloc hook -# disabled. But if you've built custom binaries and kept tcmalloc -# hook enabled, then be aware that in such case it is required -# for the cefpython module to be the very first import in -# python scripts. See Issue 73 in the CEF Python Issue Tracker -# for more details. - -import ctypes, os, sys -libcef_so = os.path.join(os.path.dirname( - os.path.abspath(__file__)), 'libcef.so') -if os.path.exists(libcef_so): - # Import local module - ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) - if 0x02070000 <= sys.hexversion < 0x03000000: - import cefpython_py27 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import from package - from cefpython3 import cefpython - -from kivy.app import App -from kivy.uix.widget import Widget -from kivy.graphics import Color, Rectangle, GraphicException -from kivy.clock import Clock -from kivy.graphics.texture import Texture -from kivy.core.window import Window -from kivy.lang import Builder -from kivy.uix.boxlayout import BoxLayout -from kivy.base import EventLoop - -####Kivy APP #### -Builder.load_string(""" - -: - orientation: 'vertical' - BoxLayout: - size_hint_y: None - width: 80 - Button: - text: "Back" - on_press: browser.go_back() - Button: - text: "Forward" - on_press: browser.go_forward() - CefBrowser: - id: browser - -""") - - - -class BrowserLayout(BoxLayout): - - def __init__(self, **kwargs): - super(BrowserLayout, self).__init__(**kwargs) - - - -class CefBrowser(Widget): - - # Keyboard mode: "global" or "local". - # 1. Global mode forwards keys to CEF all the time. - # 2. Local mode forwards keys to CEF only when an editable - # control is focused (input type=text|password or textarea). - keyboard_mode = "global" - - '''Represent a browser widget for kivy, which can be used like a normal widget. - ''' - def __init__(self, start_url='http://www.google.com/', **kwargs): - super(CefBrowser, self).__init__(**kwargs) - - self.start_url = start_url - - #Workaround for flexible size: - #start browser when the height has changed (done by layout) - #This has to be done like this because I wasn't able to change - #the texture size - #until runtime without core-dump. - self.bind(size = self.size_changed) - - - starting = True - def size_changed(self, *kwargs): - '''When the height of the cefbrowser widget got changed, create the browser - ''' - if self.starting: - if self.height != 100: - self.start_cef() - self.starting = False - else: - self.texture = Texture.create( - size=self.size, colorfmt='rgba', bufferfmt='ubyte') - self.texture.flip_vertical() - with self.canvas: - Color(1, 1, 1) - # This will cause segmentation fault: - # | self.rect = Rectangle(size=self.size, texture=self.texture) - # Update only the size: - self.rect.size = self.size - self.browser.WasResized() - - - def _cef_mes(self, *kwargs): - '''Get called every frame. - ''' - cefpython.MessageLoopWork() - - - def _update_rect(self, *kwargs): - '''Get called whenever the texture got updated. - => we need to reset the texture for the rectangle - ''' - self.rect.texture = self.texture - - - def start_cef(self): - '''Starts CEF. - ''' - # create texture & add it to canvas - self.texture = Texture.create( - size=self.size, colorfmt='rgba', bufferfmt='ubyte') - self.texture.flip_vertical() - with self.canvas: - Color(1, 1, 1) - self.rect = Rectangle(size=self.size, texture=self.texture) - - #configure cef - settings = { - "debug": True, # cefpython debug messages in console and in log_file - "log_severity": cefpython.LOGSEVERITY_INFO, - "log_file": "debug.log", - # This directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - "browser_subprocess_path": "%s/%s" % (cefpython.GetModuleDirectory(), "subprocess")} - - #start idle - Clock.schedule_interval(self._cef_mes, 0) - - #init CEF - cefpython.Initialize(settings) - - #WindowInfo offscreen flag - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsOffscreen(0) - - #Create Broswer and naviagte to empty page <= OnPaint won't get called yet - browserSettings = {} - # The render handler callbacks are not yet set, thus an - # error report will be thrown in the console (when release - # DCHECKS are enabled), however don't worry, it is harmless. - # This is happening because calling GetViewRect will return - # false. That's why it is initially navigating to "about:blank". - # Later, a real url will be loaded using the LoadUrl() method - # and the GetViewRect will be called again. This time the render - # handler callbacks will be available, it will work fine from - # this point. - # -- - # Do not use "about:blank" as navigateUrl - this will cause - # the GoBack() and GoForward() methods to not work. - self.browser = cefpython.CreateBrowserSync(windowInfo, browserSettings, - navigateUrl=self.start_url) - - #set focus - self.browser.SendFocusEvent(True) - - self._client_handler = ClientHandler(self) - self.browser.SetClientHandler(self._client_handler) - self.set_js_bindings() - - #Call WasResized() => force cef to call GetViewRect() and OnPaint afterwards - self.browser.WasResized() - - # The browserWidget instance is required in OnLoadingStateChange(). - self.browser.SetUserData("browserWidget", self) - - if self.keyboard_mode == "global": - self.request_keyboard() - - # Clock.schedule_once(self.change_url, 5) - - - _client_handler = None - _js_bindings = None - - def set_js_bindings(self): - if not self._js_bindings: - self._js_bindings = cefpython.JavascriptBindings( - bindToFrames=True, bindToPopups=True) - self._js_bindings.SetFunction("__kivy__request_keyboard", - self.request_keyboard) - self._js_bindings.SetFunction("__kivy__release_keyboard", - self.release_keyboard) - self.browser.SetJavascriptBindings(self._js_bindings) - - - def change_url(self, *kwargs): - # Doing a javascript redirect instead of Navigate() - # solves the js bindings error. The url here need to - # be preceded with "http://". Calling StopLoad() - # might be a good idea before making the js navigation. - - self.browser.StopLoad() - self.browser.GetMainFrame().ExecuteJavascript( - "window.location='http://www.youtube.com/'") - - # Do not use Navigate() or GetMainFrame()->LoadURL(), - # as it causes the js bindings to be removed. There is - # a bug in CEF, that happens after a call to Navigate(). - # The OnBrowserDestroyed() callback is fired and causes - # the js bindings to be removed. See this topic for more - # details: - # http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=11009 - - # OFF: - # | self.browser.Navigate("http://www.youtube.com/") - - _keyboard = None - - def request_keyboard(self): - print("request_keyboard()") - self._keyboard = EventLoop.window.request_keyboard( - self.release_keyboard, self) - self._keyboard.bind(on_key_down=self.on_key_down) - self._keyboard.bind(on_key_up=self.on_key_up) - self.is_shift1 = False - self.is_shift2 = False - self.is_ctrl1 = False - self.is_ctrl2 = False - self.is_alt1 = False - self.is_alt2 = False - # Not sure if it is still required to send the focus - # (some earlier bug), but it shouldn't hurt to call it. - self.browser.SendFocusEvent(True) - - - def release_keyboard(self): - # When using local keyboard mode, do all the request - # and releases of the keyboard through js bindings, - # otherwise some focus problems arise. - self.is_shift1 = False - self.is_shift2 = False - self.is_ctrl1 = False - self.is_ctrl2 = False - self.is_alt1 = False - self.is_alt2 = False - if not self._keyboard: - return - print("release_keyboard()") - self._keyboard.unbind(on_key_down=self.on_key_down) - self._keyboard.unbind(on_key_up=self.on_key_up) - self._keyboard.release() - - # Kivy does not provide modifiers in on_key_up, but these - # must be sent to CEF as well. - is_shift1 = False - is_shift2 = False - is_ctrl1 = False - is_ctrl2 = False - is_alt1 = False - is_alt2 = False - - def on_key_down(self, keyboard, keycode, text, modifiers): - # Notes: - # - right alt modifier is not sent by Kivy through modifiers param. - # print("on_key_down(): keycode = %s, text = %s, modifiers = %s" % ( - # keycode, text, modifiers)) - if keycode[0] == 27: - # On escape release the keyboard, see the injected - # javascript in OnLoadStart(). - self.browser.GetFocusedFrame().ExecuteJavascript( - "__kivy__on_escape()") - return - cefModifiers = cefpython.EVENTFLAG_NONE - if "shift" in modifiers: - cefModifiers |= cefpython.EVENTFLAG_SHIFT_DOWN - if "ctrl" in modifiers: - cefModifiers |= cefpython.EVENTFLAG_CONTROL_DOWN - if "alt" in modifiers: - cefModifiers |= cefpython.EVENTFLAG_ALT_DOWN - if "capslock" in modifiers: - cefModifiers |= cefpython.EVENTFLAG_CAPS_LOCK_ON - # print("on_key_down(): cefModifiers = %s" % cefModifiers) - cef_keycode = self.translate_to_cef_keycode(keycode[0]) - keyEvent = { - "type": cefpython.KEYEVENT_RAWKEYDOWN, - "native_key_code": cef_keycode, - "modifiers": cefModifiers - } - # print("keydown keyEvent: %s" % keyEvent) - self.browser.SendKeyEvent(keyEvent) - if keycode[0] == 304: - self.is_shift1 = True - elif keycode[0] == 303: - self.is_shift2 = True - elif keycode[0] == 306: - self.is_ctrl1 = True - elif keycode[0] == 305: - self.is_ctrl2 = True - elif keycode[0] == 308: - self.is_alt1 = True - elif keycode[0] == 313: - self.is_alt2 = True - - - def on_key_up(self, keyboard, keycode): - # print("on_key_up(): keycode = %s" % (keycode,)) - cefModifiers = cefpython.EVENTFLAG_NONE - if self.is_shift1 or self.is_shift2: - cefModifiers |= cefpython.EVENTFLAG_SHIFT_DOWN - if self.is_ctrl1 or self.is_ctrl2: - cefModifiers |= cefpython.EVENTFLAG_CONTROL_DOWN - if self.is_alt1: - cefModifiers |= cefpython.EVENTFLAG_ALT_DOWN - # Capslock todo. - cef_keycode = self.translate_to_cef_keycode(keycode[0]) - keyEvent = { - "type": cefpython.KEYEVENT_KEYUP, - "native_key_code": cef_keycode, - "modifiers": cefModifiers - } - # print("keyup keyEvent: %s" % keyEvent) - self.browser.SendKeyEvent(keyEvent) - keyEvent = { - "type": cefpython.KEYEVENT_CHAR, - "native_key_code": cef_keycode, - "modifiers": cefModifiers - } - # print("char keyEvent: %s" % keyEvent) - self.browser.SendKeyEvent(keyEvent) - if keycode[0] == 304: - self.is_shift1 = False - elif keycode[0] == 303: - self.is_shift2 = False - elif keycode[0] == 306: - self.is_ctrl1 = False - elif keycode[0] == 305: - self.is_ctrl2 = False - elif keycode[0] == 308: - self.is_alt1 = False - elif keycode[0] == 313: - self.is_alt2 = False - - - def translate_to_cef_keycode(self, keycode): - # TODO: this works on Linux, but on Windows the key - # mappings will probably be different. - # TODO: what if the Kivy keyboard layout is changed - # from qwerty to azerty? (F1 > options..) - cef_keycode = keycode - if self.is_alt2: - # The key mappings here for right alt were tested - # with the utf-8 charset on a webpage. If the charset - # is different there is a chance they won't work correctly. - alt2_map = { - # tilde - "96":172, - # 0-9 (48..57) - "48":125, "49":185, "50":178, "51":179, "52":188, - "53":189, "54":190, "55":123, "56":91, "57":93, - # minus - "45":92, - # a-z (97..122) - "97":433, "98":2771, "99":486, "100":240, "101":490, - "102":496, "103":959, "104":689, "105":2301, "106":65121, - "107":930, "108":435, "109":181, "110":497, "111":243, - "112":254, "113":64, "114":182, "115":438, "116":956, - "117":2302, "118":2770, "119":435, "120":444, "121":2299, - "122":447, - } - if str(keycode) in alt2_map: - cef_keycode = alt2_map[str(keycode)] - else: - print("Kivy to CEF key mapping not found (right alt), " \ - "key code = %s" % keycode) - shift_alt2_map = { - # tilde - "96":172, - # 0-9 (48..57) - "48":176, "49":161, "50":2755, "51":163, "52":36, - "53":2756, "54":2757, "55":2758, "56":2761, "57":177, - # minus - "45":191, - # A-Z (97..122) - "97":417, "98":2769, "99":454, "100":208, "101":458, - "102":170, "103":957, "104":673, "105":697, "106":65122, - "107":38, "108":419, "109":186, "110":465, "111":211, - "112":222, "113":2009, "114":174, "115":422, "116":940, - "117":2300, "118":2768, "119":419, "120":428, "121":165, - "122":431, - # special: <>? :" {} - "44":215, "46":247, "47":65110, - "59":65113, "39":65114, - "91":65112, "93":65108, - } - if self.is_shift1 or self.is_shift2: - if str(keycode) in shift_alt2_map: - cef_keycode = shift_alt2_map[str(keycode)] - else: - print("Kivy to CEF key mapping not found " \ - "(shift + right alt), key code = %s" % keycode) - elif self.is_shift1 or self.is_shift2: - shift_map = { - # tilde - "96":126, - # 0-9 (48..57) - "48":41, "49":33, "50":64, "51":35, "52":36, "53":37, - "54":94, "55":38, "56":42, "57":40, - # minus, plus - "45":95, "61":43, - # a-z (97..122) - "97":65, "98":66, "99":67, "100":68, "101":69, "102":70, - "103":71, "104":72, "105":73, "106":74, "107":75, "108":76, - "109":77, "110":78, "111":79, "112":80, "113":81, "114":82, - "115":83, "116":84, "117":85, "118":86, "119":87, "120":88, - "121":89, "122":90, - # special: <>? :" {} - "44":60, "46":62, "47":63, - "59":58, "39":34, - "91":123, "93":125, - } - if str(keycode) in shift_map: - cef_keycode = shift_map[str(keycode)] - # Other keys: - other_keys_map = { - # Escape - "27":65307, - # F1-F12 - "282":65470, "283":65471, "284":65472, "285":65473, - "286":65474, "287":65475, "288":65476, "289":65477, - "290":65478, "291":65479, "292":65480, "293":65481, - # Tab - "9":65289, - # Left Shift, Right Shift - "304":65505, "303":65506, - # Left Ctrl, Right Ctrl - "306":65507, "305": 65508, - # Left Alt, Right Alt - "308":65513, "313":65027, - # Backspace - "8":65288, - # Enter - "13":65293, - # PrScr, ScrLck, Pause - "316":65377, "302":65300, "19":65299, - # Insert, Delete, - # Home, End, - # Pgup, Pgdn - "277":65379, "127":65535, - "278":65360, "279":65367, - "280":65365, "281":65366, - # Arrows (left, up, right, down) - "276":65361, "273":65362, "275":65363, "274":65364, - } - if str(keycode) in other_keys_map: - cef_keycode = other_keys_map[str(keycode)] - return cef_keycode - - - def go_forward(self): - '''Going to forward in browser history - ''' - print "go forward" - self.browser.GoForward() - - - def go_back(self): - '''Going back in browser history - ''' - print "go back" - self.browser.GoBack() - - - def on_touch_down(self, touch, *kwargs): - if not self.collide_point(*touch.pos): - return - touch.grab(self) - - y = self.height-touch.pos[1] - self.browser.SendMouseClickEvent(touch.x, y, cefpython.MOUSEBUTTON_LEFT, - mouseUp=False, clickCount=1) - - - def on_touch_move(self, touch, *kwargs): - if touch.grab_current is not self: - return - - y = self.height-touch.pos[1] - self.browser.SendMouseMoveEvent(touch.x, y, mouseLeave=False) - - - def on_touch_up(self, touch, *kwargs): - if touch.grab_current is not self: - return - - y = self.height-touch.pos[1] - self.browser.SendMouseClickEvent(touch.x, y, cefpython.MOUSEBUTTON_LEFT, - mouseUp=True, clickCount=1) - touch.ungrab(self) - - -class ClientHandler: - - def __init__(self, browserWidget): - self.browserWidget = browserWidget - - - def _fix_select_boxes(self, frame): - # This is just a temporary fix, until proper Popup widgets - # painting is implemented (PET_POPUP in OnPaint). Currently - # there is no way to obtain a native window handle (GtkWindow - # pointer) in Kivy, and this may cause things like context menus, - # select boxes and plugins not to display correctly. Although, - # this needs to be tested. The popup widget buffers are - # available in a separate paint buffer, so they could positioned - # freely so that it doesn't go out of the window. So the native - # window handle might not necessarily be required to make it work - # in most cases (99.9%). Though, this still needs testing to confirm. - # -- - # See this topic on the CEF Forum regarding the NULL window handle: - # http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10851 - # -- - # See also a related topic on the Kivy-users group: - # https://groups.google.com/d/topic/kivy-users/WdEQyHI5vTs/discussion - # -- - # The javascript select boxes library used: - # http://marcj.github.io/jquery-selectBox/ - # -- - # Cannot use "file://" urls to load local resources, error: - # | Not allowed to load local resource - print("_fix_select_boxes()") - resources_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "kivy-select-boxes") - if not os.path.exists(resources_dir): - print("The kivy-select-boxes directory does not exist, " \ - "select boxes fix won't be applied.") - return - js_file = os.path.join(resources_dir, "kivy-selectBox.js") - js_content = "" - with open(js_file, "r") as myfile: - js_content = myfile.read() - css_file = os.path.join(resources_dir, "kivy-selectBox.css") - css_content = "" - with open(css_file, "r") as myfile: - css_content = myfile.read() - css_content = css_content.replace("\r", "") - css_content = css_content.replace("\n", "") - jsCode = """ - %(js_content)s - var __kivy_temp_head = document.getElementsByTagName('head')[0]; - var __kivy_temp_style = document.createElement('style'); - __kivy_temp_style.type = 'text/css'; - __kivy_temp_style.appendChild(document.createTextNode("%(css_content)s")); - __kivy_temp_head.appendChild(__kivy_temp_style); - """ % locals() - frame.ExecuteJavascript(jsCode, - "kivy_.py > ClientHandler > OnLoadStart > _fix_select_boxes()") - - - def OnLoadStart(self, browser, frame): - self._fix_select_boxes(frame); - browserWidget = browser.GetUserData("browserWidget") - if browserWidget and browserWidget.keyboard_mode == "local": - print("OnLoadStart(): injecting focus listeners for text controls") - # The logic is similar to the one found in kivy-berkelium: - # https://github.com/kivy/kivy-berkelium/blob/master/berkelium/__init__.py - jsCode = """ - var __kivy__keyboard_requested = false; - function __kivy__keyboard_interval() { - var element = document.activeElement; - if (!element) { - return; - } - var tag = element.tagName; - var type = element.type; - if (tag == "INPUT" && (type == "" || type == "text" - || type == "password") || tag == "TEXTAREA") { - if (!__kivy__keyboard_requested) { - __kivy__request_keyboard(); - __kivy__keyboard_requested = true; - } - return; - } - if (__kivy__keyboard_requested) { - __kivy__release_keyboard(); - __kivy__keyboard_requested = false; - } - } - function __kivy__on_escape() { - if (document.activeElement) { - document.activeElement.blur(); - } - if (__kivy__keyboard_requested) { - __kivy__release_keyboard(); - __kivy__keyboard_requested = false; - } - } - setInterval(__kivy__keyboard_interval, 100); - """ - frame.ExecuteJavascript(jsCode, - "kivy_.py > ClientHandler > OnLoadStart") - - - def OnLoadEnd(self, browser, frame, httpStatusCode): - # Browser lost its focus after the LoadURL() and the - # OnBrowserDestroyed() callback bug. When keyboard mode - # is local the fix is in the request_keyboard() method. - # Call it from OnLoadEnd only when keyboard mode is global. - browserWidget = browser.GetUserData("browserWidget") - if browserWidget and browserWidget.keyboard_mode == "global": - browser.SendFocusEvent(True) - - - def OnLoadingStateChange(self, browser, isLoading, canGoBack, - canGoForward): - print("OnLoadingStateChange(): isLoading = %s" % isLoading) - browserWidget = browser.GetUserData("browserWidget") - - - def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, - height): - # print "OnPaint()" - if paintElementType != cefpython.PET_VIEW: - print "Popups aren't implemented yet" - return - - #update buffer - buffer = buffer.GetString(mode="bgra", origin="top-left") - - #update texture of canvas rectangle - self.browserWidget.texture.blit_buffer(buffer, colorfmt='bgra', - bufferfmt='ubyte') - self.browserWidget._update_rect() - - return True - - - def GetViewRect(self, browser, rect): - width, height = self.browserWidget.texture.size - rect.append(0) - rect.append(0) - rect.append(width) - rect.append(height) - # print("GetViewRect(): %s x %s" % (width, height)) - return True - - - def OnJavascriptDialog(self, browser, originUrl, dialogType, - messageText, defaultPromptText, callback, - suppressMessage): - suppressMessage[0] = True - return False - - - def OnBeforeUnloadJavascriptDialog(self, browser, messageText, isReload, - callback): - callback.Continue(allow=True, userInput="") - return True - - -if __name__ == '__main__': - class CefBrowserApp(App): - def build(self): - return BrowserLayout() - CefBrowserApp().run() - - cefpython.Shutdown() diff --git a/src/linux/binaries_32bit/pygtk_.py b/src/linux/binaries_32bit/pygtk_.py deleted file mode 100644 index 3cb7b58f..00000000 --- a/src/linux/binaries_32bit/pygtk_.py +++ /dev/null @@ -1,199 +0,0 @@ -# An example of embedding the CEF browser in PyGTK on Linux. -# Tested with GTK "2.24.10". - -# The official CEF Python binaries come with tcmalloc hook -# disabled. But if you've built custom binaries and kept tcmalloc -# hook enabled, then be aware that in such case it is required -# for the cefpython module to be the very first import in -# python scripts. See Issue 73 in the CEF Python Issue Tracker -# for more details. - -import ctypes, os, sys -libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)),\ - 'libcef.so') -if os.path.exists(libcef_so): - # Import local module - ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) - if 0x02070000 <= sys.hexversion < 0x03000000: - import cefpython_py27 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import from package - from cefpython3 import cefpython - -import pygtk -pygtk.require('2.0') -import gtk -import gobject -import re - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (_exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[pygtk_.py]: WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class PyGTKExample: - mainWindow = None - container = None - browser = None - exiting = None - searchEntry = None - vbox = None - - def __init__(self): - self.mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL) - self.mainWindow.connect('destroy', self.OnExit) - self.mainWindow.set_size_request(width=800, height=600) - self.mainWindow.set_title('PyGTK CEF example') - self.mainWindow.realize() - - self.vbox = gtk.VBox(False, 0) - self.vbox.pack_start(self.CreateMenu(), False, False, 0) - self.mainWindow.add(self.vbox) - - m = re.search("GtkVBox at 0x(\w+)", str(self.vbox)) - hexID = m.group(1) - windowID = int(hexID, 16) - - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(windowID) - # Linux requires adding "file://" for local files, - # otherwise /home/some will be replaced as http://home/some - self.browser = cefpython.CreateBrowserSync( - windowInfo, - browserSettings={}, - navigateUrl="file://"+GetApplicationPath("example.html")) - - self.vbox.show() - self.mainWindow.show() - gobject.timeout_add(10, self.OnTimer) - - def CreateMenu(self): - file = gtk.MenuItem('File') - file.show() - filemenu = gtk.Menu() - item = gtk.MenuItem('Open') - filemenu.append(item) - item.show() - item = gtk.MenuItem('Exit') - filemenu.append(item) - item.show() - file.set_submenu(filemenu) - - about = gtk.MenuItem('About') - about.show() - aboutmenu = gtk.Menu() - item = gtk.MenuItem('CEF Python') - aboutmenu.append(item) - item.show() - about.set_submenu(aboutmenu) - - menubar = gtk.MenuBar() - menubar.append(file) - menubar.append(about) - menubar.show() - - return menubar - - def OnWidgetClick(self, widget, data): - self.mainWindow.get_window().focus() - - def OnTimer(self): - if self.exiting: - return False - cefpython.MessageLoopWork() - return True - - def OnFocusIn(self, widget, data): - # This function is currently not called by any of code, - # but if you would like for browser to have automatic focus - # add such line: - # self.mainWindow.connect('focus-in-event', self.OnFocusIn) - self.browser.SetFocus(True) - - def OnExit(self, widget, data=None): - self.exiting = True - gtk.main_quit() - -if __name__ == '__main__': - version = '.'.join(map(str, list(gtk.gtk_version))) - print('[pygtk_.py] GTK version: %s' % version) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - settings = { - "debug": True, # cefpython debug messages in console and in log_file - "log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE - "log_file": GetApplicationPath("debug.log"), # Set to "" to disable - # This directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess"), - } - - cefpython.Initialize(settings) - - gobject.threads_init() # Timer for the message loop - PyGTKExample() - gtk.main() - - cefpython.Shutdown() diff --git a/src/linux/binaries_32bit/pyqt.py b/src/linux/binaries_32bit/pyqt.py deleted file mode 100644 index 1326946c..00000000 --- a/src/linux/binaries_32bit/pyqt.py +++ /dev/null @@ -1,238 +0,0 @@ -# An example of embedding the CEF browser in a PyQt4 application. -# Tested with PyQt "4.9.1". -# Command for installing PyQt4: "sudo apt-get install python-qt4". - -# The official CEF Python binaries come with tcmalloc hook -# disabled. But if you've built custom binaries and kept tcmalloc -# hook enabled, then be aware that in such case it is required -# for the cefpython module to be the very first import in -# python scripts. See Issue 73 in the CEF Python Issue Tracker -# for more details. - -import ctypes, os, sys -libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)),\ - 'libcef.so') -if os.path.exists(libcef_so): - # Import local module - ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) - if 0x02070000 <= sys.hexversion < 0x03000000: - import cefpython_py27 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import from package - from cefpython3 import cefpython - -from PyQt4 import QtGui -from PyQt4 import QtCore - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[pyqt.py] WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainWindow(QtGui.QMainWindow): - mainFrame = None - - def __init__(self): - super(MainWindow, self).__init__(None) - self.createMenu() - self.mainFrame = MainFrame(self) - self.setCentralWidget(self.mainFrame) - self.resize(1024, 768) - self.setWindowTitle('PyQT CEF 3 example') - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - def createMenu(self): - menubar = self.menuBar() - filemenu = menubar.addMenu("&File") - filemenu.addAction(QtGui.QAction("Open", self)) - filemenu.addAction(QtGui.QAction("Exit", self)) - aboutmenu = menubar.addMenu("&About") - - def focusInEvent(self, event): - # cefpython.WindowUtils.OnSetFocus( - # int(self.centralWidget().winId()), 0, 0, 0) - pass - - def closeEvent(self, event): - self.mainFrame.browser.CloseBrowser() - -class MainFrame(QtGui.QX11EmbedContainer): - browser = None - plug = None - - def __init__(self, parent=None): - super(MainFrame, self).__init__(parent) - - # QX11EmbedContainer provides an X11 window. The CEF - # browser can be embedded only by providing a GtkWidget - # pointer. So we're embedding a GtkPlug inside the X11 - # window. In latest CEF trunk it is possible to embed - # the CEF browser by providing X11 window id. So it will - # be possible to remove the GTK dependency from CEF - # Python in the future. - gtkPlugPtr = cefpython.WindowUtils.gtk_plug_new(\ - int(self.winId())) - print("[pyqt.py] MainFrame: GDK Native Window id: "+str(self.winId())) - print("[pyqt.py] MainFrame: GTK Plug ptr: "+str(gtkPlugPtr)) - - """ - Embedding GtkPlug is also possible with the pygtk module. - --------------------------------------------------------- - self.plug = gtk.Plug(self.winId()) - import re - m = re.search("GtkPlug at 0x(\w+)", str(self.plug)) - hexId = m.group(1) - gtkPlugPtr = int(hexId, 16) - ... - plug.show() - self.show() - --------------------------------------------------------- - """ - - windowInfo = cefpython.WindowInfo() - # Need to pass to CEF the GtkWidget* pointer - windowInfo.SetAsChild(gtkPlugPtr) - # Linux requires adding "file://" for local files, - # otherwise /home/some will be replaced as http://home/some - self.browser = cefpython.CreateBrowserSync(windowInfo, - browserSettings={}, - navigateUrl="file://"+GetApplicationPath("example.html")) - - cefpython.WindowUtils.gtk_widget_show(gtkPlugPtr) - self.show() - - def moveEvent(self, event): - # cefpython.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) - pass - - def resizeEvent(self, event): - # cefpython.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) - pass - -class CefApplication(QtGui.QApplication): - timer = None - - def __init__(self, args): - super(CefApplication, self).__init__(args) - self.createTimer() - - def createTimer(self): - self.timer = QtCore.QTimer() - self.timer.timeout.connect(self.onTimer) - self.timer.start(10) - - def onTimer(self): - # The proper way of doing message loop should be: - # 1. In createTimer() call self.timer.start(0) - # 2. In onTimer() call MessageLoopWork() only when - # QtGui.QApplication.instance()->hasPendingEvents() - # returns False. - # But there is a bug in Qt, hasPendingEvents() returns - # always true. - # (The behavior described above was tested on Windows - # with pyqt 4.8, maybe this is not true anymore, - # test it TODO) - cefpython.MessageLoopWork() - - def stopTimer(self): - # Stop the timer after Qt message loop ended, calls to - # MessageLoopWork() should not happen anymore. - self.timer.stop() - -if __name__ == '__main__': - print("[pyqt.py] PyQt version: %s" % QtCore.PYQT_VERSION_STR) - print("[pyqt.py] QtCore version: %s" % QtCore.qVersion()) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - settings = { - "debug": True, # cefpython debug messages in console and in log_file - "log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE - "log_file": GetApplicationPath("debug.log"), # Set to "" to disable. - # This directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess"), - } - - # Command line switches set programmatically - switches = { - # "proxy-server": "socks5://127.0.0.1:8888", - # "enable-media-stream": "", - # "--invalid-switch": "" -> Invalid switch name - } - - cefpython.Initialize(settings, switches) - - app = CefApplication(sys.argv) - mainWindow = MainWindow() - mainWindow.show() - app.exec_() - app.stopTimer() - - # Need to destroy QApplication(), otherwise Shutdown() fails. - # Unset main window also just to be safe. - del mainWindow - del app - - cefpython.Shutdown() diff --git a/src/linux/binaries_32bit/wxpython-response.py b/src/linux/binaries_32bit/wxpython-response.py deleted file mode 100644 index 8992e14a..00000000 --- a/src/linux/binaries_32bit/wxpython-response.py +++ /dev/null @@ -1,452 +0,0 @@ -# An example of embedding CEF browser in wxPython on Linux. -# Tested with wxPython 2.8.12.1 (gtk2-unicode). -# To install wxPython type "sudo apt-get install python-wxtools". - -# This example implements a custom "_OnResourceResponse" callback -# that emulates reading response by utilizing Resourcehandler -# and WebRequest. - -FIX_ENCODING_BUG = True -BROWSER_DEFAULT_ENCODING = "utf-8" - -# The official CEF Python binaries come with tcmalloc hook -# disabled. But if you've built custom binaries and kept tcmalloc -# hook enabled, then be aware that in such case it is required -# for the cefpython module to be the very first import in -# python scripts. See Issue 73 in the CEF Python Issue Tracker -# for more details. - -import ctypes, os, sys -libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'libcef.so') -if os.path.exists(libcef_so): - # Import local module - ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) - if 0x02070000 <= sys.hexversion < 0x03000000: - import cefpython_py27 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import from package - from cefpython3 import cefpython - -import wx -import time -import re -import uuid -import platform -import shutil - -class ClientHandler: - - # RequestHandler.GetResourceHandler() - def GetResourceHandler(self, browser, frame, request): - # Called on the IO thread before a resource is loaded. - # To allow the resource to load normally return None. - print("GetResourceHandler(): url = %s" % request.GetUrl()) - resHandler = ResourceHandler() - resHandler._clientHandler = self - resHandler._browser = browser - resHandler._frame = frame - resHandler._request = request - self._AddStrongReference(resHandler) - return resHandler - - def _OnResourceResponse(self, browser, frame, request, requestStatus, - requestError, response, data): - # This callback is emulated through ResourceHandler - # and WebRequest. Real "OnResourceResponse" is not yet - # available in CEF 3 (as of CEF revision 1450). See - # issue 515 in the CEF Issue Tracker: - # https://code.google.com/p/chromiumembedded/issues/detail?id=515 - # ---- - # requestStatus => cefpython.WebRequest.Status - # {"Unknown", "Success", "Pending", "Canceled", "Failed"} - # For "file://" requests the status will be "Unknown". - # requestError => see the NetworkError wiki page - # response.GetStatus() => http status code - print("_OnResourceResponse()") - print("data length = %s" % len(data)) - # Return the new data - you can modify it. - if request.GetUrl().startswith("file://") \ - and request.GetUrl().endswith("example.html"): - data = "This text was inserted through " \ - + "_OnResourceResponse()
" + data - # Non-english characters are not displaying correctly. - # This is a bug in CEF. A quick fix is to get the charset - # from response headers and insert into - # the html page. - # Bug reported on the CEF C++ Forum: - # http://www.magpcss.org/ceforum/viewtopic.php?p=18401#p18401 - if FIX_ENCODING_BUG: - contentType = response.GetHeader("Content-Type") - if contentType: - contentType = contentType.lower() - isHtml = False - headerCharset = "" - if contentType and "text/html" in contentType: - isHtml = True - if contentType and "charset" in contentType: - match = re.search(r"charset\s*=\s*([^\s]+)", contentType) - if match and match.group(1): - headerCharset = match.group(1).lower() - if isHtml and headerCharset \ - and headerCharset != BROWSER_DEFAULT_ENCODING.lower(): - if not re.search(r"]+charset\s*=", data, \ - re.IGNORECASE): - # Only apply the fix if there is no - # available on a page. - data = ("" % headerCharset) + data - return data - - # A strong reference to ResourceHandler must be kept - # during the request. Some helper functions for that. - # 1. Add reference in GetResourceHandler() - # 2. Release reference in ResourceHandler.ReadResponse() - # after request is completed. - - _resourceHandlers = {} - _resourceHandlerMaxId = 0 - - def _AddStrongReference(self, resHandler): - self._resourceHandlerMaxId += 1 - resHandler._resourceHandlerId = self._resourceHandlerMaxId - self._resourceHandlers[resHandler._resourceHandlerId] = resHandler - - def _ReleaseStrongReference(self, resHandler): - if resHandler._resourceHandlerId in self._resourceHandlers: - del self._resourceHandlers[resHandler._resourceHandlerId] - else: - print("_ReleaseStrongReference() FAILED: resource handler " \ - "not found, id = %s" % (resHandler._resourceHandlerId)) - -class ResourceHandler: - - # The methods of this class will always be called - # on the IO thread. - - _resourceHandlerId = None - _clientHandler = None - _browser = None - _frame = None - _request = None - _responseHeadersReadyCallback = None - _webRequest = None - _webRequestClient = None - _offsetRead = 0 - - def ProcessRequest(self, request, callback): - print("ProcessRequest()") - # 1. Start the request using WebRequest - # 2. Return True to handle the request - # 3. Once response headers are ready call - # callback.Continue() - self._responseHeadersReadyCallback = callback - self._webRequestClient = WebRequestClient() - self._webRequestClient._resourceHandler = self - # Need to set AllowCacheCredentials and AllowCookies for - # the cookies to work during POST requests (Issue 127). - # To skip cache set the SkipCache request flag. - request.SetFlags(cefpython.Request.Flags["AllowCachedCredentials"]\ - | cefpython.Request.Flags["AllowCookies"]) - # A strong reference to the WebRequest object must kept. - self._webRequest = cefpython.WebRequest.Create( - request, self._webRequestClient) - return True - - def GetResponseHeaders(self, response, responseLengthOut, redirectUrlOut): - print("GetResponseHeaders()") - # 1. If the response length is not known set - # responseLengthOut[0] to -1 and ReadResponse() - # will be called until it returns False. - # 2. If the response length is known set - # responseLengthOut[0] to a positive value - # and ReadResponse() will be called until it - # returns False or the specified number of bytes - # have been read. - # 3. Use the |response| object to set the mime type, - # http status code and other optional header values. - # 4. To redirect the request to a new URL set - # redirectUrlOut[0] to the new url. - assert self._webRequestClient._response, "Response object empty" - wrcResponse = self._webRequestClient._response - response.SetStatus(wrcResponse.GetStatus()) - response.SetStatusText(wrcResponse.GetStatusText()) - response.SetMimeType(wrcResponse.GetMimeType()) - if wrcResponse.GetHeaderMultimap(): - response.SetHeaderMultimap(wrcResponse.GetHeaderMultimap()) - print("headers: ") - print(wrcResponse.GetHeaderMap()) - responseLengthOut[0] = self._webRequestClient._dataLength - if not responseLengthOut[0]: - # Probably a cached page? Or a redirect? - pass - - def ReadResponse(self, dataOut, bytesToRead, bytesReadOut, callback): - # print("ReadResponse()") - # 1. If data is available immediately copy up to - # bytesToRead bytes into dataOut[0], set - # bytesReadOut[0] to the number of bytes copied, - # and return true. - # 2. To read the data at a later time set - # bytesReadOut[0] to 0, return true and call - # callback.Continue() when the data is available. - # 3. To indicate response completion return false. - if self._offsetRead < self._webRequestClient._dataLength: - dataChunk = self._webRequestClient._data[\ - self._offsetRead:(self._offsetRead + bytesToRead)] - self._offsetRead += len(dataChunk) - dataOut[0] = dataChunk - bytesReadOut[0] = len(dataChunk) - return True - self._clientHandler._ReleaseStrongReference(self) - print("no more data, return False") - return False - - def CanGetCookie(self, cookie): - # Return true if the specified cookie can be sent - # with the request or false otherwise. If false - # is returned for any cookie then no cookies will - # be sent with the request. - return True - - def CanSetCookie(self, cookie): - # Return true if the specified cookie returned - # with the response can be set or false otherwise. - return True - - def Cancel(self): - # Request processing has been canceled. - pass - -class WebRequestClient: - - _resourceHandler = None - _data = "" - _dataLength = -1 - _response = None - - def OnUploadProgress(self, webRequest, current, total): - pass - - def OnDownloadProgress(self, webRequest, current, total): - pass - - def OnDownloadData(self, webRequest, data): - # print("OnDownloadData()") - self._data += data - - def OnRequestComplete(self, webRequest): - print("OnRequestComplete()") - # cefpython.WebRequest.Status = {"Unknown", "Success", - # "Pending", "Canceled", "Failed"} - statusText = "Unknown" - if webRequest.GetRequestStatus() in cefpython.WebRequest.Status: - statusText = cefpython.WebRequest.Status[\ - webRequest.GetRequestStatus()] - print("status = %s" % statusText) - print("error code = %s" % webRequest.GetRequestError()) - # Emulate OnResourceResponse() in ClientHandler: - self._response = webRequest.GetResponse() - # Are webRequest.GetRequest() and - # self._resourceHandler._request the same? What if - # there was a redirect, what will GetUrl() return - # for both of them? - self._data = self._resourceHandler._clientHandler._OnResourceResponse( - self._resourceHandler._browser, - self._resourceHandler._frame, - webRequest.GetRequest(), - webRequest.GetRequestStatus(), - webRequest.GetRequestError(), - webRequest.GetResponse(), - self._data) - self._dataLength = len(self._data) - # ResourceHandler.GetResponseHeaders() will get called - # after _responseHeadersReadyCallback.Continue() is called. - self._resourceHandler._responseHeadersReadyCallback.Continue() - -# Which method to use for message loop processing. -# EVT_IDLE - wx application has priority (default) -# EVT_TIMER - cef browser has priority -# It seems that Flash content behaves better when using a timer. -# IMPORTANT! On Linux EVT_IDLE does not work, the events seems to -# be propagated only when you move your mouse, which is not the -# expected behavior, it is recommended to use EVT_TIMER on Linux, -# so set this value to False. -USE_EVT_IDLE = False - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("cefpython: WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainFrame(wx.Frame): - browser = None - mainPanel = None - - def __init__(self): - wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, - title='wxPython CEF 3 example', size=(800,600)) - self.CreateMenu() - - # Cannot attach browser to the main frame as this will cause - # the menu not to work. - # -- - # You also have to set the wx.WANTS_CHARS style for - # all parent panels/controls, if it's deeply embedded. - self.mainPanel = wx.Panel(self, style=wx.WANTS_CHARS) - - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(self.mainPanel.GetGtkWidget()) - # Linux requires adding "file://" for local files, - # otherwise /home/some will be replaced as http://home/some - self.browser = cefpython.CreateBrowserSync( - windowInfo, - # If there are problems with Flash you can disable it here, - # by disabling all plugins. - browserSettings={"plugins_disabled": False, - "default_encoding": BROWSER_DEFAULT_ENCODING}, - navigateUrl="file://"+GetApplicationPath("example.html")) - - clientHandler = ClientHandler() - self.browser.SetClientHandler(clientHandler) - - self.Bind(wx.EVT_CLOSE, self.OnClose) - if USE_EVT_IDLE: - # Bind EVT_IDLE only for the main application frame. - self.Bind(wx.EVT_IDLE, self.OnIdle) - - def CreateMenu(self): - filemenu = wx.Menu() - filemenu.Append(1, "Open") - exit = filemenu.Append(2, "Exit") - self.Bind(wx.EVT_MENU, self.OnClose, exit) - aboutmenu = wx.Menu() - aboutmenu.Append(1, "CEF Python") - menubar = wx.MenuBar() - menubar.Append(filemenu,"&File") - menubar.Append(aboutmenu, "&About") - self.SetMenuBar(menubar) - - def OnClose(self, event): - # In wx.chromectrl calling browser.CloseBrowser() and/or - # self.Destroy() in OnClose is causing crashes when embedding - # multiple browser tabs. The solution is to call only - # browser.ParentWindowWillClose. Behavior of this example - # seems different as it extends wx.Frame, while ChromeWindow - # from chromectrl extends wx.Window. Calling CloseBrowser - # and Destroy does not cause crashes, but is not recommended. - # Call ParentWindowWillClose and event.Skip() instead. See - # also Issue 107. - self.browser.ParentWindowWillClose() - event.Skip() - - def OnIdle(self, event): - cefpython.MessageLoopWork() - -class MyApp(wx.App): - timer = None - timerID = 1 - timerCount = 0 - - def OnInit(self): - if not USE_EVT_IDLE: - self.CreateTimer() - frame = MainFrame() - self.SetTopWindow(frame) - frame.Show() - return True - - def CreateTimer(self): - # See "Making a render loop": - # http://wiki.wxwidgets.org/Making_a_render_loop - # Another approach is to use EVT_IDLE in MainFrame, - # see which one fits you better. - self.timer = wx.Timer(self, self.timerID) - self.timer.Start(10) # 10ms - wx.EVT_TIMER(self, self.timerID, self.OnTimer) - - def OnTimer(self, event): - self.timerCount += 1 - # print("wxpython.py: OnTimer() %d" % self.timerCount) - cefpython.MessageLoopWork() - - def OnExit(self): - # When app.MainLoop() returns, MessageLoopWork() should - # not be called anymore. - if not USE_EVT_IDLE: - self.timer.Stop() - -if __name__ == '__main__': - sys.excepthook = ExceptHook - settings = { - "debug": False, # cefpython debug messages in console and in log_file - "log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE - "log_file": GetApplicationPath("debug.log"), # Set to "" to disable. - # This directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess") - } - # print("browser_subprocess_path="+settings["browser_subprocess_path"]) - cefpython.Initialize(settings) - print('wx.version=%s' % wx.version()) - app = MyApp(False) - app.MainLoop() - # Let wx.App destructor do the cleanup before calling cefpython.Shutdown(). - del app - cefpython.Shutdown() diff --git a/src/linux/binaries_32bit/wxpython.html b/src/linux/binaries_32bit/wxpython.html deleted file mode 100644 index caf056ff..00000000 --- a/src/linux/binaries_32bit/wxpython.html +++ /dev/null @@ -1,707 +0,0 @@ - - - - - wxPython CEF 3 example (utf-8: Ä…Å›) - - - - - - -Use the mouse context menu to go Back/Forward in history navigation. - -

Table of contents

-
    -
  1. Google search
  2. -
  3. User agent
  4. -
  5. Popups
  6. -
  7. HTML 5 video
  8. -
  9. Developer Tools
  10. -
  11. Downloads
  12. -
  13. HTML controls
  14. -
  15. Browser object
  16. -
  17. Frame object
  18. -
  19. Javascript bindings
  20. -
  21. Javascript callbacks
  22. -
  23. Python callbacks
  24. -
  25. Display handler
  26. -
  27. Keyboard handler
  28. -
  29. Request handler
  30. -
  31. Cookie tests
  32. -
  33. Load handler
  34. -
  35. Javascript Dialog handler
  36. -
  37. Other tests
  38. -
- - - - - - -

Google search

- -http://www.google.com/ - - - - - - - -

User agent

- - - - - - - - - -

Popups

- -
    -
  1. - window.open('wxpython.html') -
  2. -
  3. - target=_blank href="wxpython.html" -
  4. -
- -

CreateAnotherBrowser

- -This will create a window on its own and embed browser in it. -When using "window.open" the window is created implicitilly -by CEF. You can intercept such popup creation using the -OnBeforePopup callback in LifespanHandler. You can return -True in OnBeforePopup and create popup window on your own -using the CreateAnotherBrowser function. - - - - external.CreateAnotherBrowser() - - - - - - - -

HTML5 video and accelerated content

- - -HTML 5 video
- - -Accelerated canvas
- - -Accelerated layers
- - - - - - -

Developer Tools

- -You can open devtools popup window in a few different ways: -
    -
  1. Call Browser.ShowDevTools() method: - - external.ShowDevTools()
  2. -
  3. Through mouse context menu
  4. -
  5. Through F12 key which is handled in KeyboardHandler.OnKeyEvent
  6. -
- - - - - - -

Downloads

- -Download sample Ubuntu wallpapers:
- - https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip - -

-Notes: On Linux it seems that OnLoadError with errorCode = ERR_ABORTED -is called even for successful downloads, you can ignore this behavior. -The proper console messages about successful/aborted download originate -from C++ Browser process code, these are: -

- -
Browser: About to download file: ubuntu-wallpapers2.zip
-Browser: Download completed, saved to: /Downloads/ubuntu-wallpapers2.zip
-
- -If download was aborted the messages will be: - -
Browser: About to download file: ubuntu-wallpapers2.zip
-Browser: Download was cancelled
-
- -

-Additionally on Linux there are more errors reported by Chromium -about org.gnome.SessionManager.inhibit. These can be safely ignored as well. -

- -A download handler with callbacks like `OnBeforeDownload` and -`OnDownloadUpdated` may be exposed to CEF Python in the future. - - - - - - -

HTML controls

- -

Textarea

- -
- -

Inputs

-Text:
-Password:
- -

Select

- - -

Buttons

-Submit:
-Button:
- - - - - - -

Browser object

- -Tests for the Browser object methods. - -

GoBack

- -external.GoBack() - -

GoForward

- -external.GoForward() - -

LoadUrl, GetUrl

- - - window.open('data:text/html,Test#Browser.LoadUrl') - -

ReloadIgnoreCache, StopLoad

-Press F5 to reload page and ignore cache.
-Press Esc during webpage loading to abort.
- -Also, when Esc is pressed OnLoadError may get called. See how abort -of page loading or file download is handled: - - - - - - - -

Frame object

- -Tests for the Frame object methods. TODO. - - - - - - -

Javascript bindings

- -

PyPrint

- - - window.PyPrint('printing in python console from js') -
- -

Window properties

- -
jsBindings.SetProperty("pyProperty", "This was set in Python")
-jsBindings.SetProperty("pyConfig", ["This was set in Python",
-        {"name": "Nested dictionary", "isNested": True},
-        [1,"2", None]])
-
- - - window.alert(window.pyProperty)
- - window.alert(JSON.stringify(window.pyConfig)) -
- -

Print

- - - - external.Print('printing again from js') -
- -

TestAllTypes

- - - - external.TestAllTypes - (undefined, null, true, 1, - ((1<<31)>>>0), 2.14, 'Date not yet supported', 'string', - {key1: 1, key2: 2}, {key1: {'key1.1': 'nested object'}, 'key1.2': [1]}, - [1, 2], [1, [2.1, 'nested array']], [{key1: [{}]}]) -
- -

ExecuteFunction

- - - -
<script>
-function JavascriptAlert(message) { window.alert(message); }
-</script>
-
- - - - external.ExecuteFunction('JavascriptAlert', - 'python called from js and then js called from python') -
- -

GetSource, GetText

- - - - - - - external.GetSource() -
- - external.GetText() - - - - - - -

Javascript callbacks

- -

TestJSCallback

- - - -
<script>
-function JSCallback(arg1) {
-    window.alert(arg1)
-}
-</script>
-
- - - - external.TestJSCallback(JSCallback) - -

TestJSCallbackComplexArguments

- - - -
<script>
-function JSCallback2() {
-    window.alert(JSON.stringify(arguments))
-}
-</script>
-
- - - - external.TestJSCallbackComplexArguments({"myCallback": JSCallback2}) - - - - - - - -

Python callbacks

- -

TestPythonCallback

- - - -
<script>
-function JSCallback3(pyCallback) {
-    pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
-}
-</script>
-
- - - - - - external.TestPythonCallback(JSCallback3) - - - - - - -

Display handler

- -

OnAddressChange

- -See messages in the console during loading of a webpage. - -

OnTitleChange

- -See messages in the console during loading of a webpage. - -

OnTooltip

- -See messages in the console when hovering over a google logo: -http://www.google.com/ - -

OnStatusMessage

- -See messages in the console when hovering over links. - -

OnConsoleMessage

- -Try this: - - http://patik.com/code/console-log-polyfill/ - - - - - - -

Keyboard handler

- -

- Press F5 to reload the page.
- On Linux it is required to click anywhere in the window first - so that keyboard focus is set. See Issue 77 in the CEF Python - Issue Tracker. -

- - - - - - - - - -

Request handler

- -

OnBeforeResourceLoad

- -See messages in the console. - -

OnResourceRedirect

- -Try this: - - http://tinyurl.com/google404redirect - -

GetAuthCredentials

- -Try this: - - http://browserspy.dk/password-ok.php - -

OnQuotaRequest

- - - - -
<script>
-function DoRequestQuota() {
-    // Request Quota (only for File System API)  
-    try {
-        navigator.webkitPersistentStorage.requestQuota(PERSISTENT, 1024*1024,
-                function(bytes){ window.alert("Granted bytes: "+bytes);},
-                function(error){ window.alert(error); });
-    } catch(e) {
-        navigator.webkitPersistentStorage.requestQuota(1024*1024,
-                function(bytes){ window.alert("Granted bytes: "+bytes);},
-                function(error){ window.alert(error); });
-    }
-}
-</script>
-
- -Try this: - - https://googledrive.com/host/0B1di2XiBBfacMnhRRkI1YlotUEk/requestquota.html - -

OnProtocolExecution

- -Try this: - - magnet:?xt=urn:btih:a4224b45b27f436374391379cc5c7e629e2e5189 - -

_OnBeforePluginLoad

- -Try OnBeforePluginLoad() with Flash: - - http://www.adobe.com/software/flash/about/ - -

_OnCertificateError

- - -The url below won't be allowed. Click twice "Back" from context menu to return back -here after visiting the url:
- - https://tv.eurosport.com/do-not-allow -
- -This url will be allowed:
- - https://tv.eurosport.com/ - -

OnRendererProcessTerminated

- -Try to terminate the "subprocess.exe" renderer process through -task manager. - -

OnPluginCrashed

- -No test for that yet. - - - - - - -

Cookie tests

- -See messages in the console. - -

GetCookieManager

- - - -RequestHandler.GetCookieManager() - an example of having an unique -cookie manager for each browser. -
    -
  1. Visit the url below and set some cookies. Use "Back" from - context menu to get back here (you might have to click "Back" - multiple times).
    - Visit it in the current browser:
    - - http://www.html-kit.com/tools/cookietester/ -
    - Or visit it in a js popup:
    - - javascript:window.open('http://www.html-kit.com/tools/cookietester/') -
  2. -
  3. Open cookietester in a popup:
    - - javascript:external.CreateAnotherBrowser('http://www.html-kit.com/tools/cookietester/') -
  4. -
- -

-Popup browsers created javascript's window.open share -the same renderer process and request context. If you want -to have separate cookie managers for popups created using -window.open then you have to implement the -LifespanHandler.`OnBeforePopup` callback. Return True in that -callback to cancel popup creation and instead create the -window on your own and embed browser in it. The CreateAnotherBrowser() -function from the wxpython example does that. -

- -

VisitAllCookies

- -Visit all cookies: -external.VisitAllCookies() -

- -Note: visit some http:// webpage first, otherwise cookie manager is not -yet created. -
- -

VisitUrlCookies

- -Visit a subset of cookies for the given url: - - external.VisitUrlCookies("http://www.html-kit.com/tools/cookietester/") -
- -

SetCookie

- -Set a cookie: -external.SetCookie() -
- -

DeleteCookies

- -Delete the single cookie previously created via SetCookie(): - - external.DeleteCookies() -
- - - - - - -

Load Handler

- -See messages in the console during loading of a webpage. - -

OnLoadingStateChange

- - -

OnLoadStart

- - -

OnLoadEnd

- - -

OnLoadError

- -Try this: - - http://www.non-existent.nono/ -

- -Note: after you see the custom error message you have to hit -twice the Back from the context menu, to get back to this page. - - - - - - -

Javascript Dialog Handler

- -See messages in the console. - -

OnJavascriptDialog

- - - alert('Test js dialog handler') - - -

OnBeforeUnloadJavascriptDialog

- - - -
<script>
-function TestOnBeforeUnloadJavascriptDialog() {
-    window.onbeforeunload = function() {
-        return 'Testing the OnBeforeUnloadJavascriptDialog() callback';
-    }
-    location.href = "wxpython.html";
-}
-</script>
-
- - - TestOnBeforeUnloadJavascriptDialog() - - -

OnResetJavascriptDialogState

- - -

OnJavascriptDialogClosed

- - - - - - - -

Other tests

- -

HTTPS caching with SSL certificate errors

-Set ApplicationSettings["ignore_certificate_errors"] to True. - - - - - - - - - - - - - diff --git a/src/linux/binaries_32bit/wxpython.py b/src/linux/binaries_32bit/wxpython.py deleted file mode 100644 index 212132d8..00000000 --- a/src/linux/binaries_32bit/wxpython.py +++ /dev/null @@ -1,782 +0,0 @@ -# An example of embedding CEF browser in wxPython on Linux. -# Tested with wxPython 2.8.12.1 (gtk2-unicode). -# To install wxPython type "sudo apt-get install python-wxtools". - -# The official CEF Python binaries come with tcmalloc hook -# disabled. But if you've built custom binaries and kept tcmalloc -# hook enabled, then be aware that in such case it is required -# for the cefpython module to be the very first import in -# python scripts. See Issue 73 in the CEF Python Issue Tracker -# for more details. - -import ctypes, os, sys -libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)),\ - 'libcef.so') -if os.path.exists(libcef_so): - # Import a local module - ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) - if 0x02070000 <= sys.hexversion < 0x03000000: - import cefpython_py27 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -import wx -import time -import re -import uuid -import platform -import inspect - -g_browserSettings = None - -# Which method to use for message loop processing. -# EVT_IDLE - wx application has priority (default) -# EVT_TIMER - cef browser has priority -# It seems that Flash content behaves better when using a timer. -# IMPORTANT! On Linux EVT_IDLE does not work, the events seems to -# be propagated only when you move your mouse, which is not the -# expected behavior, it is recommended to use EVT_TIMER on Linux, -# so set this value to False. -USE_EVT_IDLE = False - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[wxpython.py] WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainFrame(wx.Frame): - browser = None - mainPanel = None - - def __init__(self, url=None): - wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, - title='wxPython CEF 3 example', size=(1024,768)) - if not url: - url = "file://"+GetApplicationPath("wxpython.html") - # Test hash in url. - # url += "#test-hash" - - self.CreateMenu() - - # Cannot attach browser to the main frame as this will cause - # the menu not to work. - # -- - # You also have to set the wx.WANTS_CHARS style for - # all parent panels/controls, if it's deeply embedded. - self.mainPanel = wx.Panel(self, style=wx.WANTS_CHARS) - - # Global client callbacks must be set before browser is created. - clientHandler = ClientHandler() - cefpython.SetGlobalClientCallback("OnCertificateError", - clientHandler._OnCertificateError) - cefpython.SetGlobalClientCallback("OnBeforePluginLoad", - clientHandler._OnBeforePluginLoad) - cefpython.SetGlobalClientCallback("OnAfterCreated", - clientHandler._OnAfterCreated) - - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(self.mainPanel.GetGtkWidget()) - # Linux requires adding "file://" for local files, - # otherwise /home/some will be replaced as http://home/some - self.browser = cefpython.CreateBrowserSync( - windowInfo, - # If there are problems with Flash you can disable it here, - # by disabling all plugins. - browserSettings=g_browserSettings, - navigateUrl=url) - - clientHandler.mainBrowser = self.browser - self.browser.SetClientHandler(clientHandler) - - jsBindings = cefpython.JavascriptBindings( - bindToFrames=False, bindToPopups=True) - jsBindings.SetFunction("PyPrint", PyPrint) - jsBindings.SetProperty("pyProperty", "This was set in Python") - jsBindings.SetProperty("pyConfig", ["This was set in Python", - {"name": "Nested dictionary", "isNested": True}, - [1,"2", None]]) - jsBindings.SetObject("external", JavascriptExternal(self.browser)) - jsBindings.SetProperty("sources", GetSources()) - self.browser.SetJavascriptBindings(jsBindings) - - self.Bind(wx.EVT_CLOSE, self.OnClose) - if USE_EVT_IDLE: - # Bind EVT_IDLE only for the main application frame. - self.Bind(wx.EVT_IDLE, self.OnIdle) - - def CreateMenu(self): - filemenu = wx.Menu() - filemenu.Append(1, "Open") - exit = filemenu.Append(2, "Exit") - self.Bind(wx.EVT_MENU, self.OnClose, exit) - aboutmenu = wx.Menu() - aboutmenu.Append(1, "CEF Python") - menubar = wx.MenuBar() - menubar.Append(filemenu,"&File") - menubar.Append(aboutmenu, "&About") - self.SetMenuBar(menubar) - - def OnClose(self, event): - # In wx.chromectrl calling browser.CloseBrowser() and/or - # self.Destroy() in OnClose is causing crashes when embedding - # multiple browser tabs. The solution is to call only - # browser.ParentWindowWillClose. Behavior of this example - # seems different as it extends wx.Frame, while ChromeWindow - # from chromectrl extends wx.Window. Calling CloseBrowser - # and Destroy does not cause crashes, but is not recommended. - # Call ParentWindowWillClose and event.Skip() instead. See - # also Issue 107. - self.browser.ParentWindowWillClose() - event.Skip() - - def OnIdle(self, event): - cefpython.MessageLoopWork() - -def PyPrint(message): - print("[wxpython.py] PyPrint: "+message) - -class JavascriptExternal: - mainBrowser = None - stringVisitor = None - - def __init__(self, mainBrowser): - self.mainBrowser = mainBrowser - - def GoBack(self): - self.mainBrowser.GoBack() - - def GoForward(self): - self.mainBrowser.GoForward() - - def CreateAnotherBrowser(self, url=None): - """ - TODO: There are errors in the console when closing the window: - >> Check failed: window - >> Gdk: _gdk_window_destroy_hierarchy: assertion `GDK_IS_WINDOW\ - >> (window)' failed - >> GLib-GObject: g_object_unref: assertion `G_IS_OBJECT (object)' failed - """ - frame = MainFrame(url=url) - frame.Show() - - def Print(self, message): - print("[wxpython.py] Print: "+message) - - def TestAllTypes(self, *args): - print("[wxpython.py] TestAllTypes: "+str(args)) - - def ExecuteFunction(self, *args): - self.mainBrowser.GetMainFrame().ExecuteFunction(*args) - - def TestJSCallback(self, jsCallback): - print("[wxpython.py] jsCallback.GetFunctionName() = %s"\ - % jsCallback.GetFunctionName()) - print("[wxpython.py] jsCallback.GetFrame().GetIdentifier() = %s" % \ - jsCallback.GetFrame().GetIdentifier()) - jsCallback.Call("This message was sent from python using js callback") - - def TestJSCallbackComplexArguments(self, jsObject): - jsCallback = jsObject["myCallback"]; - jsCallback.Call(1, None, 2.14, "string", ["list", ["nested list", \ - {"nested object":None}]], \ - {"nested list next":[{"deeply nested object":1}]}) - - def TestPythonCallback(self, jsCallback): - jsCallback.Call(self.PyCallback) - - def PyCallback(self, *args): - message = "PyCallback() was executed successfully! "\ - "Arguments: %s" % str(args) - print("[wxpython.py] "+message) - self.mainBrowser.GetMainFrame().ExecuteJavascript( - "window.alert(\"%s\")" % message) - - def GetSource(self): - # Must keep a strong reference to the StringVisitor object - # during the visit. - self.stringVisitor = StringVisitor() - self.mainBrowser.GetMainFrame().GetSource(self.stringVisitor) - - def GetText(self): - # Must keep a strong reference to the StringVisitor object - # during the visit. - self.stringVisitor = StringVisitor() - self.mainBrowser.GetMainFrame().GetText(self.stringVisitor) - - def ShowDevTools(self): - print("[wxpython.py] external.ShowDevTools called") - self.mainBrowser.ShowDevTools() - - # ------------------------------------------------------------------------- - # Cookies - # ------------------------------------------------------------------------- - cookieVisitor = None - - def VisitAllCookies(self): - # Need to keep the reference alive. - self.cookieVisitor = CookieVisitor() - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.VisitAllCookies(self.cookieVisitor) - - def VisitUrlCookies(self): - # Need to keep the reference alive. - self.cookieVisitor = CookieVisitor() - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.VisitUrlCookies( - "http://www.html-kit.com/tools/cookietester/", - False, self.cookieVisitor) - # .www.html-kit.com - - def SetCookie(self): - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - "the cookietester website first and create some cookies") - return - cookie = cefpython.Cookie() - cookie.SetName("Created_Via_Python") - cookie.SetValue("yeah really") - cookieManager.SetCookie("http://www.html-kit.com/tools/cookietester/", - cookie) - print("\n[wxpython.py] Cookie created! Visit html-kit cookietester to"\ - " see it") - - def DeleteCookies(self): - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.DeleteCookies( - "http://www.html-kit.com/tools/cookietester/", - "Created_Via_Python") - print("\n[wxpython.py] Cookie deleted! Visit html-kit cookietester "\ - "to see the result") - -class StringVisitor: - def Visit(self, string): - print("\n[wxpython.py] StringVisitor.Visit(): string:") - print("--------------------------------") - print(string) - print("--------------------------------") - -class CookieVisitor: - def Visit(self, cookie, count, total, deleteCookie): - if count == 0: - print("\n[wxpython.py] CookieVisitor.Visit(): total cookies: %s"\ - % total) - print("\n[wxpython.py] CookieVisitor.Visit(): cookie:") - print(" "+str(cookie.Get())) - # True to continue visiting cookies - return True - -class ClientHandler: - mainBrowser = None # May be None for global client callbacks. - - def __init__(self): - pass - - # ------------------------------------------------------------------------- - # DisplayHandler - # ------------------------------------------------------------------------- - - def OnAddressChange(self, browser, frame, url): - print("[wxpython.py] DisplayHandler::OnAddressChange()") - print(" url = %s" % url) - - def OnTitleChange(self, browser, title): - print("[wxpython.py] DisplayHandler::OnTitleChange()") - print(" title = %s" % title) - - def OnTooltip(self, browser, textOut): - # OnTooltip not yet implemented (both Linux and Windows), - # will be fixed in next CEF release, see Issue 783: - # https://code.google.com/p/chromiumembedded/issues/detail?id=783 - print("[wxpython.py] DisplayHandler::OnTooltip()") - print(" text = %s" % textOut[0]) - - statusMessageCount = 0 - def OnStatusMessage(self, browser, value): - if not value: - # Do not notify in the console about empty statuses. - return - self.statusMessageCount += 1 - if self.statusMessageCount > 3: - # Do not spam too much. - return - print("[wxpython.py] DisplayHandler::OnStatusMessage()") - print(" value = %s" % value) - - def OnConsoleMessage(self, browser, message, source, line): - print("[wxpython.py] DisplayHandler::OnConsoleMessage()") - print(" message = %s" % message) - print(" source = %s" % source) - print(" line = %s" % line) - - # ------------------------------------------------------------------------- - # KeyboardHandler - # ------------------------------------------------------------------------- - - def OnPreKeyEvent(self, browser, event, eventHandle, - isKeyboardShortcutOut): - print("[wxpython.py] KeyboardHandler::OnPreKeyEvent()") - - def OnKeyEvent(self, browser, event, eventHandle): - if event["type"] == cefpython.KEYEVENT_KEYUP: - # OnKeyEvent is called twice for F5/Esc keys, with event - # type KEYEVENT_RAWKEYDOWN and KEYEVENT_KEYUP. - # Normal characters a-z should have KEYEVENT_CHAR. - return False - print("[wxpython.py] KeyboardHandler::OnKeyEvent()") - print(" type=%s" % event["type"]) - print(" modifiers=%s" % event["modifiers"]) - print(" windows_key_code=%s" % event["windows_key_code"]) - print(" native_key_code=%s" % event["native_key_code"]) - print(" is_system_key=%s" % event["is_system_key"]) - print(" character=%s" % event["character"]) - print(" unmodified_character=%s" % event["unmodified_character"]) - print(" focus_on_editable_field=%s"\ - % event["focus_on_editable_field"]) - if platform.system() == "Linux": - # F5 - if event["native_key_code"] == 71: - print("[wxpython.py] F5 pressed, calling"\ - " browser.ReloadIgnoreCache()") - browser.ReloadIgnoreCache() - return True - # Escape - if event["native_key_code"] == 9: - print("[wxpython.py] Esc pressed, calling browser.StopLoad()") - browser.StopLoad() - return True - # F12 - if event["native_key_code"] == 96: - print("[wxpython.py] F12 pressed, calling"\ - " browser.ShowDevTools()") - browser.ShowDevTools() - return True - elif platform.system() == "Windows": - # F5 todo - # Escape todo - pass - return False - - # ------------------------------------------------------------------------- - # RequestHandler - # ------------------------------------------------------------------------- - - def OnBeforeBrowse(self, browser, frame, request, isRedirect): - print("[wxpython.py] RequestHandler::OnBeforeBrowse()") - print(" url = %s" % request.GetUrl()[:100]) - return False - - def OnBeforeResourceLoad(self, browser, frame, request): - print("[wxpython.py] RequestHandler::OnBeforeResourceLoad()") - print(" url = %s" % request.GetUrl()[:100]) - return False - - def OnResourceRedirect(self, browser, frame, oldUrl, newUrlOut, request): - print("[wxpython.py] RequestHandler::OnResourceRedirect()") - print(" old url = %s" % oldUrl[:100]) - print(" new url = %s" % newUrlOut[0][:100]) - - def GetAuthCredentials(self, browser, frame, isProxy, host, port, realm, - scheme, callback): - # This callback is called on the IO thread, thus print messages - # may not be visible. - print("[wxpython.py] RequestHandler::GetAuthCredentials()") - print(" host = %s" % host) - print(" realm = %s" % realm) - callback.Continue(username="test", password="test") - return True - - def OnQuotaRequest(self, browser, originUrl, newSize, callback): - print("[wxpython.py] RequestHandler::OnQuotaRequest()") - print(" origin url = %s" % originUrl) - print(" new size = %s" % newSize) - callback.Continue(True) - return True - - def GetCookieManager(self, browser, mainUrl): - # Create unique cookie manager for each browser. - # You must set the "unique_request_context_per_browser" - # application setting to True for the cookie manager - # to work. - # Return None to have one global cookie manager for - # all CEF browsers. - if not browser: - # The browser param may be empty in some exceptional - # case, see docs. - return None - cookieManager = browser.GetUserData("cookieManager") - if cookieManager: - return cookieManager - else: - print("[wxpython.py] RequestHandler::GetCookieManager():"\ - " created cookie manager") - cookieManager = cefpython.CookieManager.CreateManager("") - browser.SetUserData("cookieManager", cookieManager) - return cookieManager - - def OnProtocolExecution(self, browser, url, allowExecutionOut): - # There's no default implementation for OnProtocolExecution on Linux, - # you have to make OS system call on your own. You probably also need - # to use LoadHandler::OnLoadError() when implementing this on Linux. - print("[wxpython.py] RequestHandler::OnProtocolExecution()") - print(" url = %s" % url) - if url.startswith("magnet:"): - print("[wxpython.py] Magnet link allowed!") - allowExecutionOut[0] = True - - def _OnBeforePluginLoad(self, browser, mimeType, pluginUrl, topOriginUrl, - info): - # This is a global callback set using SetGlobalClientCallback(). - # Plugins are loaded on demand, only when website requires it, - # the same plugin may be called multiple times. - # This callback is called on various threads, thus print messages - # may not be visible. - print("[wxpython.py] RequestHandler::_OnBeforePluginLoad()") - print(" mimeType = %s" % mimeType) - print(" pluginUrl = %s" % pluginUrl) - print(" topOriginUrl = %s" % topOriginUrl) - print(" info.GetName() = %s" % info.GetName()) - print(" info.GetPath() = %s" % info.GetPath()) - print(" info.GetVersion() = %s" % info.GetVersion()) - print(" info.GetDescription() = %s" % info.GetDescription()) - # False to allow, True to block plugin. - return False - - def _OnCertificateError(self, certError, requestUrl, callback): - # This is a global callback set using SetGlobalClientCallback(). - print("[wxpython.py] RequestHandler::_OnCertificateError()") - print(" certError = %s" % certError) - print(" requestUrl = %s" % requestUrl) - if requestUrl.startswith( - "https://tv.eurosport.com/do-not-allow"): - print(" Not allowed!") - return False - if requestUrl.startswith( - "https://tv.eurosport.com/"): - print(" Allowed!") - callback.Continue(True) - return True - return False - - def OnRendererProcessTerminated(self, browser, status): - print("[wxpython.py] RequestHandler::OnRendererProcessTerminated()") - statuses = { - cefpython.TS_ABNORMAL_TERMINATION: "TS_ABNORMAL_TERMINATION", - cefpython.TS_PROCESS_WAS_KILLED: "TS_PROCESS_WAS_KILLED", - cefpython.TS_PROCESS_CRASHED: "TS_PROCESS_CRASHED" - } - statusName = "Unknown" - if status in statuses: - statusName = statuses[status] - print(" status = %s" % statusName) - - def OnPluginCrashed(self, browser, pluginPath): - print("[wxpython.py] RequestHandler::OnPluginCrashed()") - print(" plugin path = %s" % pluginPath) - - # ------------------------------------------------------------------------- - # LoadHandler - # ------------------------------------------------------------------------- - - def OnLoadingStateChange(self, browser, isLoading, canGoBack, - canGoForward): - print("[wxpython.py] LoadHandler::OnLoadingStateChange()") - print(" isLoading = %s, canGoBack = %s, canGoForward = %s" \ - % (isLoading, canGoBack, canGoForward)) - - def OnLoadStart(self, browser, frame): - print("[wxpython.py] LoadHandler::OnLoadStart()") - print(" frame url = %s" % frame.GetUrl()[:100]) - - def OnLoadEnd(self, browser, frame, httpStatusCode): - print("[wxpython.py] LoadHandler::OnLoadEnd()") - print(" frame url = %s" % frame.GetUrl()[:100]) - # For file:// urls the status code = 0 - print(" http status code = %s" % httpStatusCode) - # Tests for the Browser object methods - self._Browser_LoadUrl(browser) - - def _Browser_LoadUrl(self, browser): - if browser.GetUrl() == "data:text/html,Test#Browser.LoadUrl": - browser.LoadUrl("file://"+GetApplicationPath("wxpython.html")) - - def OnLoadError(self, browser, frame, errorCode, errorTextList, failedUrl): - print("[wxpython.py] LoadHandler::OnLoadError()") - print(" frame url = %s" % frame.GetUrl()[:100]) - print(" error code = %s" % errorCode) - print(" error text = %s" % errorTextList[0]) - print(" failed url = %s" % failedUrl) - # Handle ERR_ABORTED error code, to handle the following cases: - # 1. Esc key was pressed which calls browser.StopLoad() in OnKeyEvent - # 2. Download of a file was aborted - if errorCode == cefpython.ERR_ABORTED: - print("[wxpython.py] LoadHandler::OnLoadError(): Ignoring load"\ - " error: Esc was pressed or file download was aborted") - return; - customErrorMessage = "My custom error message!" - frame.LoadUrl("data:text/html,%s" % customErrorMessage) - - # ------------------------------------------------------------------------- - # LifespanHandler - # ------------------------------------------------------------------------- - - # ** This callback is executed on the IO thread ** - # Empty place-holders: popupFeatures, client. - def OnBeforePopup(self, browser, frame, targetUrl, targetFrameName, - targetDisposition, userGesture, - popupFeatures, windowInfo, client, browserSettings, - noJavascriptAccess): - print("[wxpython.py] LifespanHandler::OnBeforePopup()") - print(" targetUrl = %s" % targetUrl) - # Custom browser settings for popups: - # > browserSettings[0] = {"plugins_disabled": True} - # Set WindowInfo object: - # > windowInfo[0] = cefpython.WindowInfo() - allowPopups = True - return not allowPopups - - def _OnAfterCreated(self, browser): - # This is a global callback set using SetGlobalClientCallback(). - print("[wxpython.py] LifespanHandler::_OnAfterCreated()") - print(" browserId=%s" % browser.GetIdentifier()) - - def DoClose(self, browser): - print("[wxpython.py] LifespanHandler::DoClose()") - print(" browserId=%s" % browser.GetIdentifier()) - - def OnBeforeClose(self, browser): - print("[wxpython.py] LifespanHandler::OnBeforeClose") - print(" browserId=%s" % browser.GetIdentifier()) - - # ------------------------------------------------------------------------- - # JavascriptDialogHandler - # ------------------------------------------------------------------------- - - def OnJavascriptDialog(self, browser, originUrl, dialogType, - messageText, defaultPromptText, callback, - suppressMessage): - print("[wxpython.py] JavascriptDialogHandler::OnJavascriptDialog()") - print(" originUrl="+originUrl) - print(" dialogType="+str(dialogType)) - print(" messageText="+messageText) - print(" defaultPromptText="+defaultPromptText) - # If you want to suppress the javascript dialog: - # suppressMessage[0] = True - return False - - def OnBeforeUnloadJavascriptDialog(self, browser, messageText, isReload, - callback): - print("[wxpython.py] OnBeforeUnloadJavascriptDialog()") - print(" messageText="+messageText) - print(" isReload="+str(isReload)) - # Return True if the application will use a custom dialog: - # callback.Continue(allow=True, userInput="") - # return True - return False - - def OnResetJavascriptDialogState(self, browser): - print("[wxpython.py] OnResetDialogState()") - - def OnJavascriptDialogClosed(self, browser): - print("[wxpython.py] OnDialogClosed()") - - -class MyApp(wx.App): - timer = None - timerID = 1 - timerCount = 0 - - def OnInit(self): - if not USE_EVT_IDLE: - self.CreateTimer() - frame = MainFrame() - self.SetTopWindow(frame) - frame.Show() - return True - - def CreateTimer(self): - # See "Making a render loop": - # http://wiki.wxwidgets.org/Making_a_render_loop - # Another approach is to use EVT_IDLE in MainFrame, - # see which one fits you better. - self.timer = wx.Timer(self, self.timerID) - self.timer.Start(10) # 10ms - wx.EVT_TIMER(self, self.timerID, self.OnTimer) - - def OnTimer(self, event): - self.timerCount += 1 - # print("[wxpython.py] OnTimer() %d" % self.timerCount) - cefpython.MessageLoopWork() - - def OnExit(self): - # When app.MainLoop() returns, MessageLoopWork() should - # not be called anymore. - if not USE_EVT_IDLE: - self.timer.Stop() - -def GetSources(): - # Get sources of all python functions and methods from this file. - # This is to provide sources preview to wxpython.html. - # The dictionary of functions is binded to "window.sources". - thisModule = sys.modules[__name__] - functions = inspect.getmembers(thisModule, inspect.isfunction) - classes = inspect.getmembers(thisModule, inspect.isclass) - sources = {} - for funcTuple in functions: - sources[funcTuple[0]] = inspect.getsource(funcTuple[1]) - for classTuple in classes: - className = classTuple[0] - classObject = classTuple[1] - methods = inspect.getmembers(classObject) - for methodTuple in methods: - try: - sources[methodTuple[0]] = inspect.getsource(\ - methodTuple[1]) - except: - pass - return sources - -if __name__ == '__main__': - print('[wxpython.py] wx.version=%s' % wx.version()) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - settings = { - # CEF Python debug messages in console and in log_file - "debug": True, - # Set it to LOGSEVERITY_VERBOSE for more details - "log_severity": cefpython.LOGSEVERITY_INFO, - # Set to "" to disable logging to a file - "log_file": GetApplicationPath("debug.log"), - # These directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - # The "subprocess" executable that launches the Renderer - # and GPU processes among others. You may rename that - # executable if you like. - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess"), - # This option is required for the GetCookieManager callback - # to work. It affects renderer processes, when this option - # is set to True. It will force a separate renderer process - # for each browser created using CreateBrowserSync. - "unique_request_context_per_browser": True, - # Downloads are handled automatically. A default SaveAs file - # dialog provided by OS will be displayed. - "downloads_enabled": True, - # Remote debugging port, required for Developer Tools support. - # A value of 0 will generate a random port. To disable devtools - # support set it to -1. - "remote_debugging_port": 0, - # Mouse context menu - "context_menu": { - "enabled": True, - "navigation": True, # Back, Forward, Reload - "print": True, - "view_source": True, - "external_browser": True, # Open in external browser - "devtools": True, # Developer Tools - }, - # See also OnCertificateError which allows you to ignore - # certificate errors for specific websites. - "ignore_certificate_errors": False, - } - - # Browser settings. You may have different settings for each - # browser, see the call to CreateBrowserSync. - g_browserSettings = { - # "plugins_disabled": True, - # "file_access_from_file_urls_allowed": True, - # "universal_access_from_file_urls_allowed": True, - } - - # Command line switches set programmatically - switches = { - # "proxy-server": "socks5://127.0.0.1:8888", - # "no-proxy-server": "", - # "enable-media-stream": "", - # "remote-debugging-port": "12345", - # "disable-gpu": "", - # "--invalid-switch": "" -> Invalid switch name - } - - cefpython.Initialize(settings, switches) - - app = MyApp(False) - app.MainLoop() - # Let wx.App destructor do the cleanup before calling cefpython.Shutdown(). - del app - - cefpython.Shutdown() diff --git a/src/linux/compile.py b/src/linux/compile.py index 623c3b07..16acf092 100644 --- a/src/linux/compile.py +++ b/src/linux/compile.py @@ -300,7 +300,7 @@ def check_cython_version(): # Make installer, install, run examples and unit tests, # and return to src/linux/ dir. if KIVY_FLAG: - run_examples = " && {python} ../src/linux/binaries_64bit/kivy_.py" + run_examples = " && {python} ../src/linux/deprecated_64bit/kivy_.py" else: run_examples = (" && {python} hello_world.py" " && {python} wxpython.py" @@ -310,7 +310,7 @@ def check_cython_version(): " && {python} tkinter_.py" " && {python} qt.py pyqt" " && {python} qt.py pyside" - " && {python} ../src/linux/binaries_64bit/kivy_.py") + " && {python} ../src/linux/deprecated_64bit/kivy_.py") commands = ("cd ./installer/" " && {python} make-setup.py --version {ver}" " && cd cefpython3-{ver}-*-setup/" diff --git a/src/linux/rebuild.sh b/src/linux/rebuild.sh deleted file mode 100644 index f703ba9c..00000000 --- a/src/linux/rebuild.sh +++ /dev/null @@ -1 +0,0 @@ -python compile.py diff --git a/src/linux/wxpython.sh b/src/linux/wxpython.sh deleted file mode 100644 index f28b13c1..00000000 --- a/src/linux/wxpython.sh +++ /dev/null @@ -1,2 +0,0 @@ -cd binaries_64bit -python wxpython.py diff --git a/src/mac/.gitignore b/src/mac/.gitignore deleted file mode 100644 index af654df0..00000000 --- a/src/mac/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -Resources/ -*.dylib -*.so -subprocess -webcache/ diff --git a/src/mac/binaries_64bit/prism.css b/src/mac/binaries_64bit/prism.css deleted file mode 100644 index f94cca7c..00000000 --- a/src/mac/binaries_64bit/prism.css +++ /dev/null @@ -1,130 +0,0 @@ -/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+python */ -/** - * prism.js default theme for JavaScript, CSS and HTML - * Based on dabblet (http://dabblet.com) - * @author Lea Verou - */ - -code[class*="language-"], -pre[class*="language-"] { - color: black; - text-shadow: 0 1px white; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, -code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { - text-shadow: none; - background: #b3d4fc; -} - -pre[class*="language-"]::selection, pre[class*="language-"] ::selection, -code[class*="language-"]::selection, code[class*="language-"] ::selection { - text-shadow: none; - background: #b3d4fc; -} - -@media print { - code[class*="language-"], - pre[class*="language-"] { - text-shadow: none; - } -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #f5f2f0; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #999; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag, -.token.boolean, -.token.number, -.token.constant, -.token.symbol { - color: #905; -} - -.token.selector, -.token.attr-name, -.token.string, -.token.builtin { - color: #690; -} - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string, -.token.variable { - color: #a67f59; - background: hsla(0,0%,100%,.5); -} - -.token.atrule, -.token.attr-value, -.token.keyword { - color: #07a; -} - -.token.function { - color: #DD4A68; -} - -.token.regex, -.token.important { - color: #e90; -} - -.token.important { - font-weight: bold; -} - -.token.entity { - cursor: help; -} - diff --git a/src/mac/binaries_64bit/prism.js b/src/mac/binaries_64bit/prism.js deleted file mode 100644 index ebaa4b42..00000000 --- a/src/mac/binaries_64bit/prism.js +++ /dev/null @@ -1,5 +0,0 @@ -/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+python */ -self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content)):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(f instanceof r)){l.lastIndex=0;var h=l.exec(f);if(h){c&&(g=h[1].length);var d=h.index-1+g,h=h[0].slice(g),p=h.length,m=d+p,v=f.slice(0,d+1),y=f.slice(m+1),k=[u,1];v&&k.push(v);var b=new r(o,s?t.tokenize(h,s):h);k.push(b),y&&k.push(y),Array.prototype.splice.apply(a,k)}}}}return a},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[],r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(n)}}},n=t.Token=function(e,t){this.type=e,this.content=t};if(n.stringify=function(e,r,a){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,r,e)}).join("");var i={type:e.type,content:n.stringify(e.content,r,a),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:a};"comment"==i.type&&(i.attributes.spellcheck="true"),t.hooks.run("wrap",i);var o="";for(var l in i.attributes)o+=l+'="'+(i.attributes[l]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,a=n.code;self.postMessage(JSON.stringify(t.tokenize(a,t.languages[r]))),self.close()},!1),self.Prism):self.Prism;var r=document.getElementsByTagName("script");return r=r[r.length-1],r&&(t.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; -Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; -Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});; -Prism.languages.python={comment:{pattern:/(^|[^\\])#.*?(\r?\n|$)/g,lookbehind:!0},string:/"""[\s\S]+?"""|("|')(\\?.)*?\1/g,keyword:/\b(as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/g,"boolean":/\b(True|False)\b/g,number:/\b-?(0x)?\d*\.?[\da-f]+\b/g,operator:/[-+]{1,2}|=?<|=?>|!|={1,2}|(&){1,2}|(&){1,2}|\|?\||\?|\*|\/|~|\^|%|\b(or|and|not)\b/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; diff --git a/src/mac/binaries_64bit/wxpython.html b/src/mac/binaries_64bit/wxpython.html deleted file mode 100644 index c23b384f..00000000 --- a/src/mac/binaries_64bit/wxpython.html +++ /dev/null @@ -1,708 +0,0 @@ - - - - - wxPython CEF 3 example (utf-8: Ä…Å›) - - - - - - -Use mouse context menu to go Back/Forward in navigation history.
- -

Table of contents

-
    -
  1. Google search
  2. -
  3. User agent
  4. -
  5. Popups
  6. -
  7. HTML 5 video
  8. -
  9. Developer Tools
  10. -
  11. Downloads
  12. -
  13. HTML controls
  14. -
  15. Browser object
  16. -
  17. Frame object
  18. -
  19. Javascript bindings
  20. -
  21. Javascript callbacks
  22. -
  23. Python callbacks
  24. -
  25. Display handler
  26. -
  27. Keyboard handler
  28. -
  29. Request handler
  30. -
  31. Cookie tests
  32. -
  33. Load handler
  34. -
  35. Javascript Dialog handler
  36. -
  37. Other tests
  38. -
- - - - - - -

Google search

- -http://www.google.com/ - - - - - - - -

User agent

- - - - - - - - - -

Popups

- -
    -
  1. - window.open('wxpython.html') -
  2. -
  3. - target=_blank href="wxpython.html" -
  4. -
- -

CreateAnotherBrowser

- -This will create a window on its own and embed browser in it. -When using "window.open" the window is created implicitilly -by CEF. You can intercept such popup creation using the -OnBeforePopup callback in LifespanHandler. You can return -True in OnBeforePopup and create popup window on your own -using the CreateAnotherBrowser function. - - - - external.CreateAnotherBrowser() - - - - - - - -

HTML5 video and accelerated content

- - -HTML 5 video
- - -Accelerated canvas
- - -Accelerated layers
- - - - - - -

Developer Tools

- -You can open devtools popup window in a few different ways: -
    -
  1. Call Browser.ShowDevTools() method: - - external.ShowDevTools()
  2. -
  3. Through mouse context menu
  4. -
  5. Through F12 key (on Mac: Cmd+Opt+I) which is handled in - KeyboardHandler.OnKeyEvent.
  6. -
- - - - - - -

Downloads

- -Download sample Ubuntu wallpapers:
- - https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip - -

-Notes: On Linux it seems that OnLoadError with errorCode = ERR_ABORTED -is called even for successful downloads, you can ignore this behavior. -The proper console messages about successful/aborted download originate -from C++ Browser process code, these are: -

- -
Browser: About to download file: ubuntu-wallpapers2.zip
-Browser: Download completed, saved to: /Downloads/ubuntu-wallpapers2.zip
-
- -If download was aborted the messages will be: - -
Browser: About to download file: ubuntu-wallpapers2.zip
-Browser: Download was cancelled
-
- -

-Additionally on Linux there are more errors reported by Chromium -about org.gnome.SessionManager.inhibit. These can be safely ignored as well. -

- -A download handler with callbacks like `OnBeforeDownload` and -`OnDownloadUpdated` may be exposed to CEF Python in the future. - - - - - - -

HTML controls

- -

Textarea

- -
- -

Inputs

-Text:
-Password:
- -

Select

- - -

Buttons

-Submit:
-Button:
- - - - - - -

Browser object

- -Tests for the Browser object methods. - -

GoBack

- -external.GoBack() - -

GoForward

- -external.GoForward() - -

LoadUrl, GetUrl

- - - window.open('data:text/html,Test#Browser.LoadUrl') - -

ReloadIgnoreCache, StopLoad

-Press F5 to reload page and ignore cache.
-Press Esc during webpage loading to abort.
- -Also, when Esc is pressed OnLoadError may get called. See how abort -of page loading or file download is handled: - - - - - - - -

Frame object

- -Tests for the Frame object methods. TODO. - - - - - - -

Javascript bindings

- -

PyPrint

- - - window.PyPrint('printing in python console from js') -
- -

Window properties

- -
jsBindings.SetProperty("pyProperty", "This was set in Python")
-jsBindings.SetProperty("pyConfig", ["This was set in Python",
-        {"name": "Nested dictionary", "isNested": True},
-        [1,"2", None]])
-
- - - window.alert(window.pyProperty)
- - window.alert(JSON.stringify(window.pyConfig)) -
- -

Print

- - - - external.Print('printing again from js') -
- -

TestAllTypes

- - - - external.TestAllTypes - (undefined, null, true, 1, - ((1<<31)>>>0), 2.14, 'Date not yet supported', 'string', - {key1: 1, key2: 2}, {key1: {'key1.1': 'nested object'}, 'key1.2': [1]}, - [1, 2], [1, [2.1, 'nested array']], [{key1: [{}]}]) -
- -

ExecuteFunction

- - - -
<script>
-function JavascriptAlert(message) { window.alert(message); }
-</script>
-
- - - - external.ExecuteFunction('JavascriptAlert', - 'python called from js and then js called from python') -
- -

GetSource, GetText

- - - - - - - external.GetSource() -
- - external.GetText() - - - - - - -

Javascript callbacks

- -

TestJSCallback

- - - -
<script>
-function JSCallback(arg1) {
-    window.alert(arg1)
-}
-</script>
-
- - - - external.TestJSCallback(JSCallback) - -

TestJSCallbackComplexArguments

- - - -
<script>
-function JSCallback2() {
-    window.alert(JSON.stringify(arguments))
-}
-</script>
-
- - - - external.TestJSCallbackComplexArguments({"myCallback": JSCallback2}) - - - - - - - -

Python callbacks

- -

TestPythonCallback

- - - -
<script>
-function JSCallback3(pyCallback) {
-    pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
-}
-</script>
-
- - - - - - external.TestPythonCallback(JSCallback3) - - - - - - -

Display handler

- -

OnAddressChange

- -See messages in the console during loading of a webpage. - -

OnTitleChange

- -See messages in the console during loading of a webpage. - -

OnTooltip

- -See messages in the console when hovering over a google logo: -http://www.google.com/ - -

OnStatusMessage

- -See messages in the console when hovering over links. - -

OnConsoleMessage

- -Try this: - - http://patik.com/code/console-log-polyfill/ - - - - - - -

Keyboard handler

- -

- Press F5 to reload the page.
- On Linux it is required to click anywhere in the window first - so that keyboard focus is set. See Issue 77 in the CEF Python - Issue Tracker. -

- - - - - - - - - -

Request handler

- -

OnBeforeResourceLoad

- -See messages in the console. - -

OnResourceRedirect

- -Try this: - - http://tinyurl.com/google404redirect - -

GetAuthCredentials

- -Try this: - - http://browserspy.dk/password-ok.php - -

OnQuotaRequest

- - - - -
<script>
-function DoRequestQuota() {
-    // Request Quota (only for File System API)
-    try {
-        navigator.webkitPersistentStorage.requestQuota(PERSISTENT, 1024*1024,
-                function(bytes){ window.alert("Granted bytes: "+bytes);},
-                function(error){ window.alert(error); });
-    } catch(e) {
-        navigator.webkitPersistentStorage.requestQuota(1024*1024,
-                function(bytes){ window.alert("Granted bytes: "+bytes);},
-                function(error){ window.alert(error); });
-    }
-}
-</script>
-
- -Try this: - - https://googledrive.com/host/0B1di2XiBBfacMnhRRkI1YlotUEk/requestquota.html - -

OnProtocolExecution

- -Try this: - - magnet:?xt=urn:btih:a4224b45b27f436374391379cc5c7e629e2e5189 - -

_OnBeforePluginLoad

- -Try OnBeforePluginLoad() with Flash: - - http://www.adobe.com/software/flash/about/ - -

_OnCertificateError

- - -The url below won't be allowed. Click twice "Back" from context menu to return back -here after visiting the url:
- - https://tv.eurosport.com/do-not-allow -
- -This url will be allowed:
- - https://tv.eurosport.com/ - -

OnRendererProcessTerminated

- -Try to terminate the "subprocess.exe" renderer process through -task manager. - -

OnPluginCrashed

- -No test for that yet. - - - - - - -

Cookie tests

- -See messages in the console. - -

GetCookieManager

- - - -RequestHandler.GetCookieManager() - an example of having an unique -cookie manager for each browser. -
    -
  1. Visit the url below and set some cookies. Use "Back" from - context menu to get back here (you might have to click "Back" - multiple times).
    - Visit it in the current browser:
    - - http://www.html-kit.com/tools/cookietester/ -
    - Or visit it in a js popup:
    - - javascript:window.open('http://www.html-kit.com/tools/cookietester/') -
  2. -
  3. Open cookietester in a popup:
    - - javascript:external.CreateAnotherBrowser('http://www.html-kit.com/tools/cookietester/') -
  4. -
- -

-Popup browsers created javascript's window.open share -the same renderer process and request context. If you want -to have separate cookie managers for popups created using -window.open then you have to implement the -LifespanHandler.`OnBeforePopup` callback. Return True in that -callback to cancel popup creation and instead create the -window on your own and embed browser in it. The CreateAnotherBrowser() -function from the wxpython example does that. -

- -

VisitAllCookies

- -Visit all cookies: -external.VisitAllCookies() -

- -Note: visit some http:// webpage first, otherwise cookie manager is not -yet created. -
- -

VisitUrlCookies

- -Visit a subset of cookies for the given url: - - external.VisitUrlCookies("http://www.html-kit.com/tools/cookietester/") -
- -

SetCookie

- -Set a cookie: -external.SetCookie() -
- -

DeleteCookies

- -Delete the single cookie previously created via SetCookie(): - - external.DeleteCookies() -
- - - - - - -

Load Handler

- -See messages in the console during loading of a webpage. - -

OnLoadingStateChange

- - -

OnLoadStart

- - -

OnLoadEnd

- - -

OnLoadError

- -Try this: - - http://www.non-existent.nono/ -

- -Note: after you see the custom error message you have to hit -twice the Back from the context menu, to get back to this page. - - - - - - -

Javascript Dialog Handler

- -See messages in the console. - -

OnJavascriptDialog

- - - alert('Test js dialog handler') - - -

OnBeforeUnloadJavascriptDialog

- - - -
<script>
-function TestOnBeforeUnloadJavascriptDialog() {
-    window.onbeforeunload = function() {
-        return 'Testing the OnBeforeUnloadJavascriptDialog() callback';
-    }
-    location.href = "wxpython.html";
-}
-</script>
-
- - - TestOnBeforeUnloadJavascriptDialog() - - -

OnResetJavascriptDialogState

- - -

OnJavascriptDialogClosed

- - - - - - - -

Other tests

- -

HTTPS caching with SSL certificate errors

-Set ApplicationSettings["ignore_certificate_errors"] to True. - - - - - - - - - - - - - diff --git a/src/mac/binaries_64bit/wxpython.py b/src/mac/binaries_64bit/wxpython.py deleted file mode 100644 index bdcca1ce..00000000 --- a/src/mac/binaries_64bit/wxpython.py +++ /dev/null @@ -1,851 +0,0 @@ -# An example of embedding CEF browser in wxPython on Mac. -# Tested with wxPython3.0-osx-3.0.2.0-cocoa-py2.7.dmg which -# was downloaded from the wxpython.org website. - -# IMPORTANT - importing CEF Python -# -------------------------------------------------------------- -# The cefpython library must be the very first library imported. -# This is because CEF was compiled with the tcmalloc memory -# allocator which hooks globally and replaces the default -# malloc allocator. If memory was allocated using malloc and -# then freed using tcmalloc then this would result in random -# segmentation faults in an application. See Issue 155 which -# is to provide CEF builds on Mac with tcmalloc disabled: -# https://github.com/cztomczak/cefpython/issues/155 - -import ctypes, os, sys -libcef_so = os.path.join(os.path.dirname(os.path.abspath(__file__)),\ - 'libcef.dylib') -if os.path.exists(libcef_so): - # Import a local module - ctypes.CDLL(libcef_so, ctypes.RTLD_GLOBAL) - if 0x02070000 <= sys.hexversion < 0x03000000: - import cefpython_py27 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -import wx -import time -import re -import uuid -import platform -import inspect -import struct - -g_applicationSettings = None -g_browserSettings = None -g_switches = None -g_countWindows = 0 - -# Which method to use for message loop processing. -# EVT_IDLE - wx application has priority (default) -# EVT_TIMER - cef browser has priority -# It seems that Flash content behaves better when using a timer. -# IMPORTANT! On Linux EVT_IDLE does not work, the events seems to -# be propagated only when you move your mouse, which is not the -# expected behavior, it is recommended to use EVT_TIMER on Linux, -# so set this value to False. -USE_EVT_IDLE = False - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[wxpython.py] WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainFrame(wx.Frame): - browser = None - mainPanel = None - clientHandler = None - javascriptExternal = None - - def __init__(self, url=None): - wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, - title='wxPython CEF 3 example', size=(800,600)) - - global g_countWindows - g_countWindows += 1 - - if not url: - url = "file://"+GetApplicationPath("wxpython.html") - # Test hash in url. - # url += "#test-hash" - - self.CreateMenu() - - # Cannot attach browser to the main frame as this will cause - # the menu not to work. - # -- - # You also have to set the wx.WANTS_CHARS style for - # all parent panels/controls, if it's deeply embedded. - self.mainPanel = wx.Panel(self, style=wx.WANTS_CHARS) - - # Global client callbacks must be set before browser is created. - self.clientHandler = ClientHandler() - cefpython.SetGlobalClientCallback("OnCertificateError", - self.clientHandler._OnCertificateError) - cefpython.SetGlobalClientCallback("OnBeforePluginLoad", - self.clientHandler._OnBeforePluginLoad) - cefpython.SetGlobalClientCallback("OnAfterCreated", - self.clientHandler._OnAfterCreated) - - windowInfo = cefpython.WindowInfo() - (width, height) = self.mainPanel.GetClientSizeTuple() - windowInfo.SetAsChild(self.mainPanel.GetHandle(), - [0, 0, width, height]) - # Linux requires adding "file://" for local files, - # otherwise /home/some will be replaced as http://home/some - self.browser = cefpython.CreateBrowserSync( - windowInfo, - # If there are problems with Flash you can disable it here, - # by disabling all plugins. - browserSettings=g_browserSettings, - navigateUrl=url) - - self.clientHandler.mainBrowser = self.browser - self.browser.SetClientHandler(self.clientHandler) - - jsBindings = cefpython.JavascriptBindings( - bindToFrames=False, bindToPopups=True) - jsBindings.SetFunction("PyPrint", PyPrint) - jsBindings.SetProperty("pyProperty", "This was set in Python") - jsBindings.SetProperty("pyConfig", ["This was set in Python", - {"name": "Nested dictionary", "isNested": True}, - [1,"2", None]]) - self.javascriptExternal = JavascriptExternal(self.browser) - jsBindings.SetObject("external", self.javascriptExternal) - jsBindings.SetProperty("sources", GetSources()) - self.browser.SetJavascriptBindings(jsBindings) - - self.Bind(wx.EVT_CLOSE, self.OnClose) - if USE_EVT_IDLE: - # Bind EVT_IDLE only for the main application frame. - self.Bind(wx.EVT_IDLE, self.OnIdle) - - def CreateMenu(self): - filemenu = wx.Menu() - filemenu.Append(1, "Open") - exit = filemenu.Append(2, "Exit") - self.Bind(wx.EVT_MENU, self.OnClose, exit) - aboutmenu = wx.Menu() - aboutmenu.Append(1, "CEF Python") - menubar = wx.MenuBar() - menubar.Append(filemenu,"&File") - menubar.Append(aboutmenu, "&About") - self.SetMenuBar(menubar) - - def OnClose(self, event): - # Remove all CEF browser references so that browser is closed - # cleanly. Otherwise there may be issues for example with cookies - # not being flushed to disk when closing app immediately - # (Issue 158). - del self.javascriptExternal.mainBrowser - del self.clientHandler.mainBrowser - del self.browser - - # Destroy wx frame, this will complete the destruction of CEF browser - self.Destroy() - - # In wx.chromectrl calling browser.CloseBrowser and/or self.Destroy - # may cause crashes when embedding multiple browsers in tab - # (Issue 107). In such case instead of calling CloseBrowser/Destroy - # try this code: - # | self.browser.ParentWindowWillClose() - # | event.Skip() - - global g_countWindows - g_countWindows -= 1 - if g_countWindows == 0: - # On Win/Linux the call to cefpython.Shutdown() is after - # app.MainLoop() returns, but on Mac it needs to be here. - cefpython.Shutdown() - print("[wxpython.py] OnClose: Exiting") - wx.GetApp().Exit() - - def OnIdle(self, event): - cefpython.MessageLoopWork() - -def PyPrint(message): - print("[wxpython.py] PyPrint: "+message) - -class JavascriptExternal: - mainBrowser = None - stringVisitor = None - - def __init__(self, mainBrowser): - self.mainBrowser = mainBrowser - - def GoBack(self): - self.mainBrowser.GoBack() - - def GoForward(self): - self.mainBrowser.GoForward() - - def CreateAnotherBrowser(self, url=None): - """ - TODO: There are errors in the console when closing the window: - >> Check failed: window - >> Gdk: _gdk_window_destroy_hierarchy: assertion `GDK_IS_WINDOW\ - >> (window)' failed - >> GLib-GObject: g_object_unref: assertion `G_IS_OBJECT (object)' failed - """ - frame = MainFrame(url=url) - frame.Show() - - def Print(self, message): - print("[wxpython.py] Print: "+message) - - def TestAllTypes(self, *args): - print("[wxpython.py] TestAllTypes: "+str(args)) - - def ExecuteFunction(self, *args): - self.mainBrowser.GetMainFrame().ExecuteFunction(*args) - - def TestJSCallback(self, jsCallback): - print("[wxpython.py] jsCallback.GetFunctionName() = %s"\ - % jsCallback.GetFunctionName()) - print("[wxpython.py] jsCallback.GetFrame().GetIdentifier() = %s" % \ - jsCallback.GetFrame().GetIdentifier()) - jsCallback.Call("This message was sent from python using js callback") - - def TestJSCallbackComplexArguments(self, jsObject): - jsCallback = jsObject["myCallback"]; - jsCallback.Call(1, None, 2.14, "string", ["list", ["nested list", \ - {"nested object":None}]], \ - {"nested list next":[{"deeply nested object":1}]}) - - def TestPythonCallback(self, jsCallback): - jsCallback.Call(self.PyCallback) - - def PyCallback(self, *args): - message = "PyCallback() was executed successfully! "\ - "Arguments: %s" % str(args) - print("[wxpython.py] "+message) - self.mainBrowser.GetMainFrame().ExecuteJavascript( - "window.alert(\"%s\")" % message) - - def GetSource(self): - # Must keep a strong reference to the StringVisitor object - # during the visit. - self.stringVisitor = StringVisitor() - self.mainBrowser.GetMainFrame().GetSource(self.stringVisitor) - - def GetText(self): - # Must keep a strong reference to the StringVisitor object - # during the visit. - self.stringVisitor = StringVisitor() - self.mainBrowser.GetMainFrame().GetText(self.stringVisitor) - - def ShowDevTools(self): - print("[wxpython.py] external.ShowDevTools called") - self.mainBrowser.ShowDevTools() - - # ------------------------------------------------------------------------- - # Cookies - # ------------------------------------------------------------------------- - cookieVisitor = None - - def VisitAllCookies(self): - # Need to keep the reference alive. - self.cookieVisitor = CookieVisitor() - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.VisitAllCookies(self.cookieVisitor) - - def VisitUrlCookies(self): - # Need to keep the reference alive. - self.cookieVisitor = CookieVisitor() - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.VisitUrlCookies( - "http://www.html-kit.com/tools/cookietester/", - False, self.cookieVisitor) - # .www.html-kit.com - - def SetCookie(self): - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - "the cookietester website first and create some cookies") - return - cookie = cefpython.Cookie() - cookie.SetName("Created_Via_Python") - cookie.SetValue("yeah really") - cookieManager.SetCookie("http://www.html-kit.com/tools/cookietester/", - cookie) - print("\n[wxpython.py] Cookie created! Visit html-kit cookietester to"\ - " see it") - - def DeleteCookies(self): - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.DeleteCookies( - "http://www.html-kit.com/tools/cookietester/", - "Created_Via_Python") - print("\n[wxpython.py] Cookie deleted! Visit html-kit cookietester "\ - "to see the result") - -class StringVisitor: - def Visit(self, string): - print("\n[wxpython.py] StringVisitor.Visit(): string:") - print("--------------------------------") - print(string) - print("--------------------------------") - -class CookieVisitor: - def Visit(self, cookie, count, total, deleteCookie): - if count == 0: - print("\n[wxpython.py] CookieVisitor.Visit(): total cookies: %s"\ - % total) - print("\n[wxpython.py] CookieVisitor.Visit(): cookie:") - print(" "+str(cookie.Get())) - # True to continue visiting cookies - return True - -class ClientHandler: - mainBrowser = None # May be None for global client callbacks. - - def __init__(self): - pass - - # ------------------------------------------------------------------------- - # DisplayHandler - # ------------------------------------------------------------------------- - - def OnAddressChange(self, browser, frame, url): - print("[wxpython.py] DisplayHandler::OnAddressChange()") - print(" url = %s" % url) - - def OnTitleChange(self, browser, title): - print("[wxpython.py] DisplayHandler::OnTitleChange()") - print(" title = %s" % title) - - def OnTooltip(self, browser, textOut): - # OnTooltip not yet implemented (both Linux and Windows), - # will be fixed in next CEF release, see Issue 783: - # https://code.google.com/p/chromiumembedded/issues/detail?id=783 - print("[wxpython.py] DisplayHandler::OnTooltip()") - print(" text = %s" % textOut[0]) - - statusMessageCount = 0 - def OnStatusMessage(self, browser, value): - if not value: - # Do not notify in the console about empty statuses. - return - self.statusMessageCount += 1 - if self.statusMessageCount > 3: - # Do not spam too much. - return - print("[wxpython.py] DisplayHandler::OnStatusMessage()") - print(" value = %s" % value) - - def OnConsoleMessage(self, browser, message, source, line): - print("[wxpython.py] DisplayHandler::OnConsoleMessage()") - print(" message = %s" % message) - print(" source = %s" % source) - print(" line = %s" % line) - - # ------------------------------------------------------------------------- - # KeyboardHandler - # ------------------------------------------------------------------------- - - def OnPreKeyEvent(self, browser, event, eventHandle, - isKeyboardShortcutOut): - print("[wxpython.py] KeyboardHandler::OnPreKeyEvent()") - - def OnKeyEvent(self, browser, event, eventHandle): - if event["type"] == cefpython.KEYEVENT_KEYUP: - # OnKeyEvent is called twice for F5/Esc keys, with event - # type KEYEVENT_RAWKEYDOWN and KEYEVENT_KEYUP. - # Normal characters a-z should have KEYEVENT_CHAR. - return False - print("[wxpython.py] KeyboardHandler::OnKeyEvent()") - print(" type=%s" % event["type"]) - print(" modifiers=%s" % event["modifiers"]) - print(" windows_key_code=%s" % event["windows_key_code"]) - print(" native_key_code=%s" % event["native_key_code"]) - print(" is_system_key=%s" % event["is_system_key"]) - print(" character=%s" % event["character"]) - print(" unmodified_character=%s" % event["unmodified_character"]) - print(" focus_on_editable_field=%s"\ - % event["focus_on_editable_field"]) - if platform.system() == "Linux": - # F5 - if event["native_key_code"] == 71: - print("[wxpython.py] F5 pressed, calling"\ - " browser.ReloadIgnoreCache()") - browser.ReloadIgnoreCache() - return True - # Escape - if event["native_key_code"] == 9: - print("[wxpython.py] Esc pressed, calling browser.StopLoad()") - browser.StopLoad() - return True - # F12 - if event["native_key_code"] == 96: - print("[wxpython.py] F12 pressed, calling"\ - " browser.ShowDevTools()") - browser.ShowDevTools() - return True - elif platform.system() == "Windows": - # F5 todo - # Escape todo - pass - elif platform.system() == "Darwin": - # Cmd+Opt+I - if event["modifiers"] == 136 and event["character"] == 94: - browser.ShowDevTools() - return True - # F5 - if event["modifiers"] == 0 and event["character"] == 63240: - browser.ReloadIgnoreCache() - return True - # Esc - if event["modifiers"] == 0 and event["character"] == 27: - browser.StopLoad() - return True - return False - - # ------------------------------------------------------------------------- - # RequestHandler - # ------------------------------------------------------------------------- - - def OnBeforeBrowse(self, browser, frame, request, isRedirect): - print("[wxpython.py] RequestHandler::OnBeforeBrowse()") - print(" url = %s" % request.GetUrl()[:100]) - return False - - def OnBeforeResourceLoad(self, browser, frame, request): - print("[wxpython.py] RequestHandler::OnBeforeResourceLoad()") - print(" url = %s" % request.GetUrl()[:100]) - return False - - def OnResourceRedirect(self, browser, frame, oldUrl, newUrlOut, request): - print("[wxpython.py] RequestHandler::OnResourceRedirect()") - print(" old url = %s" % oldUrl[:100]) - print(" new url = %s" % newUrlOut[0][:100]) - - def GetAuthCredentials(self, browser, frame, isProxy, host, port, realm, - scheme, callback): - # This callback is called on the IO thread, thus print messages - # may not be visible. - print("[wxpython.py] RequestHandler::GetAuthCredentials()") - print(" host = %s" % host) - print(" realm = %s" % realm) - callback.Continue(username="test", password="test") - return True - - def OnQuotaRequest(self, browser, originUrl, newSize, callback): - print("[wxpython.py] RequestHandler::OnQuotaRequest()") - print(" origin url = %s" % originUrl) - print(" new size = %s" % newSize) - callback.Continue(True) - return True - - def GetCookieManager(self, browser, mainUrl): - # Create unique cookie manager for each browser. - # You must set the "unique_request_context_per_browser" - # application setting to True for the cookie manager - # to work. - # Return None to have one global cookie manager for - # all CEF browsers. - if not browser: - # The browser param may be empty in some exceptional - # case, see docs. - return None - cookieManager = browser.GetUserData("cookieManager") - if cookieManager: - return cookieManager - else: - print("[wxpython.py] RequestHandler::GetCookieManager():"\ - " created cookie manager") - cookieManager = cefpython.CookieManager.CreateManager("") - if "cache_path" in g_applicationSettings: - path = g_applicationSettings["cache_path"] - # path = os.path.join(path, "cookies_browser_{}".format( - # browser.GetIdentifier())) - cookieManager.SetStoragePath(path) - browser.SetUserData("cookieManager", cookieManager) - return cookieManager - - def OnProtocolExecution(self, browser, url, allowExecutionOut): - # There's no default implementation for OnProtocolExecution on Linux, - # you have to make OS system call on your own. You probably also need - # to use LoadHandler::OnLoadError() when implementing this on Linux. - print("[wxpython.py] RequestHandler::OnProtocolExecution()") - print(" url = %s" % url) - if url.startswith("magnet:"): - print("[wxpython.py] Magnet link allowed!") - allowExecutionOut[0] = True - - def _OnBeforePluginLoad(self, browser, mimeType, pluginUrl, topOriginUrl, - info): - # This is a global callback set using SetGlobalClientCallback(). - # Plugins are loaded on demand, only when website requires it, - # the same plugin may be called multiple times. - # This callback is called on various threads, thus print messages - # may not be visible. - print("[wxpython.py] RequestHandler::_OnBeforePluginLoad()") - print(" mimeType = %s" % mimeType) - print(" pluginUrl = %s" % pluginUrl) - print(" topOriginUrl = %s" % topOriginUrl) - print(" info.GetName() = %s" % info.GetName()) - print(" info.GetPath() = %s" % info.GetPath()) - print(" info.GetVersion() = %s" % info.GetVersion()) - print(" info.GetDescription() = %s" % info.GetDescription()) - # False to allow, True to block plugin. - return False - - def _OnCertificateError(self, certError, requestUrl, callback): - # This is a global callback set using SetGlobalClientCallback(). - print("[wxpython.py] RequestHandler::_OnCertificateError()") - print(" certError = %s" % certError) - print(" requestUrl = %s" % requestUrl) - if requestUrl.startswith( - "https://tv.eurosport.com/do-not-allow"): - print(" Not allowed!") - return False - if requestUrl.startswith( - "https://tv.eurosport.com/"): - print(" Allowed!") - callback.Continue(True) - return True - return False - - def OnRendererProcessTerminated(self, browser, status): - print("[wxpython.py] RequestHandler::OnRendererProcessTerminated()") - statuses = { - cefpython.TS_ABNORMAL_TERMINATION: "TS_ABNORMAL_TERMINATION", - cefpython.TS_PROCESS_WAS_KILLED: "TS_PROCESS_WAS_KILLED", - cefpython.TS_PROCESS_CRASHED: "TS_PROCESS_CRASHED" - } - statusName = "Unknown" - if status in statuses: - statusName = statuses[status] - print(" status = %s" % statusName) - - def OnPluginCrashed(self, browser, pluginPath): - print("[wxpython.py] RequestHandler::OnPluginCrashed()") - print(" plugin path = %s" % pluginPath) - - # ------------------------------------------------------------------------- - # LoadHandler - # ------------------------------------------------------------------------- - - def OnLoadingStateChange(self, browser, isLoading, canGoBack, - canGoForward): - print("[wxpython.py] LoadHandler::OnLoadingStateChange()") - print(" isLoading = %s, canGoBack = %s, canGoForward = %s" \ - % (isLoading, canGoBack, canGoForward)) - - def OnLoadStart(self, browser, frame): - print("[wxpython.py] LoadHandler::OnLoadStart()") - print(" frame url = %s" % frame.GetUrl()[:100]) - - def OnLoadEnd(self, browser, frame, httpStatusCode): - print("[wxpython.py] LoadHandler::OnLoadEnd()") - print(" frame url = %s" % frame.GetUrl()[:100]) - # For file:// urls the status code = 0 - print(" http status code = %s" % httpStatusCode) - # Tests for the Browser object methods - self._Browser_LoadUrl(browser) - - def _Browser_LoadUrl(self, browser): - if browser.GetUrl() == "data:text/html,Test#Browser.LoadUrl": - browser.LoadUrl("file://"+GetApplicationPath("wxpython.html")) - - def OnLoadError(self, browser, frame, errorCode, errorTextList, failedUrl): - print("[wxpython.py] LoadHandler::OnLoadError()") - print(" frame url = %s" % frame.GetUrl()[:100]) - print(" error code = %s" % errorCode) - print(" error text = %s" % errorTextList[0]) - print(" failed url = %s" % failedUrl) - # Handle ERR_ABORTED error code, to handle the following cases: - # 1. Esc key was pressed which calls browser.StopLoad() in OnKeyEvent - # 2. Download of a file was aborted - if errorCode == cefpython.ERR_ABORTED: - print("[wxpython.py] LoadHandler::OnLoadError(): Ignoring load"\ - " error: Esc was pressed or file download was aborted") - return; - customErrorMessage = "My custom error message!" - frame.LoadUrl("data:text/html,%s" % customErrorMessage) - - # ------------------------------------------------------------------------- - # LifespanHandler - # ------------------------------------------------------------------------- - - # ** This callback is executed on the IO thread ** - # Empty place-holders: popupFeatures, client. - def OnBeforePopup(self, browser, frame, targetUrl, targetFrameName, - targetDisposition, userGesture, - popupFeatures, windowInfo, client, browserSettings, - noJavascriptAccess): - print("[wxpython.py] LifespanHandler::OnBeforePopup()") - print(" targetUrl = %s" % targetUrl) - # Custom browser settings for popups: - # > browserSettings[0] = {"plugins_disabled": True} - # Set WindowInfo object: - # > windowInfo[0] = cefpython.WindowInfo() - allowPopups = True - return not allowPopups - - def _OnAfterCreated(self, browser): - # This is a global callback set using SetGlobalClientCallback(). - print("[wxpython.py] LifespanHandler::_OnAfterCreated()") - print(" browserId=%s" % browser.GetIdentifier()) - - def DoClose(self, browser): - print("[wxpython.py] LifespanHandler::DoClose()") - print(" browserId=%s" % browser.GetIdentifier()) - - def OnBeforeClose(self, browser): - print("[wxpython.py] LifespanHandler::OnBeforeClose") - print(" browserId=%s" % browser.GetIdentifier()) - - # ------------------------------------------------------------------------- - # JavascriptDialogHandler - # ------------------------------------------------------------------------- - - def OnJavascriptDialog(self, browser, originUrl, dialogType, - messageText, defaultPromptText, callback, - suppressMessage): - print("[wxpython.py] JavascriptDialogHandler::OnJavascriptDialog()") - print(" originUrl="+originUrl) - print(" dialogType="+str(dialogType)) - print(" messageText="+messageText) - print(" defaultPromptText="+defaultPromptText) - # If you want to suppress the javascript dialog: - # suppressMessage[0] = True - return False - - def OnBeforeUnloadJavascriptDialog(self, browser, messageText, isReload, - callback): - print("[wxpython.py] OnBeforeUnloadJavascriptDialog()") - print(" messageText="+messageText) - print(" isReload="+str(isReload)) - # Return True if the application will use a custom dialog: - # callback.Continue(allow=True, userInput="") - # return True - return False - - def OnResetJavascriptDialogState(self, browser): - print("[wxpython.py] OnResetDialogState()") - - def OnJavascriptDialogClosed(self, browser): - print("[wxpython.py] OnDialogClosed()") - - -class MyApp(wx.App): - timer = None - timerID = 1 - timerCount = 0 - - def OnInit(self): - if not USE_EVT_IDLE: - self.CreateTimer() - frame = MainFrame() - self.SetTopWindow(frame) - frame.Show() - return True - - def CreateTimer(self): - # See "Making a render loop": - # http://wiki.wxwidgets.org/Making_a_render_loop - # Another approach is to use EVT_IDLE in MainFrame, - # see which one fits you better. - self.timer = wx.Timer(self, self.timerID) - self.timer.Start(10) # 10ms - wx.EVT_TIMER(self, self.timerID, self.OnTimer) - - def OnTimer(self, event): - self.timerCount += 1 - # print("[wxpython.py] OnTimer() %d" % self.timerCount) - cefpython.MessageLoopWork() - - def OnExit(self): - # When app.MainLoop() returns, MessageLoopWork() should - # not be called anymore. - if not USE_EVT_IDLE: - self.timer.Stop() - -def GetSources(): - # Get sources of all python functions and methods from this file. - # This is to provide sources preview to wxpython.html. - # The dictionary of functions is binded to "window.sources". - thisModule = sys.modules[__name__] - functions = inspect.getmembers(thisModule, inspect.isfunction) - classes = inspect.getmembers(thisModule, inspect.isclass) - sources = {} - for funcTuple in functions: - sources[funcTuple[0]] = inspect.getsource(funcTuple[1]) - for classTuple in classes: - className = classTuple[0] - classObject = classTuple[1] - methods = inspect.getmembers(classObject) - for methodTuple in methods: - try: - sources[methodTuple[0]] = inspect.getsource(\ - methodTuple[1]) - except: - pass - return sources - -if __name__ == '__main__': - print('[wxpython.py] architecture=%s-bit' % (8 * struct.calcsize("P"))) - print('[wxpython.py] wx.version=%s' % wx.version()) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - g_applicationSettings = { - # Disk cache - # "cache_path": "webcache/", - - # CEF Python debug messages in console and in log_file - "debug": True, - # Set it to LOGSEVERITY_VERBOSE for more details - "log_severity": cefpython.LOGSEVERITY_INFO, - # Set to "" to disable logging to a file - "log_file": GetApplicationPath("debug.log"), - - # "resources_dir_path" must be set on Mac, "locales_dir_path" not. - # You must also set "locale_pak" using command line switch. - "resources_dir_path": cefpython.GetModuleDirectory()+"/Resources", - # The "subprocess" executable that launches the Renderer - # and GPU processes among others. You may rename that - # executable if you like. - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess"), - - # This option is required for the GetCookieManager callback - # to work. It affects renderer processes, when this option - # is set to True. It will force a separate renderer process - # for each browser created using CreateBrowserSync. - "unique_request_context_per_browser": True, - - # Downloads are handled automatically. A default SaveAs file - # dialog provided by OS will be displayed. - "downloads_enabled": True, - - # Remote debugging port, required for Developer Tools support. - # A value of 0 will generate a random port. To disable devtools - # support set it to -1. - "remote_debugging_port": 0, - - # Mouse context menu - "context_menu": { - "enabled": True, - "navigation": True, # Back, Forward, Reload - "print": True, - "view_source": True, - "external_browser": True, # Open in external browser - "devtools": True, # Developer Tools - }, - - # See also OnCertificateError which allows you to ignore - # certificate errors for specific websites. - "ignore_certificate_errors": False, - } - - # Browser settings. You may have different settings for each - # browser, see the call to CreateBrowserSync. - g_browserSettings = { - # "plugins_disabled": True, - # "file_access_from_file_urls_allowed": True, - # "universal_access_from_file_urls_allowed": True, - } - - # Command line switches set programmatically - g_switches = { - # On Mac it is required to provide path to a specific - # locale.pak file. On Win/Linux you only specify the - # ApplicationSettings.locales_dir_path option. - "locale_pak": cefpython.GetModuleDirectory() - +"/Resources/en.lproj/locale.pak", - - # "proxy-server": "socks5://127.0.0.1:8888", - # "no-proxy-server": "", - # "enable-media-stream": "", - # "remote-debugging-port": "12345", - # "disable-gpu": "", - # "--invalid-switch": "" -> Invalid switch name - } - - cefpython.Initialize(g_applicationSettings, g_switches) - - app = MyApp(False) - app.MainLoop() - - # Let wx.App destructor do the cleanup before calling - # cefpython.Shutdown(). This is to ensure reliable CEF shutdown. - del app - - # On Mac cefpython.Shutdown() is called in MainFrame.OnClose, - # followed by wx.GetApp.Exit(). diff --git a/src/mac/compile.py b/src/mac/compile.py deleted file mode 100644 index 2d8a048d..00000000 --- a/src/mac/compile.py +++ /dev/null @@ -1,181 +0,0 @@ -import sys -import os -import glob -import shutil -import subprocess -import platform -import stat -import re - -# This will not show "Segmentation fault" error message: -# | subprocess.call(["python", "./wxpython.py"]) -# You need to call it with shell=True for this kind of -# error message to be shown: -# | subprocess.call("python wxpython.py", shell=True) - -# How to debug: -# 1. Install "python-dbg" package -# 2. Install "python-wxgtk2.8-dbg" package -# 3. Run "python compile.py debug" -# 4. In cygdb type "cy run" -# 5. To display debug backtrace type "cy bt" -# 6. More commands: http://docs.cython.org/src/userguide/debugging.html - -if len(sys.argv) > 1 and "--debug" in sys.argv: - DEBUG = True - print("DEBUG mode On") -else: - DEBUG = False - -if len(sys.argv) > 1 and re.search(r"^\d+\.\d+$", sys.argv[1]): - VERSION = sys.argv[1] -else: - print("[compile.py] ERROR: expected first argument to be version number") - print(" Allowed version format: \\d+\.\\d+") - sys.exit(1) - -print("VERSION=%s"%VERSION) - -BITS = platform.architecture()[0] -assert (BITS == "32bit" or BITS == "64bit") -PYTHON_CMD = "python" -if sys.maxint == 2147483647: - BITS = "32bit" - PYTHON_CMD = "arch -i386 python" - -if BITS == "32bit": - if "i386" not in os.getenv("ARCHFLAGS", ""): - raise Exception("Detected Python 32bit, but ARCHFLAGS is not i386") - if "i386" not in os.getenv("CEF_CCFLAGS", ""): - raise Exception("Detected Python 32bit, but CEF_CCFLAGS is not i386") -elif BITS == "64bit": - if "x86_64" not in os.getenv("ARCHFLAGS", ""): - raise Exception("Detected Python 64bit, but ARCHFLAGS is not x86_64") - if "x86_64" not in os.getenv("CEF_CCFLAGS", ""): - raise Exception("Detected Python 64bit, but CEF_CCFLAGS is not x86_64") - -PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) -print("PYVERSION = %s" % PYVERSION) -print("BITS = %s" % BITS) - -os.environ["CC"] = "gcc" -os.environ["CXX"] = "g++" - -print("Compiling C++ projects") - -# Need to allow continuing even when make fails, as it may -# fail because the "public" function declaration is not yet -# in "cefpython.h", but for it to be generated we need to run -# cython compiling, so in this case you continue even when make -# fails and then run the compile.py script again and this time -# make should succeed. - -os.chdir("./../cpp_utils/") -subprocess.call("rm -f *.o *.a", shell=True) - -ret = subprocess.call("make -f Makefile", shell=True) -if ret != 0: - what = raw_input("make failed, press 'y' to continue, 'n' to stop: ") - if what != "y": - sys.exit(1) - -os.chdir("./../client_handler/") -subprocess.call("rm -f *.o *.a", shell=True) - -ret = subprocess.call("make -f Makefile", shell=True) -if ret != 0: - what = raw_input("make failed, press 'y' to continue, 'n' to stop: ") - if what != "y": - sys.exit(1) - -os.chdir("./../subprocess/") -subprocess.call("rm -f *.o *.a", shell=True) -subprocess.call("rm -f subprocess", shell=True) - -ret = subprocess.call("make -f Makefile-libcefpythonapp", shell=True) -if ret != 0: - what = raw_input("make failed, press 'y' to continue, 'n' to stop: ") - if what != "y": - sys.exit(1) - -ret = subprocess.call("make -f Makefile", shell=True) -if ret != 0: - what = raw_input("make failed, press 'y' to continue, 'n' to stop: ") - if what != "y": - sys.exit(1) -subprocess_exe = "./../mac/binaries_%s/subprocess" % (BITS) -if os.path.exists("./subprocess"): - # .copy() will also copy Permission bits - shutil.copy("./subprocess", subprocess_exe) - -# os.chdir("./../v8function_handler/") -# ret = subprocess.call("make -f Makefile", shell=True) -# if ret != 0: -# what = raw_input("make failed, press 'y' to continue, 'n' to stop: ") -# if what != "y": -# sys.exit(1) - -os.chdir("./../mac/") - -try: - os.remove("./binaries_%s/cefpython_py%s.so" % (BITS, PYVERSION)) -except OSError: - pass - -os.system("rm ./setup/cefpython_py*.so") - -pyx_files = glob.glob("./setup/*.pyx") -for f in pyx_files: - os.remove(f) - -try: - shutil.rmtree("./setup/build") -except OSError: - pass - -os.chdir("./setup") - -ret = subprocess.call(PYTHON_CMD+" fix_pyx_files.py", shell=True) -if ret != 0: - sys.exit("ERROR") - -# Create __version__.pyx after fix_pyx_files.py was called, -# as that script deletes old pyx files before copying new ones. -print("Creating __version__.pyx file") -with open("__version__.pyx", "w") as fo: - fo.write('__version__ = "{}"\n'.format(VERSION)) - -if DEBUG: - ret = subprocess.call(PYTHON_CMD+"-dbg setup.py build_ext --inplace" - " --cython-gdb", shell=True) -else: - ret = subprocess.call(PYTHON_CMD+" setup.py build_ext --inplace", shell=True) - -if DEBUG: - shutil.rmtree("./../binaries_%s/cython_debug/" % BITS, ignore_errors=True) - shutil.copytree("./cython_debug/", "./../binaries_%s/cython_debug/" % BITS) - -os.chdir("../") - -oldpyxfiles = glob.glob("./setup/*.pyx") -print("Removing old pyx files in /setup/: %s" % oldpyxfiles) -for pyxfile in oldpyxfiles: - if os.path.exists(pyxfile): - os.remove(pyxfile) - -if ret != 0: - sys.exit("ERROR") - -exitcode = os.system("mv ./setup/cefpython_py{0}*.so" - " ./binaries_{1}/cefpython_py{0}.so" - .format(PYVERSION, BITS)) -if exitcode: - raise RuntimeError("Failed to move the cefpython module") - -print("DONE") - -os.chdir("./binaries_%s" % BITS) -if DEBUG: - subprocess.call("cygdb . --args python-dbg wxpython.py", shell=True) -else: - subprocess.call(PYTHON_CMD+" wxpython.py", shell=True) diff --git a/src/linux/binaries_32bit/prism.css b/src/mac/deprecated_64bit/prism.css similarity index 100% rename from src/linux/binaries_32bit/prism.css rename to src/mac/deprecated_64bit/prism.css diff --git a/src/linux/binaries_32bit/prism.js b/src/mac/deprecated_64bit/prism.js similarity index 100% rename from src/linux/binaries_32bit/prism.js rename to src/mac/deprecated_64bit/prism.js diff --git a/src/mac/binaries_32bit/wxpython.html b/src/mac/deprecated_64bit/wxpython.html similarity index 100% rename from src/mac/binaries_32bit/wxpython.html rename to src/mac/deprecated_64bit/wxpython.html diff --git a/src/mac/binaries_32bit/wxpython.py b/src/mac/deprecated_64bit/wxpython.py similarity index 100% rename from src/mac/binaries_32bit/wxpython.py rename to src/mac/deprecated_64bit/wxpython.py diff --git a/src/mac/installer/make-setup.py b/src/mac/installer/make-setup.py index 81a70825..61a7e593 100644 --- a/src/mac/installer/make-setup.py +++ b/src/mac/installer/make-setup.py @@ -101,8 +101,8 @@ def main(): f.write(SETUP_CONTENT) # Create fat binaries if both 32bit and 64bit are available - b32 = os.path.abspath(installer_dir+"/../binaries_32bit/") - b64 = os.path.abspath(installer_dir+"/../binaries_64bit/") + b32 = os.path.abspath(installer_dir+"/../deprecated_32bit/") + b64 = os.path.abspath(installer_dir+"/../deprecated_64bit/") bfat = os.path.abspath(installer_dir+"/binaries_fat/") binaries_dir = get_binaries_dir(b32, b64, bfat) print("Copying binaries to package dir") diff --git a/src/mac/mac32.sh b/src/mac/mac32.sh deleted file mode 100644 index 4d8b1709..00000000 --- a/src/mac/mac32.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -PATH=/usr/local/bin:$PATH -export PATH -alias python="arch -i386 python" - -CEF_CCFLAGS="-arch i386" -export CEF_CCFLAGS - -ARCHFLAGS="-arch i386" -export ARCHFLAGS - -export CC=gcc -export CXX=g++ diff --git a/src/mac/setup/.gitignore b/src/mac/setup/.gitignore deleted file mode 100644 index 67f1cf10..00000000 --- a/src/mac/setup/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -build/ -cefpython.cpp -cython_debug/ -*.pyx -*.cpp -*.a diff --git a/src/mac/setup/cefpython.h b/src/mac/setup/cefpython.h deleted file mode 100644 index 2495aabb..00000000 --- a/src/mac/setup/cefpython.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef __PYX_HAVE__cefpython_py27 -#define __PYX_HAVE__cefpython_py27 - - -#ifndef __PYX_HAVE_API__cefpython_py27 - -#ifndef __PYX_EXTERN_C - #ifdef __cplusplus - #define __PYX_EXTERN_C extern "C" - #else - #define __PYX_EXTERN_C extern - #endif -#endif - -__PYX_EXTERN_C DL_IMPORT(void) PyBrowser_ShowDevTools(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) PyTaskRunnable(int); -__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextCreated(CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextReleased(int, int64); -__PYX_EXTERN_C DL_IMPORT(void) V8FunctionHandler_Execute(CefRefPtr, CefRefPtr, CefString &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) RemovePythonCallbacksForFrame(int); -__PYX_EXTERN_C DL_IMPORT(bool) ExecutePythonCallback(CefRefPtr, int, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) LifespanHandler_OnBeforePopup(CefRefPtr, CefRefPtr, CefString const &, CefString const &, int const , CefWindowInfo &, CefRefPtr &, CefBrowserSettings &, bool *); -__PYX_EXTERN_C DL_IMPORT(void) LifespanHandler_OnAfterCreated(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) LifespanHandler_DoClose(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) LifespanHandler_OnBeforeClose(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) DisplayHandler_OnAddressChange(CefRefPtr, CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) DisplayHandler_OnTitleChange(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(bool) DisplayHandler_OnTooltip(CefRefPtr, CefString &); -__PYX_EXTERN_C DL_IMPORT(void) DisplayHandler_OnStatusMessage(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(bool) DisplayHandler_OnConsoleMessage(CefRefPtr, CefString const &, CefString const &, int); -__PYX_EXTERN_C DL_IMPORT(bool) KeyboardHandler_OnPreKeyEvent(CefRefPtr, CefKeyEvent const &, CefEventHandle, bool *); -__PYX_EXTERN_C DL_IMPORT(bool) KeyboardHandler_OnKeyEvent(CefRefPtr, CefKeyEvent const &, CefEventHandle); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforeResourceLoad(CefRefPtr, CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforeBrowse(CefRefPtr, CefRefPtr, CefRefPtr, bool); -__PYX_EXTERN_C DL_IMPORT(CefRefPtr) RequestHandler_GetResourceHandler(CefRefPtr, CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnResourceRedirect(CefRefPtr, CefRefPtr, CefString const &, CefString &); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_GetAuthCredentials(CefRefPtr, CefRefPtr, bool, CefString const &, int, CefString const &, CefString const &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnQuotaRequest(CefRefPtr, CefString const &, int64, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(CefRefPtr) RequestHandler_GetCookieManager(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnProtocolExecution(CefRefPtr, CefString const &, bool &); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforePluginLoad(CefRefPtr, CefString const &, CefString const &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnCertificateError(int, CefString const &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnRendererProcessTerminated(CefRefPtr, enum cef_termination_status_t); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnPluginCrashed(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(bool) CookieVisitor_Visit(int, CefCookie const &, int, int, bool &); -__PYX_EXTERN_C DL_IMPORT(void) StringVisitor_Visit(int, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadingStateChange(CefRefPtr, bool, bool, bool); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadStart(CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadEnd(CefRefPtr, CefRefPtr, int); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadError(CefRefPtr, CefRefPtr, enum cef_errorcode_t, CefString const &, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) BrowserProcessHandler_OnRenderProcessThreadCreated(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) BrowserProcessHandler_OnBeforeChildProcessLaunch(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetRootScreenRect(CefRefPtr, CefRect &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetViewRect(CefRefPtr, CefRect &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetScreenRect(CefRefPtr, CefRect &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetScreenPoint(CefRefPtr, int, int, int &, int &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetScreenInfo(CefRefPtr, CefScreenInfo &); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnPopupShow(CefRefPtr, bool); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnPopupSize(CefRefPtr, CefRect const &); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnPaint(CefRefPtr, cef_paint_element_type_t, std::vector &, void const *, int, int); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnCursorChange(CefRefPtr, CefCursorHandle); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnScrollOffsetChanged(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_ProcessRequest(int, CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) ResourceHandler_GetResponseHeaders(int, CefRefPtr, int64 &, CefString &); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_ReadResponse(int, void *, int, int &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_CanGetCookie(int, CefCookie const &); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_CanSetCookie(int, CefCookie const &); -__PYX_EXTERN_C DL_IMPORT(void) ResourceHandler_Cancel(int); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnUploadProgress(int, CefRefPtr, uint64, uint64); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnDownloadProgress(int, CefRefPtr, uint64, uint64); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnDownloadData(int, CefRefPtr, void const *, size_t); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnRequestComplete(int, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) App_OnBeforeCommandLineProcessing_BrowserProcess(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) JavascriptDialogHandler_OnJavascriptDialog(CefRefPtr, CefString const &, CefString const &, enum cef_jsdialog_type_t, CefString const &, CefString const &, CefRefPtr, bool &); -__PYX_EXTERN_C DL_IMPORT(bool) JavascriptDialogHandler_OnBeforeUnloadJavascriptDialog(CefRefPtr, CefString const &, bool, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) JavascriptDialogHandler_OnResetJavascriptDialogState(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) JavascriptDialogHandler_OnJavascriptDialogClosed(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) cefpython_GetDebugOptions(bool *, std::string *); -__PYX_EXTERN_C DL_IMPORT(bool) ApplicationSettings_GetBool(char const *); -__PYX_EXTERN_C DL_IMPORT(bool) ApplicationSettings_GetBoolFromDict(char const *, char const *); -__PYX_EXTERN_C DL_IMPORT(std::string) ApplicationSettings_GetString(char const *); -__PYX_EXTERN_C DL_IMPORT(int) CommandLineSwitches_GetInt(char const *); - -#endif /* !__PYX_HAVE_API__cefpython_py27 */ - -#if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC initcefpython_py27(void); -#else -PyMODINIT_FUNC PyInit_cefpython_py27(void); -#endif - -#endif /* !__PYX_HAVE__cefpython_py27 */ diff --git a/src/mac/setup/fix_pyx_files.py b/src/mac/setup/fix_pyx_files.py deleted file mode 100644 index 0c91e336..00000000 --- a/src/mac/setup/fix_pyx_files.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# First, it copies all .pyx files from upper directory to setup/. -# Then, fixes repeating of "include" statements in pyx files. - -# Only the mainfile needs to have "include" statements, -# but we're using PyCharm and to get rid of "unresolved references" -# and other errors displayed in pycharm we are adding "include" -# statements in all of the pyx files. - -# I'm not 100% sure how includes work in Cython, but I suspect that -# a few includes of the same file will include the same content more -# than once, it should work, but function and variable definitions are -# duplicated, it is some kind of overhead and it could lead to some -# problems in the future, better to fix it now. - -# It also checks cdef & cpdef functions whether they are not missing "except *", -# it is required to add it when returning non-python type. - -import glob -import os -import re -import shutil -import sys - -def ExceptAllMissing(content): - - # This is not perfect, won't detect C++ custom types, but will find - # the built-in types, templates and pointers. - patterns = [] - patterns.append( - r"\bcp?def\s+" - "((int|short|long|double|char|unsigned|float|double|cpp_bool" - "|cpp_string|cpp_wstring|uint64_t|uintptr_t|void" - "|CefString)\s+)+" - "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") - patterns.append( - r"\bcp?def\s+" - # A template ends with bracket: CefRefPtr[CefBrowser] - # or a pointer ends with asterisk: CefBrowser* - "[^\s]+[\]*]\s+" - "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") - patterns.append( - r"\bcp?def\s+" - # A reference, eg. CefString& - "[^\s]+&\s+" - "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") - - for pattern in patterns: - match = re.search(pattern, content) - if match: break - - if match: - lineNumber = (content.count("\n", 0, match.start()) + 1) - return lineNumber - -print("\n") -mainfile = "cefpython.pyx" - -pyxfiles = glob.glob("../../*.pyx") -if not len(pyxfiles): - sys.exit(1) -pyxfiles = [file for file in pyxfiles if file.find(mainfile) == -1] -# Now, pyxfiles contains all pyx files except the mainfile (cefpython.pyx), -# we do not fix includes in mainfile. - -# So that this is the right directory we're in. -if os.path.exists("setup"): - print("Wrong directory, we should be inside setup!") - sys.exit(1) - -# Remove old pyx files in setup directory. -oldpyxfiles = glob.glob("./*.pyx") -print("Removing old pyx files in /setup/: %s" % oldpyxfiles) -for pyxfile in oldpyxfiles: - if os.path.exists(pyxfile): - os.remove(pyxfile) - -# Copying pyxfiles and reading its contents. - -print("Copying .pyx files to /setup/: %s" % pyxfiles) -shutil.copy("../../%s" % mainfile, "./%s" % mainfile) -# Rest of the files will be copied in for loop below. - -print("Fixing includes in .pyx files:") -for pyxfile in pyxfiles: - newfile = "./%s" % os.path.basename(pyxfile) - shutil.copy(pyxfile, newfile) - pyxfile = newfile - with open(pyxfile, "r") as pyxfileopened: - content = pyxfileopened.read() - lineNumber = ExceptAllMissing(content) - if lineNumber: - print("WARNING: 'except *' missing in a cdef/cpdef function, " - "in file %s on line %d" % (os.path.basename(pyxfile), lineNumber)) - sys.exit(1) - # Do not remove the newline - so that line numbers are exact with originals. - (content, subs) = re.subn(r"^include[\t ]+[\"'][^\"'\n\r]+[\"'][\t ]*", "", content, flags=re.MULTILINE) - if subs: - print("%s includes removed in: %s" % (subs, os.path.basename(pyxfile))) - # Reading and writing with the same handle using "r+" mode doesn't work, - # you need to seek(0) and write the same amount of bytes that was in the - # file, otherwise old data from the end of file stays. - with open(pyxfile, "w") as pyxfileopened: - pyxfileopened.write(content) - -print("\n") diff --git a/src/mac/setup/lib_32bit/README b/src/mac/setup/lib_32bit/README deleted file mode 100644 index 623f0324..00000000 --- a/src/mac/setup/lib_32bit/README +++ /dev/null @@ -1 +0,0 @@ -Put libcef_dll_wrapper.a here diff --git a/src/mac/setup/lib_64bit/README b/src/mac/setup/lib_64bit/README deleted file mode 100644 index 623f0324..00000000 --- a/src/mac/setup/lib_64bit/README +++ /dev/null @@ -1 +0,0 @@ -Put libcef_dll_wrapper.a here diff --git a/src/mac/setup/setup.py b/src/mac/setup/setup.py deleted file mode 100644 index 36f8dc5f..00000000 --- a/src/mac/setup/setup.py +++ /dev/null @@ -1,110 +0,0 @@ -from distutils.core import setup -# Use "Extension" from Cython.Distutils so that "cython_directives" works. -# from distutils.extension import Extension -from Cython.Distutils import build_ext, Extension -import sys -import os -import platform -from Cython.Compiler import Options -import Cython - -print("Cython version: %s" % Cython.__version__) - -BITS = platform.architecture()[0] -assert (BITS == "32bit" or BITS == "64bit") -if sys.maxint == 2147483647: - BITS = "32bit" - -print("BITS=%s" % BITS) - -os.environ["CC"] = "gcc" -os.environ["CXX"] = "g++" - -# Stop on first error, otherwise hundreds of errors appear in the console. -Options.fast_fail = True - -# Python version string: "27" or "32". -PYTHON_VERSION = str(sys.version_info.major) + str(sys.version_info.minor) - -def CompileTimeConstants(): - - print("Generating: compile_time_constants.pxi") - with open("./../../compile_time_constants.pxi", "w") as fd: - fd.write('# This file was generated by setup.py\n') - # A way around Python 3.2 bug: UNAME_SYSNAME is not set. - fd.write('DEF UNAME_SYSNAME = "%s"\n' % platform.uname()[0]) - fd.write('DEF PY_MAJOR_VERSION = %s\n' % sys.version_info.major) - -CompileTimeConstants() - -ext_modules = [Extension( - - "cefpython_py%s" % PYTHON_VERSION, - ["cefpython.pyx"], - - # Ignore the warning in the console: - # > C:\Python27\lib\distutils\extension.py:133: UserWarning: - # > Unknown Extension options: 'cython_directives' warnings.warn(msg) - cython_directives={ - # Any conversion to unicode must be explicit using .decode(). - "c_string_type": "bytes", - "c_string_encoding": "utf-8", - }, - - language='c++', - include_dirs=[ - r'./../', - r'./../../', - r'./../../extern/', - r'./../../extern/cef/', - '/usr/include/gtk-2.0', - '/usr/include/glib-2.0', - '/usr/include/cairo', - '/usr/include/pango-1.0', - '/usr/include/gdk-pixbuf-2.0', - '/usr/include/atk-1.0', - # Ubuntu - '/usr/lib/x86_64-linux-gnu/glib-2.0/include', - '/usr/lib/x86_64-linux-gnu/gtk-2.0/include', - '/usr/lib/i386-linux-gnu/gtk-2.0/include', - '/usr/lib/i386-linux-gnu/glib-2.0/include', - # Fedora - '/usr/lib64/glib-2.0/include', - '/usr/lib64/gtk-2.0/include', - '/usr/lib/glib-2.0/include', - '/usr/lib/gtk-2.0/include', - ], - - # http_authentication not implemented on Linux. - library_dirs=[ - r'./lib_%s' % BITS, - r'./../../client_handler/', - r'./../../subprocess/', # libcefpythonapp - r'./../../cpp_utils/' - ], - - libraries=[ - 'client_handler', - 'cef_dll_wrapper', - 'cefpythonapp', - 'cpp_utils' - ], - - # Loading libcef.so will only work when running scripts from the same - # directory that libcef.so resides in when you put "./" in here. - # runtime_library_dirs=[ - # './' - #], - - extra_compile_args=[], - extra_link_args=[], - - # Defining macros: - # define_macros = [("UNICODE","1"), ("_UNICODE","1"), ] -)] - -setup( - name = 'cefpython_py%s' % PYTHON_VERSION, - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/src/subprocess/libcefpythonapp_py27_64bit.vcproj b/src/subprocess/deprecated_libcefpythonapp_py27_64bit.vcproj similarity index 100% rename from src/subprocess/libcefpythonapp_py27_64bit.vcproj rename to src/subprocess/deprecated_libcefpythonapp_py27_64bit.vcproj diff --git a/src/subprocess/libcefpythonapp_py34_32bit.vcproj b/src/subprocess/deprecated_libcefpythonapp_py34_32bit.vcproj similarity index 100% rename from src/subprocess/libcefpythonapp_py34_32bit.vcproj rename to src/subprocess/deprecated_libcefpythonapp_py34_32bit.vcproj diff --git a/src/subprocess/subprocess_64bit.vcproj b/src/subprocess/deprecated_subprocess_64bit.vcproj similarity index 100% rename from src/subprocess/subprocess_64bit.vcproj rename to src/subprocess/deprecated_subprocess_64bit.vcproj diff --git a/src/subprocess/libcefpythonapp_py27_32bit.vcproj b/src/subprocess/libcefpythonapp_py27_32bit.vcproj index 6bea39c1..a08ddafc 100644 --- a/src/subprocess/libcefpythonapp_py27_32bit.vcproj +++ b/src/subprocess/libcefpythonapp_py27_32bit.vcproj @@ -2,41 +2,29 @@ - + - - - - - + + + + + - - - - - - - - - + + + + + + + + + @@ -85,22 +55,10 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > - - - - - - - - + + + + - - - - - - - - + + + + diff --git a/src/subprocess/subprocess_32bit.vcproj b/src/subprocess/subprocess_32bit.vcproj index fd1d9930..d5cf7d4a 100644 --- a/src/subprocess/subprocess_32bit.vcproj +++ b/src/subprocess/subprocess_32bit.vcproj @@ -1,162 +1,103 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ProjectType="Visual C++" + Version="9.00" + Name="subprocess_win32" + RootNamespace="subprocess_win32" + ProjectGUID="{0EB22FA5-229C-4FCF-BC1E-325CA1CB7637}" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/version/cef_version_win.h b/src/version/cef_version_win.h new file mode 100644 index 00000000..3a20c3f4 --- /dev/null +++ b/src/version/cef_version_win.h @@ -0,0 +1,102 @@ +// 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.2883.1553.g80bd606" +#define CEF_VERSION_MAJOR 3 +#define CEF_COMMIT_NUMBER 1553 +#define CEF_COMMIT_HASH "80bd6062d7ac4c5fd1d7bc7ee78e8e59d4a040aa" +#define COPYRIGHT_YEAR 2017 + +#define CHROME_VERSION_MAJOR 55 +#define CHROME_VERSION_MINOR 0 +#define CHROME_VERSION_BUILD 2883 +#define CHROME_VERSION_PATCH 87 + +#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 "87b7eefcb86c87b28f86bfd7919f7d7a6cffc0d8" +#if defined(OS_WIN) +#define CEF_API_HASH_PLATFORM "00823905486d7b7222da5654fe35d2d15f65543a" +#elif defined(OS_MACOSX) +#define CEF_API_HASH_PLATFORM "f0180f006643782254250f34e858b98110a40e6e" +#elif defined(OS_LINUX) +#define CEF_API_HASH_PLATFORM "14b19454a4231fa10a77b8955954dc95f073af6b" +#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/window_info.pyx b/src/window_info.pyx index eb9b5fb1..c3bef322 100644 --- a/src/window_info.pyx +++ b/src/window_info.pyx @@ -63,7 +63,7 @@ cdef void SetCefWindowInfo( windowInfo.parentWindowHandle, windowName) - cdef cpp_bool transparent = windowInfo.transparentPainting + cdef cpp_bool transparent = bool(windowInfo.transparentPainting) if windowInfo.windowType == "offscreen": cefWindowInfo.SetAsWindowless( windowInfo.parentWindowHandle, diff --git a/src/window_utils_win.pyx b/src/window_utils_win.pyx index 912ead1e..81d80143 100644 --- a/src/window_utils_win.pyx +++ b/src/window_utils_win.pyx @@ -102,7 +102,7 @@ class WindowUtils: iconSmall = SendMessage( windowHandle, WM_GETICON, ICON_SMALL, 0) - cdef int parentWindowHandle + cdef long long parentWindowHandle if not iconBig and not iconSmall: parentWindowHandle = pyBrowser.GetOpenerWindowHandle() diff --git a/src/windows/.gitignore b/src/windows/.gitignore deleted file mode 100644 index fb779dfc..00000000 --- a/src/windows/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.exe -*.dll -*.pak -*.lib - -!msvcr90.dll -!msvcp90.dll -!msvcm90.dll diff --git a/src/windows/binaries_32bit/prism.css b/src/windows/binaries_32bit/prism.css deleted file mode 100644 index f94cca7c..00000000 --- a/src/windows/binaries_32bit/prism.css +++ /dev/null @@ -1,130 +0,0 @@ -/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+python */ -/** - * prism.js default theme for JavaScript, CSS and HTML - * Based on dabblet (http://dabblet.com) - * @author Lea Verou - */ - -code[class*="language-"], -pre[class*="language-"] { - color: black; - text-shadow: 0 1px white; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, -code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { - text-shadow: none; - background: #b3d4fc; -} - -pre[class*="language-"]::selection, pre[class*="language-"] ::selection, -code[class*="language-"]::selection, code[class*="language-"] ::selection { - text-shadow: none; - background: #b3d4fc; -} - -@media print { - code[class*="language-"], - pre[class*="language-"] { - text-shadow: none; - } -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #f5f2f0; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #999; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag, -.token.boolean, -.token.number, -.token.constant, -.token.symbol { - color: #905; -} - -.token.selector, -.token.attr-name, -.token.string, -.token.builtin { - color: #690; -} - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string, -.token.variable { - color: #a67f59; - background: hsla(0,0%,100%,.5); -} - -.token.atrule, -.token.attr-value, -.token.keyword { - color: #07a; -} - -.token.function { - color: #DD4A68; -} - -.token.regex, -.token.important { - color: #e90; -} - -.token.important { - font-weight: bold; -} - -.token.entity { - cursor: help; -} - diff --git a/src/windows/binaries_32bit/prism.js b/src/windows/binaries_32bit/prism.js deleted file mode 100644 index ebaa4b42..00000000 --- a/src/windows/binaries_32bit/prism.js +++ /dev/null @@ -1,5 +0,0 @@ -/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+python */ -self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content)):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(f instanceof r)){l.lastIndex=0;var h=l.exec(f);if(h){c&&(g=h[1].length);var d=h.index-1+g,h=h[0].slice(g),p=h.length,m=d+p,v=f.slice(0,d+1),y=f.slice(m+1),k=[u,1];v&&k.push(v);var b=new r(o,s?t.tokenize(h,s):h);k.push(b),y&&k.push(y),Array.prototype.splice.apply(a,k)}}}}return a},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[],r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(n)}}},n=t.Token=function(e,t){this.type=e,this.content=t};if(n.stringify=function(e,r,a){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,r,e)}).join("");var i={type:e.type,content:n.stringify(e.content,r,a),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:a};"comment"==i.type&&(i.attributes.spellcheck="true"),t.hooks.run("wrap",i);var o="";for(var l in i.attributes)o+=l+'="'+(i.attributes[l]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,a=n.code;self.postMessage(JSON.stringify(t.tokenize(a,t.languages[r]))),self.close()},!1),self.Prism):self.Prism;var r=document.getElementsByTagName("script");return r=r[r.length-1],r&&(t.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; -Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; -Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});; -Prism.languages.python={comment:{pattern:/(^|[^\\])#.*?(\r?\n|$)/g,lookbehind:!0},string:/"""[\s\S]+?"""|("|')(\\?.)*?\1/g,keyword:/\b(as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/g,"boolean":/\b(True|False)\b/g,number:/\b-?(0x)?\d*\.?[\da-f]+\b/g,operator:/[-+]{1,2}|=?<|=?>|!|={1,2}|(&){1,2}|(&){1,2}|\|?\||\?|\*|\/|~|\^|%|\b(or|and|not)\b/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; diff --git a/src/windows/binaries_64bit/LICENSE.txt b/src/windows/binaries_64bit/LICENSE.txt deleted file mode 100644 index 45e6f23e..00000000 --- a/src/windows/binaries_64bit/LICENSE.txt +++ /dev/null @@ -1,39 +0,0 @@ -Copyright (c) 2012 The CEF Python authors. All rights -reserved. Website: http://code.google.com/p/cefpython/ - -This product includes the following third party libraries: -* Chromium Embedded Framework licensed under the BSD 3-clause - license. Website: http://code.google.com/p/chromiumembedded/ - -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 name of CEF Python 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. diff --git a/src/windows/binaries_64bit/README.txt b/src/windows/binaries_64bit/README.txt deleted file mode 100644 index 3158cb80..00000000 --- a/src/windows/binaries_64bit/README.txt +++ /dev/null @@ -1,119 +0,0 @@ -Chromium Embedded Framework (CEF) Standard Binary Distribution for Windows -------------------------------------------------------------------------------- - -Date: March 14, 2014 - -CEF Version: 3.1650.1639 -CEF URL: http://chromiumembedded.googlecode.com/svn/branches/1650/cef3@1639 - -Chromium Verison: 31.0.1650.57 -Chromium URL: http://src.chromium.org/svn/branches/1650/src@235101 - -This distribution contains all components necessary to build and distribute an -application using CEF on the Windows platform. Please see the LICENSING -section of this document for licensing terms and conditions. - - -CONTENTS --------- - -cefclient Contains the cefclient sample application configured to build - using the files in this distribution. - -Debug Contains libcef.dll, libcef.lib and other components required to - build and run the debug version of CEF-based applications. By - default these files should be placed in the same directory as the - executable and will be copied there as part of the build process. - -include Contains all required CEF header files. - -libcef_dll Contains the source code for the libcef_dll_wrapper static library - that all applications using the CEF C++ API must link against. - -Release Contains libcef.dll, libcef.lib and other components required to - build and run the release version of CEF-based applications. By - default these files should be placed in the same directory as the - executable and will be copied there as part of the build process. - -Resources Contains resources required by libcef.dll. By default these files - should be placed in the same directory as libcef.dll. By default - these files should be placed in the same directory as libcef.dll - and will be copied there as part of the build process. - - -USAGE ------ - -Visual Studio 2012 and Visual Studio 2010: - Open the cefclient2010.sln solution in Visual Studio and build. - -Visual Studio 2008: - Open the cefclient2008.sln solution in Visual Studio and build. - -Visual Studio 2005: - 1. Open the cefclient.vcproj and libcef_dll_wrapper.vcproj files in a text - editor. Change Version="9.00" to Version="8.00". - 2. Open the cefclient2005.sln file in a text editor. Change "Version 9.00" to - "Version 8.00". - 3. Open the cefclient2005.sln solution in Visual Studio and build. - -Please visit the CEF Website for additional usage information. - -http://code.google.com/p/chromiumembedded - - -REDISTRIBUTION --------------- - -This binary distribution contains the below components. Components listed under -the "required" section must be redistributed with all applications using CEF. -Components listed under the "optional" section may be excluded if the related -features will not be used. - -Required components: - -* CEF core library - libcef.dll - -* Unicode support - icudt.dll - -Optional components: - -* Localized resources - locales/ - Note: Contains localized strings for WebKit UI controls. A .pak file is loaded - from this folder based on the CefSettings.locale value. Only configured - locales need to be distributed. If no locale is configured the default locale - of "en-US" will be used. Locale file loading can be disabled completely using - CefSettings.pack_loading_disabled. The locales folder path can be customized - using CefSettings.locales_dir_path. - -* Other resources - cef.pak - devtools_resources.pak - Note: Contains WebKit image and inspector resources. Pack file loading can be - disabled completely using CefSettings.pack_loading_disabled. The resources - directory path can be customized using CefSettings.resources_dir_path. - -* FFmpeg audio and video support - ffmpegsumo.dll - Note: Without this component HTML5 audio and video will not function. - -* Angle and Direct3D support - d3dcompiler_43.dll (required for Windows XP) - d3dcompiler_46.dll (required for Windows Vista and newer) - libEGL.dll - libGLESv2.dll - Note: Without these components HTML5 accelerated content like 2D canvas, 3D - CSS and WebGL will not function. - - -LICENSING ---------- - -The CEF project is BSD licensed. Please read the LICENSE.txt file included with -this binary distribution for licensing terms and conditions. Other software -included in this distribution is provided under other licenses. Please visit -"about:credits" in a CEF-based application for complete Chromium and third-party -licensing information. diff --git a/src/windows/binaries_64bit/cefwindow.py b/src/windows/binaries_64bit/cefwindow.py deleted file mode 100644 index fd0ac73e..00000000 --- a/src/windows/binaries_64bit/cefwindow.py +++ /dev/null @@ -1,205 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -import win32gui -import win32con -import win32api -import time -import math -import os -import sys -import re - -if sys.version_info.major == 2: - from urllib import pathname2url as urllib_pathname2url -else: - from urllib.request import pathname2url as urllib_pathname2url - -g_debug = False -g_windows = {} # windowID(int): className -g_registeredClasses = {} - -def Debug(msg): - - if not g_debug: - return - msg = "cefwindow: "+str(msg) - print(msg) - with open(GetRealPath("debug.log"), "a") as file: - file.write(msg+"\n") - -def GetRealPath(file=None, encodeURL=False): - - # This function is defined in 2 files: cefpython.pyx and cefwindow.py, if you make changes edit both files. - # If file is None return current directory, without trailing slash. - - # encodeURL param - will call urllib.pathname2url(), only when file is empty (current dir) - # or is relative path ("test.html", "some/test.html"), we need to encode it before passing - # to CreateBrowser(), otherwise it is encoded by CEF internally and becomes (chinese characters): - # >> %EF%BF%97%EF%BF%80%EF%BF%83%EF%BF%A6 - # but should be: - # >> %E6%A1%8C%E9%9D%A2 - - if file is None: file = "" - if file.find("/") != 0 and file.find("\\") != 0 and not re.search(r"^[a-zA-Z]+:[/\\]?", file): - # Execute this block only when relative path ("test.html", "some\test.html") or file is empty (current dir). - # 1. find != 0 >> not starting with / or \ (/ - linux absolute path, \ - just to be sure) - # 2. not re.search >> not (D:\\ or D:/ or D: or http:// or ftp:// or file://), - # "D:" is also valid absolute path ("D:cefpython" in chrome becomes "file:///D:/cefpython/") - if hasattr(sys, "frozen"): path = os.path.dirname(sys.executable) - elif "__file__" in globals(): path = os.path.dirname(os.path.realpath(__file__)) - else: path = os.getcwd() - path = path + os.sep + file - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) # directory without trailing slash. - if encodeURL: - return urllib_pathname2url(path) - else: - return path - return file - -def CreateWindow(title, className, width, height, xpos=None, ypos=None, icon=None, windowProc=None): - - """ - for key in g_windows: - if g_windows[key] == className: - raise Exception("There was already created a window with that className: %s." - "Each created window must have an unique className." % className) - """ - - if not windowProc: - windowProc = {win32con.WM_CLOSE: WM_CLOSE} - - bigIcon = "" - smallIcon = "" - - if icon: - icon = GetRealPath(icon) - - # Load small and big icon. - # WNDCLASSEX (along with hIconSm) is not supported by pywin32, - # we need to use WM_SETICON message after window creation. - - # http://stackoverflow.com/questions/2234988/how-to-set-hicon-on-a-window-ico-with-multiple-sizes - # http://blog.barthe.ph/2009/07/17/wmseticon/ - - bigX = win32api.GetSystemMetrics(win32con.SM_CXICON) - bigY = win32api.GetSystemMetrics(win32con.SM_CYICON) - bigIcon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, bigX, bigY, win32con.LR_LOADFROMFILE) - smallX = win32api.GetSystemMetrics(win32con.SM_CXSMICON) - smallY = win32api.GetSystemMetrics(win32con.SM_CYSMICON) - smallIcon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, smallX, smallY, win32con.LR_LOADFROMFILE) - - wndclass = win32gui.WNDCLASS() - wndclass.hInstance = win32api.GetModuleHandle(None) - wndclass.lpszClassName = className - wndclass.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW - # win32con.CS_GLOBALCLASS - wndclass.hbrBackground = win32con.COLOR_WINDOW - wndclass.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW) - wndclass.lpfnWndProc = windowProc - - #noinspection PyUnusedLocal - global g_registeredClasses - if not className in g_registeredClasses: - g_registeredClasses[className] = True - atomclass = win32gui.RegisterClass(wndclass) - Debug("win32gui.RegisterClass(%s)" % className) - - if xpos is None or ypos is None: - # Center window on the screen. - Debug("Centering window on the screen.") - screenx = win32api.GetSystemMetrics(win32con.SM_CXSCREEN) - screeny = win32api.GetSystemMetrics(win32con.SM_CYSCREEN) - xpos = int(math.floor((screenx - width) / 2)) - ypos = int(math.floor((screeny - height) / 2)) - if xpos < 0: xpos = 0 - if ypos < 0: ypos = 0 - - windowID = win32gui.CreateWindow(className, title, - win32con.WS_OVERLAPPEDWINDOW | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE, - xpos, ypos, width, height, # xpos, ypos, width, height - 0, 0, wndclass.hInstance, None) - g_windows[windowID] = className - - if icon: - if bigIcon: - win32api.SendMessage(windowID, win32con.WM_SETICON, win32con.ICON_BIG, bigIcon) - if smallIcon: - win32api.SendMessage(windowID, win32con.WM_SETICON, win32con.ICON_SMALL, smallIcon) - - Debug("windowID = %s" % windowID) - return windowID - - -# Memory error when calling win32gui.DestroyWindow() -# after we called cefpython.CloseBrowser() - -def DestroyWindow(windowID): - - win32gui.DestroyWindow(windowID) - #className = GetWindowClassName(windowID) - #win32gui.UnregisterClass(className, None) - #del g_windows[windowID] # Let window with this className be created again. - - -def GetWindowClassName(windowID): - - for key in g_windows: - if key == windowID: - return g_windows[key] - -def MoveWindow(windowID, xpos=None, ypos=None, width=None, height=None, center=None): - - (left, top, right, bottom) = win32gui.GetWindowRect(windowID) - if xpos is None and ypos is None: - xpos = left - ypos = top - if width is None and height is None: - width = right - left - height = bottom - top - # Case: only ypos provided - if xpos is None and ypos is not None: - xpos = left - if ypos is None and xpos is not None: - ypos = top - # Case: only height provided - if not width: - width = right - left - if not height: - height = bottom - top - if center: - screenx = win32api.GetSystemMetrics(win32con.SM_CXSCREEN) - screeny = win32api.GetSystemMetrics(win32con.SM_CYSCREEN) - xpos = int(math.floor((screenx - width) / 2)) - ypos = int(math.floor((screeny - height) / 2)) - if xpos < 0: xpos = 0 - if ypos < 0: ypos = 0 - win32gui.MoveWindow(windowID, xpos, ypos, width, height, 1) - - -#noinspection PyUnusedLocal -def WM_CLOSE(windowID, msg, wparam, lparam): - - DestroyWindow(windowID) - win32gui.PostQuitMessage(0) - - -def GetLastError(): - - code = win32api.GetLastError() - return "(%d) %s" % (code, win32api.FormatMessage(code)) - -#noinspection PyUnusedLocal -def MessageLoop(className): - - while not win32gui.PumpWaitingMessages(): - time.sleep(0.001) - - -if __name__ == "__main__": - - g_debug = True - hwnd = CreateWindow("Test window", "testwindow", 800, 600) - MessageLoop("testwindow") \ No newline at end of file diff --git a/src/windows/binaries_64bit/example.html b/src/windows/binaries_64bit/example.html deleted file mode 100644 index 96bbac9b..00000000 --- a/src/windows/binaries_64bit/example.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CEF Python 3 example (utf-8: Ä…Å›) - - - - - - -

Use mouse context menu to go Back/Forward in history navigation.

- - - -

Google Search

-https://www.google.com/ - - - -

User agent

- - - - -

Popup

- - window.open('example.html') - - - -

HTML5 video and accelerated content

- -HTML 5 video
- -Accelerated canvas
- -Accelerated layers
- - - -

Advanced example

- -See the wxpython.py script for an advanced usage of CEF Python 3 -features - including javascript bindings, js and python callbacks, -client handlers and others. - - -



-



-



-



- - - diff --git a/src/windows/binaries_64bit/icon.ico b/src/windows/binaries_64bit/icon.ico deleted file mode 100644 index 435045aec271dc2677d61810be947aa2420cd76d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198275 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 Jm0$eg{{a!zwfz79 diff --git a/src/windows/binaries_64bit/prism.css b/src/windows/binaries_64bit/prism.css deleted file mode 100644 index f94cca7c..00000000 --- a/src/windows/binaries_64bit/prism.css +++ /dev/null @@ -1,130 +0,0 @@ -/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+python */ -/** - * prism.js default theme for JavaScript, CSS and HTML - * Based on dabblet (http://dabblet.com) - * @author Lea Verou - */ - -code[class*="language-"], -pre[class*="language-"] { - color: black; - text-shadow: 0 1px white; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, -code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { - text-shadow: none; - background: #b3d4fc; -} - -pre[class*="language-"]::selection, pre[class*="language-"] ::selection, -code[class*="language-"]::selection, code[class*="language-"] ::selection { - text-shadow: none; - background: #b3d4fc; -} - -@media print { - code[class*="language-"], - pre[class*="language-"] { - text-shadow: none; - } -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #f5f2f0; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #999; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag, -.token.boolean, -.token.number, -.token.constant, -.token.symbol { - color: #905; -} - -.token.selector, -.token.attr-name, -.token.string, -.token.builtin { - color: #690; -} - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string, -.token.variable { - color: #a67f59; - background: hsla(0,0%,100%,.5); -} - -.token.atrule, -.token.attr-value, -.token.keyword { - color: #07a; -} - -.token.function { - color: #DD4A68; -} - -.token.regex, -.token.important { - color: #e90; -} - -.token.important { - font-weight: bold; -} - -.token.entity { - cursor: help; -} - diff --git a/src/windows/binaries_64bit/prism.js b/src/windows/binaries_64bit/prism.js deleted file mode 100644 index ebaa4b42..00000000 --- a/src/windows/binaries_64bit/prism.js +++ /dev/null @@ -1,5 +0,0 @@ -/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+python */ -self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content)):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(f instanceof r)){l.lastIndex=0;var h=l.exec(f);if(h){c&&(g=h[1].length);var d=h.index-1+g,h=h[0].slice(g),p=h.length,m=d+p,v=f.slice(0,d+1),y=f.slice(m+1),k=[u,1];v&&k.push(v);var b=new r(o,s?t.tokenize(h,s):h);k.push(b),y&&k.push(y),Array.prototype.splice.apply(a,k)}}}}return a},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[],r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(n)}}},n=t.Token=function(e,t){this.type=e,this.content=t};if(n.stringify=function(e,r,a){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,r,e)}).join("");var i={type:e.type,content:n.stringify(e.content,r,a),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:a};"comment"==i.type&&(i.attributes.spellcheck="true"),t.hooks.run("wrap",i);var o="";for(var l in i.attributes)o+=l+'="'+(i.attributes[l]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,a=n.code;self.postMessage(JSON.stringify(t.tokenize(a,t.languages[r]))),self.close()},!1),self.Prism):self.Prism;var r=document.getElementsByTagName("script");return r=r[r.length-1],r&&(t.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; -Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; -Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});; -Prism.languages.python={comment:{pattern:/(^|[^\\])#.*?(\r?\n|$)/g,lookbehind:!0},string:/"""[\s\S]+?"""|("|')(\\?.)*?\1/g,keyword:/\b(as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/g,"boolean":/\b(True|False)\b/g,number:/\b-?(0x)?\d*\.?[\da-f]+\b/g,operator:/[-+]{1,2}|=?<|=?>|!|={1,2}|(&){1,2}|(&){1,2}|\|?\||\?|\*|\/|~|\^|%|\b(or|and|not)\b/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; diff --git a/src/windows/binaries_64bit/pygtk_.py b/src/windows/binaries_64bit/pygtk_.py deleted file mode 100644 index 85e4d4f7..00000000 --- a/src/windows/binaries_64bit/pygtk_.py +++ /dev/null @@ -1,196 +0,0 @@ -# An example of embedding CEF browser in PyGTK on Windows. -# Tested with PyGTK 2.24.10 - -import os, sys -libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'libcef.dll') -if os.path.exists(libcef_dll): - # Import a local module - if (2,7) <= sys.version_info < (2,8): - import cefpython_py27 as cefpython - elif (3,4) <= sys.version_info < (3,4): - import cefpython_py34 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -import pygtk -pygtk.require('2.0') -import gtk -import gobject - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (_exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[pygtk_.py]: WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class PyGTKExample: - mainWindow = None - container = None - browser = None - exiting = None - searchEntry = None - - def __init__(self): - gobject.timeout_add(10, self.OnTimer) - - self.mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL) - self.mainWindow.connect('destroy', self.OnExit) - self.mainWindow.set_size_request(width=1024, height=768) - self.mainWindow.set_title('PyGTK CEF 3 example') - self.mainWindow.realize() - - self.container = gtk.DrawingArea() - self.container.set_property('can-focus', True) - self.container.connect('size-allocate', self.OnSize) - self.container.show() - - self.searchEntry = gtk.Entry() - # By default, clicking a GTK widget doesn't grab the focus away from a native Win32 control (browser). - self.searchEntry.connect('button-press-event', self.OnWidgetClick) - self.searchEntry.show() - - table = gtk.Table(3, 1, homogeneous=False) - self.mainWindow.add(table) - table.attach(self.CreateMenu(), 0, 1, 0, 1, yoptions=gtk.SHRINK) - table.attach(self.searchEntry, 0, 1, 1, 2, yoptions=gtk.SHRINK) - table.attach(self.container, 0, 1, 2, 3) - table.show() - - windowID = self.container.get_window().handle - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(windowID) - self.browser = cefpython.CreateBrowserSync(windowInfo, - browserSettings={}, - navigateUrl=GetApplicationPath('example.html')) - - self.mainWindow.show() - - # Browser took focus, we need to get it back and give to searchEntry. - self.mainWindow.get_window().focus() - self.searchEntry.grab_focus() - - def CreateMenu(self): - file = gtk.MenuItem('File') - file.show() - filemenu = gtk.Menu() - item = gtk.MenuItem('Open') - filemenu.append(item) - item.show() - item = gtk.MenuItem('Exit') - filemenu.append(item) - item.show() - file.set_submenu(filemenu) - about = gtk.MenuItem('About') - about.show() - menubar = gtk.MenuBar() - menubar.append(file) - menubar.append(about) - menubar.show() - return menubar - - def OnWidgetClick(self, widget, data): - self.mainWindow.get_window().focus() - - def OnTimer(self): - if self.exiting: - return False - cefpython.MessageLoopWork() - return True - - def OnFocusIn(self, widget, data): - # This function is currently not called by any of code, but if you would like - # for browser to have automatic focus add such line: - # self.mainWindow.connect('focus-in-event', self.OnFocusIn) - cefpython.WindowUtils.OnSetFocus(self.container.get_window().handle, 0, 0, 0) - - def OnSize(self, widget, sizeAlloc): - cefpython.WindowUtils.OnSize(self.container.get_window().handle, 0, 0, 0) - - def OnExit(self, widget, data=None): - self.exiting = True - gtk.main_quit() - -if __name__ == '__main__': - version = '.'.join(map(str, list(gtk.gtk_version))) - print('[pygtk_.py] GTK version: %s' % version) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - settings = { - "debug": True, # cefpython debug messages in console and in log_file - "log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE - "log_file": GetApplicationPath("debug.log"), # Set to "" to disable - # This directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess"), - } - - cefpython.Initialize(settings) - - gobject.threads_init() # Timer for the message loop - PyGTKExample() - gtk.main() - - cefpython.Shutdown() diff --git a/src/windows/binaries_64bit/pyqt.py b/src/windows/binaries_64bit/pyqt.py deleted file mode 100644 index ae7d7a85..00000000 --- a/src/windows/binaries_64bit/pyqt.py +++ /dev/null @@ -1,190 +0,0 @@ -# An example of embedding CEF browser in a PyQt4 application. -# Tested with PyQt 4.10.3 (Qt 4.8.5). - -import os, sys -libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'libcef.dll') -if os.path.exists(libcef_dll): - # Import a local module - if (2,7) <= sys.version_info < (2,8): - import cefpython_py27 as cefpython - elif (3,4) <= sys.version_info < (3,4): - import cefpython_py34 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -from PyQt4 import QtGui -from PyQt4 import QtCore - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[pyqt.py] WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainWindow(QtGui.QMainWindow): - mainFrame = None - - def __init__(self): - super(MainWindow, self).__init__(None) - self.createMenu() - self.mainFrame = MainFrame(self) - self.setCentralWidget(self.mainFrame) - self.resize(1024, 768) - self.setWindowTitle('PyQT CEF 3 example') - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - def createMenu(self): - menubar = self.menuBar() - filemenu = menubar.addMenu("&File") - filemenu.addAction(QtGui.QAction("Open", self)) - filemenu.addAction(QtGui.QAction("Exit", self)) - aboutmenu = menubar.addMenu("&About") - - def focusInEvent(self, event): - cefpython.WindowUtils.OnSetFocus(int(self.centralWidget().winId()), 0, 0, 0) - - def closeEvent(self, event): - self.mainFrame.browser.CloseBrowser() - -class MainFrame(QtGui.QWidget): - browser = None - - def __init__(self, parent=None): - super(MainFrame, self).__init__(parent) - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(int(self.winId())) - self.browser = cefpython.CreateBrowserSync(windowInfo, - browserSettings={}, - navigateUrl=GetApplicationPath("example.html")) - self.show() - - def moveEvent(self, event): - cefpython.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) - - def resizeEvent(self, event): - cefpython.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) - -class CefApplication(QtGui.QApplication): - timer = None - - def __init__(self, args): - super(CefApplication, self).__init__(args) - self.createTimer() - - def createTimer(self): - self.timer = QtCore.QTimer() - self.timer.timeout.connect(self.onTimer) - self.timer.start(10) - - def onTimer(self): - # The proper way of doing message loop should be: - # 1. In createTimer() call self.timer.start(0) - # 2. In onTimer() call MessageLoopWork() only when - # QtGui.QApplication.instance()->hasPendingEvents() returns False. - # But... there is a bug in Qt, hasPendingEvents() returns always true. - cefpython.MessageLoopWork() - - def stopTimer(self): - # Stop the timer after Qt message loop ended, calls to MessageLoopWork() - # should not happen anymore. - self.timer.stop() - -if __name__ == '__main__': - print("[pyqt.py] PyQt version: %s" % QtCore.PYQT_VERSION_STR) - print("[pyqt.py] QtCore version: %s" % QtCore.qVersion()) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - settings = { - # "cache_path": "webcache/", # Disk cache - "debug": True, # cefpython debug messages in console and in log_file - "log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE - "log_file": GetApplicationPath("debug.log"), # Set to "" to disable. - # This directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess") - } - - # Command line switches set programmatically - switches = { - # "proxy-server": "socks5://127.0.0.1:8888", - # "enable-media-stream": "", - # "--invalid-switch": "" -> Invalid switch name - } - - cefpython.Initialize(settings, switches) - - app = CefApplication(sys.argv) - mainWindow = MainWindow() - mainWindow.show() - app.exec_() - app.stopTimer() - - # Need to destroy QApplication(), otherwise Shutdown() fails. - # Unset main window also just to be safe. - del mainWindow - del app - - cefpython.Shutdown() diff --git a/src/windows/binaries_64bit/pyside.py b/src/windows/binaries_64bit/pyside.py deleted file mode 100644 index 9a5f68fe..00000000 --- a/src/windows/binaries_64bit/pyside.py +++ /dev/null @@ -1,188 +0,0 @@ -# An example of embedding CEF Python in PySide application. - -import os, sys -libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'libcef.dll') -if os.path.exists(libcef_dll): - # Import a local module - if (2,7) <= sys.version_info < (2,8): - import cefpython_py27 as cefpython - elif (3,4) <= sys.version_info < (3,4): - import cefpython_py34 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -import PySide -from PySide import QtGui -from PySide import QtCore -import ctypes - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("cefpython: WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainWindow(QtGui.QMainWindow): - mainFrame = None - - def __init__(self): - super(MainWindow, self).__init__(None) - self.createMenu() - self.mainFrame = MainFrame(self) - self.setCentralWidget(self.mainFrame) - self.resize(1024, 768) - self.setWindowTitle('PySide example') - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - def createMenu(self): - menubar = self.menuBar() - filemenu = menubar.addMenu("&File") - filemenu.addAction(QtGui.QAction("Open", self)) - filemenu.addAction(QtGui.QAction("Exit", self)) - aboutmenu = menubar.addMenu("&About") - - def focusInEvent(self, event): - cefpython.WindowUtils.OnSetFocus(int(self.centralWidget().winIdFixed()), 0, 0, 0) - - def closeEvent(self, event): - self.mainFrame.browser.CloseBrowser() - -class MainFrame(QtGui.QWidget): - browser = None - - def __init__(self, parent=None): - super(MainFrame, self).__init__(parent) - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(int(self.winIdFixed())) - self.browser = cefpython.CreateBrowserSync(windowInfo, - browserSettings={}, - navigateUrl=GetApplicationPath("example.html")) - self.show() - - def winIdFixed(self): - # PySide bug: QWidget.winId() returns , - # there is no easy way to convert it to int. - try: - return int(self.winId()) - except: - if sys.version_info[0] == 2: - ctypes.pythonapi.PyCObject_AsVoidPtr.restype = ctypes.c_void_p - ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = [ctypes.py_object] - return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId()) - elif sys.version_info[0] == 3: - ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p - ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object] - return ctypes.pythonapi.PyCapsule_GetPointer(self.winId(), None) - - def moveEvent(self, event): - cefpython.WindowUtils.OnSize(int(self.winIdFixed()), 0, 0, 0) - - def resizeEvent(self, event): - cefpython.WindowUtils.OnSize(int(self.winIdFixed()), 0, 0, 0) - -class CefApplication(QtGui.QApplication): - timer = None - - def __init__(self, args): - super(CefApplication, self).__init__(args) - self.createTimer() - - def createTimer(self): - self.timer = QtCore.QTimer() - self.timer.timeout.connect(self.onTimer) - self.timer.start(10) - - def onTimer(self): - # The proper way of doing message loop should be: - # 1. In createTimer() call self.timer.start(0) - # 2. In onTimer() call MessageLoopWork() only when - # QtGui.QApplication.instance()->hasPendingEvents() returns False. - # But... there is a bug in Qt, hasPendingEvents() returns always true. - cefpython.MessageLoopWork() - - def stopTimer(self): - # Stop the timer after Qt message loop ended, calls to MessageLoopWork() - # should not happen anymore. - self.timer.stop() - -if __name__ == '__main__': - print("PySide version: %s" % PySide.__version__) - print("QtCore version: %s" % QtCore.__version__) - - sys.excepthook = ExceptHook - settings = {} - settings["log_file"] = GetApplicationPath("debug.log") - settings["log_severity"] = cefpython.LOGSEVERITY_INFO - settings["browser_subprocess_path"] = "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess") - cefpython.Initialize(settings) - - app = CefApplication(sys.argv) - mainWindow = MainWindow() - mainWindow.show() - app.exec_() - app.stopTimer() - - # Need to destroy QApplication(), otherwise Shutdown() fails. - # Unset main window also just to be safe. - del mainWindow - del app - - cefpython.Shutdown() diff --git a/src/windows/binaries_64bit/pywin32.py b/src/windows/binaries_64bit/pywin32.py deleted file mode 100644 index 74776269..00000000 --- a/src/windows/binaries_64bit/pywin32.py +++ /dev/null @@ -1,151 +0,0 @@ -# Example of embedding CEF browser using the PyWin32 extension. -# Tested with pywin32 version 218. - -import os, sys -libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'libcef.dll') -if os.path.exists(libcef_dll): - # Import a local module - if (2,7) <= sys.version_info < (2,8): - import cefpython_py27 as cefpython - elif (3,4) <= sys.version_info < (3,4): - import cefpython_py34 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -import cefwindow -import win32con -import win32gui -import win32api -import time - -DEBUG = True - -# ----------------------------------------------------------------------------- -# Helper functions. - -def Log(msg): - print("[pywin32.py] %s" % str(msg)) - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[pywin32.py] WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -# ----------------------------------------------------------------------------- - -def CefAdvanced(): - sys.excepthook = ExceptHook - - appSettings = dict() - # appSettings["cache_path"] = "webcache/" # Disk cache - if DEBUG: - # cefpython debug messages in console and in log_file - appSettings["debug"] = True - cefwindow.g_debug = True - appSettings["log_file"] = GetApplicationPath("debug.log") - appSettings["log_severity"] = cefpython.LOGSEVERITY_INFO - appSettings["browser_subprocess_path"] = "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess") - cefpython.Initialize(appSettings) - - wndproc = { - win32con.WM_CLOSE: CloseWindow, - win32con.WM_DESTROY: QuitApplication, - win32con.WM_SIZE: cefpython.WindowUtils.OnSize, - win32con.WM_SETFOCUS: cefpython.WindowUtils.OnSetFocus, - win32con.WM_ERASEBKGND: cefpython.WindowUtils.OnEraseBackground - } - - browserSettings = dict() - browserSettings["universal_access_from_file_urls_allowed"] = True - browserSettings["file_access_from_file_urls_allowed"] = True - - if os.path.exists("icon.ico"): - icon = os.path.abspath("icon.ico") - else: - icon = "" - - windowHandle = cefwindow.CreateWindow(title="pywin32 example", - className="cefpython3_example", width=1024, height=768, - icon=icon, windowProc=wndproc) - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(windowHandle) - browser = cefpython.CreateBrowserSync(windowInfo, browserSettings, - navigateUrl=GetApplicationPath("example.html")) - cefpython.MessageLoop() - cefpython.Shutdown() - -def CloseWindow(windowHandle, message, wparam, lparam): - browser = cefpython.GetBrowserByWindowHandle(windowHandle) - browser.CloseBrowser() - return win32gui.DefWindowProc(windowHandle, message, wparam, lparam) - -def QuitApplication(windowHandle, message, wparam, lparam): - win32gui.PostQuitMessage(0) - return 0 - -def GetPywin32Version(): - fixed_file_info = win32api.GetFileVersionInfo(win32api.__file__, '\\') - return fixed_file_info['FileVersionLS'] >> 16 - -if __name__ == "__main__": - Log("pywin32 version = %s" % GetPywin32Version()) - CefAdvanced() diff --git a/src/windows/binaries_64bit/smoke.css b/src/windows/binaries_64bit/smoke.css deleted file mode 100644 index 2936c818..00000000 --- a/src/windows/binaries_64bit/smoke.css +++ /dev/null @@ -1,110 +0,0 @@ -.smoke-base { - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; - visibility: hidden; - opacity: 0; -} - -.smoke-base.smoke-visible { - opacity: 1; - visibility: visible; -} - -.smokebg { - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; -} - -.smoke-base .dialog { - position: absolute; -} - -.dialog-prompt { - margin-top: 15px; - text-align: center; -} - -.dialog-buttons { - margin: 20px 0 5px 0 -} - -.smoke { -} - -.dialog-buttons button { - display: inline-block; - vertical-align: baseline; - cursor: pointer; - font-family: Menlo, 'Andale Mono', monospace; - font-style: normal; - text-decoration: none; - border: 0; - outline: 0; - margin: 0 5px; - -webkit-background-clip: padding-box; - font-size: 13px; - line-height: 13px; - font-weight: normal; - padding: 9px 12px; -} - -.dialog-prompt input { - margin: 0; - border: 0; - font-family: sans-serif; - outline: none; - font-family: Menlo, 'Andale Mono', monospace; - border: 1px solid #aaa; - width: 75%; - display: inline-block; - background-color: transparent; - font-size: 16px; - padding: 8px; -} - -.smoke-base { - background: rgba(0,0,0,.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#90000000,endColorstr=#900000000); -} - -.smoke-base .dialog { - top: 25%; - width: 40%; - left: 50%; - margin-left: -20%; -} - -.smoke-base .dialog-inner { - padding: 15px; - - color:#202020; -} - -.smoke { - background-color: rgba(255,255,255,0.95); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff,endColorstr=#ffffff); - box-shadow: 0 2px 8px #666; -} - - -.dialog-buttons button { - background-color: rgba(0,0,0,.85); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#222222,endColorstr=#222222); - border-radius: 0; - color: #fff; -} - -button.cancel { - background-color: rgba(0,0,0,.40); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#444444,endColorstr=#444444); -} - -.queue{ - display:none; -} diff --git a/src/windows/binaries_64bit/smoke.min.js b/src/windows/binaries_64bit/smoke.min.js deleted file mode 100644 index 61fb4f0c..00000000 --- a/src/windows/binaries_64bit/smoke.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(e,t){var n={smoketimeout:[],init:false,zindex:1e3,i:0,bodyload:function(e){var r=t.createElement("div");r.setAttribute("id","smoke-out-"+e);r.className="smoke-base";r.style.zIndex=n.zindex;n.zindex++;t.body.appendChild(r)},newdialog:function(){var t=(new Date).getTime();t=Math.random(1,99)+t;if(!n.init){n.listen(e,"load",function(){n.bodyload(t)})}else{n.bodyload(t)}return t},forceload:function(){},build:function(t,r){n.i++;r.stack=n.i;t=t.replace(/\n/g,"
");t=t.replace(/\r/g,"
");var i="",s="OK",o="Cancel",u="",a="",f;if(r.type==="prompt"){i='

"}if(r.params.ok){s=r.params.ok}if(r.params.cancel){o=r.params.cancel}if(r.params.classname){u=r.params.classname}if(r.type!=="signal"){a='
';if(r.type==="alert"){a+='"}else if(r.type==="quiz"){if(r.params.button_1){a+='"}if(r.params.button_2){a+='"}if(r.params.button_3){a+='"}if(r.params.button_cancel){a+='"}}else if(r.type==="prompt"||r.type==="confirm"){if(r.params.reverseButtons){a+='"+'"}else{a+='"+'"}}a+="
"}f='
'+'
'+'
'+t+i+a+"
"+"
";if(!n.init){n.listen(e,"load",function(){n.finishbuild(t,r,f)})}else{n.finishbuild(t,r,f)}},finishbuild:function(e,r,i){var s=t.getElementById("smoke-out-"+r.newid);s.className="smoke-base smoke-visible smoke-"+r.type;s.innerHTML=i;while(s.innerHTML===""){s.innerHTML=i}if(n.smoketimeout[r.newid]){clearTimeout(n.smoketimeout[r.newid])}n.listen(t.getElementById("smoke-bg-"+r.newid),"click",function(){n.destroy(r.type,r.newid);if(r.type==="prompt"||r.type==="confirm"||r.type==="quiz"){r.callback(false)}else if(r.type==="alert"&&typeof r.callback!=="undefined"){r.callback()}});switch(r.type){case"alert":n.finishbuildAlert(e,r,i);break;case"confirm":n.finishbuildConfirm(e,r,i);break;case"quiz":n.finishbuildQuiz(e,r,i);break;case"prompt":n.finishbuildPrompt(e,r,i);break;case"signal":n.finishbuildSignal(e,r,i);break;default:throw"Unknown type: "+r.type}},finishbuildAlert:function(r,i,s){n.listen(t.getElementById("alert-ok-"+i.newid),"click",function(){n.destroy(i.type,i.newid);if(typeof i.callback!=="undefined"){i.callback()}});t.onkeyup=function(t){if(!t){t=e.event}if(t.keyCode===13||t.keyCode===32||t.keyCode===27){n.destroy(i.type,i.newid);if(typeof i.callback!=="undefined"){i.callback()}}}},finishbuildConfirm:function(r,i,s){n.listen(t.getElementById("confirm-cancel-"+i.newid),"click",function(){n.destroy(i.type,i.newid);i.callback(false)});n.listen(t.getElementById("confirm-ok-"+i.newid),"click",function(){n.destroy(i.type,i.newid);i.callback(true)});t.onkeyup=function(t){if(!t){t=e.event}if(t.keyCode===13||t.keyCode===32){n.destroy(i.type,i.newid);i.callback(true)}else if(t.keyCode===27){n.destroy(i.type,i.newid);i.callback(false)}}},finishbuildQuiz:function(r,i,s){var o,u,a;n.listen(t.getElementById("quiz-cancel-"+i.newid),"click",function(){n.destroy(i.type,i.newid);i.callback(false)});if(o=t.getElementById("quiz-ok1-"+i.newid))n.listen(o,"click",function(){n.destroy(i.type,i.newid);i.callback(o.innerHTML)});if(u=t.getElementById("quiz-ok2-"+i.newid))n.listen(u,"click",function(){n.destroy(i.type,i.newid);i.callback(u.innerHTML)});if(a=t.getElementById("quiz-ok3-"+i.newid))n.listen(a,"click",function(){n.destroy(i.type,i.newid);i.callback(a.innerHTML)});t.onkeyup=function(t){if(!t){t=e.event}if(t.keyCode===27){n.destroy(i.type,i.newid);i.callback(false)}}},finishbuildPrompt:function(r,i,s){var o=t.getElementById("dialog-input-"+i.newid);setTimeout(function(){o.focus();o.select()},100);n.listen(t.getElementById("prompt-cancel-"+i.newid),"click",function(){n.destroy(i.type,i.newid);i.callback(false)});n.listen(t.getElementById("prompt-ok-"+i.newid),"click",function(){n.destroy(i.type,i.newid);i.callback(o.value)});t.onkeyup=function(t){if(!t){t=e.event}if(t.keyCode===13){n.destroy(i.type,i.newid);i.callback(o.value)}else if(t.keyCode===27){n.destroy(i.type,i.newid);i.callback(false)}}},finishbuildSignal:function(r,i,s){t.onkeyup=function(t){if(!t){t=e.event}if(t.keyCode===27){n.destroy(i.type,i.newid);if(typeof i.callback!=="undefined"){i.callback()}}};n.smoketimeout[i.newid]=setTimeout(function(){n.destroy(i.type,i.newid);if(typeof i.callback!=="undefined"){i.callback()}},i.timeout)},destroy:function(e,r){var i=t.getElementById("smoke-out-"+r);if(e!=="quiz"){var s=t.getElementById(e+"-ok-"+r)}var o=t.getElementById(e+"-cancel-"+r);i.className="smoke-base";if(s){n.stoplistening(s,"click",function(){});t.onkeyup=null}if(e==="quiz"){var u=t.getElementsByClassName("quiz-button");for(var a=0;a - - - - wxPython CEF 3 example (utf-8: Ä…Å›) - - - - - - - - - -Use the mouse context menu to go Back/Forward in history navigation. - -

Table of contents

-
    -
  1. Google search
  2. -
  3. User agent
  4. -
  5. Popups
  6. -
  7. HTML 5 video
  8. -
  9. Developer Tools
  10. -
  11. Downloads
  12. -
  13. HTML controls
  14. -
  15. Browser object
  16. -
  17. Frame object
  18. -
  19. Javascript bindings
  20. -
  21. Javascript callbacks
  22. -
  23. Python callbacks
  24. -
  25. Display handler
  26. -
  27. Keyboard handler
  28. -
  29. Request handler
  30. -
  31. Cookie tests
  32. -
  33. Load handler
  34. -
  35. Javascript Dialog handler
  36. -
  37. Other tests
  38. -
- - - - - - -

Google search

- -http://www.google.com/ - - - - - - - -

User agent

- - - - - - - - - -

Popups

- -
    -
  1. - window.open('wxpython.html') -
  2. -
  3. - target=_blank href="wxpython.html" -
  4. -
  5. - window.open('https://www.google.com/') -
  6. -
- -There are problems with keyboard in wxPython when popup windows are created -by CEF ( -Issue 80). To create the popup window of our own, the -LifespanHandler::OnBeforePopup callback was implemented. Note that this has -its implications, the popup window and parent window will not be able to -script each other. There will be no "window.opener" property available -in the popup window. - -See its source: - - - - -

CreateAnotherBrowser

- -This will create a window on its own and embed browser in it. -When using "window.open" the window is created implicitilly -by CEF. You can intercept such popup creation using the -OnBeforePopup callback in LifespanHandler. You can return -True in OnBeforePopup and create popup window on your own -using the CreateAnotherBrowser function. - - - - external.CreateAnotherBrowser() - - - - - - - -

HTML5 video and accelerated content

- - -HTML 5 video
- - -Accelerated canvas
- - -Accelerated layers
- - - - - - -

Developer Tools

- -You can open devtools popup window in a few different ways: -
    -
  1. Call Browser.ShowDevTools() method: - - external.ShowDevTools()
  2. -
  3. Through mouse context menu
  4. -
  5. Through F12 key which is handled in KeyboardHandler.OnKeyEvent
  6. -
- - - - - - -

Downloads

- -Download sample Ubuntu wallpapers:
- - https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip - -

-Notes: On Linux it seems that OnLoadError with errorCode = ERR_ABORTED -is called even for successful downloads, you can ignore this behavior. -The proper console messages about successful/aborted download originate -from C++ Browser process code, these are: -

- -
Browser: About to download file: ubuntu-wallpapers2.zip
-Browser: Download completed, saved to: /Downloads/ubuntu-wallpapers2.zip
-
- -If download was aborted the messages will be: - -
Browser: About to download file: ubuntu-wallpapers2.zip
-Browser: Download was cancelled
-
- -

-Additionally on Linux there are more errors reported by Chromium -about org.gnome.SessionManager.inhibit. These can be safely ignored as well. -

- -A download handler with callbacks like `OnBeforeDownload` and -`OnDownloadUpdated` may be exposed to CEF Python in the future. - - - - - - -

HTML controls

- -

Textarea

- -
- -

Inputs

-Text:
-Password:
- -

Select

- - -

Buttons

-Submit:
-Button:
- - - - - - -

Browser object

- -Tests for the Browser object methods. - -

GoBack

- -external.GoBack() - -

GoForward

- -external.GoForward() - -

LoadUrl, GetUrl

- - - window.open('data:text/html,Test#Browser.LoadUrl') - -

SetZoomLevel

-(You must set ApplicationSettings.auto_zooming = "" for SetZoomLevel calls -to work)
-external.SetZoomLevel(2.0)
-external.SetZoomLevel(1.0) - -

ReloadIgnoreCache, StopLoad

-Press F5 to reload page and ignore cache.
-Press Esc during webpage loading to abort.
- -Also, when Esc is pressed OnLoadError may get called. See how abort -of page loading or file download is handled: - - - - - - - -

Frame object

- -Tests for the Frame object methods. TODO. - - - - - - -

Javascript bindings

- -

PyPrint

- - - window.PyPrint('printing in python console from js') -
- -

Window properties

- -
jsBindings.SetProperty("pyProperty", "This was set in Python")
-jsBindings.SetProperty("pyConfig", ["This was set in Python",
-        {"name": "Nested dictionary", "isNested": True},
-        [1,"2", None]])
-
- - - window.alert(window.pyProperty)
- - window.alert(JSON.stringify(window.pyConfig)) -
- -

Print

- - - - external.Print('printing again from js') -
- -

TestAllTypes

- - - - external.TestAllTypes - (undefined, null, true, 1, - ((1<<31)>>>0), 2.14, 'Date not yet supported', 'string', - {key1: 1, key2: 2}, {key1: {'key1.1': 'nested object'}, 'key1.2': [1]}, - [1, 2], [1, [2.1, 'nested array']], [{key1: [{}]}]) -
- -

ExecuteFunction

- - - -
<script>
-function JavascriptAlert(message) { window.alert(message); }
-</script>
-
- - - - external.ExecuteFunction('JavascriptAlert', - 'python called from js and then js called from python', 0, [1,2]) -
- -

GetSource, GetText

- - - - - - - external.GetSource() -
- - external.GetText() - - - - - - -

Javascript callbacks

- -

TestJSCallback

- - - -
<script>
-function JSCallback(arg1) {
-    window.alert(arg1)
-}
-</script>
-
- - - - external.TestJSCallback(JSCallback) - -

TestJSCallbackComplexArguments

- - - -
<script>
-function JSCallback2() {
-    window.alert(JSON.stringify(arguments))
-}
-</script>
-
- - - - external.TestJSCallbackComplexArguments({"myCallback": JSCallback2}) - - - - - - - -

Python callbacks

- -

TestPythonCallback

- - - -
<script>
-function JSCallback3(pyCallback) {
-    pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
-}
-</script>
-
- - - - - - external.TestPythonCallback(JSCallback3) - - - - - - -

Display handler

- -

OnAddressChange

- -See messages in the console during loading of a webpage. - -

OnTitleChange

- -See messages in the console during loading of a webpage. - -

OnTooltip

- -See messages in the console when hovering over a google logo: -http://www.google.com/ - -

OnStatusMessage

- -See messages in the console when hovering over links. - -

OnConsoleMessage

- -Try this: - - http://patik.com/code/console-log-polyfill/ - - - - - - -

Keyboard handler

- -

- Press F5 to reload the page.
- On Linux it is required to click anywhere in the window first - so that keyboard focus is set. See Issue 77 in the CEF Python - Issue Tracker. -

- - - - - - - - - -

Request handler

- -

OnBeforeResourceLoad

- -See messages in the console. - -

OnResourceRedirect

- -Try this: - - http://tinyurl.com/google404redirect - -

GetAuthCredentials

- -Try this: - - http://browserspy.dk/password-ok.php - -

OnQuotaRequest

- - - - -
<script>
-function DoRequestQuota() {
-    // Request Quota (only for File System API)
-    try {
-        navigator.webkitPersistentStorage.requestQuota(PERSISTENT, 1024*1024,
-                function(bytes){ window.alert("Granted bytes: "+bytes);},
-                function(error){ window.alert(error); });
-    } catch(e) {
-        navigator.webkitPersistentStorage.requestQuota(1024*1024,
-                function(bytes){ window.alert("Granted bytes: "+bytes);},
-                function(error){ window.alert(error); });
-    }
-}
-</script>
-
- -Try this: - - https://googledrive.com/host/0B1di2XiBBfacMnhRRkI1YlotUEk/requestquota.html - -

OnProtocolExecution

- - -Try magnet link to download a torrent: - - magnet:?xt=urn:btih:a4224b45b27f436374391379cc5c7e629e2e5189 - -

_OnBeforePluginLoad

- -Try OnBeforePluginLoad() with Flash: - - http://www.adobe.com/software/flash/about/ - -

_OnCertificateError

- - -The url below won't be allowed. Click twice "Back" from context menu -to return back here after visiting that url:
- - https://testssl-expire.disig.sk/index.en.html -
- -This url will be allowed:
- - https://testssl-expire.disig.sk/index.en.html?allow=1 -
-After you've clicked the second url, the first one will now be allowed, -as this is the same domain and it's cached now. - -

OnRendererProcessTerminated

- -Try to terminate the "subprocess.exe" renderer process through -task manager. - -

OnPluginCrashed

- -No test for that yet. - - - - - - -

Cookie tests

- -See messages in the console. - -

GetCookieManager

- - - -RequestHandler.GetCookieManager() - an example of having an unique -cookie manager for each browser. -
    -
  1. Visit the url below and set some cookies. Use "Back" from - context menu to get back here (you might have to click "Back" - multiple times).
    - Visit it in the current browser:
    - - http://www.html-kit.com/tools/cookietester/ -
  2. -
  3. Open cookietester in a popup - a separate cookie manager is used:
    - - javascript:external.CreateAnotherBrowser('http://www.html-kit.com/tools/cookietester/') -
  4. -
- -

-Popup browsers created javascript's window.open share -the same renderer process and request context. If you want -to have separate cookie managers for popups created using -window.open then you have to implement the -LifespanHandler.`OnBeforePopup` callback. Return True in that -callback to cancel popup creation and instead create the -window on your own and embed browser in it. The CreateAnotherBrowser() -function from the wxpython example does that. -

- -

VisitAllCookies

- -Visit all cookies: -external.VisitAllCookies() -

- -Note: visit some http:// webpage first, otherwise cookie manager is not -yet created. -
- -

VisitUrlCookies

- -Visit a subset of cookies for the given url: - - external.VisitUrlCookies("http://www.html-kit.com/tools/cookietester/") -
- -

SetCookie

- -Set a cookie on html-kit.com: -external.SetCookie() -
-Go see the cookie that was created: - - http://www.html-kit.com/tools/cookietester/ - -

DeleteCookies

- -Delete the single cookie previously created via SetCookie(): - - external.DeleteCookies() -
-Go check if cookie was deleted: - - http://www.html-kit.com/tools/cookietester/ - - - - - - -

Load Handler

- -See messages in the console during loading of a webpage. - -

OnLoadingStateChange

- - -

OnLoadStart

- - -

OnLoadEnd

- - -

OnLoadError

- -After you see the custom error message you have to hit -twice the Back from the context menu, to get back to this page. -
-Try this: - - http://www.non-existent.nono/ -
- - - - - - - - -

Javascript Dialog Handler

- -See messages in the console. - -

OnJavascriptDialog

- - - window.alert('Test js dialog handler') - - -

OnBeforeUnloadJavascriptDialog

- - - -
<script>
-function TestOnBeforeUnloadJavascriptDialog() {
-    window.onbeforeunload = function() {
-        return 'Testing the OnBeforeUnloadJavascriptDialog() callback';
-    }
-    location.href = "wxpython.html";
-}
-</script>
-
- - - TestOnBeforeUnloadJavascriptDialog() - - -

OnResetJavascriptDialogState

- - -

OnJavascriptDialogClosed

- - - - - - - -

Other tests

- -

HTTPS caching with SSL certificate errors

-Set ApplicationSettings["ignore_certificate_errors"] to True. - - - - - - - - - - - - - diff --git a/src/windows/binaries_64bit/wxpython.py b/src/windows/binaries_64bit/wxpython.py deleted file mode 100644 index ed6bb44b..00000000 --- a/src/windows/binaries_64bit/wxpython.py +++ /dev/null @@ -1,906 +0,0 @@ -# An example of embedding CEF browser in wxPython on Windows. -# Tested with wxPython 2.8.12.1 and 3.0.2.0. - -import os, sys -libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'libcef.dll') -if os.path.exists(libcef_dll): - # Import a local module - if (2,7) <= sys.version_info < (2,8): - import cefpython_py27 as cefpython - elif (3,4) <= sys.version_info < (3,4): - import cefpython_py34 as cefpython - else: - raise Exception("Unsupported python version: %s" % sys.version) -else: - # Import an installed package - from cefpython3 import cefpython - -import wx -import time -import re -import uuid -import platform -import inspect -import struct - -# ----------------------------------------------------------------------------- -# Globals - -g_applicationSettings = None -g_browserSettings = None -g_commandLineSwitches = None - -# Which method to use for message loop processing. -# EVT_IDLE - wx application has priority -# EVT_TIMER - cef browser has priority (default) -# It seems that Flash content behaves better when using a timer. -# Not sure if using EVT_IDLE is correct, it doesn't work on Linux, -# on Windows it works fine. See also the post by Robin Dunn: -# https://groups.google.com/d/msg/wxpython-users/hcNdMEx8u48/MD5Jgbm_k1kJ -USE_EVT_IDLE = False # If False then Timer will be used - -TEST_EMBEDDING_IN_PANEL = True - -# ----------------------------------------------------------------------------- - -def GetApplicationPath(file=None): - import re, os, platform - # On Windows after downloading file and calling Browser.GoForward(), - # current working directory is set to %UserProfile%. - # Calling os.path.dirname(os.path.realpath(__file__)) - # returns for eg. "C:\Users\user\Downloads". A solution - # is to cache path on first call. - if not hasattr(GetApplicationPath, "dir"): - if hasattr(sys, "frozen"): - dir = os.path.dirname(sys.executable) - elif "__file__" in globals(): - dir = os.path.dirname(os.path.realpath(__file__)) - else: - dir = os.getcwd() - GetApplicationPath.dir = dir - # If file is None return current directory without trailing slash. - if file is None: - file = "" - # Only when relative path. - if not file.startswith("/") and not file.startswith("\\") and ( - not re.search(r"^[\w-]+:", file)): - path = GetApplicationPath.dir + os.sep + file - if platform.system() == "Windows": - path = re.sub(r"[/\\]+", re.escape(os.sep), path) - path = re.sub(r"[/\\]+$", "", path) - return path - return str(file) - -def ExceptHook(excType, excValue, traceObject): - import traceback, os, time, codecs - # This hook does the following: in case of exception write it to - # the "error.log" file, display it to the console, shutdown CEF - # and exit application immediately by ignoring "finally" (os._exit()). - errorMsg = "\n".join(traceback.format_exception(excType, excValue, - traceObject)) - errorFile = GetApplicationPath("error.log") - try: - appEncoding = cefpython.g_applicationSettings["string_encoding"] - except: - appEncoding = "utf-8" - if type(errorMsg) == bytes: - errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace") - try: - with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp: - fp.write("\n[%s] %s\n" % ( - time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg)) - except: - print("[wxpython.py] WARNING: failed writing to error file: %s" % ( - errorFile)) - # Convert error message to ascii before printing, otherwise - # you may get error like this: - # | UnicodeEncodeError: 'charmap' codec can't encode characters - errorMsg = errorMsg.encode("ascii", errors="replace") - errorMsg = errorMsg.decode("ascii", errors="replace") - print("\n"+errorMsg+"\n") - cefpython.QuitMessageLoop() - cefpython.Shutdown() - os._exit(1) - -class MainFrame(wx.Frame): - browser = None - mainPanel = None - - def GetHandleForBrowser(self): - if self.mainPanel: - return self.mainPanel.GetHandle() - else: - return self.GetHandle() - - def __init__(self, url=None, popup=False): - if popup: - title = "wxPython Popup" - else: - title = "wxPython CEF 3 example" - wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, - title=title) - size=(800,600) - - # This is an optional code to enable High DPI support. - if "auto_zooming" in g_applicationSettings \ - and g_applicationSettings["auto_zooming"] == "system_dpi": - # This utility function will adjust width/height using - # OS DPI settings. For 800/600 with Win7 DPI settings - # being set to "Larger 150%" will return 1200/900. - size = cefpython.DpiAware.CalculateWindowSize(size[0], size[1]) - - self.SetSize(size) - - if not url: - url = "file://"+GetApplicationPath("wxpython.html") - # Test hash in url. - # url += "#test-hash" - - self.CreateMenu() - - if TEST_EMBEDDING_IN_PANEL: - print("Embedding in a wx.Panel!") - # You also have to set the wx.WANTS_CHARS style for - # all parent panels/controls, if it's deeply embedded. - self.mainPanel = wx.Panel(self, style=wx.WANTS_CHARS) - - # Global client callbacks must be set before browser is created. - self.clientHandler = ClientHandler() - cefpython.SetGlobalClientCallback("OnCertificateError", - self.clientHandler._OnCertificateError) - cefpython.SetGlobalClientCallback("OnBeforePluginLoad", - self.clientHandler._OnBeforePluginLoad) - cefpython.SetGlobalClientCallback("OnAfterCreated", - self.clientHandler._OnAfterCreated) - - windowInfo = cefpython.WindowInfo() - windowInfo.SetAsChild(self.GetHandleForBrowser()) - self.browser = cefpython.CreateBrowserSync(windowInfo, - browserSettings=g_browserSettings, - navigateUrl=url) - - self.clientHandler.mainBrowser = self.browser - self.browser.SetClientHandler(self.clientHandler) - - jsBindings = cefpython.JavascriptBindings( - bindToFrames=False, bindToPopups=True) - jsBindings.SetFunction("PyPrint", PyPrint) - jsBindings.SetProperty("pyProperty", "This was set in Python") - jsBindings.SetProperty("pyConfig", ["This was set in Python", - {"name": "Nested dictionary", "isNested": True}, - [1,"2", None]]) - self.javascriptExternal = JavascriptExternal(self.browser) - jsBindings.SetObject("external", self.javascriptExternal) - jsBindings.SetProperty("sources", GetSources()) - self.browser.SetJavascriptBindings(jsBindings) - - if self.mainPanel: - self.mainPanel.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) - self.mainPanel.Bind(wx.EVT_SIZE, self.OnSize) - else: - self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) - self.Bind(wx.EVT_SIZE, self.OnSize) - - self.Bind(wx.EVT_CLOSE, self.OnClose) - if USE_EVT_IDLE and not popup: - # Bind EVT_IDLE only for the main application frame. - print("Using EVT_IDLE to execute the CEF message loop work") - self.Bind(wx.EVT_IDLE, self.OnIdle) - - def CreateMenu(self): - filemenu = wx.Menu() - filemenu.Append(1, "Open") - exit = filemenu.Append(2, "Exit") - self.Bind(wx.EVT_MENU, self.OnClose, exit) - aboutmenu = wx.Menu() - aboutmenu.Append(1, "CEF Python") - menubar = wx.MenuBar() - menubar.Append(filemenu,"&File") - menubar.Append(aboutmenu, "&About") - self.SetMenuBar(menubar) - - def OnSetFocus(self, event): - cefpython.WindowUtils.OnSetFocus(self.GetHandleForBrowser(), 0, 0, 0) - - def OnSize(self, event): - cefpython.WindowUtils.OnSize(self.GetHandleForBrowser(), 0, 0, 0) - - def OnClose(self, event): - # Remove all CEF browser references so that browser is closed - # cleanly. Otherwise there may be issues for example with cookies - # not being flushed to disk when closing app immediately - # (Issue 158). - del self.javascriptExternal.mainBrowser - del self.clientHandler.mainBrowser - del self.browser - - # Destroy wx frame, this will complete the destruction of CEF browser - self.Destroy() - - # In wx.chromectrl calling browser.CloseBrowser and/or self.Destroy - # may cause crashes when embedding multiple browsers in tab - # (Issue 107). In such case instead of calling CloseBrowser/Destroy - # try this code: - # | self.browser.ParentWindowWillClose() - # | event.Skip() - - def OnIdle(self, event): - cefpython.MessageLoopWork() - -def PyPrint(message): - print("[wxpython.py] PyPrint: "+message) - -class JavascriptExternal: - mainBrowser = None - stringVisitor = None - - def __init__(self, mainBrowser): - self.mainBrowser = mainBrowser - - def GoBack(self): - self.mainBrowser.GoBack() - - def GoForward(self): - self.mainBrowser.GoForward() - - def SetZoomLevel(self, zoomLevel): - self.mainBrowser.SetZoomLevel(zoomLevel) - - def CreateAnotherBrowser(self, url=None): - frame = MainFrame(url=url) - frame.Show() - - def Print(self, message): - print("[wxpython.py] Print: "+message) - - def TestAllTypes(self, *args): - print("[wxpython.py] TestAllTypes: "+str(args)) - - def ExecuteFunction(self, *args): - self.mainBrowser.ExecuteFunction(*args) - - def TestJSCallback(self, jsCallback): - print("[wxpython.py] jsCallback.GetFunctionName() = %s"\ - % jsCallback.GetFunctionName()) - print("[wxpython.py] jsCallback.GetFrame().GetIdentifier() = %s" % \ - jsCallback.GetFrame().GetIdentifier()) - jsCallback.Call("This message was sent from python using js callback") - - def TestJSCallbackComplexArguments(self, jsObject): - jsCallback = jsObject["myCallback"]; - jsCallback.Call(1, None, 2.14, "string", ["list", ["nested list", \ - {"nested object":None}]], \ - {"nested list next":[{"deeply nested object":1}]}) - - def TestPythonCallback(self, jsCallback): - jsCallback.Call(self.PyCallback) - - def PyCallback(self, *args): - message = "PyCallback() was executed successfully! "\ - "Arguments: %s" % str(args) - print("[wxpython.py] "+message) - self.mainBrowser.GetMainFrame().ExecuteJavascript( - "window.alert(\"%s\")" % message) - - def GetSource(self): - # Must keep a strong reference to the StringVisitor object - # during the visit. - self.stringVisitor = StringVisitor() - self.mainBrowser.GetMainFrame().GetSource(self.stringVisitor) - - def GetText(self): - # Must keep a strong reference to the StringVisitor object - # during the visit. - self.stringVisitor = StringVisitor() - self.mainBrowser.GetMainFrame().GetText(self.stringVisitor) - - def ShowDevTools(self): - print("[wxpython.py] external.ShowDevTools called") - self.mainBrowser.ShowDevTools() - - # ------------------------------------------------------------------------- - # Cookies - # ------------------------------------------------------------------------- - cookieVisitor = None - - def VisitAllCookies(self): - # Need to keep the reference alive. - self.cookieVisitor = CookieVisitor() - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.VisitAllCookies(self.cookieVisitor) - - def VisitUrlCookies(self): - # Need to keep the reference alive. - self.cookieVisitor = CookieVisitor() - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.VisitUrlCookies( - "http://www.html-kit.com/tools/cookietester/", - False, self.cookieVisitor) - # .www.html-kit.com - - def SetCookie(self): - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - "the cookietester website first and create some cookies") - return - cookie = cefpython.Cookie() - cookie.SetName("Created_Via_Python") - cookie.SetValue("yeah really") - cookieManager.SetCookie("http://www.html-kit.com/tools/cookietester/", - cookie) - print("\n[wxpython.py] Cookie created! Visit html-kit cookietester to"\ - " see it") - - def DeleteCookies(self): - cookieManager = self.mainBrowser.GetUserData("cookieManager") - if not cookieManager: - print("\n[wxpython.py] Cookie manager not yet created! Visit"\ - " the cookietester website first and create some cookies") - return - cookieManager.DeleteCookies( - "http://www.html-kit.com/tools/cookietester/", - "Created_Via_Python") - print("\n[wxpython.py] Cookie deleted! Visit html-kit cookietester "\ - "to see the result") - -class StringVisitor: - def Visit(self, string): - print("\n[wxpython.py] StringVisitor.Visit(): string:") - print("--------------------------------") - print(string) - print("--------------------------------") - -class CookieVisitor: - def Visit(self, cookie, count, total, deleteCookie): - if count == 0: - print("\n[wxpython.py] CookieVisitor.Visit(): total cookies: %s"\ - % total) - print("\n[wxpython.py] CookieVisitor.Visit(): cookie:") - print(" "+str(cookie.Get())) - # True to continue visiting cookies - return True - -class ClientHandler: - mainBrowser = None # May be None for global client callbacks. - - def __init__(self): - pass - - # ------------------------------------------------------------------------- - # DisplayHandler - # ------------------------------------------------------------------------- - - def OnAddressChange(self, browser, frame, url): - print("[wxpython.py] DisplayHandler::OnAddressChange()") - print(" url = %s" % url) - - def OnTitleChange(self, browser, title): - print("[wxpython.py] DisplayHandler::OnTitleChange()") - print(" title = %s" % title) - - def OnTooltip(self, browser, textOut): - # OnTooltip not yet implemented (both Linux and Windows), - # will be fixed in next CEF release, see Issue 783: - # https://code.google.com/p/chromiumembedded/issues/detail?id=783 - print("[wxpython.py] DisplayHandler::OnTooltip()") - print(" text = %s" % textOut[0]) - - statusMessageCount = 0 - def OnStatusMessage(self, browser, value): - if not value: - # Do not notify in the console about empty statuses. - return - self.statusMessageCount += 1 - if self.statusMessageCount > 3: - # Do not spam too much. - return - print("[wxpython.py] DisplayHandler::OnStatusMessage()") - print(" value = %s" % value) - - def OnConsoleMessage(self, browser, message, source, line): - print("[wxpython.py] DisplayHandler::OnConsoleMessage()") - print(" message = %s" % message) - print(" source = %s" % source) - print(" line = %s" % line) - - # ------------------------------------------------------------------------- - # KeyboardHandler - # ------------------------------------------------------------------------- - - def OnPreKeyEvent(self, browser, event, eventHandle, - isKeyboardShortcutOut): - print("[wxpython.py] KeyboardHandler::OnPreKeyEvent()") - - def OnKeyEvent(self, browser, event, eventHandle): - if event["type"] == cefpython.KEYEVENT_KEYUP: - # OnKeyEvent is called twice for F5/Esc keys, with event - # type KEYEVENT_RAWKEYDOWN and KEYEVENT_KEYUP. - # Normal characters a-z should have KEYEVENT_CHAR. - return False - print("[wxpython.py] KeyboardHandler::OnKeyEvent()") - print(" type=%s" % event["type"]) - print(" modifiers=%s" % event["modifiers"]) - print(" windows_key_code=%s" % event["windows_key_code"]) - print(" native_key_code=%s" % event["native_key_code"]) - print(" is_system_key=%s" % event["is_system_key"]) - print(" character=%s" % event["character"]) - print(" unmodified_character=%s" % event["unmodified_character"]) - print(" focus_on_editable_field=%s" \ - % event["focus_on_editable_field"]) - linux = (platform.system() == "Linux") - windows = (platform.system() == "Windows") - # F5 - if (linux and event["native_key_code"] == 71) \ - or (windows and event["windows_key_code"] == 116): - print("[wxpython.py] F5 pressed, calling" - " browser.ReloadIgnoreCache()") - browser.ReloadIgnoreCache() - return True - # Escape - if (linux and event["native_key_code"] == 9) \ - or (windows and event["windows_key_code"] == 27): - print("[wxpython.py] Esc pressed, calling browser.StopLoad()") - browser.StopLoad() - return True - # F12 - if (linux and event["native_key_code"] == 96) \ - or (windows and event["windows_key_code"] == 123): - print("[wxpython.py] F12 pressed, calling" - " browser.ShowDevTools()") - browser.ShowDevTools() - return True - return False - - # ------------------------------------------------------------------------- - # RequestHandler - # ------------------------------------------------------------------------- - - def OnBeforeBrowse(self, browser, frame, request, isRedirect): - print("[wxpython.py] RequestHandler::OnBeforeBrowse()") - print(" url = %s" % request.GetUrl()[:100]) - # Handle "magnet:" links. - if request.GetUrl().startswith("magnet:"): - print("[wxpython.p] RequestHandler::OnBeforeBrowse(): " - "magnet link clicked, cancelling browse request") - return True - return False - - def OnBeforeResourceLoad(self, browser, frame, request): - print("[wxpython.py] RequestHandler::OnBeforeResourceLoad()") - print(" url = %s" % request.GetUrl()[:100]) - return False - - def OnResourceRedirect(self, browser, frame, oldUrl, newUrlOut, request): - print("[wxpython.py] RequestHandler::OnResourceRedirect()") - print(" old url = %s" % oldUrl[:100]) - print(" new url = %s" % newUrlOut[0][:100]) - - def GetAuthCredentials(self, browser, frame, isProxy, host, port, realm, - scheme, callback): - # This callback is called on the IO thread, thus print messages - # may not be visible. - print("[wxpython.py] RequestHandler::GetAuthCredentials()") - print(" host = %s" % host) - print(" realm = %s" % realm) - callback.Continue(username="test", password="test") - return True - - def OnQuotaRequest(self, browser, originUrl, newSize, callback): - print("[wxpython.py] RequestHandler::OnQuotaRequest()") - print(" origin url = %s" % originUrl) - print(" new size = %s" % newSize) - callback.Continue(True) - return True - - def GetCookieManager(self, browser, mainUrl): - # Create unique cookie manager for each browser. - # You must set the "unique_request_context_per_browser" - # application setting to True for the cookie manager - # to work. - # Return None to have one global cookie manager for - # all CEF browsers. - if not browser: - # The browser param may be empty in some exceptional - # case, see docs. - return None - cookieManager = browser.GetUserData("cookieManager") - if cookieManager: - return cookieManager - else: - print("[wxpython.py] RequestHandler::GetCookieManager():"\ - " created cookie manager") - cookieManager = cefpython.CookieManager.CreateManager("") - if "cache_path" in g_applicationSettings: - path = g_applicationSettings["cache_path"] - # path = os.path.join(path, "cookies_browser_{}".format( - # browser.GetIdentifier())) - cookieManager.SetStoragePath(path) - browser.SetUserData("cookieManager", cookieManager) - return cookieManager - - def OnProtocolExecution(self, browser, url, allowExecutionOut): - # There's no default implementation for OnProtocolExecution on Linux, - # you have to make OS system call on your own. You probably also need - # to use LoadHandler::OnLoadError() when implementing this on Linux. - print("[wxpython.py] RequestHandler::OnProtocolExecution()") - print(" url = %s" % url) - if url.startswith("magnet:"): - print("[wxpython.py] Magnet link allowed!") - allowExecutionOut[0] = True - - def _OnBeforePluginLoad(self, browser, mimeType, pluginUrl, topOriginUrl, - info): - # This is a global callback set using SetGlobalClientCallback(). - # Plugins are loaded on demand, only when website requires it, - # the same plugin may be called multiple times. - # This callback is called on various threads, thus print messages - # may not be visible. - print("[wxpython.py] RequestHandler::_OnBeforePluginLoad()") - print(" mimeType = %s" % mimeType) - print(" pluginUrl = %s" % pluginUrl) - print(" topOriginUrl = %s" % topOriginUrl) - print(" info.GetName() = %s" % info.GetName()) - print(" info.GetPath() = %s" % info.GetPath()) - print(" info.GetVersion() = %s" % info.GetVersion()) - print(" info.GetDescription() = %s" % info.GetDescription()) - # False to allow, True to block plugin. - return False - - def _OnCertificateError(self, certError, requestUrl, callback): - # This is a global callback set using SetGlobalClientCallback(). - print("[wxpython.py] RequestHandler::_OnCertificateError()") - print(" certError = %s" % certError) - print(" requestUrl = %s" % requestUrl) - if requestUrl == "https://testssl-expire.disig.sk/index.en.html": - print(" Not allowed!") - return False - if requestUrl \ - == "https://testssl-expire.disig.sk/index.en.html?allow=1": - print(" Allowed!") - callback.Continue(True) - return True - return False - - def OnRendererProcessTerminated(self, browser, status): - print("[wxpython.py] RequestHandler::OnRendererProcessTerminated()") - statuses = { - cefpython.TS_ABNORMAL_TERMINATION: "TS_ABNORMAL_TERMINATION", - cefpython.TS_PROCESS_WAS_KILLED: "TS_PROCESS_WAS_KILLED", - cefpython.TS_PROCESS_CRASHED: "TS_PROCESS_CRASHED" - } - statusName = "Unknown" - if status in statuses: - statusName = statuses[status] - print(" status = %s" % statusName) - - def OnPluginCrashed(self, browser, pluginPath): - print("[wxpython.py] RequestHandler::OnPluginCrashed()") - print(" plugin path = %s" % pluginPath) - - # ------------------------------------------------------------------------- - # LoadHandler - # ------------------------------------------------------------------------- - - def OnLoadingStateChange(self, browser, isLoading, canGoBack, - canGoForward): - print("[wxpython.py] LoadHandler::OnLoadingStateChange()") - print(" isLoading = %s, canGoBack = %s, canGoForward = %s" \ - % (isLoading, canGoBack, canGoForward)) - - def OnLoadStart(self, browser, frame): - print("[wxpython.py] LoadHandler::OnLoadStart()") - print(" frame url = %s" % frame.GetUrl()[:100]) - - def OnLoadEnd(self, browser, frame, httpStatusCode): - print("[wxpython.py] LoadHandler::OnLoadEnd()") - print(" frame url = %s" % frame.GetUrl()[:100]) - # For file:// urls the status code = 0 - print(" http status code = %s" % httpStatusCode) - # Tests for the Browser object methods - self._Browser_LoadUrl(browser) - - def _Browser_LoadUrl(self, browser): - if browser.GetUrl() == "data:text/html,Test#Browser.LoadUrl": - browser.LoadUrl("file://"+GetApplicationPath("wxpython.html")) - - def OnLoadError(self, browser, frame, errorCode, errorTextList, failedUrl): - print("[wxpython.py] LoadHandler::OnLoadError()") - print(" frame url = %s" % frame.GetUrl()[:100]) - print(" error code = %s" % errorCode) - print(" error text = %s" % errorTextList[0]) - print(" failed url = %s" % failedUrl) - # Handle ERR_ABORTED error code, to handle the following cases: - # 1. Esc key was pressed which calls browser.StopLoad() in OnKeyEvent - # 2. Download of a file was aborted - # 3. Certificate error - if errorCode == cefpython.ERR_ABORTED: - print("[wxpython.py] LoadHandler::OnLoadError(): Ignoring load " - "error: Esc was pressed or file download was aborted, " - "or there was certificate error") - return; - customErrorMessage = "My custom error message!" - frame.LoadUrl("data:text/html,%s" % customErrorMessage) - - # ------------------------------------------------------------------------- - # LifespanHandler - # ------------------------------------------------------------------------- - - # ** This callback is executed on the IO thread ** - # Empty place-holders: popupFeatures, client. - def OnBeforePopup(self, browser, frame, targetUrl, targetFrameName, - targetDisposition, userGesture, - popupFeatures, windowInfo, client, browserSettings, - noJavascriptAccess): - print("[wxpython.py] LifespanHandler::OnBeforePopup()") - print(" targetUrl = %s" % targetUrl) - - # Custom browser settings for popups: - # > browserSettings[0] = {"plugins_disabled": True} - - # Set WindowInfo object: - # > windowInfo[0] = cefpython.WindowInfo() - - # On Windows there are keyboard problems in popups, when popup - # is created using "window.open" or "target=blank". This issue - # occurs only in wxPython. PyGTK or PyQt do not require this fix. - # The solution is to create window explicitilly, and not depend - # on CEF to create window internally. See Issue 80 for details: - # https://github.com/cztomczak/cefpython/issues/80 - - # If you set allowPopups=True then CEF will create popup window. - # The wx.Frame cannot be created here, as this callback is - # executed on the IO thread. Window should be created on the UI - # thread. One solution is to call cefpython.CreateBrowser() - # which runs asynchronously and can be called on any thread. - # The other solution is to post a task on the UI thread, so - # that cefpython.CreateBrowserSync() can be used. - - # Note that if you return True and create the popup window yourself, - # then the popup window and parent window will not be able to script - # each other. There will be no "window.opener" property available - # in the popup window. - - cefpython.PostTask(cefpython.TID_UI, self._CreatePopup, targetUrl) - - allowPopups = False - return not allowPopups - - def _CreatePopup(self, url): - frame = MainFrame(url=url, popup=True) - frame.Show() - - def _OnAfterCreated(self, browser): - # This is a global callback set using SetGlobalClientCallback(). - print("[wxpython.py] LifespanHandler::_OnAfterCreated()") - print(" browserId=%s" % browser.GetIdentifier()) - - def DoClose(self, browser): - print("[wxpython.py] LifespanHandler::DoClose()") - print(" browserId=%s" % browser.GetIdentifier()) - - def OnBeforeClose(self, browser): - print("[wxpython.py] LifespanHandler::OnBeforeClose") - print(" browserId=%s" % browser.GetIdentifier()) - - # ------------------------------------------------------------------------- - # JavascriptDialogHandler - # ------------------------------------------------------------------------- - - def OnJavascriptDialog(self, browser, originUrl, dialogType, - messageText, defaultPromptText, callback, - suppressMessage): - print("[wxpython.py] JavascriptDialogHandler::OnJavascriptDialog()") - print(" originUrl="+originUrl) - print(" dialogType="+str(dialogType)) - print(" messageText="+messageText) - print(" defaultPromptText="+defaultPromptText) - # If you want to suppress the javascript dialog: - # suppressMessage[0] = True - return False - - def OnBeforeUnloadJavascriptDialog(self, browser, messageText, isReload, - callback): - print("[wxpython.py] OnBeforeUnloadJavascriptDialog()") - print(" messageText="+messageText) - print(" isReload="+str(isReload)) - # Return True if the application will use a custom dialog: - # callback.Continue(allow=True, userInput="") - # return True - return False - - def OnResetJavascriptDialogState(self, browser): - print("[wxpython.py] OnResetDialogState()") - - def OnJavascriptDialogClosed(self, browser): - print("[wxpython.py] OnDialogClosed()") - - -class MyApp(wx.App): - timer = None - timerID = 1 - mainFrame = None - - def OnInit(self): - if not USE_EVT_IDLE: - print("[wxpython.py] Using TIMER to run CEF message loop") - self.CreateTimer() - self.mainFrame = MainFrame() - self.SetTopWindow(self.mainFrame) - self.mainFrame.Show() - return True - - def CreateTimer(self): - # See "Making a render loop": - # http://wiki.wxwidgets.org/Making_a_render_loop - # Another approach is to use EVT_IDLE in MainFrame, - # see which one fits you better. - self.timer = wx.Timer(self, self.timerID) - self.timer.Start(10) # 10ms - wx.EVT_TIMER(self, self.timerID, self.OnTimer) - - def OnTimer(self, event): - cefpython.MessageLoopWork() - - def OnExit(self): - # When app.MainLoop() returns, MessageLoopWork() should - # not be called anymore. - print("[wxpython.py] MyApp.OnExit") - if not USE_EVT_IDLE: - self.timer.Stop() - - -def GetSources(): - # Get sources of all python functions and methods from this file. - # This is to provide sources preview to wxpython.html. - # The dictionary of functions is binded to "window.sources". - thisModule = sys.modules[__name__] - functions = inspect.getmembers(thisModule, inspect.isfunction) - classes = inspect.getmembers(thisModule, inspect.isclass) - sources = {} - for funcTuple in functions: - sources[funcTuple[0]] = inspect.getsource(funcTuple[1]) - for classTuple in classes: - className = classTuple[0] - classObject = classTuple[1] - methods = inspect.getmembers(classObject) - for methodTuple in methods: - try: - sources[methodTuple[0]] = inspect.getsource(\ - methodTuple[1]) - except: - pass - return sources - - -if __name__ == '__main__': - print('[wxpython.py] architecture=%s-bit' % (8 * struct.calcsize("P"))) - print('[wxpython.py] wx.version=%s' % wx.version()) - - # Intercept python exceptions. Exit app immediately when exception - # happens on any of the threads. - sys.excepthook = ExceptHook - - # Application settings - g_applicationSettings = { - # Disk cache - # "cache_path": "webcache/", - - # CEF Python debug messages in console and in log_file - "debug": True, - # Set it to LOGSEVERITY_VERBOSE for more details - "log_severity": cefpython.LOGSEVERITY_INFO, - # Set to "" to disable logging to a file - "log_file": GetApplicationPath("debug.log"), - - # These directories must be set on Linux - "locales_dir_path": cefpython.GetModuleDirectory()+"/locales", - "resources_dir_path": cefpython.GetModuleDirectory(), - # The "subprocess" executable that launches the Renderer - # and GPU processes among others. You may rename that - # executable if you like. - "browser_subprocess_path": "%s/%s" % ( - cefpython.GetModuleDirectory(), "subprocess"), - - # This option is required for the GetCookieManager callback - # to work. It affects renderer processes, when this option - # is set to True. It will force a separate renderer process - # for each browser created using CreateBrowserSync. - "unique_request_context_per_browser": True, - # Downloads are handled automatically. A default SaveAs file - # dialog provided by OS will be displayed. - - "downloads_enabled": True, - # Remote debugging port, required for Developer Tools support. - # A value of 0 will generate a random port. To disable devtools - # support set it to -1. - "remote_debugging_port": 0, - # Mouse context menu - "context_menu": { - "enabled": True, - "navigation": True, # Back, Forward, Reload - "print": True, - "view_source": True, - "external_browser": True, # Open in external browser - "devtools": True, # Developer Tools - }, - - # See also OnCertificateError which allows you to ignore - # certificate errors for specific websites. - "ignore_certificate_errors": True, - } - - # You can comment out the code below if you do not want High - # DPI support. If you disable it text will look fuzzy on - # high DPI displays. - # - # Enabling High DPI support in app can be done by - # embedding a DPI awareness xml manifest in executable - # (see Issue 112 comment #2), or by calling SetProcessDpiAware - # function. Embedding xml manifest is the most reliable method. - # The downside of calling SetProcessDpiAware is that scrollbar - # in CEF browser is smaller than it should be. This is because - # DPI awareness was set too late, after the CEF dll was loaded. - # To fix that embed DPI awareness xml manifest in the .exe file. - # - # There is one bug when enabling High DPI support - fonts in - # javascript dialogs (alert) are tiny. However, you can implement - # custom javascript dialogs using JavascriptDialogHandler. - # - # Additionally you have to set "auto_zomming" application - # setting. High DPI support is available only on Windows. - # You may set auto_zooming to "system_dpi" and browser - # contents will be zoomed using OS DPI settings. On Win7 - # these can be set in: Control Panel > Appearance and - # Personalization > Display. - # - # Example values for auto_zooming are: - # "system_dpi", "0.0" (96 DPI), "1.0" (120 DPI), - # "2.0" (144 DPI), "-1.0" (72 DPI) - # Numeric value means a zoom level. - # Example values that can be set in Win7 DPI settings: - # Smaller 100% (Default) = 96 DPI = 0.0 zoom level - # Medium 125% = 120 DPI = 1.0 zoom level - # Larger 150% = 144 DPI = 2.0 zoom level - # Custom 75% = 72 DPI = -1.0 zoom level - g_applicationSettings["auto_zooming"] = "system_dpi" - print("[wxpython.py] Calling SetProcessDpiAware") - cefpython.DpiAware.SetProcessDpiAware() - - # Browser settings. You may have different settings for each - # browser, see the call to CreateBrowserSync. - g_browserSettings = { - # "plugins_disabled": True, - # "file_access_from_file_urls_allowed": True, - # "universal_access_from_file_urls_allowed": True, - } - - # Command line switches set programmatically - g_commandLineSwitches = { - # "proxy-server": "socks5://127.0.0.1:8888", - # "no-proxy-server": "", - # "enable-media-stream": "", - # "disable-gpu": "", - - } - - cefpython.Initialize(g_applicationSettings, g_commandLineSwitches) - - app = MyApp(False) - app.MainLoop() - - # Let wx.App destructor do the cleanup before calling - # cefpython.Shutdown(). This is to ensure reliable CEF shutdown. - del app - - cefpython.Shutdown() - diff --git a/src/windows/cefpython.rc b/src/windows/cefpython.rc deleted file mode 100644 index feb9c0a5..00000000 --- a/src/windows/cefpython.rc +++ /dev/null @@ -1,31 +0,0 @@ -1 VERSIONINFO -FILEVERSION 31,2,0,0 -PRODUCTVERSION 31,2,0,0 -FILEFLAGSMASK 0x3FL -FILEFLAGS 0x0L -FILEOS 0x4L -FILETYPE 0x2L -FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", "CEF Python" - VALUE "FileDescription", "CEF Python DLL" - VALUE "FileVersion", "31.2.0.0" - VALUE "InternalName", "cefpython" - VALUE "LegalCopyright", "(c) 2012 The CEF Python authors" - VALUE "LegalTrademarks", "" - VALUE "OriginalFilename", "cefpython_py27.pyd" - VALUE "ProductName", "CEF Python 3" - VALUE "ProductVersion", "31.2.0.0" - VALUE "Comments", "Licensed under the BSD 3-clause license" - VALUE "Aditional Notes", "" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END diff --git a/src/windows/compile.cmd b/src/windows/compile.cmd deleted file mode 100644 index 1e7613c0..00000000 --- a/src/windows/compile.cmd +++ /dev/null @@ -1 +0,0 @@ -cmd.exe /K compile.bat 99.99 diff --git a/src/windows/binaries_32bit/LICENSE.txt b/src/windows/deprecated_32bit/LICENSE.txt similarity index 100% rename from src/windows/binaries_32bit/LICENSE.txt rename to src/windows/deprecated_32bit/LICENSE.txt diff --git a/src/windows/binaries_32bit/README.txt b/src/windows/deprecated_32bit/README.txt similarity index 100% rename from src/windows/binaries_32bit/README.txt rename to src/windows/deprecated_32bit/README.txt diff --git a/src/windows/binaries_32bit/cefwindow.py b/src/windows/deprecated_32bit/cefwindow.py similarity index 100% rename from src/windows/binaries_32bit/cefwindow.py rename to src/windows/deprecated_32bit/cefwindow.py diff --git a/src/windows/binaries_32bit/example.html b/src/windows/deprecated_32bit/example.html similarity index 100% rename from src/windows/binaries_32bit/example.html rename to src/windows/deprecated_32bit/example.html diff --git a/src/windows/binaries_32bit/icon.ico b/src/windows/deprecated_32bit/icon.ico similarity index 100% rename from src/windows/binaries_32bit/icon.ico rename to src/windows/deprecated_32bit/icon.ico diff --git a/src/mac/binaries_32bit/prism.css b/src/windows/deprecated_32bit/prism.css similarity index 100% rename from src/mac/binaries_32bit/prism.css rename to src/windows/deprecated_32bit/prism.css diff --git a/src/mac/binaries_32bit/prism.js b/src/windows/deprecated_32bit/prism.js similarity index 100% rename from src/mac/binaries_32bit/prism.js rename to src/windows/deprecated_32bit/prism.js diff --git a/src/windows/binaries_32bit/pygtk_.py b/src/windows/deprecated_32bit/pygtk_.py similarity index 100% rename from src/windows/binaries_32bit/pygtk_.py rename to src/windows/deprecated_32bit/pygtk_.py diff --git a/src/windows/binaries_32bit/pyqt.py b/src/windows/deprecated_32bit/pyqt.py similarity index 100% rename from src/windows/binaries_32bit/pyqt.py rename to src/windows/deprecated_32bit/pyqt.py diff --git a/src/windows/binaries_32bit/pyside.py b/src/windows/deprecated_32bit/pyside.py similarity index 100% rename from src/windows/binaries_32bit/pyside.py rename to src/windows/deprecated_32bit/pyside.py diff --git a/src/windows/binaries_32bit/pywin32.py b/src/windows/deprecated_32bit/pywin32.py similarity index 100% rename from src/windows/binaries_32bit/pywin32.py rename to src/windows/deprecated_32bit/pywin32.py diff --git a/src/windows/binaries_32bit/smoke.css b/src/windows/deprecated_32bit/smoke.css similarity index 100% rename from src/windows/binaries_32bit/smoke.css rename to src/windows/deprecated_32bit/smoke.css diff --git a/src/windows/binaries_32bit/smoke.min.js b/src/windows/deprecated_32bit/smoke.min.js similarity index 100% rename from src/windows/binaries_32bit/smoke.min.js rename to src/windows/deprecated_32bit/smoke.min.js diff --git a/src/windows/binaries_32bit/wxpython.html b/src/windows/deprecated_32bit/wxpython.html similarity index 100% rename from src/windows/binaries_32bit/wxpython.html rename to src/windows/deprecated_32bit/wxpython.html diff --git a/src/windows/binaries_32bit/wxpython.py b/src/windows/deprecated_32bit/wxpython.py similarity index 100% rename from src/windows/binaries_32bit/wxpython.py rename to src/windows/deprecated_32bit/wxpython.py diff --git a/src/windows/setup/.gitignore b/src/windows/setup/.gitignore deleted file mode 100644 index d4b2c081..00000000 --- a/src/windows/setup/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.cpp -*.pyx - diff --git a/src/windows/setup/cefpython.h b/src/windows/setup/cefpython.h deleted file mode 100644 index 180a379b..00000000 --- a/src/windows/setup/cefpython.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma warning(disable:4190) - -#ifndef __PYX_HAVE__cefpython_py27 -#define __PYX_HAVE__cefpython_py27 - - -#ifndef __PYX_HAVE_API__cefpython_py27 - -#ifndef __PYX_EXTERN_C - #ifdef __cplusplus - #define __PYX_EXTERN_C extern "C" - #else - #define __PYX_EXTERN_C extern - #endif -#endif - -__PYX_EXTERN_C DL_IMPORT(void) PyBrowser_ShowDevTools(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) PyTaskRunnable(int); -__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextCreated(CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextReleased(int, int64); -__PYX_EXTERN_C DL_IMPORT(void) V8FunctionHandler_Execute(CefRefPtr, CefRefPtr, CefString &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) RemovePythonCallbacksForFrame(int); -__PYX_EXTERN_C DL_IMPORT(bool) ExecutePythonCallback(CefRefPtr, int, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) LifespanHandler_OnBeforePopup(CefRefPtr, CefRefPtr, CefString const &, CefString const &, int const , CefWindowInfo &, CefRefPtr &, CefBrowserSettings &, bool *); -__PYX_EXTERN_C DL_IMPORT(void) LifespanHandler_OnAfterCreated(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) LifespanHandler_DoClose(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) LifespanHandler_OnBeforeClose(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) DisplayHandler_OnAddressChange(CefRefPtr, CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) DisplayHandler_OnTitleChange(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(bool) DisplayHandler_OnTooltip(CefRefPtr, CefString &); -__PYX_EXTERN_C DL_IMPORT(void) DisplayHandler_OnStatusMessage(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(bool) DisplayHandler_OnConsoleMessage(CefRefPtr, CefString const &, CefString const &, int); -__PYX_EXTERN_C DL_IMPORT(bool) KeyboardHandler_OnPreKeyEvent(CefRefPtr, CefKeyEvent const &, CefEventHandle, bool *); -__PYX_EXTERN_C DL_IMPORT(bool) KeyboardHandler_OnKeyEvent(CefRefPtr, CefKeyEvent const &, CefEventHandle); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforeResourceLoad(CefRefPtr, CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforeBrowse(CefRefPtr, CefRefPtr, CefRefPtr, bool); -__PYX_EXTERN_C DL_IMPORT(CefRefPtr) RequestHandler_GetResourceHandler(CefRefPtr, CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnResourceRedirect(CefRefPtr, CefRefPtr, CefString const &, CefString &); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_GetAuthCredentials(CefRefPtr, CefRefPtr, bool, CefString const &, int, CefString const &, CefString const &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnQuotaRequest(CefRefPtr, CefString const &, int64, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(CefRefPtr) RequestHandler_GetCookieManager(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnProtocolExecution(CefRefPtr, CefString const &, bool &); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnBeforePluginLoad(CefRefPtr, CefString const &, CefString const &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RequestHandler_OnCertificateError(int, CefString const &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnRendererProcessTerminated(CefRefPtr, enum cef_termination_status_t); -__PYX_EXTERN_C DL_IMPORT(void) RequestHandler_OnPluginCrashed(CefRefPtr, CefString const &); -__PYX_EXTERN_C DL_IMPORT(bool) CookieVisitor_Visit(int, CefCookie const &, int, int, bool &); -__PYX_EXTERN_C DL_IMPORT(void) StringVisitor_Visit(int, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadingStateChange(CefRefPtr, bool, bool, bool); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadStart(CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadEnd(CefRefPtr, CefRefPtr, int); -__PYX_EXTERN_C DL_IMPORT(void) LoadHandler_OnLoadError(CefRefPtr, CefRefPtr, enum cef_errorcode_t, CefString const &, CefString const &); -__PYX_EXTERN_C DL_IMPORT(void) BrowserProcessHandler_OnRenderProcessThreadCreated(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) BrowserProcessHandler_OnBeforeChildProcessLaunch(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetRootScreenRect(CefRefPtr, CefRect &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetViewRect(CefRefPtr, CefRect &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetScreenRect(CefRefPtr, CefRect &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetScreenPoint(CefRefPtr, int, int, int &, int &); -__PYX_EXTERN_C DL_IMPORT(bool) RenderHandler_GetScreenInfo(CefRefPtr, CefScreenInfo &); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnPopupShow(CefRefPtr, bool); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnPopupSize(CefRefPtr, CefRect const &); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnPaint(CefRefPtr, cef_paint_element_type_t, std::vector &, void const *, int, int); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnCursorChange(CefRefPtr, CefCursorHandle); -__PYX_EXTERN_C DL_IMPORT(void) RenderHandler_OnScrollOffsetChanged(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_ProcessRequest(int, CefRefPtr, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) ResourceHandler_GetResponseHeaders(int, CefRefPtr, int64 &, CefString &); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_ReadResponse(int, void *, int, int &, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_CanGetCookie(int, CefCookie const &); -__PYX_EXTERN_C DL_IMPORT(bool) ResourceHandler_CanSetCookie(int, CefCookie const &); -__PYX_EXTERN_C DL_IMPORT(void) ResourceHandler_Cancel(int); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnUploadProgress(int, CefRefPtr, uint64, uint64); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnDownloadProgress(int, CefRefPtr, uint64, uint64); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnDownloadData(int, CefRefPtr, void const *, size_t); -__PYX_EXTERN_C DL_IMPORT(void) WebRequestClient_OnRequestComplete(int, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) App_OnBeforeCommandLineProcessing_BrowserProcess(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(bool) JavascriptDialogHandler_OnJavascriptDialog(CefRefPtr, CefString const &, CefString const &, enum cef_jsdialog_type_t, CefString const &, CefString const &, CefRefPtr, bool &); -__PYX_EXTERN_C DL_IMPORT(bool) JavascriptDialogHandler_OnBeforeUnloadJavascriptDialog(CefRefPtr, CefString const &, bool, CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) JavascriptDialogHandler_OnResetJavascriptDialogState(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) JavascriptDialogHandler_OnJavascriptDialogClosed(CefRefPtr); -__PYX_EXTERN_C DL_IMPORT(void) cefpython_GetDebugOptions(bool *, std::string *); -__PYX_EXTERN_C DL_IMPORT(bool) ApplicationSettings_GetBool(char const *); -__PYX_EXTERN_C DL_IMPORT(bool) ApplicationSettings_GetBoolFromDict(char const *, char const *); -__PYX_EXTERN_C DL_IMPORT(std::string) ApplicationSettings_GetString(char const *); -__PYX_EXTERN_C DL_IMPORT(int) CommandLineSwitches_GetInt(char const *); - -#endif /* !__PYX_HAVE_API__cefpython_py27 */ - -#if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC initcefpython_py27(void); -#else -PyMODINIT_FUNC PyInit_cefpython_py27(void); -#endif - -#endif /* !__PYX_HAVE__cefpython_py27 */ diff --git a/src/windows/setup/compile_rc.py b/src/windows/setup/compile_rc.py deleted file mode 100644 index 8d53d8e2..00000000 --- a/src/windows/setup/compile_rc.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# Compile cefptython.rc to a .res object. -# In setup.py the .res object is added to Extension."extra_objects". - -import os -import sys -import re -import subprocess -import shutil - -PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) # eg. "27" -RC_EXE = r"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\rc.exe" -RC_FILE = os.path.abspath(r"../cefpython.rc") -RES_FILE_OUT = os.path.abspath(r"../cefpython.res") -RES_FILE_MOVE = os.path.abspath(r"./cefpython.res") -RC_PYD_NAME = r"cefpython_py27.pyd" - -def log(msg): - print("[compile_rc.py] %s" % str(msg)) - -def main(): - # Arguments - if len(sys.argv) == 3 \ - and sys.argv[1] == "-v" \ - and re.search(r"^\d+\.\d+$", sys.argv[2]): - version = sys.argv[2] - else: - log("Invalid version string or missing. Usage: compile_rc.py -v 31.0") - exit(1) - - # Print paths - log("version="+version) - log("PYVERSION="+PYVERSION) - log("RC_EXE="+RC_EXE) - log("RC_FILE="+RC_FILE) - log("RES_FILE_OUT="+RES_FILE_OUT) - log("RES_FILE_MOVE="+RES_FILE_MOVE) - log("RC_PYD_NAME="+RC_PYD_NAME) - - # Check paths - assert os.path.exists(RC_EXE) - assert os.path.exists(RC_FILE) - - # Change version numbers in .rc file - with open(RC_FILE, "r") as f: - contents = f.read() - # FILEVERSION 31,1,0,0 - # "FileVersion", "31.1.0.0" - (contents, subn) = re.subn( - r"\d+\.\d+\.\d+\.\d+", - r"%s.0.0" % version, - contents) - assert subn == 2, "Replacing dots versions failed (rc file)" - version_commas = re.sub(r"\.", r",", version) - (contents, subn) = re.subn( - r"\d+,\d+,\d+,\d+", - r"%s,0,0" % version_commas, - contents) - assert subn == 2, "Replacing commas verions failed (rc file)" - - # Change pyd module name in .rc - assert contents.find(RC_PYD_NAME) != -1, "pyd file name not found in .rc" - assert RC_PYD_NAME.find("py27") != -1, "invalid pyd file name defined" - new_pyd_name = RC_PYD_NAME.replace("py27", "py"+PYVERSION) - contents = contents.replace(RC_PYD_NAME, new_pyd_name) - - # Save modified .rc file - log("Saving modified %s" % RC_FILE) - with open(RC_FILE, "w") as f: - f.write(contents) - - log("Calling rc.exe to compile .rc file") - # rc.exe usage: rc.exe /x file.rc - # /x - ignore INCLUDE environment variable - exit_status = subprocess.call([ - RC_EXE, - "/x", - RC_FILE, - ], shell=True) - if exit_status != 0: - raise Exception("Calling rc.exe failed") - - log("Moving .res object to setup/") - shutil.move(RES_FILE_OUT, RES_FILE_MOVE) - - -if __name__ == '__main__': - main() diff --git a/src/windows/setup/delete_pyx_files.bat b/src/windows/setup/delete_pyx_files.bat deleted file mode 100644 index ae1c5fbd..00000000 --- a/src/windows/setup/delete_pyx_files.bat +++ /dev/null @@ -1,2 +0,0 @@ -for /R %~dp0 %%f in (*.pyx) do del "%%f" -pause \ No newline at end of file diff --git a/src/windows/setup/fix_cefpython_h.py b/src/windows/setup/fix_cefpython_h.py deleted file mode 100644 index dbcb535e..00000000 --- a/src/windows/setup/fix_cefpython_h.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Get rid of warnings like this: - - cefpython.h(36) : warning C4190: 'RequestHandler_GetResourceHandler' - has C-linkage specified, but returns UDT 'CefRefPtr' which is - incompatible with C -""" - -import os - - -def main(): - if not os.path.exists("cefpython.h"): - print("[fix_cefpython_h.py] cefpython.h was not yet generated") - return - with open("cefpython.h", "r") as fo: - content = fo.read() - pragma = "#pragma warning(disable:4190)" - if pragma in content: - print("[fix_cefpython_h.py] cefpython.h is already fixed") - return - content = ("%s\n\n" % (pragma)) + content - with open("cefpython.h", "w") as fo: - fo.write(content) - print("[fix_cefpython_h.py] Saved cefpthon.h") - - -if __name__ == '__main__': - main() diff --git a/src/windows/setup/fix_pyx_files.py b/src/windows/setup/fix_pyx_files.py deleted file mode 100644 index 86ba1dbc..00000000 --- a/src/windows/setup/fix_pyx_files.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# First, it copies all .pyx files from upper directory to setup/. -# Then, fixes repeating of "include" statements in pyx files. - -# Only the mainfile needs to have "include" statements, -# but we're using PyCharm and to get rid of "unresolved references" -# and other errors displayed in pycharm we are adding "include" -# statements in all of the pyx files. - -# I'm not 100% sure how includes work in Cython, but I suspect that -# a few includes of the same file will include the same content more -# than once, it should work, but function and variable definitions are -# duplicated, it is some kind of overhead and it could lead to some -# problems in the future, better to fix it now. - -# It also checks cdef & cpdef functions whether they are not missing "except *", -# it is required to add it when returning non-python type. - -import glob -import os -import re -import shutil -import sys - -def ExceptAllMissing(content): - - # This is not perfect, won't detect C++ custom types, but will find - # the built-in types, templates and pointers. - patterns = [] - patterns.append( - r"\bcp?def\s+" - "((int|short|long|double|char|unsigned|float|double)\s+)+" - "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") - patterns.append( - r"\bcp?def\s+" - # A template ends with bracket: CefRefPtr[CefBrowser] - # or a pointer ends with asterisk: CefBrowser* - "[^\s]+[\]*]\s+" - "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") - patterns.append( - r"\bcp?def\s+" - # A reference, eg. CefString& - "[^\s]+&\s+" - "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") - - for pattern in patterns: - match = re.search(pattern, content) - if match: break - - if match: - lineNumber = (content.count("\n", 0, match.start()) + 1) - return lineNumber - -print("\n") -mainfile = "cefpython.pyx" - -pyxfiles = glob.glob("../../*.pyx") -if not len(pyxfiles): - sys.exit(1) -pyxfiles = [file for file in pyxfiles if file.find(mainfile) == -1] -# Now, pyxfiles contains all pyx files except the mainfile (cefpython.pyx), -# we do not fix includes in mainfile. - -# So that this is the right directory we're in. -if os.path.exists("setup"): - print("Wrong directory, we should be inside setup!") - sys.exit(1) - -# Remove old pyx files in setup directory. -oldpyxfiles = glob.glob("./*.pyx") -print("Removing old pyx files in /setup/: %s" % oldpyxfiles) -for pyxfile in oldpyxfiles: - if os.path.exists(pyxfile): - os.remove(pyxfile) - -# Copying pyxfiles and reading its contents. - -print("Copying .pyx files to /setup/: %s" % pyxfiles) -shutil.copy("../../%s" % mainfile, "./%s" % mainfile) -# Rest of the files will be copied in for loop below. - -print("Fixing includes in .pyx files:") -for pyxfile in pyxfiles: - newfile = "./%s" % os.path.basename(pyxfile) - shutil.copy(pyxfile, newfile) - pyxfile = newfile - with open(pyxfile, "r") as pyxfileopened: - content = pyxfileopened.read() - lineNumber = ExceptAllMissing(content) - if lineNumber: - print("WARNING: 'except *' missing in a cdef/cpdef function, " - "in file %s on line %d" % (os.path.basename(pyxfile), lineNumber)) - sys.exit(1) - # Do not remove the newline - so that line numbers are exact with originals. - (content, subs) = re.subn(r"^include[\t ]+[\"'][^\"'\n\r]+[\"'][\t ]*", "", content, flags=re.MULTILINE) - if subs: - print("%s includes removed in: %s" % (subs, os.path.basename(pyxfile))) - # Reading and writing with the same handle using "r+" mode doesn't work, - # you need to seek(0) and write the same amount of bytes that was in the - # file, otherwise old data from the end of file stays. - with open(pyxfile, "w") as pyxfileopened: - pyxfileopened.write(content) - -print("\n") diff --git a/src/windows/setup/lib_32bit/README b/src/windows/setup/lib_32bit/README deleted file mode 100644 index 72d1c607..00000000 --- a/src/windows/setup/lib_32bit/README +++ /dev/null @@ -1 +0,0 @@ -Put libcef.lib and libcef_dll_wrapper.lib here. diff --git a/src/windows/setup/lib_64bit/README b/src/windows/setup/lib_64bit/README deleted file mode 100644 index 72d1c607..00000000 --- a/src/windows/setup/lib_64bit/README +++ /dev/null @@ -1 +0,0 @@ -Put libcef.lib and libcef_dll_wrapper.lib here. diff --git a/src/windows/setup/setup.py b/src/windows/setup/setup.py deleted file mode 100644 index d4044215..00000000 --- a/src/windows/setup/setup.py +++ /dev/null @@ -1,115 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension - -from Cython.Distutils import build_ext -from Cython.Compiler import Options -import Cython - -import sys -import platform -import struct -import os - -BITS = str(8 * struct.calcsize('P')) + "bit" - -print("[setup.py] Python architecture: %s" % BITS) -print("[setup.py] Cython version: %s" % Cython.__version__) - -# Stop on first error, otherwise hundreds of errors appear in the console. -Options.fast_fail = True - -# Python version string: "27" or "32". -PYVER = str(sys.version_info.major) + str(sys.version_info.minor) - -# Generate compile_time_constants.pxi -def CompileTimeConstants(): - print("[setup.py] Generating: compile_time_constants.pxi") - with open("../../compile_time_constants.pxi", "w") as fd: - fd.write('# This file was generated by setup.py\n') - # A way around Python 3.2 bug: UNAME_SYSNAME is not set. - fd.write('DEF UNAME_SYSNAME = "%s"\n' % platform.uname()[0]) - fd.write('DEF PY_MAJOR_VERSION = %s\n' % sys.version_info.major) -CompileTimeConstants() - -# Windows SDK Lib directory. -# It's also hardcoded in compile.bat -if BITS == "32bit": - winsdk_lib = r"C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib" -elif BITS == "64bit": - winsdk_lib = r"C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\x64" -if not os.path.exists(winsdk_lib): - raise Exception("Windows SDK Lib directory not found: %s" % winsdk_lib) - -ext_modules = [Extension( - - "cefpython_py%s" % PYVER, - ["cefpython.pyx"], - - # Ignore the warning in the console: - # > C:\Python27\lib\distutils\extension.py:133: UserWarning: - # > Unknown Extension options: 'cython_directives' warnings.warn(msg) - cython_directives={ - # Any conversion to unicode must be explicit using .decode(). - "c_string_type": "bytes", - "c_string_encoding": "utf-8", - }, - - language='c++', - - include_dirs=[ - r'./../', - r'./../../', - r'./../../extern/', - r'./../../extern/cef/', - ], - - library_dirs=[ - winsdk_lib, - r'./', - r'./lib_%s' % (BITS), - r'./../../client_handler/Release_py%s_%s/' % (PYVER, BITS), - r'./../../subprocess/Release_py%s_%s/' % (PYVER, BITS), - r'./../../cpp_utils/Release_%s/' % (BITS), - ], - - libraries=[ - 'libcef', - 'libcef_dll_wrapper_md', - 'User32', - 'client_handler_py%s_%s' % (PYVER, BITS), - 'libcefpythonapp_py%s_%s' % (PYVER, BITS), - 'cpp_utils_%s' % (BITS), - ], - - extra_objects=[ - "cefpython.res" - ], - - # /EHsc - using STL string, multimap and others that use C++ exceptions. - # - # /ignore:4217 - disable warnings such as this: - # - # client_handler_py27_32bit.lib(client_handler.obj) : warning LNK4217: - # locally defined symbol _RemovePythonCallbacksForFrame imported in - # function "public: virtual bool __thiscall - # ClientHandler::OnProcessMessageReceived - # - # The above warning LNK4217 is caused by the warning below which occurs - # when building the client_handler.lib static library: - # - # cefpython.h(36) : warning C4190: 'RequestHandler_GetResourceHandler' - # has C-linkage specified, but returns UDT 'CefRefPtr' which is - # incompatible with C - # - # The C4190 warning is disabled with pragma in cefpython.h, see the - # fix_cefpython_h.py script. - - extra_compile_args=['/EHsc'], - extra_link_args=['/ignore:4217'] -)] - -setup( - name = 'cefpython_py%s' % PYVER, - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/src/windows/wxpython.bat b/src/windows/wxpython.bat deleted file mode 100644 index a5b131aa..00000000 --- a/src/windows/wxpython.bat +++ /dev/null @@ -1,3 +0,0 @@ -cd binaries -call python "wxpython.py" -cd %~dp0 diff --git a/tools/automate.py b/tools/automate.py index 5b5a0667..4da35108 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -36,46 +36,31 @@ """ +from common import * import os import sys import shlex import subprocess import platform import docopt -import struct -import re import stat import glob import shutil import multiprocessing -# CONSTANTS -ARCH32 = (8 * struct.calcsize('P') == 32) -ARCH64 = (8 * struct.calcsize('P') == 64) -OS_POSTFIX = ("win" if platform.system() == "Windows" else - "linux" if platform.system() == "Linux" else - "mac" if platform.system() == "Darwin" else "unknown") -OS_POSTFIX2 = "unknown" -if OS_POSTFIX == "win": - OS_POSTFIX2 = "win32" if ARCH32 else "win64" -elif OS_POSTFIX == "mac": - OS_POSTFIX2 = "mac32" if ARCH32 else "mac64" -elif OS_POSTFIX == "linux": - OS_POSTFIX2 = "linux32" if ARCH32 else "linux64" - CEF_GIT_URL = "https://bitbucket.org/chromiumembedded/cef.git" VS2015_VCVARS = "\"C:\Program Files (x86)\\Microsoft Visual Studio 14.0" \ - "\\VC\\bin\\vcvars32.bat\"" \ + "\\VC\\vcvarsall.bat\" x86" \ if ARCH32 else \ "\"C:\Program Files (x86)\\Microsoft Visual Studio 14.0" \ - "\\VC\\bin\\amd64\\vcvars64.bat\"" + "\\VC\\vcvarsall.bat\" amd64" VS2013_VCVARS = "\"C:\Program Files (x86)\\Microsoft Visual Studio 12.0" \ - "\\VC\\bin\\vcvars32.bat\"" \ + "\\VC\\vcvarsall.bat\" x86" \ if ARCH32 else \ "\"C:\Program Files (x86)\\Microsoft Visual Studio 12.0" \ - "\\VC\\bin\\amd64\\vcvars64.bat\"" + "\\VC\\cvarsall.bat\" amd64" VS2008_VCVARS = "\"%LocalAppData%\\Programs\\Common\\Microsoft" \ "\\Visual C++ for Python\\9.0\\vcvarsall.bat\" x86" \ @@ -94,11 +79,12 @@ class Options(object): no_cef_update = False cef_branch = "" cef_commit = "" + cef_version = "" build_dir = "" cef_build_dir = "" ninja_jobs = None - gyp_generators = "ninja" - gyp_msvs_version = "" + gyp_generators = "ninja" # Even though CEF uses now GN, still some GYP + gyp_msvs_version = "" # env variables are being used. # Internal options depot_tools_dir = "" @@ -122,6 +108,8 @@ def main(): if Options.build_cef: build_cef() + # Build cefclient, cefsimple, ceftests, libcef_dll_wrapper + build_cef_projects() create_prebuilt_binaries() elif Options.prebuilt_cef: prebuilt_cef() @@ -146,6 +134,7 @@ def setup_options(docopt_args): # Use branch/commit from the src/version/cef_version_*.h file Options.cef_branch = get_cefpython_version()["CHROME_VERSION_BUILD"] Options.cef_commit = get_cefpython_version()["CEF_COMMIT_HASH"] + Options.cef_version = get_cefpython_version()["CEF_VERSION"] # --gyp-msvs-version if not Options.gyp_msvs_version: @@ -210,9 +199,37 @@ def build_cef(): # Run automate-git.py run_automate_git() + print("[automate.py] Binary distrib created in %s" + % Options.binary_distrib) + + +def prebuilt_cef(): + """Use prebuilt binaries.""" + + # TODO: Option to download CEF prebuilt binaries from GitHub Releases, + # eg. tag 'upstream-cef47'. + + # Find cef_binary directory in the build directory + if Options.cef_version: + cef_binary = os.path.join(Options.build_dir, + "cef_binary_{cef_version}_*{sep}" + .format(cef_version=Options.cef_version, + sep=os.sep)) + else: + cef_binary = os.path.join(Options.build_dir, + "cef_binary_3.{cef_branch}.*{sep}" + .format(cef_branch=Options.cef_branch, + sep=os.sep)) + dirs = glob.glob(cef_binary) + if len(dirs) == 1: + Options.cef_binary = dirs[0] + else: + print("ERROR: Could not find prebuilt binaries in the build dir.") + print(" Eg. cef_binary_3.2883.1553.g80bd606_windows32/") + sys.exit(1) - # Build cefclient, cefsimple, ceftests, libcef_dll_wrapper build_cef_projects() + create_prebuilt_binaries() def create_cef_directories(): @@ -282,113 +299,168 @@ def update_cef_patches(): def build_cef_projects(): """Build cefclient, cefsimple, ceftests, libcef_dll_wrapper.""" - print("[automate.py] Binary distrib created in %s" - % Options.binary_distrib) print("[automate.py] Building cef projects...") + fix_cef_include_files() + # Find cef_binary directories and create the cef_binary/build/ dir - if platform.system() == "Windows": - files = glob.glob(os.path.join(Options.binary_distrib, - "cef_binary_*_symbols")) - assert len(files) == 1, "More than one dir with release symbols found" - symbols = files[0] - if Options.release_build: - cef_binary = symbols.replace("_release_symbols", "") + if not Options.cef_binary: + if platform.system() == "Windows": + files = glob.glob(os.path.join(Options.binary_distrib, + "cef_binary_*_symbols")) + assert len(files) == 1, ("More than one dir with release" + " symbols found") + symbols = files[0] + if Options.release_build: + cef_binary = symbols.replace("_release_symbols", "") + else: + cef_binary = symbols.replace("_debug_symbols", "") + assert "symbols" not in os.path.basename(cef_binary) else: - cef_binary = symbols.replace("_debug_symbols", "") - assert "symbols" not in os.path.basename(cef_binary) + files = glob.glob(os.path.join(Options.binary_distrib, + "cef_binary_*_"+OS_POSTFIX2)) + assert len(files) == 1, "Error finding binary distrib" + cef_binary = files[0] + assert os.path.exists(cef_binary) + Options.cef_binary = cef_binary + + print("[automate.py] Creating build_cefclient dir in cef_binary dir") + build_cefclient = os.path.join(Options.cef_binary, "build_cefclient") + already_built = False + if os.path.exists(build_cefclient): + already_built = True else: - files = glob.glob(os.path.join(Options.binary_distrib, - "cef_binary_*_"+OS_POSTFIX2)) - assert len(files) == 1, "Error finding binary distrib" - cef_binary = files[0] - assert os.path.exists(cef_binary) - Options.cef_binary = cef_binary - build_cefclient = os.path.join(cef_binary, "build_cefclient") - os.makedirs(build_cefclient) + os.makedirs(build_cefclient) # Build cefclient, cefsimple, ceftests - print("[automate.py] Building cefclient, cefsimple, ceftests ...") - command = "" - if platform.system() == "Windows": - if int(Options.cef_branch) >= 2704: - command += VS2015_VCVARS + " && " - else: - command += VS2013_VCVARS + " && " - command += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ - % Options.build_type - run_command(command, build_cefclient) - print("[automate.py] OK") - # On Linux cannot pass "&&" and run two commands using run_command() - command = "ninja cefclient cefsimple ceftests" - run_command(command, build_cefclient) - print("[automate.py] OK") - if platform.system() == "Windows": - assert(os.path.exists(os.path.join(build_cefclient, - "tests", - "cefclient", - Options.build_type, - "cefclient.exe"))) + if already_built: + print("[automate.py] Already built: cefclient, cefsimple, ceftests") else: - assert (os.path.exists(os.path.join(build_cefclient, - "tests", - "cefclient", - Options.build_type, - "cefclient"))) + print("[automate.py] Building cefclient, cefsimple, ceftests ...") + command = prepare_build_command() + command += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ + % Options.build_type + run_command(command, build_cefclient) + print("[automate.py] OK") + # On Linux cannot pass "&&" and run two commands using run_command() + command = prepare_build_command() + command += "ninja cefclient cefsimple ceftests" + run_command(command, build_cefclient) + print("[automate.py] OK") + if platform.system() == "Windows": + assert(os.path.exists(os.path.join(build_cefclient, + "tests", + "cefclient", + Options.build_type, + "cefclient.exe"))) + else: + assert (os.path.exists(os.path.join(build_cefclient, + "tests", + "cefclient", + Options.build_type, + "cefclient"))) # Build libcef_dll_wrapper libs if platform.system() == "Windows": - build_wrapper_windows(cef_binary) + build_wrapper_windows(Options.cef_binary) -def build_wrapper_windows(cef_binary): - # Directories - build_wrapper_mt = os.path.join(cef_binary, "build_wrapper_mt") - build_wrapper_md = os.path.join(cef_binary, "build_wrapper_md") - os.makedirs(build_wrapper_mt) - os.makedirs(build_wrapper_md) +def prepare_build_command(build_lib=False): + """On Windows VS env variables must be set up by calling vcvarsall.bat""" + command = "" + if platform.system() == "Windows": + if build_lib: + msvs = get_msvs_for_python() + command = globals()["VS"+msvs+"_VCVARS"] + " && " + else: + if int(Options.cef_branch) >= 2704: + command = VS2015_VCVARS + " && " + else: + command = VS2013_VCVARS + " && " + return command + +def build_wrapper_windows(cef_binary): # Command to build libcef_dll_wrapper - wrapper_cmake = "" - wrapper_cmake += VS2008_VCVARS + " && " + wrapper_cmake = prepare_build_command(build_lib=True) wrapper_cmake += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ % Options.build_type # Build libcef_dll_wrapper_mt.lib - print("[automate.py] Building libcef_dll_wrapper /MT") - old_gyp_msvs_version = Options.gyp_msvs_version - Options.gyp_msvs_version = get_msvs_for_python() - run_command(wrapper_cmake, build_wrapper_mt) - Options.gyp_msvs_version = old_gyp_msvs_version - print("[automate.py] cmake OK") - run_command("ninja libcef_dll_wrapper", build_wrapper_mt) - print("[automate.py] ninja OK") - assert(os.path.exists(os.path.join(build_wrapper_mt, "libcef_dll", - "libcef_dll_wrapper.lib"))) + build_wrapper_mt = os.path.join(cef_binary, "build_wrapper_mt") + mt_already_built = False + if os.path.exists(build_wrapper_mt): + mt_already_built = True + else: + os.makedirs(build_wrapper_mt) + if mt_already_built: + print("[automate.py] Already built: libcef_dll_wrapper /MT") + else: + print("[automate.py] Building libcef_dll_wrapper /MT") + old_gyp_msvs_version = Options.gyp_msvs_version + Options.gyp_msvs_version = get_msvs_for_python() + run_command(wrapper_cmake, build_wrapper_mt) + Options.gyp_msvs_version = old_gyp_msvs_version + print("[automate.py] cmake OK") + ninja_wrapper = prepare_build_command(build_lib=True) + ninja_wrapper += "ninja libcef_dll_wrapper" + run_command(ninja_wrapper, build_wrapper_mt) + print("[automate.py] ninja OK") + assert(os.path.exists(os.path.join(build_wrapper_mt, + "libcef_dll_wrapper", + "libcef_dll_wrapper.lib"))) # Build libcef_dll_wrapper_md.lib - print("[automate.py] Building libcef_dll_wrapper /MD") - old_gyp_msvs_version = Options.gyp_msvs_version - Options.gyp_msvs_version = get_msvs_for_python() - # Replace /MT with /MD /wd\"4275\" in CMakeLists.txt - # Warnings are treated as errors so this needs to be ignored: - # >> warning C4275: non dll-interface class 'stdext::exception' - # >> used as base for dll-interface class 'std::bad_cast' - # This warning occurs only in VS2008, in VS2013 not. - cmakelists = os.path.join(cef_binary, "CMakeLists.txt") - with open(cmakelists, "rb") as fp: - contents = fp.read() - contents = contents.replace(r"/MT ", r"/MD /wd\"4275\" ") - contents = contents.replace(r"/MTd ", r"/MDd /wd\"4275\" ") - with open(cmakelists, "wb") as fp: - fp.write(contents) - run_command(wrapper_cmake, build_wrapper_md) - Options.gyp_msvs_version = old_gyp_msvs_version - print("[automate.py] cmake OK") - run_command("ninja libcef_dll_wrapper", build_wrapper_md) - print("[automate.py] ninja OK") - assert(os.path.exists(os.path.join(build_wrapper_md, "libcef_dll", - "libcef_dll_wrapper.lib"))) + build_wrapper_md = os.path.join(cef_binary, "build_wrapper_md") + md_already_built = False + if os.path.exists(build_wrapper_md): + md_already_built = True + else: + os.makedirs(build_wrapper_md) + if md_already_built: + print("[automate.py] Already built: libcef_dll_wrapper /MD") + else: + print("[automate.py] Building libcef_dll_wrapper /MD") + old_gyp_msvs_version = Options.gyp_msvs_version + Options.gyp_msvs_version = get_msvs_for_python() + # Replace /MT with /MD /wd\"4275\" in CMakeLists.txt + # Warnings are treated as errors so this needs to be ignored: + # >> warning C4275: non dll-interface class 'stdext::exception' + # >> used as base for dll-interface class 'std::bad_cast' + # This warning occurs only in VS2008, in VS2013 not. + cmakelists = os.path.join(cef_binary, "CMakeLists.txt") + with open(cmakelists, "rb") as fp: + contents = fp.read() + contents = contents.replace(r"/MT ", r"/MD /wd\"4275\" ") + contents = contents.replace(r"/MTd ", r"/MDd /wd\"4275\" ") + with open(cmakelists, "wb") as fp: + fp.write(contents) + run_command(wrapper_cmake, build_wrapper_md) + Options.gyp_msvs_version = old_gyp_msvs_version + print("[automate.py] cmake OK") + ninja_wrapper = prepare_build_command(build_lib=True) + ninja_wrapper += "ninja libcef_dll_wrapper" + run_command(ninja_wrapper, build_wrapper_md) + print("[automate.py] ninja OK") + assert(os.path.exists(os.path.join(build_wrapper_md, + "libcef_dll_wrapper", + "libcef_dll_wrapper.lib"))) + + +def fix_cef_include_files(): + """Fixes to CEF include header files for eg. VS2008 on Windows.""" + if platform.system() == "Windows" and get_msvs_for_python() == "2008": + print("[automate.py] Fixing CEF include/ files") + # cef_types_wrappers.h + cef_types_wrappers = os.path.join(Options.cef_binary, "include", + "internal", "cef_types_wrappers.h") + with open(cef_types_wrappers, "rb") as fp: + contents = fp.read() + # error C2059: syntax error : '{' + contents = contents.replace("s->range = {0, 0};", + "s->range.from = 0; s->range.to = 0;") + with open(cef_types_wrappers, "wb") as fp: + fp.write(contents) def create_prebuilt_binaries(): @@ -417,12 +489,14 @@ def create_prebuilt_binaries(): "build_cefclient", "tests", "cefclient", Options.build_type, "cefclient") - cefclient_files = os.path.join( - src, - "build_cefclient", "tests", "cefclient", - Options.build_type, - "cefclient_files") - cpdir(cefclient_files, os.path.join(bindir, "cefclient_files")) + if platform.system() != "Windows": + # On Windows resources/*.html files are embedded inside exe + cefclient_files = os.path.join( + src, + "build_cefclient", "tests", "cefclient", + Options.build_type, + "cefclient_files") + cpdir(cefclient_files, os.path.join(bindir, "cefclient_files")) # cefsimple cefsimple = os.path.join( @@ -437,12 +511,14 @@ def create_prebuilt_binaries(): "build_cefclient", "tests", "ceftests", Options.build_type, "ceftests") - ceftests_files = os.path.join( - src, - "build_cefclient", "tests", "ceftests", - Options.build_type, - "ceftests_files") - cpdir(ceftests_files, os.path.join(bindir, "ceftests_files")) + if platform.system() != "Windows": + # 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")) if platform.system() == "Windows": cefclient += ".exe" @@ -460,19 +536,29 @@ def create_prebuilt_binaries(): # libcef.lib and cef_sandbox.lib mvfiles(bindir, libdir, ".lib") # MT lib - libsrc = os.path.join(src, "build_wrapper_mt", "libcef_dll", + libsrc = os.path.join(src, "build_wrapper_mt", "libcef_dll_wrapper", "libcef_dll_wrapper.lib") - libdst = os.path.join(libdir, "licef_dll_wrapper_mt.lib") + libdst = os.path.join(libdir, "libcef_dll_wrapper_mt.lib") shutil.copy(libsrc, libdst) # MD lib - libsrc = os.path.join(src, "build_wrapper_md", "libcef_dll", + libsrc = os.path.join(src, "build_wrapper_md", "libcef_dll_wrapper", "libcef_dll_wrapper.lib") - libdst = os.path.join(libdir, "licef_dll_wrapper_md.lib") + libdst = os.path.join(libdir, "libcef_dll_wrapper_md.lib") shutil.copy(libsrc, libdst) elif platform.system() == "Linux": cpdir(os.path.join(src, "build_cefclient", "libcef_dll_wrapper"), libdir) + # Remove .lib files from bin/ only after libraries were copied + libs = glob.glob(os.path.join(bindir, "*.lib")) + for lib in libs: + os.remove(lib) + + # Remove cef_sandbox.lib (huge file) + cef_sandbox = os.path.join(libdir, "cef_sandbox.lib") + if os.path.exists(cef_sandbox): + os.remove(cef_sandbox) + # Copy README.txt and LICENSE.txt shutil.copy(os.path.join(src, "README.txt"), dst) shutil.copy(os.path.join(src, "LICENSE.txt"), dst) @@ -493,12 +579,6 @@ def get_msvs_for_python(): sys.exit(1) -def prebuilt_cef(): - """Download CEF prebuilt binaries from GitHub Releases, - eg tag 'upstream-cef47'.""" - pass - - def getenv(): """Env variables passed to shell when running commands.""" env = os.environ @@ -623,24 +703,6 @@ def onerror(func, path, _): raise Exception("Not a file permission error, dunno what to do") -def get_cefpython_version(): - """Get CEF version from the 'src/version/' directory.""" - header_file = os.path.join(Options.cefpython_dir, "src", "version", - "cef_version_"+OS_POSTFIX+".h") - return get_version_from_file(header_file) - - -def get_version_from_file(header_file): - with open(header_file, "rU") as fp: - contents = fp.read() - ret = dict() - matches = re.findall(r'^#define (\w+) "?([^\s"]+)"?', contents, - re.MULTILINE) - for match in matches: - ret[match[0]] = match[1] - return ret - - def get_prebuilt_name(header_file=""): if header_file: version = get_version_from_file(header_file) diff --git a/tools/build.py b/tools/build.py new file mode 100644 index 00000000..48c13874 --- /dev/null +++ b/tools/build.py @@ -0,0 +1,638 @@ +""" +Build the cefpython module, install package and run example. + +Usage: + build.py VERSION [--debug] [--fast] + +Options: + VERSION Version in format xx.xx + --debug Debug mode + --fast Fast mode, don't delete C++ .o .a files, nor the setup/build/ + directory, and disable optimization flags when building + the so/pyd module. + --kivy Run Kivy example +""" + +# How to debug on Linux: +# 1. Install "python-dbg" package +# 2. Install "python-wxgtk2.8-dbg" package +# 3. Run "python compile.py debug" +# 4. In cygdb type "cy run" +# 5. To display debug backtrace type "cy bt" +# 6. More commands: http://docs.cython.org/src/userguide/debugging.html + +# This will not show "Segmentation fault" error message: +# | subprocess.call(["python", "./wxpython.py"]) +# You need to call it with shell=True for this kind of +# error message to be shown: +# | subprocess.call("python wxpython.py", shell=True) + +from common import * +import sys +import os +import glob +import shutil +import subprocess +import re + +# raw_input() was renamed to input() in Python 3 +try: + # noinspection PyUnresolvedReferences + # noinspection PyShadowingBuiltins + input = raw_input +except NameError: + pass + +# Command line args variables +DEBUG_FLAG = False +FAST_FLAG = False +KIVY_FLAG = False +REBUILD_CPP = False +VERSION = "" + +# Module extension +if WINDOWS: + MODULE_EXT = "pyd" +else: + MODULE_EXT = "so" + +# Compiler options +if MAC: + os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] + os.environ["CC"] = "gcc" + os.environ["CXX"] = "g++" + os.environ["CEF_CCFLAGS"] = "-arch x86_64" + os.environ["ARCHFLAGS"] = "-arch x86_64" + if ARCH32: + raise Exception("Python 32bit is not supported on Mac") + + +def main(): + print("[build.py] PYVERSION = %s" % PYVERSION) + print("[build.py] OS_POSTFIX2 = %s" % OS_POSTFIX2) + setup_environ_path() + check_cython_version() + command_line_args() + check_directories() + fix_cefpython_h() + if WINDOWS: + compile_cpp_projects_windows() + elif MAC or LINUX: + compile_cpp_projects_unix() + clear_cache() + copy_and_fix_pyx_files() + create_version_pyx_file() + build_cefpython_module() + fix_cefpython_h() + install_and_run() + + +def setup_environ_path(): + print("[build.py] Setup environment PATH") + if not WINDOWS: + return + if ARCH32: + os.environ["PATH"] = ("C:\\Windows\\system32;C:\\Windows;" + "C:\\Windows\\System32\\Wbem;C:\\Python27") + else: + raise Exception("Only 32-bit is currently supported") + + print("[build.py] PATH: {path}".format(path=os.environ["PATH"])) + + """ + set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; + C:\Python27_x64;C:\Python27_amd64;C:\Python27_64 + + set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; + C:\Python34 + + set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; + C:\Python34_x64;C:\Python34_amd64;C:\Python34_64 + """ + + +def check_cython_version(): + print("[build.py] Check Cython version") + with open(os.path.join(TOOLS_DIR, "requirements.txt"), "r") as fileobj: + contents = fileobj.read() + match = re.search(r"cython\s*==\s*([\d.]+)", contents, + flags=re.IGNORECASE) + assert match, "cython package not found in requirements.txt" + require_version = match.group(1) + try: + import Cython + version = Cython.__version__ + except ImportError: + # noinspection PyUnusedLocal + Cython = None + print("[build.py] ERROR: Cython is not installed ({0} required)" + .format(require_version)) + sys.exit(1) + if version != require_version: + print("[build.py] ERROR: Wrong Cython version: {0}. Required: {1}" + .format(version, require_version)) + sys.exit(1) + print("[build.py] Cython version: {0}".format(version)) + + +def command_line_args(): + global DEBUG_FLAG, FAST_FLAG, KIVY_FLAG, REBUILD_CPP, VERSION + + print("[build.py] Parse command line arguments") + + # -- debug flag + if len(sys.argv) > 1 and "--debug" in sys.argv: + DEBUG_FLAG = True + print("[build.py] DEBUG mode On") + + # --fast flag + if len(sys.argv) > 1 and "--fast" in sys.argv: + # Fast mode doesn't delete C++ .o .a files. + # Fast mode also disables optimization flags in setup/setup.py . + FAST_FLAG = True + print("[build.py] FAST mode On") + + # --kivy flag + if len(sys.argv) > 1 and "--kivy" in sys.argv: + KIVY_FLAG = True + print("[build.py] KIVY mode enabled") + + # --rebuild-cpp flag + # Rebuild c++ projects + if len(sys.argv) > 1 and "--rebuild-cpp" in sys.argv: + REBUILD_CPP = True + print("[build.py] REBUILD_CPP mode enabled") + + # version arg + if len(sys.argv) > 1 and re.search(r"^\d+\.\d+$", sys.argv[1]): + VERSION = sys.argv[1] + else: + print("[build.py] ERROR: expected first arg to be a version number") + print(" Allowed version format: \\d+\.\\d+") + sys.exit(1) + + print("[build.py] VERSION=%s" % VERSION) + + +def check_directories(): + print("[build.py] Check directories") + # Create directories if necessary + if not os.path.exists(CEFPYTHON_BINARY): + os.makedirs(CEFPYTHON_BINARY) + if not os.path.exists(BUILD_CEFPYTHON): + os.makedirs(BUILD_CEFPYTHON) + + # Check directories exist + assert os.path.exists(BUILD_DIR) + assert os.path.exists(BUILD_CEFPYTHON) + assert os.path.exists(CEF_BINARY) + assert os.path.exists(CEFPYTHON_BINARY) + + +def fix_cefpython_h(): + os.chdir(BUILD_CEFPYTHON) + print("[build.py] Fix cefpython.h to disable warnings") + if not os.path.exists("cefpython.h"): + print("[build.py] cefpython.h was not yet generated") + return + with open("cefpython.h", "r") as fo: + content = fo.read() + pragma = "#pragma warning(disable:4190)" + if pragma in content: + print("[build.py] cefpython.h is already fixed") + return + content = ("%s\n\n" % pragma) + content + with open("cefpython.h", "w") as fo: + fo.write(content) + print("[build.py] Saved cefpython.h") + + +def compile_cpp_projects_windows(): + print("[build.py] Compile C++ projects") + + print("[build.py] Build client_handler vcproj") + vcproj = ("client_handler_py{pyver}_{os}.vcproj" + .format(pyver=PYVERSION, os=OS_POSTFIX2)) + vcproj = os.path.join(SRC_DIR, "client_handler", vcproj) + build_vcproj(vcproj) + + print("[build.py] Build libcefpythonapp vcproj") + vcproj = ("libcefpythonapp_py{pyver}_{os}.vcproj" + .format(pyver=PYVERSION, os=OS_POSTFIX2)) + vcproj = os.path.join(SRC_DIR, "subprocess", vcproj) + build_vcproj(vcproj) + + print("[build.py] Build subprocess vcproj") + vcproj = ("subprocess_{os}.vcproj" + .format(os=OS_POSTFIX2)) + vcproj = os.path.join(SRC_DIR, "subprocess", vcproj) + build_vcproj(vcproj) + + print("[build.py] Build cpp_utils vcproj") + vcproj = ("cpp_utils_{os}.vcproj" + .format(os=OS_POSTFIX2)) + vcproj = os.path.join(SRC_DIR, "cpp_utils", vcproj) + build_vcproj(vcproj) + + +def build_vcproj(vcproj): + if PYVERSION == "27": + args = list() + args.append("%LocalAppData%\\Programs\\Common\\" + "Microsoft\\Visual C++ for Python\\9.0\\" + "VC\\bin\\amd64\\vcbuild.exe") + args.append("/nocolor") + args.append("/nologo") + args.append("/nohtmllog") + if REBUILD_CPP: + args.append("/rebuild") + args.append(vcproj) + ret = subprocess.call(args, shell=True) + if ret != 0: + compile_ask_to_continue() + else: + raise Exception("Only Python 2.7 32-bit is currently supported") + + """ + In VS2010 vcbuild was replaced by msbuild.exe. + /clp:disableconsolecolor + msbuild /p:BuildProjectReferences=false project.proj + MSBuild.exe MyProject.proj /t:build + """ + + +def compile_ask_to_continue(): + print("[build.py] **INFO**: On first run you should continue despite" + " errors and after completion re-run the build.py script again") + # noinspection PyUnboundLocalVariable + what = input("[build.py] make failed, 'y' to continue, Enter to stop: ") + if what != "y": + sys.exit(1) + + +def compile_cpp_projects_unix(): + print("[build.py] Compile C++ projects") + + # Need to allow continuing even when make fails, as it may + # fail because the "public" function declaration is not yet + # in "cefpython.h", but for it to be generated we need to run + # cython compiling, so in this case you continue even when make + # fails and then run the compile.py script again and this time + # make should succeed. + + # -------- CPP_UTILS_DIR + + os.chdir(CPP_UTILS_DIR) + if not FAST_FLAG: + subprocess.call("rm -f *.o *.a", shell=True) + + ret = subprocess.call("make -f Makefile", shell=True) + if ret != 0: + compile_ask_to_continue() + + # -------- CLIENT_HANDLER_DIR + + os.chdir(CLIENT_HANDLER_DIR) + if not FAST_FLAG: + subprocess.call("rm -f *.o *.a", shell=True) + + ret = subprocess.call("make -f Makefile", shell=True) + if ret != 0: + compile_ask_to_continue() + + # -------- SUBPROCESS_DIR + + os.chdir(SUBPROCESS_DIR) + if not FAST_FLAG: + subprocess.call("rm -f *.o *.a", shell=True) + subprocess.call("rm -f subprocess", shell=True) + + ret = subprocess.call("make -f Makefile-libcefpythonapp", shell=True) + if ret != 0: + compile_ask_to_continue() + + ret = subprocess.call("make -f Makefile", shell=True) + if ret != 0: + compile_ask_to_continue() + + subprocess_exe = os.path.join(CEFPYTHON_BINARY, "subprocess") + if os.path.exists("./subprocess"): + # .copy() will also copy Permission bits + shutil.copy("./subprocess", subprocess_exe) + + +def clear_cache(): + print("[build.py] Clean build cache") + # Cache in CEFPYTHON_BINARY directory (eg. cefpython_linux64/) + os.chdir(CEFPYTHON_BINARY) + delete_files_by_pattern("./cefpython_py*.{ext}".format(ext=MODULE_EXT)) + + # Cache in build_cefpython/ directory + os.chdir(BUILD_CEFPYTHON) + + delete_files_by_pattern("./cefpython_py*.{ext}".format(ext=MODULE_EXT)) + delete_files_by_pattern("./*.pyx") + + try: + if not FAST_FLAG: + # Cython's internal build/ directory + shutil.rmtree(os.path.join(BUILD_CEFPYTHON, "build")) + except OSError: + pass + + +def copy_and_fix_pyx_files(): + print("[build.py] Copy and fix pyx files") + # First, it copies all .pyx files from upper directory to setup/. + # Then, fixes repeating of "include" statements in pyx files. + + # Only the mainfile needs to have "include" statements, + # but we're using PyCharm and to get rid of "unresolved references" + # and other errors displayed in pycharm we are adding "include" + # statements in all of the pyx files. + + # I'm not 100% sure how includes work in Cython, but I suspect that + # a few includes of the same file will include the same content more + # than once, it should work, but function and variable definitions are + # duplicated, it is some kind of overhead and it could lead to some + # problems in the future, better to fix it now. + + # It also checks cdef & cpdef functions whether they are not missing + # "except *", it is required to add it when returning non-python type. + + os.chdir(BUILD_CEFPYTHON) + print("\n") + mainfile = "cefpython.pyx" + + pyxfiles = glob.glob("../../src/*.pyx") + if not len(pyxfiles): + print("[build.py] ERROR: no .pyx files found in root") + sys.exit(1) + pyxfiles = [f for f in pyxfiles if f.find(mainfile) == -1] + # Now, pyxfiles contains all pyx files except the mainfile (cefpython.pyx), + # we do not fix includes in mainfile. + + pyxfiles2 = glob.glob("../../src/handlers/*.pyx") + if not len(pyxfiles2): + print("[build.py] ERROR: no .pyx files found in handlers/") + sys.exit(1) + + pyxfiles = pyxfiles + pyxfiles2 + + # Remove old pyx files + oldpyxfiles = glob.glob("./*.pyx") + print("[build.py] Clean pyx files in build_cefpython/") + for pyxfile in oldpyxfiles: + if os.path.exists(pyxfile): + os.remove(pyxfile) + + # Copying pyxfiles and reading its contents. + print("[build.py] Copying pyx files to build_cefpython/: %s" % pyxfiles) + + # Copy cefpython.pyx and fix includes in cefpython.pyx, eg.: + # include "handlers/focus_handler.pyx" becomes include "focus_handler.pyx" + shutil.copy("../../src/%s" % mainfile, "./%s" % mainfile) + with open("./%s" % mainfile, "r") as fo: + content = fo.read() + (content, subs) = re.subn(r"^include \"handlers/", + "include \"", + content, + flags=re.MULTILINE) + with open("./%s" % mainfile, "w") as fo: + fo.write(content) + print("[build.py] %s includes fixed in %s" % (subs, mainfile)) + + # Copy the rest of the files + print("[build.py] Fixing includes in .pyx files:") + for pyxfile in pyxfiles: + newfile = "./%s" % os.path.basename(pyxfile) + shutil.copy(pyxfile, newfile) + pyxfile = newfile + with open(pyxfile, "r") as pyxfileopened: + content = pyxfileopened.read() + lineNumber = except_all_missing(content) + if lineNumber: + print("[build.py] WARNING: 'except *' missing" + " in a cdef/cpdef function," + " in file %s on line %d" + % (os.path.basename(pyxfile), lineNumber)) + sys.exit(1) + # Do not remove the newline - so that line numbers + # are exact with originals. + (content, subs) = re.subn( + r"^include[\t ]+[\"'][^\"'\n\r]+[\"'][\t ]*", + "", + content, + flags=re.MULTILINE) + if subs: + print("[build.py] %s includes removed in: %s" + % (subs, os.path.basename(pyxfile))) + with open(pyxfile, "w") as pyxfileopened: + pyxfileopened.write(content) + + print("\n") + + +def except_all_missing(content): + # This is not perfect, won't detect C++ custom types, but will find + # the built-in types, templates and pointers. + patterns = list() + patterns.append( + r"\bcp?def\s+" + "((int|short|long|double|char|unsigned|float|double|cpp_bool" + "|cpp_string|cpp_wstring|uint64_t|uintptr_t|void" + "|CefString)\s+)+" + "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") + patterns.append( + r"\bcp?def\s+" + # A template ends with bracket: CefRefPtr[CefBrowser] + # or a pointer ends with asterisk: CefBrowser* + "[^\s]+[\]*]\s+" + "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") + patterns.append( + r"\bcp?def\s+" + # A reference, eg. CefString& + "[^\s]+&\s+" + "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") + + match = None + for pattern in patterns: + match = re.search(pattern, content) + if match: + break + + if match: + lineNumber = (content.count("\n", 0, match.start()) + 1) + return lineNumber + + +def create_version_pyx_file(): + os.chdir(BUILD_CEFPYTHON) + print("[build.py] Create __version__.pyx file") + with open("__version__.pyx", "w") as fo: + fo.write('__version__ = "{}"\n'.format(VERSION)) + + +def build_cefpython_module(): + os.chdir(BUILD_CEFPYTHON) + # if DEBUG_FLAG: + # ret = subprocess.call("python-dbg setup.py build_ext --inplace" + # " --cython-gdb", shell=True) + + print("[build.py] Execute build_module.py script") + print("") + if FAST_FLAG: + ret = subprocess.call([sys.executable, + "{tools_dir}/build_module.py" + .format(tools_dir=TOOLS_DIR), + "build_ext", "--inplace", "--fast"], + shell=True) + else: + ret = subprocess.call([sys.executable, + "{tools_dir}/build_module.py" + .format(tools_dir=TOOLS_DIR), + "build_ext", "--inplace"], + shell=True) + + # if DEBUG_FLAG: + # shutil.rmtree("./../binaries_%s/cython_debug/" % BITS, + # ignore_errors=True) + # shutil.copytree("./cython_debug/", + # "./../binaries_%s/cython_debug/" % BITS) + + # Remove .pyx files + oldpyxfiles = glob.glob("./*.pyx") + print("") + print("[build.py] Cleanup: remove pyx files in build_cefpython/") + for pyxfile in oldpyxfiles: + if os.path.exists(pyxfile): + os.remove(pyxfile) + + # Check if built succeeded after pyx files were removed + if ret != 0: + print("[build.py] FAILED to build the cefpython module") + sys.exit(1) + + # Move the cefpython module + move_file_by_pattern("./cefpython_py{pyver}*.{ext}" + .format(pyver=PYVERSION, ext=MODULE_EXT), + os.path.join(CEFPYTHON_BINARY, + "cefpython_py{pyver}.{ext}" + .format(pyver=PYVERSION, + ext=MODULE_EXT))) + + print("[build.py] Done building the cefpython module") + + +def move_file_by_pattern(pattern, move_to): + assert len(pattern) > 2 + print("[build.py] Move file: {pattern} to {move_to}" + .format(pattern=pattern, move_to=move_to)) + files = glob.glob(pattern) + assert(len(files) == 1) + os.rename(files[0], move_to) + + +def delete_files_by_pattern(pattern): + assert len(pattern) > 2 + print("[build.py] Delete files by pattern: {pattern}" + .format(pattern=pattern)) + files = glob.glob(pattern) + for f in files: + os.remove(f) + + +def delete_directories_by_pattern(pattern): + assert len(pattern) > 2 + print("[build.py] Delete directories by pattern: {pattern}" + .format(pattern=pattern)) + paths = glob.glob(pattern) + for path in paths: + if os.path.isdir(path): + shutil.rmtree(path) + + +def install_and_run(): + os.chdir(BUILD_CEFPYTHON) + + # if DEBUG_FLAG: + # os.chdir("./binaries_%s" % BITS) + # subprocess.call("cygdb . --args python-dbg wxpython.py", shell=True) + + print("[build.py] Install and run...") + + # Clean installer directory from previous run + try: + delete_directories_by_pattern("./cefpython3-{ver}-*-setup/" + .format(ver=VERSION)) + except: + if LINUX: + os.system("sudo rm -rf ./cefpython3-{ver}-*-setup/" + .format(ver=VERSION)) + else: + raise + + # System python requires sudo when installing package + if sys.executable in ["/usr/bin/python", "/usr/bin/python3"]: + sudo = "sudo" + else: + sudo = "" + + os.chdir(BUILD_CEFPYTHON) + + # Make setup installer + print("[build.py] Make setup installer") + os.system("{python} ../../tools/setup/make.py --version {ver}" + .format(python=sys.executable, ver=VERSION)) + + # Enter setup installer directory + os.chdir("cefpython3-{ver}-{os_postfix2}-setup/" + .format(ver=VERSION, os_postfix2=OS_POSTFIX2)) + + # Install + print("[build.py] Install the cefpython package") + os.system("{sudo} {python} setup.py install" + .format(sudo=sudo, python=sys.executable)) + + # Delete setup installer directory + print("[build.py] Delete the setup installer directory") + if WINDOWS: + shutil.rmtree("./cefpython3-{ver}-{os_postfix2}-setup/" + .format(ver=VERSION, os_postfix2=OS_POSTFIX2)) + else: + os.system("{sudo} rm -rf ./cefpython3-{ver}-*-setup/" + .format(sudo=sudo, ver=VERSION)) + + # Run unittests + print("[build.py] Run unittests") + os.system("cd {unittests_dir} && {python} _test_runner.py" + .format(unittests_dir=UNITTESTS_DIR, python=sys.executable)) + + # Run examples + print("[build.py] Run examples") + if KIVY_FLAG: + run_examples = "{python} {linux_dir}/deprecated_64bit/kivy_.py" + else: + run_examples = ("cd {examples_dir}" + " && {python} hello_world.py" + " && {python} wxpython.py" + " && {python} gtk2.py" + " && {python} gtk2.py --message-loop-timer" + # " && {python} gtk3.py" + " && {python} tkinter_.py" + " && {python} qt.py pyqt" + " && {python} qt.py pyside") + if LINUX: + run_examples += (" && {python}" + " {linux_dir}/deprecated_64bit/kivy_.py") + run_examples.format(linux_dir=LINUX_DIR, examples_dir=EXAMPLES_DIR) + os.system(run_examples) + + # Enter tools dir + os.system("cd {tools_dir}".format(tools_dir=TOOLS_DIR)) + + print("[build.py] DONE") + + +if __name__ == "__main__": + main() diff --git a/tools/build_module.py b/tools/build_module.py new file mode 100644 index 00000000..bb99eb2f --- /dev/null +++ b/tools/build_module.py @@ -0,0 +1,299 @@ +# For internal use only - called by build.py. +# This is Cython's setup for building the cefpython module. + +# Use setuptools so that "Visual C++ compiler for Python 2.7" tools +# can be used. Otherwise "Unable to find vcvarsall.bat" error occurs. +try: + from setuptools import setup + from setuptools import Extension +except ImportError: + from distutils.core import setup + from distutils.extension import Extension + +# Use "Extension" from Cython.Distutils so that "cython_directives" works +from Cython.Distutils import build_ext, Extension +from Cython.Compiler import Options +from common import * +import sys +import platform +import Cython +import os + +# Cython options. Stop on first error, otherwise hundreds +# of errors appear in the console. +Options.fast_fail = True + + +def get_winsdk_lib(): + print("[build_module.py] Detect Windows SDK library directory") + ret = "" + if WINDOWS: + if ARCH32: + winsdk_libs = [ + r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Lib", + r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0\\Lib", + ] + elif ARCH64: + winsdk_libs = [ + r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Lib\\x64", + r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0\\Lib\\x64", + ] + else: + raise Exception("Unknown architecture") + for lib in winsdk_libs: + if os.path.exists(lib): + ret = lib + break + if not ret: + ret = winsdk_libs[0] + if not os.path.exists(ret): + raise Exception("Windows SDK Lib directory not found: %s" + % ret) + return ret + + +def set_compiler_options(options): + """Extends options and also sets environment variables.""" + print("[build_module.py] Set compiler options") + + extra_compile_args = list() + extra_link_args = list() + + if WINDOWS: + # /EHsc - using STL string, multimap and others that use + # C++ exceptions. + # + # /ignore:4217 - disable warnings such as this: + # + # client_handler_py27_32bit.lib(client_handler.obj): warning LNK4217: + # locally defined symbol _RemovePythonCallbacksForFrame imported in + # function "public: virtual bool __thiscall + # ClientHandler::OnProcessMessageReceived + # + # The above warning LNK4217 is caused by the warning below which occurs + # when building the client_handler.lib static library: + # + # cefpython.h(36): warning C4190: 'RequestHandler_GetResourceHandler' + # has C-linkage specified, but returns UDT 'CefRefPtr' which is + # incompatible with C + # + # The C4190 warning is disabled with pragma in cefpython.h, see the + # fix_cefpython_h() in the build.py script. + extra_compile_args.extend(['/EHsc']) + extra_link_args.extend(['/ignore:4217']) + + if LINUX: + if len(sys.argv) > 1 and "--fast" in sys.argv: + sys.argv.remove("--fast") + # Fast mode disables optimization flags + print("[build_module.py] FAST mode On") + extra_compile_args.extend(['-flto', '-std=gnu++11']) + extra_link_args.extend(['-flto']) + else: + # Fix "ImportError ... undefined symbol ..." caused by CEF's + # include/base/ headers by adding the -flto flag (Issue #230). + # Unfortunately -flto prolongs compilation time significantly. + # More on the other flags: https://stackoverflow.com/questions/ + # 6687630/ . + extra_compile_args.extend(['-flto', '-fdata-sections', + '-ffunction-sections', '-std=gnu++11']) + extra_link_args.extend(['-flto', '-Wl,--gc-sections']) + + if MAC: + os.environ["CC"] = "gcc" + os.environ["CXX"] = "g++" + + options["extra_compile_args"] = extra_compile_args + options["extra_link_args"] = extra_link_args + + +def get_include_dirs(): + print("[build_module.py] Prepare include directories") + include_dirs = list() + common_include_dirs = [ + SRC_DIR, + os.path.join(SRC_DIR, "common"), + os.path.join(SRC_DIR, "extern"), + os.path.join(SRC_DIR, "extern", "cef") + ] + if WINDOWS: + include_dirs.extend([WINDOWS_DIR]) + include_dirs.extend(common_include_dirs) + elif MAC: + include_dirs.extend([MAC_DIR]) + include_dirs.extend(common_include_dirs) + # TODO: Check these directories, are these really required on Mac? + include_dirs.extend([ + '/usr/include/gtk-2.0', + '/usr/include/glib-2.0', + '/usr/include/gtk-unix-print-2.0', + '/usr/include/cairo', + '/usr/include/pango-1.0', + '/usr/include/gdk-pixbuf-2.0', + '/usr/include/atk-1.0', + # Fedora + '/usr/lib64/gtk-2.0/include', + '/usr/lib64/gtk-unix-print-2.0', + '/usr/lib64/glib-2.0/include', + '/usr/lib/gtk-2.0/include', + '/usr/lib/gtk-2.0/gtk-unix-print-2.0', + '/usr/lib/glib-2.0/include', + ]) + elif LINUX: + include_dirs.extend([LINUX_DIR]) + include_dirs.extend(common_include_dirs) + include_dirs.extend([ + '/usr/include/gtk-2.0', + '/usr/include/glib-2.0', + '/usr/include/gtk-unix-print-2.0', + '/usr/include/cairo', + '/usr/include/pango-1.0', + '/usr/include/gdk-pixbuf-2.0', + '/usr/include/atk-1.0', + # Ubuntu + '/usr/lib/x86_64-linux-gnu/gtk-2.0/include', + '/usr/lib/x86_64-linux-gnu/gtk-unix-print-2.0', + '/usr/lib/x86_64-linux-gnu/glib-2.0/include', + '/usr/lib/i386-linux-gnu/gtk-2.0/include', + '/usr/lib/i386-linux-gnu/gtk-unix-print-2.0', + '/usr/lib/i386-linux-gnu/glib-2.0/include', + # Fedora + '/usr/lib64/gtk-2.0/include', + '/usr/lib64/gtk-unix-print-2.0', + '/usr/lib64/glib-2.0/include', + '/usr/lib/gtk-2.0/include', + '/usr/lib/gtk-2.0/gtk-unix-print-2.0', + '/usr/lib/glib-2.0/include', + ]) + return include_dirs + + +def get_library_dirs(): + print("[build_module.py] Prepare library directories") + library_dirs = [ + os.path.join(CEF_BINARY, "lib"), + ] + if WINDOWS: + library_dirs.extend([ + get_winsdk_lib(), + os.path.join(SRC_DIR, "client_handler", + "Release_py{pyver}_{os}" + .format(pyver=PYVERSION, os=OS_POSTFIX2)), + os.path.join(SRC_DIR, "subprocess", + "Release_{os}" + .format(os=OS_POSTFIX2)), + os.path.join(SRC_DIR, "cpp_utils", + "Release_py{os}" + .format(os=OS_POSTFIX2)) + ]) + if MAC or LINUX: + library_dirs.extend([ + os.path.join(SRC_DIR, "client_handler"), + os.path.join(SRC_DIR, "subprocess"), # libcefpythonapp + os.path.join(SRC_DIR, "cpp_utils"), + ]) + return library_dirs + + +def get_libraries(): + print("[build_module.py] Prepare libraries") + libraries = list() + if WINDOWS: + libraries.extend([ + "libcef", + "libcef_dll_wrapper_md", + "User32", + "client_handler_py{pyver}_{os}".format( + pyver=PYVERSION, os=OS_POSTFIX2), + "libcefpythonapp_py{pyver}_{os}".format( + pyver=PYVERSION, os=OS_POSTFIX2), + "cpp_utils_{os}".format( + os=OS_POSTFIX2), + ]) + elif MAC: + libraries.extend([ + 'client_handler', + 'cef_dll_wrapper', + 'cefpythonapp', + 'cpp_utils' + ]) + elif LINUX: + libraries.extend([ + "X11", + "gobject-2.0", + "glib-2.0", + "gtk-x11-2.0", + # CEF and CEF Python libraries + "cef_dll_wrapper", + "cefpythonapp", + "client_handler", + "cpp_utils", + ]) + return libraries + + +def get_ext_modules(options): + ext_modules = [Extension( + "cefpython_py%s" % PYVERSION, + ["cefpython.pyx"], + + # Ignore the warning in the console: + # > C:\Python27\lib\distutils\extension.py:133: UserWarning: + # > Unknown Extension options: 'cython_directives' warnings.warn(msg) + cython_directives={ + # Any conversion to unicode must be explicit using .decode(). + "c_string_type": "bytes", + "c_string_encoding": "utf-8", + }, + + language='c++', + + include_dirs=options["include_dirs"], + library_dirs=options["library_dirs"], + + # Static libraries only. Order is important, if library A depends on B, + # then B must be included before A. + libraries=options["libraries"], + + # When you put "./" in here, loading of libcef.so will only work when + # running scripts from the same directory that libcef.so resides in. + # runtime_library_dirs=[ + # './' + # ], + + extra_compile_args=options["extra_compile_args"], + extra_link_args=options["extra_link_args"], + + # Defining macros: + # define_macros = [("UNICODE","1"), ("_UNICODE","1"), ] + )] + return ext_modules + + +def compile_time_constants(): + print("[build_module.py] Generate compile_time_constants.pxi") + with open(os.path.join(SRC_DIR, "compile_time_constants.pxi"), "w") as fd: + fd.write('# This file was generated by setup.py\n') + # A way around Python 3.2 bug: UNAME_SYSNAME is not set + fd.write('DEF UNAME_SYSNAME = "%s"\n' % platform.uname()[0]) + fd.write('DEF PY_MAJOR_VERSION = %s\n' % sys.version_info.major) + + +def main(): + print("[build_module.py] Cython version: %s" % Cython.__version__) + compile_time_constants() + options = dict() + set_compiler_options(options) + options["include_dirs"] = get_include_dirs() + options["library_dirs"] = get_library_dirs() + options["libraries"] = get_libraries() + print("[build_module.py] Execute setup()") + setup( + name='cefpython_py%s' % PYVERSION, + cmdclass={'build_ext': build_ext}, + ext_modules=get_ext_modules(options) + ) + + +if __name__ == "__main__": + main() diff --git a/tools/common.py b/tools/common.py new file mode 100644 index 00000000..f42a2667 --- /dev/null +++ b/tools/common.py @@ -0,0 +1,91 @@ +# Common stuff for tools: automate.py, build.py, build_module.py + +import struct +import platform +import sys +import os +import glob +import re + +# Architecture and OS postfixes +ARCH32 = (8 * struct.calcsize('P') == 32) +ARCH64 = (8 * struct.calcsize('P') == 64) +OS_POSTFIX = ("win" if platform.system() == "Windows" else + "linux" if platform.system() == "Linux" else + "mac" if platform.system() == "Darwin" else "unknown") +OS_POSTFIX2 = "unknown" +if OS_POSTFIX == "win": + OS_POSTFIX2 = "win32" if ARCH32 else "win64" +elif OS_POSTFIX == "mac": + OS_POSTFIX2 = "mac32" if ARCH32 else "mac64" +elif OS_POSTFIX == "linux": + OS_POSTFIX2 = "linux32" if ARCH32 else "linux64" + +# Platforms +LINUX = (platform.system() == "Linux") +WINDOWS = (platform.system() == "Windows") +MAC = (platform.system() == "Darwin") + +# Python version eg. 27 +PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) + +# Directories +TOOLS_DIR = os.path.abspath(os.path.dirname(__file__)) +SRC_DIR = os.path.abspath(os.path.join(TOOLS_DIR, "../src")) +WINDOWS_DIR = os.path.abspath(os.path.join(SRC_DIR, "windows")) +MAC_DIR = os.path.abspath(os.path.join(SRC_DIR, "mac")) +LINUX_DIR = os.path.abspath(os.path.join(SRC_DIR, "linux")) +CPP_UTILS_DIR = os.path.abspath(os.path.join(SRC_DIR, "cpp_utils")) +CLIENT_HANDLER_DIR = os.path.abspath(os.path.join(SRC_DIR, "client_handler")) +SUBPROCESS_DIR = os.path.abspath(os.path.join(SRC_DIR, "subprocess")) +CEFPYTHON_DIR = os.path.abspath(os.path.join(SRC_DIR, "..")) +EXAMPLES_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "examples")) +UNITTESTS_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "unittests")) +BUILD_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "build")) +BUILD_CEFPYTHON = os.path.abspath(os.path.join(BUILD_DIR, "build_cefpython")) +# CEF_BINARY may be auto-overwritten through detect_cef_binary_directory() +CEF_BINARY = os.path.abspath(os.path.join(BUILD_DIR, "cef_"+OS_POSTFIX2)) +CEFPYTHON_BINARY = os.path.abspath(os.path.join(BUILD_DIR, + "cefpython_"+OS_POSTFIX2)) + + +def detect_cef_binary_directory(): + # Detect cef binary directory created by automate.py + # eg. build/cef55_3.2883.1553.g80bd606_win32/ + # and set CEF_BINARY to it. Otherwise CEF_BINARY will + # indicate to build/cef_{os_postfix2}/. + if not os.path.exists(CEF_BINARY): + version = get_cefpython_version() + dirs = glob.glob(os.path.join( + BUILD_DIR, + "cef{major}_{cef_version}_{os}{sep}" + .format(major=version["CHROME_VERSION_MAJOR"], + cef_version=version["CEF_VERSION"], + os=OS_POSTFIX2, + sep=os.sep))) + if len(dirs) == 1: + print("[common.py] Auto detected CEF_BINARY directory: {dir}" + .format(dir=dirs[0])) + global CEF_BINARY + CEF_BINARY = dirs[0] + + +def get_cefpython_version(): + """Get CEF version from the 'src/version/' directory.""" + header_file = os.path.join(SRC_DIR, "version", + "cef_version_"+OS_POSTFIX+".h") + return get_version_from_file(header_file) + + +def get_version_from_file(header_file): + with open(header_file, "rU") as fp: + contents = fp.read() + ret = dict() + matches = re.findall(r'^#define (\w+) "?([^\s"]+)"?', contents, + re.MULTILINE) + for match in matches: + ret[match[0]] = match[1] + return ret + + +detect_cef_binary_directory() From 60ebfb8f9d3b5f0d299148594123f8d9e1234a04 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Feb 2017 17:23:10 +0100 Subject: [PATCH 09/16] Build v55 on Windows PART 2 (#294)... There are still issues with unittests and examples. Tkinter example launches fine, but crashes sometimes. Update vcproj files and other fixes. Add additional linker flag /LARGEADDRESSAWARE to Allow 32-bit processes to access 3GB of RAM. Set UNICODE for subprocess. Update build tools. Update module comments with great details on how these tools work. Refactor make-setup.py and make it work cross-platform (#299). Moved to tools/installer/make.py . Update docs. Update requirements.txt. Update tkinter example on Windows, doesn't support PNG images. --- api/ApplicationSettings.md | 2 + docs/Build-instructions.md | 3 + examples/resources/back.gif | Bin 0 -> 1145 bytes examples/resources/forward.gif | Bin 0 -> 1141 bytes examples/resources/reload.gif | Bin 0 -> 1177 bytes examples/resources/tkinter.gif | Bin 0 -> 5661 bytes examples/resources/tkinter.png | Bin 6265 -> 6294 bytes examples/tkinter_.py | 18 +- examples/wxpython.py | 1 + .../client_handler_py27_win32.vcproj | 4 +- src/client_handler/lifespan_handler.cpp | 2 + src/cpp_utils/cpp_utils_win32.vcproj | 1 + .../installer/build-n-run-wx-chromectrl.sh | 7 - src/mac/installer/.gitignore | 3 - src/mac/installer/__init__.py.template | 28 -- src/mac/installer/build_all.sh | 47 --- src/mac/installer/build_run_chromectrl.sh | 31 -- src/mac/installer/make-setup.py | 172 ---------- src/mac/installer/setup.py.template | 79 ----- ...proj => libcefpythonapp_py27_win32.vcproj} | 9 +- src/subprocess/main.cpp | 11 +- .../main_message_loop_external_pump_win.cpp | 9 +- src/subprocess/main_message_loop/util_win.cpp | 13 +- src/subprocess/main_message_loop/util_win.h | 3 +- ...s_32bit.vcproj => subprocess_win32.vcproj} | 9 +- src/window_info.pyx | 18 +- src/windows/compile.bat | 235 ------------- src/windows/installer/.gitignore | 3 - src/windows/installer/README.txt | 18 - src/windows/installer/__init__.py.template | 12 - src/windows/installer/build_all.bat | 213 ------------ src/windows/installer/innosetup.template | 165 ---------- src/windows/installer/make-installer.py | 93 ------ src/windows/installer/make-setup.py | 178 ---------- src/windows/installer/setup.cfg.template | 2 - src/windows/installer/setup.py.template | 68 ---- tools/automate.py | 301 +++++++++++------ tools/build.py | 306 ++++++++++------- tools/build_module.py | 19 +- tools/common.py | 95 ++++-- tools/installer/cefpython3.README.txt | 13 + tools/installer/cefpython3.__init__.py | 62 ++++ tools/installer/cefpython3.setup.py | 225 +++++++++++++ tools/make_installer.py | 309 ++++++++++++++++++ tools/requirements.txt | 4 +- unittests/_test_runner.py | 16 +- unittests/main_test.py | 8 +- 47 files changed, 1178 insertions(+), 1637 deletions(-) create mode 100644 examples/resources/back.gif create mode 100644 examples/resources/forward.gif create mode 100644 examples/resources/reload.gif create mode 100644 examples/resources/tkinter.gif delete mode 100644 src/linux/installer/build-n-run-wx-chromectrl.sh delete mode 100644 src/mac/installer/.gitignore delete mode 100644 src/mac/installer/__init__.py.template delete mode 100644 src/mac/installer/build_all.sh delete mode 100644 src/mac/installer/build_run_chromectrl.sh delete mode 100644 src/mac/installer/make-setup.py delete mode 100644 src/mac/installer/setup.py.template rename src/subprocess/{libcefpythonapp_py27_32bit.vcproj => libcefpythonapp_py27_win32.vcproj} (83%) rename src/subprocess/{subprocess_32bit.vcproj => subprocess_win32.vcproj} (88%) delete mode 100644 src/windows/compile.bat delete mode 100644 src/windows/installer/.gitignore delete mode 100644 src/windows/installer/README.txt delete mode 100644 src/windows/installer/__init__.py.template delete mode 100644 src/windows/installer/build_all.bat delete mode 100644 src/windows/installer/innosetup.template delete mode 100644 src/windows/installer/make-installer.py delete mode 100644 src/windows/installer/make-setup.py delete mode 100644 src/windows/installer/setup.cfg.template delete mode 100644 src/windows/installer/setup.py.template create mode 100644 tools/installer/cefpython3.README.txt create mode 100644 tools/installer/cefpython3.__init__.py create mode 100644 tools/installer/cefpython3.setup.py create mode 100644 tools/make_installer.py diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index b0ec2d08..fba94f94 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -216,6 +216,8 @@ Custom flags that will be used when initializing the V8 Javascript engine. The consequences of using custom flags may not be well tested. Also configurable using the --js-flags switch. +To enable WebAssembly support set the `--expose-wasm` flag. + ### locale diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 1693e39f..b0c4a049 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -69,6 +69,9 @@ __Windows__ * Install an appropriate MS compiler for a specific Python version: https://wiki.python.org/moin/WindowsCompilers +* When using "Visual C++ compiler for Python 2.7" you have to install + "Microsoft Visual C++ 2008 Redistributable Package (x64)" from + [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) * To build CEF from sources: * Use Win7 x64 or later. 32-bit OS'es are not supported. For more details see [here](https://www.chromium.org/developers/how-tos/build-instructions-windows). diff --git a/examples/resources/back.gif b/examples/resources/back.gif new file mode 100644 index 0000000000000000000000000000000000000000..92362f5eecd32aac8d7bc190b3a8aad1f75215d1 GIT binary patch literal 1145 zcmZ?wbhEHb6k`x$_`a6GIQ5WO+5w}q<0k1xOwx~;q#rj*J7JW5(kT6uQTl14j8jG# zr;RgCn`E3a$po^`7-gQbEjVwKdBG_2qEQx*ykwMh*);p2N%m#aoJ%G-myNQo8s}U! z%DHZof6t=urcLn;`|`U+`47yCAK8^Zuq%IPR{7Pu>WgL7N6WfDR`q|a>;Kx+|8Xoj z>ri^ltLl2aP-Z#Q}1@1e!t_) zyPap=uQ~Q@%gGPB&wbc;;nU&EACF!8c(vKeuRZ*F z9f%$Q(YKqAzutQC?f#4J_g?*f@Z$U9*FT=T{_*t9&*yJ{K7ar3<@;anKLgS47a#t= z{`Bwdr$28$|NZ#o_s6flKYay~e?NZx_xbz3Z$JP4`u*?spMQV;{sV&l|Nk?LNTB$W zg^___9fJr^zJBsd;wX6N9HdJ&M+KFy-wfWpMagAOj7 zN{bjKUFj5A890?Km2u;P1ua6^B8i8-EPLkTGwH*T29JOycGE5e)2il`Q&*VxDsa{^ zAA0P-=g%VQ^krGl>ea!5ie4@n-ffNiPG>$UbG*D7Hv1pPr^D0E&z4|evAdAyetx#g z%5`D47B7E4*VuQ`f`VTTUT2-sgRjQKTxRPPwXyUuJgoly=IL#)$;m5a z*TSKgl;YCRC|RU)Q!cGiJNWkDaQ%}XOCK%`o$RbE4AuZQGX6mT literal 0 HcmV?d00001 diff --git a/examples/resources/forward.gif b/examples/resources/forward.gif new file mode 100644 index 0000000000000000000000000000000000000000..1032b9bee62df9f265de766f81bfdb252e9c5461 GIT binary patch literal 1141 zcmd^;|5K6&0LI@+oy^vB=ghfwXB%Cu^LFhOQ>GcBz4@g!&bGGevZ7Ogz*)M^jddy@ zC~C?Vca@sMn$9^jwK6@sCd4Aq48I`Sup?6;z}Snz3&P7g`aAaA=RWuR`uy-5&VUk# z)CeE~K>C2#JjMR}5inmxpeYHoM+BM*%zq5hMnReyq>X`eHAo+erH>KlY68RG$3e!# zf%8v6h6ZFzg6I660?$pyF((Pk={VLDfi(>>wXrNM$kKs@&l4`pCl${fELi{x4e`Z` z$t8y5k{A1ba>TROiENly>iwb&O)Nu`%DgE>Pe>PMGucyLmd--#d5En~|M^*BIhMr1 z4sfw#F7|DOj@|svQNHCU-+H{pmQ%k9RX9mJER~OvDll?|kL))eg9P66DsM)WH>(=U zu5o17I!@HXlp0r#7(OL|PfOq&vHes7LKPVct{TsZUr{Bi%qyniYcGnMjK$5SAN*W5 zU2HL*k-`NnNNyu`Nw&ytTjRE^aNAdS?JL!t%fdg6b=}LgT}Iv?HbIx|a<@r*%hWWm z+<4b`{hqO*-z>eodhM>c>7G^4wx<8;P~sIaoha~+l>*VO9^*9a(1Y&;fa4AXzUNC;lH(h z?2mQ!QJwwi0x~j-Dd(`sh4rcD4z0nVTlC|cf9ZI(>b9+I*w#F-!((@N zU?;lf^dfH5;l>cR581%n8>riZZg^0S*XQ$L{s8}*>~sfg0)T!X=FQ?ap8$e=Kpt*K zdaq2*>&Nd|I+LQ{1qS0zm?+%J?Fog77%@jqO2d8JvCmGZZW#Tle(+K`okGUNT_y21 z$8~CdrL-oYLV1xa;9bVP3sLG%kH<8n8OuXSB3}4m=?$nLZ)OFbDH_a;bVa8%r`ClE z`I+iHod+wo4hd68hAkhJ2E5;x6(6Sk?l<5Nm8-bf_0~7~@V(4Cz0~Z}^o}2+HQZl7 z>DH{z^y&P!ahr+jBk!4Javye^^%SbSTa3>zSx2S6*D=55yET!kh#VIURe4>l9}0Kf zW`D1bKC7^C!yw7--9<06S$arR8*DhHl3M1T>@{<9RI{9rlgEzClUv3jj`T(3eg0Qo zbqEl+eFXTVJ^6#!B6H2oEkO{E_s;$Efl&cH907-Z5>^0VxX?}8h%$N54*F7gYQW4P WyaotL(2zs6MT%Ml_^9wuAn`wRlK$lY literal 0 HcmV?d00001 diff --git a/examples/resources/reload.gif b/examples/resources/reload.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef96a429162f6f778d74e4ee6cb47ab5db2061e8 GIT binary patch literal 1177 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 M07*qoM6N<$f{d~iSpWb4 literal 6265 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 diff --git a/examples/tkinter_.py b/examples/tkinter_.py index 40acfd0f..c8385992 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -14,10 +14,13 @@ import Tkinter as tk import sys import os +import platform import logging as _logging # Globals logger = _logging.getLogger("tkinter_.py") +# Python 2.7 on Windows comes with Tk 8.5 which doesn't support PNG images +IMAGE_EXT = ".gif" if platform.system() == "Windows" else ".png" def main(): @@ -112,7 +115,7 @@ def get_browser_frame(self): def setup_icon(self): resources = os.path.join(os.path.dirname(__file__), "resources") - icon_path = os.path.join(resources, "tkinter.png") + icon_path = os.path.join(resources, "tkinter"+IMAGE_EXT) if os.path.exists(icon_path): self.icon = tk.PhotoImage(file=icon_path) # noinspection PyProtectedMember @@ -132,7 +135,7 @@ def __init__(self, master): resources = os.path.join(os.path.dirname(__file__), "resources") # Back button - back_png = os.path.join(resources, "back.png") + back_png = os.path.join(resources, "back"+IMAGE_EXT) if os.path.exists(back_png): self.back_image = tk.PhotoImage(file=back_png) self.back_button = tk.Button(self, image=self.back_image, @@ -140,7 +143,7 @@ def __init__(self, master): self.back_button.grid(row=0, column=0) # Forward button - forward_png = os.path.join(resources, "forward.png") + forward_png = os.path.join(resources, "forward"+IMAGE_EXT) if os.path.exists(forward_png): self.forward_image = tk.PhotoImage(file=forward_png) self.forward_button = tk.Button(self, image=self.forward_image, @@ -148,7 +151,7 @@ def __init__(self, master): self.forward_button.grid(row=0, column=1) # Reload button - reload_png = os.path.join(resources, "reload.png") + reload_png = os.path.join(resources, "reload"+IMAGE_EXT) if os.path.exists(reload_png): self.reload_image = tk.PhotoImage(file=reload_png) self.reload_button = tk.Button(self, image=self.reload_image, @@ -255,6 +258,7 @@ def embed_browser(self): window_info.SetAsChild(self.winfo_id()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") + assert self.browser self.browser.SetClientHandler(LoadHandler(self)) self.browser.SetClientHandler(FocusHandler(self)) self.message_loop_work() @@ -274,7 +278,11 @@ def on_root_configure(self): def on_mainframe_configure(self, width, height): if self.browser: - self.browser.SetBounds(0, 0, width, height) + if platform.system() == "Windows": + # noinspection PyUnresolvedReferences + cef.WindowUtils.OnSize(self.winfo_id(), 0, 0, 0) + elif platform.system() == "Linux": + self.browser.SetBounds(0, 0, width, height) self.browser.NotifyMoveOrResizeStarted() def on_focus_in(self, _): diff --git a/examples/wxpython.py b/examples/wxpython.py index d4a0a5e1..2de7a752 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -111,6 +111,7 @@ def OnSize(self, _): (width, height) = self.browser_panel.GetSizeTuple() # noinspection PyUnresolvedReferences self.browser.SetBounds(x, y, width, height) + self.browser.NotifyMoveOrResizeStarted() def OnClose(self, event): # In cefpython3.wx.chromectrl example calling browser.CloseBrowser() diff --git a/src/client_handler/client_handler_py27_win32.vcproj b/src/client_handler/client_handler_py27_win32.vcproj index 51670b21..07e4fbb1 100644 --- a/src/client_handler/client_handler_py27_win32.vcproj +++ b/src/client_handler/client_handler_py27_win32.vcproj @@ -4,7 +4,7 @@ Version="9.00" Name="client_handler_py27_win32" RootNamespace="client_handler_py27_win32" - ProjectGUID="{15AD928F-FFD0-4FA5-B469-E42ABB0B4196}" + ProjectGUID="{15AD928F-FFD0-4FA5-B469-E42AAA0B4196}" Keyword="Win32Proj" TargetFrameworkVersion="0" > @@ -27,7 +27,7 @@ browser, diff --git a/src/cpp_utils/cpp_utils_win32.vcproj b/src/cpp_utils/cpp_utils_win32.vcproj index cc2349da..e0f49c2f 100644 --- a/src/cpp_utils/cpp_utils_win32.vcproj +++ b/src/cpp_utils/cpp_utils_win32.vcproj @@ -27,6 +27,7 @@ @@ -27,7 +27,7 @@ + + + + + diff --git a/src/subprocess/main.cpp b/src/subprocess/main.cpp index fbf2d5bf..1192f4a8 100644 --- a/src/subprocess/main.cpp +++ b/src/subprocess/main.cpp @@ -8,22 +8,21 @@ #include int APIENTRY wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPTSTR lpCmdLine, - int nCmdShow) - + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); CefMainArgs mainArgs(hInstance); -#else // Mac, Linux +#else // defined(OS_WIN) int main(int argc, char **argv) { CefMainArgs mainArgs(argc, argv); -#endif +#endif // Mac, Linux CefRefPtr app(new CefPythonApp); int exitCode = CefExecuteProcess(mainArgs, app.get(), NULL); 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 index 0de7ebf8..4ba8575b 100644 --- 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 @@ -1,4 +1,5 @@ // 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 @@ -50,7 +51,7 @@ MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin() : timer_pending_(false), main_thread_target_(NULL) { HINSTANCE hInstance = GetModuleHandle(NULL); - const wchar_t* const kClassName = L"CEFMainTargetHWND"; + const char* const kClassName = "CEFMainTargetHWND"; WNDCLASSEX wcex = {}; wcex.cbSize = sizeof(WNDCLASSEX); @@ -60,7 +61,7 @@ MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin() RegisterClassEx(&wcex); // Create the message handling window. - main_thread_target_ = CreateWindowW(kClassName, NULL, WS_OVERLAPPEDWINDOW, + 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); @@ -140,8 +141,8 @@ LRESULT CALLBACK MainMessageLoopExternalPumpWin::WndProc( } // namespace // static -scoped_ptr>MainMessageLoopExternalPump> +scoped_ptr MainMessageLoopExternalPump::Create() { - return scoped_ptr>MainMessageLoopExternalPump>( + return scoped_ptr( new MainMessageLoopExternalPumpWin()); } diff --git a/src/subprocess/main_message_loop/util_win.cpp b/src/subprocess/main_message_loop/util_win.cpp index 3dbb4c31..bc1f0965 100644 --- a/src/subprocess/main_message_loop/util_win.cpp +++ b/src/subprocess/main_message_loop/util_win.cpp @@ -1,4 +1,5 @@ // 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 @@ -25,12 +26,12 @@ WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc) { 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; -} +//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; diff --git a/src/subprocess/main_message_loop/util_win.h b/src/subprocess/main_message_loop/util_win.h index 0f806ba5..39870204 100644 --- a/src/subprocess/main_message_loop/util_win.h +++ b/src/subprocess/main_message_loop/util_win.h @@ -1,4 +1,5 @@ // 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 @@ -26,7 +27,7 @@ T GetUserDataPtr(HWND hWnd) { WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc); // Return the resource string with the specified id. -std::wstring GetResourceString(UINT id); +//std::wstring GetResourceString(UINT id); int GetCefMouseModifiers(WPARAM wparam); int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam); diff --git a/src/subprocess/subprocess_32bit.vcproj b/src/subprocess/subprocess_win32.vcproj similarity index 88% rename from src/subprocess/subprocess_32bit.vcproj rename to src/subprocess/subprocess_win32.vcproj index d5cf7d4a..18cff5e7 100644 --- a/src/subprocess/subprocess_32bit.vcproj +++ b/src/subprocess/subprocess_win32.vcproj @@ -31,8 +31,8 @@ Name="VCCLCompilerTool" Optimization="2" EnableIntrinsicFunctions="true" - AdditionalIncludeDirectories="../" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;RENDERER_PROCESS;" + AdditionalIncludeDirectories="$(INCLUDE);..\;..\common" + PreprocessorDefinitions="WIN32;_WIN32;_WINDOWS;WINVER=0x0601;_WIN32_WINNT=0x0601;UNICODE;_UNICODE;NOMINMAX;WIN32_LEAN_AND_MEAN;_HAS_EXCEPTIONS=0;NDEBUG;_NDEBUG;_CRT_SECURE_NO_WARNINGS;RENDERER_PROCESS;" ExceptionHandling="1" RuntimeLibrary="0" EnableFunctionLevelLinking="true" @@ -48,9 +48,10 @@ IgnoreImportLibrary="false" LinkLibraryDependencies="true" AdditionalDependencies="libcef.lib libcef_dll_wrapper_mt.lib" + AdditionalOptions="/MANIFEST:NO /LARGEADDRESSAWARE" LinkIncremental="1" - AdditionalLibraryDirectories="../../build/cef_win32/lib" - GenerateManifest="true" + AdditionalLibraryDirectories="$(AdditionalLibraryDirectories);$(LIB)" + GenerateManifest="false" IgnoreAllDefaultLibraries="false" GenerateDebugInformation="true" SubSystem="2" diff --git a/src/window_info.pyx b/src/window_info.pyx index c3bef322..988c665e 100644 --- a/src/window_info.pyx +++ b/src/window_info.pyx @@ -81,7 +81,16 @@ cdef class WindowInfo: cpdef py_void SetAsChild(self, WindowHandle parentWindowHandle, list windowRect=None): - if not WindowUtils.IsWindowHandle(parentWindowHandle): + # Allow parent window handle to be 0, in such case CEF will + # create top window automatically as in hello_world.py example. + IF UNAME_SYSNAME == "Windows": + # On Windows when parent window handle is 0 then SetAsPopup() + # must be called instead. + if parentWindowHandle == 0: + self.SetAsPopup(parentWindowHandle, "Popup") + return + if parentWindowHandle != 0\ + and not WindowUtils.IsWindowHandle(parentWindowHandle): raise Exception("Invalid parentWindowHandle: %s"\ % parentWindowHandle) self.windowType = "child" @@ -100,7 +109,10 @@ cdef class WindowInfo: IF UNAME_SYSNAME == "Windows": cpdef py_void SetAsPopup(self, WindowHandle parentWindowHandle, py_string windowName): - if not WindowUtils.IsWindowHandle(parentWindowHandle): + # Allow parent window handle to be 0, in such case CEF will + # create top window automatically as in hello_world.py example. + if parentWindowHandle != 0\ + and not WindowUtils.IsWindowHandle(parentWindowHandle): raise Exception("Invalid parentWindowHandle: %s"\ % parentWindowHandle) self.parentWindowHandle = parentWindowHandle @@ -109,7 +121,7 @@ cdef class WindowInfo: cpdef py_void SetAsOffscreen(self, WindowHandle parentWindowHandle): - # It is allowed to pass 0 as parentWindowHandle. + # It is allowed to pass 0 as parentWindowHandle in OSR mode if parentWindowHandle and \ not WindowUtils.IsWindowHandle(parentWindowHandle): raise Exception("Invalid parentWindowHandle: %s" \ diff --git a/src/windows/compile.bat b/src/windows/compile.bat deleted file mode 100644 index eacaee71..00000000 --- a/src/windows/compile.bat +++ /dev/null @@ -1,235 +0,0 @@ -@echo off - -:: It's best to always call with a flag that specifies python -:: version and architecture (eg. --py27-32bit). This will ensure -:: that PATH contains only minimum set of directories and will -:: allow to detect possible issues early. - -:: Arguments -if [%1] == [] ( - echo [compile.bat] Version number not provided. Usage: compile.bat 31.0 - echo [compile.bat] Opt: --rebuild --py27-32bit --py27-64bit --py34-32bit - echo --py34-64bit - exit /B 1 -) - -:: --rebuild flag to rebuild all vcproj builds -set rebuild_flag=0 -echo.%*|findstr /C:"--rebuild" >nul 2>&1 -if %errorlevel% equ 0 ( - set rebuild_flag=1 -) - -:: Add only Python/ to PATH. -:: --py27-32bit flag -echo.%*|findstr /C:"--py27-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27 -) -:: --py27-64bit flag -echo.%*|findstr /C:"--py27-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27_x64;C:\Python27_amd64;C:\Python27_64 -) -:: --py34-32bit flag -echo.%*|findstr /C:"--py34-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34 -) -:: --py34-64bit flag -echo.%*|findstr /C:"--py34-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34_x64;C:\Python34_amd64;C:\Python34_64 -) -:: PATH -echo [compile.bat] PATH: %PATH% - -:: Version number -set version=%1 -echo [compile.bat] Version argument: %version% - -:: Python architecture. %bits%=="32bit" or "64bit" -FOR /F "delims=" %%i IN ('python -c "import struct, sys; sys.stdout.write(str(8 * struct.calcsize('P')) + 'bit');"') do set bits=%%i -echo [compile.bat] Python architecture: %bits% - -:: Cython version -FOR /F "delims=" %%i IN ('python -c "import sys, Cython; sys.stdout.write(Cython.__version__);"') do set cython_version=%%i -echo [compile.bat] Cython version: %cython_version% - -:: Python version -for /F %%i in ('python -c "import sys; sys.stdout.write(str(sys.version_info[0])+str(sys.version_info[1]));"') do set pyver=%%i -echo [compile.bat] Python version: py%pyver% - -:: Binaries directory -set binaries=%~dp0binaries_%bits% -echo [compile.bat] Binaries directory: %binaries% - -:: Setup directory -set setup=%~dp0setup -echo [compile.bat] Setup directory: %setup% - -:: Delete .pyd files -echo [compile.bat] Cleaning cython build files from previous run -del "%binaries%\cefpython_py%pyver%.pyd" -del "%setup%\cefpython_py%pyver%.pyd" -for /R %setup% %%f in (*.pyx) do del "%%f" -rmdir /S /Q "%setup%\build\" - -:: Fix cefpython.h -echo [compile.bat] Fixing cefpython.h -cd %setup% -python fix_cefpython_h.py -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: failed to fix cefpython.h - cd ../ - exit /B 1 -) -cd ../ - -:: Compile VS projects: client_handler, libcefpythonapp, subprocess, cpp_utils - -:: client_handler paths -set client_handler_dir=%~dp0..\client_handler -set client_handler_vcproj=%client_handler_dir%\client_handler_py%pyver%_%bits%.vcproj - -set subprocess_dir=%~dp0..\subprocess - -:: libcefpythonapp paths -set libcefpythonapp_vcproj=%subprocess_dir%\libcefpythonapp_py%pyver%_%bits%.vcproj - -:: subprocess paths -set subprocess_vcproj=%subprocess_dir%\subprocess_%bits%.vcproj - -:: cpp_utils paths -set cpp_utils_dir=%~dp0..\..\cpp_utils -set cpp_utils_vcproj=%cpp_utils_dir%\cpp_utils_%bits%.vcproj - -set success=0 -if "%pyver%"=="27" ( - if "%bits%"=="32bit" ( - set "vcbuild=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" - set success=1 - ) - if "%bits%"=="64bit" ( - REM :: The same vcbuild.exe 32-bit for building x64 - set "vcbuild=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" - set success=1 - ) - set "vcoptions=/nocolor /nologo /nohtmllog" - if %rebuild_flag% equ 1 ( - set "vcoptions=%vcoptions% /rebuild" - ) -) -if "%pyver%"=="34" ( - :: In VS2010 vcbuild was replaced by msbuild.exe. - :: /clp:disableconsolecolor - :: msbuild /p:BuildProjectReferences=false project.proj - :: MSBuild.exe MyProject.proj /t:build -) - -if %success% neq 1 ( - echo [compile.bat] ERROR: failed determining tool to build vcproj files - exit /B 1 -) - -echo [compile.bat] Building client_handler vcproj -"%vcbuild%" %vcoptions% %client_handler_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building client_handler vcproj failed - exit /B 1 -) - -echo [compile.bat] Building libcefpythonapp vcproj -"%vcbuild%" %vcoptions% %libcefpythonapp_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building libcefpythonapp vcproj failed - exit /B 1 -) - -echo [compile.bat] Building subprocess vcproj -"%vcbuild%" %vcoptions% %subprocess_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building subprocess vcproj failed - exit /B 1 -) - -echo [compile.bat] Building cpp_utils vcproj -"%vcbuild%" %vcoptions% %cpp_utils_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building cpp_utils vcproj failed - exit /B 1 -) - -:: Do not clean VS build files, as this would slow down the process -:: of recompiling. - -:: Compile .rc file to a .res object. -echo [compile.bat] Compiling cefpython.rc file to a .res object -cd %setup%\ -python compile_rc.py -v %version% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: compiling .rc file failed - exit /B 1 -) - -echo [compile.bat] Entering setup/ directory -cd %setup% - -echo [compile.bat] Copying .pyx files to setup/ directory and fixing includes -python fix_pyx_files.py -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: running fix_pyx_files.py failed - exit /B 1 -) - -:: __version__.pyx must be generated after running fix_pyx_files.py, -:: as that script deletes old pyx files before copying new ones. -echo [compile.bat] Creating __version__.pyx file -echo __version__ = "%version%">>__version__.pyx -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: writing __version__.pyx failed - exit /B 1 -) - -echo [compile.bat] Running the cython setup.py script -python setup.py build_ext --inplace -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: the cython setup.py script failed - :: Clean files from the build that failed - for /R %setup% %%f in (*.pyx) do del "%%f" - for /R %setup% %%f in (*.res) do del "%%f" - rmdir /S /Q "%setup%\build\" - cd ../ - exit /B 1 -) - -echo [compile.bat] Fixing cefpython.h -python fix_cefpython_h.py -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: failed to fix cefpython.h - exit /B 1 -) - -echo [compile.bat] Cleaning files from the build -for /R %setup% %%f in (*.pyx) do del "%%f" -for /R %setup% %%f in (*.res) do del "%%f" -rmdir /S /Q "%setup%\build\" - -echo [compile.bat] Moving the pyd module to the binaries directory -move "%setup%\cefpython_py%pyver%.pyd" "%binaries%/cefpython_py%pyver%.pyd" -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: Moving the pyd module failed - exit /B 1 -) - -echo [compile.bat] Copying subprocess.exe to the binaries directory -copy "%~dp0..\subprocess\Release_%bits%\subprocess_%bits%.exe" "%binaries%\subprocess.exe" -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: Copying subprocess.exe failed - exit /B 1 -) - -echo [compile.bat] Everything went OK. Running the wxpython.py example.. - -cd %binaries% -python wxpython.py & cd ../ diff --git a/src/windows/installer/.gitignore b/src/windows/installer/.gitignore deleted file mode 100644 index 0dc5ef2a..00000000 --- a/src/windows/installer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -cefpython3-*/ -Output/ -dist/ diff --git a/src/windows/installer/README.txt b/src/windows/installer/README.txt deleted file mode 100644 index 2325940f..00000000 --- a/src/windows/installer/README.txt +++ /dev/null @@ -1,18 +0,0 @@ -1. To install CEF Python 3 type: - - python setup.py install - -2. In the same directory that setup.py resides there is - an examples/ directory, run some example scripts from there: - - cd examples/ - python wxpython.py - python pyqt.py - python pyside.py - python pygtk_.py - python pywin32.py - - cd wx/ - python sample1.py - python sample2.py - python sample3.py diff --git a/src/windows/installer/__init__.py.template b/src/windows/installer/__init__.py.template deleted file mode 100644 index 94ef341b..00000000 --- a/src/windows/installer/__init__.py.template +++ /dev/null @@ -1,12 +0,0 @@ -__all__ = ["cefpython", "wx"] -__version__ = "%(APP_VERSION)s" -__author__ = "The CEF Python authors" - -import sys - -if 0x02070000 <= sys.hexversion < 0x03000000: - from . import cefpython_py27 as cefpython -elif 0x03000000 <= sys.hexversion < 0x04000000: - from . import cefpython_py32 as cefpython -else: - raise Exception("Unsupported python version: " + sys.version) diff --git a/src/windows/installer/build_all.bat b/src/windows/installer/build_all.bat deleted file mode 100644 index 3c2dfdea..00000000 --- a/src/windows/installer/build_all.bat +++ /dev/null @@ -1,213 +0,0 @@ -@echo off -setlocal ENABLEDELAYEDEXPANSION - -:: It's best to always call with a flag that specifies python -:: version and architecture (eg. --py27-32bit). This will ensure -:: that PATH contains only minimum set of directories and will -:: allow to detect possible issues early. - -if "%1"=="" goto usage -if "%2"=="" goto usage - -set version=%1 - -:: Add only Python/ and Python/Scripts/ to PATH. -:: --py27-32bit flag -echo.%*|findstr /C:"--py27-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27;C:\Python27\Scripts -) -:: --py27-64bit flag -echo.%*|findstr /C:"--py27-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27_x64;C:\Python27_amd64;C:\Python27_64;C:\Python27_x64\Scripts;C:\Python27_amd64\Scripts;C:\Python27_64\Scripts -) -:: --py34-32bit flag -echo.%*|findstr /C:"--py34-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34;C:\Python34\Scripts -) -:: --py34-64bit flag -echo.%*|findstr /C:"--py34-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34_x64;C:\Python34_amd64;C:\Python34_64;C:\Python34_x64\Scripts;C:\Python34_amd64\Scripts;C:\Python34_64\Scripts -) -:: PATH -echo [compile.bat] PATH: %PATH% - -:: Python architecture. %bits%=="32bit" or "64bit" -FOR /F "delims=" %%i IN ('python -c "import struct, sys; sys.stdout.write(str(8 * struct.calcsize('P')) + 'bit');"') do set bits=%%i -echo [compile.bat] Python architecture: %bits% -set success=0 -if "%bits%"=="32bit" ( - set platform=win32 - set success=1 -) -if "%bits%"=="64bit" ( - set platform=win-amd64 - set success=1 -) -if %success% neq 1 ( - echo [build_all.bat] ERROR: invalid architecture: %bits% - exit /B 1 -) - -echo [build_all.bat] PLATFORM: %platform% -echo [build_all.bat] VERSION: %version% - -:: Python version -for /F %%i in ('python -c "import sys; sys.stdout.write(str(sys.version_info[0]) + '.' + str(sys.version_info[1]));"') do set pyverdot=%%i -echo [build_all.bat] Python version: py%pyverdot% - -:: --disable-inno-setup flag -set DISABLE_INNO_SETUP=0 -echo.%*|findstr /C:"--disable-inno-setup" >nul 2>&1 -if %errorlevel% equ 0 ( - set DISABLE_INNO_SETUP=1 -) - -:: Clean directories from previous run -rmdir /s /q Output -for /f "tokens=*" %%f in ('dir .\cefpython3*setup /ad/b') do rmdir /s /q %%f -rmdir /s /q dist - -mkdir dist - -echo [build_all.bat] Installing setuptools and wheel -pip install setuptools wheel -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: pip install setuptools wheel - exit /B 1 -) - -if %DISABLE_INNO_SETUP% equ 0 ( - echo [build_all.bat] Creating Inno Setup intaller - python make-installer.py -v %version% - if !errorlevel! equ 0 ( - for /f "tokens=*" %%f in ('dir .\Output\*.exe /b') do ( - move .\Output\%%f dist/%%f - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: moving inno setup installer failed - exit /B 1 - ) - ) - rmdir Output - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: deleting Output/ directory failed - exit /B 1 - ) - ) - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: creating Inno Setup installer failed - exit /B 1 - ) -) - -echo [build_all.bat] Creating Distutils setup -python make-setup.py -v %version% -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Distutils setup - exit /B 1 -) - -:: Enter the setup directory -for /f "tokens=*" %%f in ('dir .\cefpython3*setup /ad/b') do cd %%f - -echo [build_all.bat] Creating Distutils source package -python setup.py sdist -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Distutils source package - exit /B 1 -) - -echo [build_all.bat] Creating Python Egg -python setup.py bdist_egg -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Python Egg failed - exit /B 1 -) - -echo [build_all.bat] Creating Python Wheel -python setup.py bdist_wheel -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Python Wheel failed - exit /B 1 -) - -echo [build_all.bat] Creating MSI installer -python setup.py bdist_msi -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating MSI installer failed - exit /B 1 -) - -echo [build_all.bat] Creating EXE installer -python setup.py bdist_wininst -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating EXE installer failed - exit /B 1 -) - -echo [build_all.bat] Moving all packages to the dist/ directory -set success=0 -for /f "tokens=*" %%f in ('dir .\dist\*.* /b') do ( - move .\dist\%%f .\..\dist\%%f - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: moving setup dist/ packages failed - exit /B 1 - ) - if !errorlevel! equ 0 ( - set success=1 - ) -) -if %success% neq 1 ( - echo [build_all.bat] ERROR: moving setup dist/ packages failed - exit /B 1 -) - -:: Up to the installer/ directory -cd ../ - -echo [build_all.bat] Deleting the Distutils setup directory -for /f "tokens=*" %%f in ('dir .\cefpython3*setup /ad/b') do rmdir /s /q %%f -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: failed deleting the Distutils setup directory - exit /B 1 -) - -cd dist/ - -echo [build_all.bat] Renaming some of the packages to include platform tag -for /R %%i in (*) do ( - set oldfile=%%i - set newfile=!oldfile:.egg=-%platform%.egg! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) - set oldfile=%%i - set newfile=!oldfile:.zip=-py%pyverdot%-%platform%.zip! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) - set oldfile=%%i - set newfile=!oldfile:%platform%.exe=py%pyverdot%-%platform%.exe! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) - set oldfile=%%i - set newfile=!oldfile:%platform%.msi=py%pyverdot%-%platform%.msi! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) -) - -echo [build_all.bat] Packages in the dist/ directory: -dir - -echo OK - -goto :eof -:usage -@echo [build_all.bat] ERROR: platform or version arguments missing or invalid -@echo [build_all.bat] ERROR: example usage: build_all.bat win32 31.2 -exit /B 1 diff --git a/src/windows/installer/innosetup.template b/src/windows/installer/innosetup.template deleted file mode 100644 index e3a9a51e..00000000 --- a/src/windows/installer/innosetup.template +++ /dev/null @@ -1,165 +0,0 @@ -; Parts of this code was taken from wxPython/distrib/make_installer.py - -[Setup] - -AppName = CEF Python 3 for Python %(PYTHON_VERSION)s %(APP_NAME_BITS)s -AppVersion = %(APP_VERSION)s -AppVerName = CEF Python 3 version %(APP_VERSION)s for Python %(PYTHON_VERSION)s %(PYTHON_ARCHITECTURE)s - -AppPublisher = Czarek Tomczak -AppPublisherURL = http://code.google.com/cefpython/ -AppSupportURL = https://groups.google.com/group/cefpython?hl=en -AppUpdatesURL = http://code.google.com/cefpython/ -AppCopyright = Copyright 2012-2013 Czarek Tomczak - -DefaultDirName = {code:GetInstallDir|c:\Python} - -DefaultGroupName = CEF Python 3 for Python %(PYTHON_VERSION)s %(APP_NAME_BITS)s -PrivilegesRequired = none -DisableStartupPrompt = yes -Compression = zip -DirExistsWarning = no -DisableReadyMemo = yes -DisableReadyPage = yes -DisableDirPage = no -DisableProgramGroupPage = no -UsePreviousAppDir = yes -UsePreviousGroup = yes - -SourceDir = %(BINARIES_DIR)s -OutputDir = %(INSTALLER_DIR)s\Output -OutputBaseFilename = %(PACKAGE_NAME)s-%(APP_VERSION)s.%(PLATFORM)s-py%(PYTHON_VERSION)s-innosetup - -UninstallFilesDir = {app}\%(PACKAGE_NAME)s -LicenseFile = %(BINARIES_DIR)s\LICENSE.txt - -[Icons] - -Name: "{group}\Examples"; Filename: "{app}\%(PACKAGE_NAME)s\examples"; -Name: "{group}\Uninstall Package"; Filename: "{uninstallexe}"; - -[Run] - -Filename: "{app}\%(PACKAGE_NAME)s\examples"; Flags: postinstall skipifsilent shellexec; - -[Files] - -Source: "*.dll"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "*.pak"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "locales\*.pak"; DestDir: "{app}\%(PACKAGE_NAME)s\locales"; Flags: ignoreversion; -Source: "%(INSTALLER_DIR)s\__init__.py.generated"; DestDir: "{app}\%(PACKAGE_NAME)s"; DestName: "__init__.py"; Flags: ignoreversion; -Source: "cefclient.exe"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "cefpython_py%(PYTHON_VERSION_NODOT)s.pyd"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "LICENSE.txt"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "README.txt"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "subprocess.exe"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; - -; ------------------------------------------------------------------------------ -; wx subpackage -; ------------------------------------------------------------------------------ - -Source: "%(WX_SUBPACKAGE_DIR)s\*.py"; DestDir: "{app}\%(PACKAGE_NAME)s\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\*.txt"; DestDir: "{app}\%(PACKAGE_NAME)s\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\images\*.png"; DestDir: "{app}\%(PACKAGE_NAME)s\wx\images"; Flags: ignoreversion; - -; ------------------------------------------------------------------------------ -; wx examples -; ------------------------------------------------------------------------------ - -Source: "%(WX_SUBPACKAGE_DIR)s\examples\*.py"; DestDir: "{app}\%(PACKAGE_NAME)s\examples\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\examples\*.html"; DestDir: "{app}\%(PACKAGE_NAME)s\examples\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\examples\*.png"; DestDir: "{app}\%(PACKAGE_NAME)s\examples\wx"; Flags: ignoreversion; - -; ------------------------------------------------------------------------------ -; examples -; ------------------------------------------------------------------------------ - -Source: "*.py"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.html"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.css"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.js"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.ico"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; - -[UninstallDelete] - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\__pycache__" - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\examples\__pycache__" - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\wx\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\wx\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\examples\wx\__pycache__" - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\wx\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\wx\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\wx\__pycache__" - -[Code] - -program Setup; -var - PythonDir : String; - InstallDir : String; - -function InitializeSetup(): Boolean; -begin - - if not RegQueryStringValue(%(HKEY_CURRENT_USER)s, - 'Software\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - if not RegQueryStringValue(%(HKEY_LOCAL_MACHINE)s, - 'Software\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - if not RegQueryStringValue(%(HKEY_CURRENT_USER)s, - 'Software\Wow6432Node\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - if not RegQueryStringValue(%(HKEY_LOCAL_MACHINE)s, - 'Software\Wow6432Node\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - MsgBox('No installation of Python %(PYTHON_VERSION)s ' - + 'found in registry.' + #13 + 'Be sure to enter ' - + 'a pathname that places Python on the ' - + 'PYTHONPATH', - mbConfirmation, MB_OK); - PythonDir := 'C:\Python'; - end; - end; - end; - end; - - InstallDir := PythonDir + '\Lib\site-packages'; - Result := True; -end; - -function GetInstallDir(Default: String): String; -begin - Result := InstallDir; -end; - -function UninstallOld(FileName: String): Boolean; -var - ResultCode: Integer; -begin - Result := False; - if FileExists(FileName) then begin - Result := True; - Exec(FileName, '/SILENT', WizardDirValue(), SW_SHOWNORMAL, - ewWaitUntilTerminated, ResultCode); - end; -end; - -function NextButtonClick(CurPage: Integer): Boolean; -begin - Result := True; - if CurPage <> wpSelectDir then Exit; - UninstallOld(WizardDirValue() + '\%(PACKAGE_NAME)s\unins001.exe') - UninstallOld(WizardDirValue() + '\%(PACKAGE_NAME)s\unins000.exe') -end; diff --git a/src/windows/installer/make-installer.py b/src/windows/installer/make-installer.py deleted file mode 100644 index ab31d536..00000000 --- a/src/windows/installer/make-installer.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# Create a Windows package installer. - -import sys -import os -import platform -import argparse -import re -import struct -import sysconfig - -BITS = str(8 * struct.calcsize('P')) + 'bit' -assert (BITS == "32bit" or BITS == "64bit") - -ISCC = r"c:\Program Files (x86)\Inno Setup 5\ISCC.exe" -if "INNO5" in os.environ: - ISCC = os.environ["INNO5"] - -TEMPLATE_FILE = os.getcwd()+r"\innosetup.template" -ISS_FILE = os.getcwd()+r"\innosetup.generated" - -def main(): - parser = argparse.ArgumentParser(usage="%(prog)s [options]") - parser.add_argument("-v", "--version", help="cefpython version", - required=True) - args = parser.parse_args() - assert re.search(r"^\d+\.\d+$", args.version), "Invalid version string" - - vars = {} - vars["PACKAGE_NAME"] = "cefpython3" - vars["APP_VERSION"] = args.version - vars["PYTHON_VERSION"] = (str(sys.version_info.major) + "." - + str(sys.version_info.minor)) - vars["PYTHON_VERSION_NODOT"] = (str(sys.version_info.major) + "" - + str(sys.version_info.minor)) - vars["PYTHON_ARCHITECTURE"] = platform.architecture()[0] - vars["BINARIES_DIR"] = os.path.realpath( - os.getcwd() + r"\..\binaries_%s" % BITS) - vars["PYD_FILE"] = (vars["BINARIES_DIR"]+r"\cefpython_py" - + str(sys.version_info.major) + str(sys.version_info.minor) - + ".pyd") - vars["INSTALLER_DIR"] = os.getcwd() - vars["WX_SUBPACKAGE_DIR"] = os.path.realpath(os.getcwd()+r"\..\..\wx") - vars["PLATFORM"] = sysconfig.get_platform() - - if BITS == "32bit": - # We must keep compatibility, 32bit installers didn't contain - # architecture information in AppName. So make it an empty string. - vars["APP_NAME_BITS"] = "" - vars["HKEY_CURRENT_USER"] = "HKEY_CURRENT_USER" - vars["HKEY_LOCAL_MACHINE"] = "HKEY_LOCAL_MACHINE" - elif BITS == "64bit": - vars["APP_NAME_BITS"] = "64bit" - # Inno setup installer is a 32bit application. To query 64bit - # registry from within 32bit application you need to add _64 - # postfix. - vars["HKEY_CURRENT_USER"] = "HKEY_CURRENT_USER_64" - vars["HKEY_LOCAL_MACHINE"] = "HKEY_LOCAL_MACHINE_64" - - print("Reading template: %s" % TEMPLATE_FILE) - - f = open(TEMPLATE_FILE) - template = f.read() - f.close() - - f = open(ISS_FILE, "w") - f.write(template % vars) - f.close() - - print("Saved: %s" % ISS_FILE) - - initPyTemplate = os.getcwd()+r"\__init__.py.template" - initPyInstall = os.getcwd()+r"\__init__.py.generated" - - f = open(initPyTemplate) - initPyTemplateCode = f.read() - f.close() - - f = open(initPyInstall, "w") - f.write(initPyTemplateCode % vars) - f.close() - print("Saved: %s" % initPyInstall) - - iscc_command = '"'+ ISCC + '" ' + ISS_FILE - print("Running ISCC: %s" % iscc_command) - exit_code = os.system(iscc_command) - sys.exit(exit_code) - -if __name__ == "__main__": - main() diff --git a/src/windows/installer/make-setup.py b/src/windows/installer/make-setup.py deleted file mode 100644 index 27d521e3..00000000 --- a/src/windows/installer/make-setup.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# Create a setup package. - -import sys -import os -import platform -import argparse -import re -import platform -import shutil -import glob -import shutil -import sysconfig - -BITS = platform.architecture()[0] -assert (BITS == "32bit" or BITS == "64bit") - -PACKAGE_NAME = "cefpython3" - -README_FILE = os.getcwd()+r"/README.txt" -INIT_TEMPLATE = os.getcwd()+r"/__init__.py.template" -SETUP_TEMPLATE = os.getcwd()+r"/setup.py.template" -SETUP_CFG_TEMPLATE = os.getcwd()+r"/setup.cfg.template" - -def glob_remove(pathname): - filelist = glob.glob(pathname) - for f in filelist: - os.remove(f) - -def glob_copy(src_glob, dst_folder): - for fname in glob.iglob(src_glob): - print("Copying %s to %s" % (fname, dst_folder)) - if os.path.isdir(fname): - shutil.copytree(fname, - os.path.join(dst_folder, os.path.basename(fname))) - else: - shutil.copy(fname, - os.path.join(dst_folder, os.path.basename(fname))) - -def glob_move(src_glob, dst_folder): - if not os.path.exists(dst_folder): - os.mkdir(dst_folder) - for fname in glob.iglob(src_glob): - shutil.move(fname, - os.path.join(dst_folder, os.path.basename(fname))) - -def str_format(string, dictionary): - orig_string = string - for key, value in dictionary.iteritems(): - string = string.replace("%("+key+")s", value) - if string == orig_string: - raise Exception("Nothing to format") - if re.search(r"%\([a-zA-Z0-9_]+\)s", string): - raise Exception("Not all strings formatted") - return string - -def main(): - parser = argparse.ArgumentParser(usage="%(prog)s [options]") - parser.add_argument("-v", "--version", help="cefpython version", - required=True) - args = parser.parse_args() - assert re.search(r"^\d+\.\d+$", args.version), ( - "Invalid version string") - - vars = {} - vars["APP_VERSION"] = args.version - vars["PLATFORM"] = sysconfig.get_platform() - vars["PY_VERSION_DIGITS_ONLY"] = (str(sys.version_info.major) + "" - + str(sys.version_info.minor)) # "27" or "34" - - print("Reading template: %s" % README_FILE) - f = open(README_FILE) - README_CONTENT = f.read() - f.close() - - print("Reading template: %s" % INIT_TEMPLATE) - f = open(INIT_TEMPLATE) - INIT_CONTENT = str_format(f.read(), vars) - f.close() - - print("Reading template: %s" % SETUP_TEMPLATE) - f = open(SETUP_TEMPLATE) - SETUP_CONTENT = str_format(f.read(), vars) - f.close() - - print("Reading template: %s" % SETUP_CFG_TEMPLATE) - f = open(SETUP_CFG_TEMPLATE) - SETUP_CFG_CONTENT = str_format(f.read(), vars) - f.close() - - installer_dir = os.path.dirname(os.path.abspath(__file__)) - - pyVersion = str(sys.version_info.major) +"."+ str(sys.version_info.minor) - setup_dir = installer_dir+"/"+PACKAGE_NAME+"-"+vars["APP_VERSION"]\ - +"."+BITS+"-py"+pyVersion+"-setup" - print("Creating setup dir: "+setup_dir) - os.mkdir(setup_dir) - - package_dir = setup_dir+"/"+PACKAGE_NAME - #print("Creating package dir") - #os.mkdir(package_dir) - - print("Creating README.txt from template") - with open(setup_dir+"/README.txt", "w") as f: - f.write(README_CONTENT) - - print("Creating setup.py from template") - with open(setup_dir+"/setup.py", "w") as f: - f.write(SETUP_CONTENT) - - print("Creating setup.cfg from template") - with open(setup_dir+"/setup.cfg", "w") as f: - f.write(SETUP_CFG_CONTENT) - - binaries_dir = os.path.abspath(installer_dir+"/../binaries_"+BITS+"/") - print("Copying binaries to package dir") - shutil.copytree(binaries_dir, package_dir) - - os.chdir(package_dir) - print("Removing .log .pyc .pdb files from the package dir") - glob_remove("*.log") - glob_remove("*.pyc") - glob_remove("*.pdb") - - os.chdir(installer_dir) - - print("Creating __init__.py from template") - with open(package_dir+"/__init__.py", "w") as f: - f.write(INIT_CONTENT) - - print("Creating examples dir in package dir") - os.mkdir(package_dir+"/examples/") - - print("Creating wx dir in package dir") - os.mkdir(package_dir+"/wx/") - - print("Moving example scripts from package dir to examples dir") - examples = glob.glob(package_dir+"/*.py") - for example in examples: - # Ignore: cefpython_py27.py - dummy API script - if os.path.basename(example).startswith("cefpython_"): - continue - # Ignore: __init__.py - if os.path.basename(example).startswith("__"): - continue - os.rename(example, package_dir+"/examples/"+os.path.basename(example)) - glob_move(package_dir+"/*.html", package_dir+"/examples/") - glob_move(package_dir+"/*.css", package_dir+"/examples/") - glob_move(package_dir+"/*.js", package_dir+"/examples/") - - print("Copying wx/ to package dir") - wx_subpackage_dir = os.path.abspath(installer_dir+"/../../wx/") - glob_copy(wx_subpackage_dir+"/*", package_dir+"/wx/") - - print("Moving wx examples from wx/examples to examples/wx") - glob_move(package_dir+"/wx/examples/*", package_dir+"/examples/wx/") - os.rmdir(package_dir+"/wx/examples/") - - print("Copying package dir examples to setup dir") - glob_copy(package_dir+"/examples/", setup_dir+"/examples/") - - # Create empty debug.log files so that package uninstalls cleanly - # in case examples were launched. Issue 149. - debug_log_dirs = [package_dir, - package_dir+"/examples/", - package_dir+"/examples/wx/"] - for dir in debug_log_dirs: - print("Creating empty debug.log in %s" % dir) - with open(dir+"/debug.log", "w") as f: - f.write("") - - print("Setup Package created successfully.") - -if __name__ == "__main__": - main() diff --git a/src/windows/installer/setup.cfg.template b/src/windows/installer/setup.cfg.template deleted file mode 100644 index 067dcbfd..00000000 --- a/src/windows/installer/setup.cfg.template +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -python-tag=cp%(PY_VERSION_DIGITS_ONLY)s diff --git a/src/windows/installer/setup.py.template b/src/windows/installer/setup.py.template deleted file mode 100644 index 36fb2809..00000000 --- a/src/windows/installer/setup.py.template +++ /dev/null @@ -1,68 +0,0 @@ -try: - # The setuptools package is not installed by default - # on a clean Ubuntu. Might be also a case on Windows. - # Python Eggs and Wheels can be created only with setuptools. - from setuptools import setup - from setuptools.command.install import install as _install - from setuptools.dist import Distribution - print("[setup.py] Using setuptools") -except: - from distutils.core import setup - from distutils.command.install import install as _install - from distutils.dist import Distribution - print("[setup.py] Using distutils") - -import sys -import os -import subprocess - -def post_install(): - """ Post install tasks """ - print("[setup.py] post_install()") - # Nothing extra is required to do on Windows. - -class install(_install): - def run(self): - _install.run(self) - post_install() - -class BinaryDistribution(Distribution): - def is_pure(self): - return False - -setup( - distclass=BinaryDistribution, - cmdclass={'install': install}, - name='cefpython3', # No spaces here, so that it works with deb packages. - version='%(APP_VERSION)s', - description='Python bindings for the Chromium Embedded Framework', - license='BSD 3-Clause', - author='Czarek Tomczak', - author_email='czarek.tomczak@gmail.com', - url='http://code.google.com/p/cefpython/', - platforms=['%(PLATFORM)s'], - packages=['cefpython3', 'cefpython3.wx'], - package_data={'cefpython3': [ - 'examples/*.py', - 'examples/*.html', - 'examples/*.js', - 'examples/*.css', - 'examples/wx/*.py', - 'examples/wx/*.html', - 'examples/wx/*.js', - 'examples/wx/*.css', - 'examples/wx/*.png', - 'locales/*.pak', - 'wx/*.txt', - 'wx/images/*.png', - '*.txt', - 'cefclient.exe', - 'subprocess.exe', - '*.pyd', - '*.dll', - '*.pak', - 'debug.log', - 'examples/debug.log', - 'examples/wx/debug.log', - ]} -) diff --git a/tools/automate.py b/tools/automate.py index 4da35108..0d966067 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -1,6 +1,22 @@ # Copyright (c) 2016 CEF Python, see the Authors file. All rights reserved. -"""Automates building CEF from sources with CEF Python patches applied. +""" +Prepares CEF binaries and libraries for work with the build.py tool. + +Option 1 is to build CEF from sources with the CEF Python patches applied +using the --build-cef flag. + +Option 2 is to use CEF binaries from Spotify Automated Builds using +the --prebuilt-cef flag. In such case check the cefpython/src/version/ +directory to know which version of CEF to download from Spotify: +http://opensource.spotify.com/cefbuilds/index.html +Download and extract it so that for example you have such a directory: +cefpython/build/cef_binary_3.2883.1553.g80bd606_windows32/ . + +This tool generates CEF binaries and libraries that are ready for work +with cefpython, with the build.py script. When automate.py tool completes +job you should see a new subdirectory in the build/ directory, for example: +cefpython/build/cef55_3.2883.1553.g80bd606_win32/ . Usage: automate.py (--prebuilt-cef | --build-cef) @@ -50,24 +66,6 @@ CEF_GIT_URL = "https://bitbucket.org/chromiumembedded/cef.git" -VS2015_VCVARS = "\"C:\Program Files (x86)\\Microsoft Visual Studio 14.0" \ - "\\VC\\vcvarsall.bat\" x86" \ - if ARCH32 else \ - "\"C:\Program Files (x86)\\Microsoft Visual Studio 14.0" \ - "\\VC\\vcvarsall.bat\" amd64" - -VS2013_VCVARS = "\"C:\Program Files (x86)\\Microsoft Visual Studio 12.0" \ - "\\VC\\vcvarsall.bat\" x86" \ - if ARCH32 else \ - "\"C:\Program Files (x86)\\Microsoft Visual Studio 12.0" \ - "\\VC\\cvarsall.bat\" amd64" - -VS2008_VCVARS = "\"%LocalAppData%\\Programs\\Common\\Microsoft" \ - "\\Visual C++ for Python\\9.0\\vcvarsall.bat\" x86" \ - if ARCH32 else \ - "\"%LocalAppData%\\Programs\\Common\\Microsoft" \ - "\\Visual C++ for Python\\9.0\\vcvarsall.bat\" amd64" - class Options(object): """Options from command-line and internal options.""" @@ -94,6 +92,9 @@ class Options(object): release_build = True build_type = "" # Will be set according to "release_build" value cef_binary = "" + build_cefclient_dir = "" + build_wrapper_mt_dir = "" + build_wrapper_md_dir = "" def main(): @@ -104,6 +105,10 @@ def main(): print(" automate-git.py works only with that version.") sys.exit(1) + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) + setup_options(docopt.docopt(__doc__)) if Options.build_cef: @@ -303,7 +308,7 @@ def build_cef_projects(): fix_cef_include_files() - # Find cef_binary directories and create the cef_binary/build/ dir + # Find cef_binary directory if not Options.cef_binary: if platform.system() == "Windows": files = glob.glob(os.path.join(Options.binary_distrib, @@ -324,127 +329,224 @@ def build_cef_projects(): assert os.path.exists(cef_binary) Options.cef_binary = cef_binary + # Set build directory + Options.build_cefclient_dir = os.path.join(Options.cef_binary, + "build_cefclient") + print("[automate.py] Creating build_cefclient dir in cef_binary dir") - build_cefclient = os.path.join(Options.cef_binary, "build_cefclient") + + # Check whether already built already_built = False - if os.path.exists(build_cefclient): + if build_cefclient_succeeded(): already_built = True + elif os.path.exists(Options.build_cefclient_dir): + # Last build failed, clean directory + assert Options.build_cefclient_dir + shutil.rmtree(Options.build_cefclient_dir) + os.makedirs(Options.build_cefclient_dir) else: - os.makedirs(build_cefclient) + os.makedirs(Options.build_cefclient_dir) # Build cefclient, cefsimple, ceftests if already_built: print("[automate.py] Already built: cefclient, cefsimple, ceftests") else: - print("[automate.py] Building cefclient, cefsimple, ceftests ...") + print("[automate.py] Build cefclient, cefsimple, ceftests") + # Cmake command = prepare_build_command() - command += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ - % Options.build_type - run_command(command, build_cefclient) + command.extend(["cmake", "-G", "Ninja", + "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) + run_command(command, Options.build_cefclient_dir) print("[automate.py] OK") - # On Linux cannot pass "&&" and run two commands using run_command() + # Ninja command = prepare_build_command() - command += "ninja cefclient cefsimple ceftests" - run_command(command, build_cefclient) + command.extend(["ninja", "cefclient", "cefsimple", "ceftests"]) + run_command(command, Options.build_cefclient_dir) print("[automate.py] OK") - if platform.system() == "Windows": - assert(os.path.exists(os.path.join(build_cefclient, - "tests", - "cefclient", - Options.build_type, - "cefclient.exe"))) - else: - assert (os.path.exists(os.path.join(build_cefclient, - "tests", - "cefclient", - Options.build_type, - "cefclient"))) + assert build_cefclient_succeeded() # Build libcef_dll_wrapper libs if platform.system() == "Windows": - build_wrapper_windows(Options.cef_binary) + build_wrapper_windows() -def prepare_build_command(build_lib=False): - """On Windows VS env variables must be set up by calling vcvarsall.bat""" - command = "" - if platform.system() == "Windows": - if build_lib: - msvs = get_msvs_for_python() - command = globals()["VS"+msvs+"_VCVARS"] + " && " - else: - if int(Options.cef_branch) >= 2704: - command = VS2015_VCVARS + " && " - else: - command = VS2013_VCVARS + " && " - return command +def build_wrapper_windows(): + # When building library cmake variables file is being modified + # for the /MD build. If the build fails and variables aren't + # restored then the next /MT build would be broken. Make sure + # that original contents of cmake variables files is always + # restored. + fix_cmake_variables_for_md_library(try_undo=True) - -def build_wrapper_windows(cef_binary): # Command to build libcef_dll_wrapper - wrapper_cmake = prepare_build_command(build_lib=True) - wrapper_cmake += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ - % Options.build_type + cmake_wrapper = prepare_build_command(build_lib=True) + cmake_wrapper.extend(["cmake", "-G", "Ninja", + "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) + + # Set build directory for /MT lib. + Options.build_wrapper_mt_dir = os.path.join(Options.cef_binary, + "build_wrapper_mt") - # Build libcef_dll_wrapper_mt.lib - build_wrapper_mt = os.path.join(cef_binary, "build_wrapper_mt") + # Check whether already built mt_already_built = False - if os.path.exists(build_wrapper_mt): + if build_wrapper_mt_succeeded(): mt_already_built = True + elif os.path.exists(Options.build_wrapper_mt_dir): + # Last build failed, clean directory + assert Options.build_wrapper_mt_dir + shutil.rmtree(Options.build_wrapper_mt_dir) + os.makedirs(Options.build_wrapper_mt_dir) else: - os.makedirs(build_wrapper_mt) + os.makedirs(Options.build_wrapper_mt_dir) + + # Build /MT lib. if mt_already_built: print("[automate.py] Already built: libcef_dll_wrapper /MT") else: - print("[automate.py] Building libcef_dll_wrapper /MT") + print("[automate.py] Build libcef_dll_wrapper /MT") old_gyp_msvs_version = Options.gyp_msvs_version Options.gyp_msvs_version = get_msvs_for_python() - run_command(wrapper_cmake, build_wrapper_mt) + # Cmake + run_command(cmake_wrapper, Options.build_wrapper_mt_dir) Options.gyp_msvs_version = old_gyp_msvs_version print("[automate.py] cmake OK") + # Ninja ninja_wrapper = prepare_build_command(build_lib=True) - ninja_wrapper += "ninja libcef_dll_wrapper" - run_command(ninja_wrapper, build_wrapper_mt) + ninja_wrapper.extend(["ninja", "libcef_dll_wrapper"]) + run_command(ninja_wrapper, Options.build_wrapper_mt_dir) print("[automate.py] ninja OK") - assert(os.path.exists(os.path.join(build_wrapper_mt, - "libcef_dll_wrapper", - "libcef_dll_wrapper.lib"))) + assert build_wrapper_mt_succeeded() - # Build libcef_dll_wrapper_md.lib - build_wrapper_md = os.path.join(cef_binary, "build_wrapper_md") + # Set build directory for /MD lib. + Options.build_wrapper_md_dir = os.path.join(Options.cef_binary, + "build_wrapper_md") + + # Check whether already built md_already_built = False - if os.path.exists(build_wrapper_md): + if build_wrapper_md_succeeded(): md_already_built = True + elif os.path.exists(Options.build_wrapper_md_dir): + # Last build failed, clean directory + assert Options.build_wrapper_md_dir + shutil.rmtree(Options.build_wrapper_md_dir) + os.makedirs(Options.build_wrapper_md_dir) else: - os.makedirs(build_wrapper_md) + os.makedirs(Options.build_wrapper_md_dir) + + # Build /MD lib. if md_already_built: print("[automate.py] Already built: libcef_dll_wrapper /MD") else: - print("[automate.py] Building libcef_dll_wrapper /MD") + print("[automate.py] Build libcef_dll_wrapper /MD") old_gyp_msvs_version = Options.gyp_msvs_version Options.gyp_msvs_version = get_msvs_for_python() - # Replace /MT with /MD /wd\"4275\" in CMakeLists.txt - # Warnings are treated as errors so this needs to be ignored: - # >> warning C4275: non dll-interface class 'stdext::exception' - # >> used as base for dll-interface class 'std::bad_cast' - # This warning occurs only in VS2008, in VS2013 not. - cmakelists = os.path.join(cef_binary, "CMakeLists.txt") - with open(cmakelists, "rb") as fp: - contents = fp.read() - contents = contents.replace(r"/MT ", r"/MD /wd\"4275\" ") - contents = contents.replace(r"/MTd ", r"/MDd /wd\"4275\" ") - with open(cmakelists, "wb") as fp: - fp.write(contents) - run_command(wrapper_cmake, build_wrapper_md) + # Fix cmake variables + # Cmake + fix_cmake_variables_for_md_library() + run_command(cmake_wrapper, Options.build_wrapper_md_dir) Options.gyp_msvs_version = old_gyp_msvs_version + fix_cmake_variables_for_md_library(undo=True) print("[automate.py] cmake OK") + # Ninja ninja_wrapper = prepare_build_command(build_lib=True) - ninja_wrapper += "ninja libcef_dll_wrapper" - run_command(ninja_wrapper, build_wrapper_md) + ninja_wrapper.extend(["ninja", "libcef_dll_wrapper"]) + run_command(ninja_wrapper, Options.build_wrapper_md_dir) print("[automate.py] ninja OK") - assert(os.path.exists(os.path.join(build_wrapper_md, - "libcef_dll_wrapper", - "libcef_dll_wrapper.lib"))) + assert build_wrapper_md_succeeded() + + +def fix_cmake_variables_for_md_library(undo=False, try_undo=False): + """Fix cmake variables or undo it. The try_undo param is + for a case when want to be sure that the file wasn't modified, + for example in case the last build failed.""" + + # Replace /MT with /MD /wd4275 in cef/cmake/cef_variables.cmake + # Warnings are treated as errors so this needs to be ignored: + # >> warning C4275: non dll-interface class 'stdext::exception' + # >> used as base for dll-interface class 'std::bad_cast' + # This warning occurs only in VS2008, in VS2013 not. + # This replacements must be unique for the undo operation + # to be reliable. + + mt_find = r"/MT " + mt_replace = r"/MD /wd4275 " + + mtd_find = r"/MTd " + mtd_replace = r"/MDd /wd4275 " + + cmake_variables = os.path.join(Options.cef_binary, "cmake", + "cef_variables.cmake") + with open(cmake_variables, "rb") as fp: + contents = fp.read() + + if try_undo: + matches1 = re.findall(re.escape(mt_replace), contents) + matches2 = re.findall(re.escape(mtd_replace), contents) + if len(matches1) or len(matches2): + undo = True + else: + return + + if undo: + (contents, count) = re.subn(re.escape(mt_replace), mt_find, + contents) + assert count == 2 + (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 + (contents, count) = re.subn(re.escape(mtd_find), mtd_replace, + contents) + assert count == 1 + + with open(cmake_variables, "wb") as fp: + fp.write(contents) + + +def build_cefclient_succeeded(): + """Whether building cefclient/cefsimple/ceftests succeeded.""" + assert Options.build_cefclient_dir + cefclient_exe = "cefclient.exe" if WINDOWS else "cefclient" + return os.path.exists(os.path.join(Options.build_cefclient_dir, + "tests", + "cefclient", + Options.build_type, + cefclient_exe)) + + +def build_wrapper_mt_succeeded(): + """Whether building /MT library succeeded (Windows-only).""" + assert Options.build_wrapper_mt_dir + return os.path.exists(os.path.join(Options.build_wrapper_mt_dir, + "libcef_dll_wrapper", + "libcef_dll_wrapper.lib")) + + +def build_wrapper_md_succeeded(): + """Whether building /MD library succeeded (Windows-only).""" + assert Options.build_wrapper_md_dir + return os.path.exists(os.path.join(Options.build_wrapper_md_dir, + "libcef_dll_wrapper", + "libcef_dll_wrapper.lib")) + + +def prepare_build_command(build_lib=False): + """On Windows VS env variables must be set up by calling vcvarsall.bat""" + command = list() + if platform.system() == "Windows": + if build_lib: + msvs = get_msvs_for_python() + command.append(globals()["VS"+msvs+"_VCVARS"]) + else: + if int(Options.cef_branch) >= 2704: + command.append(VS2015_VCVARS) + else: + command.append(VS2013_VCVARS) + command.append("&&") + return command def fix_cef_include_files(): @@ -600,13 +702,16 @@ def getenv(): return env -def run_command(command_line, working_dir): +def run_command(command, working_dir): """Run command in a given directory with env variables set. On Linux multiple commands on one line with the use of && are not allowed. """ - print("[automate.py] Running '"+command_line+"' in '" + + print("[automate.py] Running '"+" ".join(command)+"' in '" + working_dir+"'...") - args = shlex.split(command_line.replace("\\", "\\\\")) + if isinstance(command, str): + args = shlex.split(command.replace("\\", "\\\\")) + else: + args = command return subprocess.check_call(args, cwd=working_dir, env=getenv(), shell=(platform.system() == "Windows")) diff --git a/tools/build.py b/tools/build.py index 48c13874..6c424a78 100644 --- a/tools/build.py +++ b/tools/build.py @@ -1,16 +1,29 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under the BSD 3-clause license. + """ Build the cefpython module, install package and run example. +Before running you must first put cefpython ready CEF binaries and +libraries in the cefpython/build/ directory (create if doesn't exist). +You have two options for obtaining these binaries and libraries. + +Option 1: Download upstream CEF binaries and libraries from cefpython +GitHub Releases page. These binaries are tagged eg. "v55-upstream". +Extract the archive so that for example you have such a directory: +cefpython/build/cef55_3.2883.1553.g80bd606_win32/ . + +Option 2: Use the automate.py tool. With this tool you can build CEF +from sources or use ready binaries from Spotify Automated Builds. + Usage: - build.py VERSION [--debug] [--fast] + build.py VERSION [--rebuild-cpp] [--fast] [--kivy] Options: - VERSION Version in format xx.xx - --debug Debug mode - --fast Fast mode, don't delete C++ .o .a files, nor the setup/build/ - directory, and disable optimization flags when building - the so/pyd module. - --kivy Run Kivy example + VERSION Version in format xx.xx + --rebuild-cpp Force rebuild of C++ projects + --fast Fast mode + --kivy Run only Kivy example """ # How to debug on Linux: @@ -56,21 +69,18 @@ else: MODULE_EXT = "so" -# Compiler options -if MAC: - os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] - os.environ["CC"] = "gcc" - os.environ["CXX"] = "g++" - os.environ["CEF_CCFLAGS"] = "-arch x86_64" - os.environ["ARCHFLAGS"] = "-arch x86_64" - if ARCH32: - raise Exception("Python 32bit is not supported on Mac") +# First run +FIRST_RUN = False +CEFPYTHON_H = os.path.join(BUILD_CEFPYTHON, "cefpython.h") def main(): + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) print("[build.py] PYVERSION = %s" % PYVERSION) print("[build.py] OS_POSTFIX2 = %s" % OS_POSTFIX2) - setup_environ_path() + setup_environ() check_cython_version() command_line_args() check_directories() @@ -87,28 +97,57 @@ def main(): install_and_run() -def setup_environ_path(): - print("[build.py] Setup environment PATH") +def setup_environ(): + """Set environment variables. Set PATH so that it contains only + minimum set of directories,to avoid any possible issues. Set Python + include path. Set Mac compiler options. Etc.""" + print("[build.py] Setup environment variables") + if not WINDOWS: return - if ARCH32: - os.environ["PATH"] = ("C:\\Windows\\system32;C:\\Windows;" - "C:\\Windows\\System32\\Wbem;C:\\Python27") - else: - raise Exception("Only 32-bit is currently supported") - print("[build.py] PATH: {path}".format(path=os.environ["PATH"])) + # PATH + if WINDOWS: + path = [ + "C:\\Windows\\system32", + "C:\\Windows", + "C:\\Windows\\System32\\Wbem", + get_python_path(), + ] + os.environ["PATH"] = os.pathsep.join(path) + print("[build.py] environ PATH: {path}" + .format(path=os.environ["PATH"])) + + # INCLUDE env for vcproj build + if WINDOWS: + if "INCLUDE" not in os.environ: + os.environ["INCLUDE"] = "" + os.environ["INCLUDE"] += os.pathsep + os.path.join(get_python_path(), + "include") + print("[build.py] environ INCLUDE: {include}" + .format(include=os.environ["INCLUDE"])) + + # LIB env for vcproj build + if WINDOWS: + os.environ["AdditionalLibraryDirectories"] = os.path.join( + CEF_BINARIES_LIBRARIES, "lib") + print("[build.py] environ AdditionalLibraryDirectories: {lib}" + .format(lib=os.environ["AdditionalLibraryDirectories"])) - """ - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; - C:\Python27_x64;C:\Python27_amd64;C:\Python27_64 + # Mac compiler options + if MAC: + os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] + os.environ["CC"] = "gcc" + os.environ["CXX"] = "g++" + os.environ["CEF_CCFLAGS"] = "-arch x86_64" + os.environ["ARCHFLAGS"] = "-arch x86_64" + if ARCH32: + raise Exception("Python 32-bit is not supported on Mac") - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; - C:\Python34 - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; - C:\Python34_x64;C:\Python34_amd64;C:\Python34_64 - """ +def get_python_path(): + """Get Python path.""" + return os.path.dirname(sys.executable) def check_cython_version(): @@ -185,7 +224,7 @@ def check_directories(): # Check directories exist assert os.path.exists(BUILD_DIR) assert os.path.exists(BUILD_CEFPYTHON) - assert os.path.exists(CEF_BINARY) + assert os.path.exists(CEF_BINARIES_LIBRARIES) assert os.path.exists(CEFPYTHON_BINARY) @@ -204,31 +243,44 @@ def fix_cefpython_h(): content = ("%s\n\n" % pragma) + content with open("cefpython.h", "w") as fo: fo.write(content) - print("[build.py] Saved cefpython.h") + print("[build.py] Save build_cefpython/cefpython.h") def compile_cpp_projects_windows(): print("[build.py] Compile C++ projects") - print("[build.py] Build client_handler vcproj") + print("[build.py] ~~ Build CLIENT_HANDLER vcproj") vcproj = ("client_handler_py{pyver}_{os}.vcproj" .format(pyver=PYVERSION, os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "client_handler", vcproj) build_vcproj(vcproj) - print("[build.py] Build libcefpythonapp vcproj") + print("[build.py] ~~ Build LIBCEFPYTHONAPP vcproj") vcproj = ("libcefpythonapp_py{pyver}_{os}.vcproj" .format(pyver=PYVERSION, os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "subprocess", vcproj) build_vcproj(vcproj) - print("[build.py] Build subprocess vcproj") + print("[build.py] ~~ Build SUBPROCESS vcproj") vcproj = ("subprocess_{os}.vcproj" .format(os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "subprocess", vcproj) - build_vcproj(vcproj) - - print("[build.py] Build cpp_utils vcproj") + ret = build_vcproj(vcproj) + + # Copy subprocess executable + subprocess_from = os.path.join( + SUBPROCESS_DIR, + "Release_{os}".format(os=OS_POSTFIX2), + "subprocess_{os}.exe".format(os=OS_POSTFIX2)) + subprocess_to = os.path.join(CEFPYTHON_BINARY, "subprocess.exe") + if os.path.exists(subprocess_to): + os.remove(subprocess_to) + if ret == 0: + print("[build.py] Copy subprocess executable") + # shutil.copy() will also copy Permission bits + shutil.copy(subprocess_from, subprocess_to) + + print("[build.py] ~~ Build CPP_UTILS vcproj") vcproj = ("cpp_utils_{os}.vcproj" .format(os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "cpp_utils", vcproj) @@ -236,11 +288,19 @@ def compile_cpp_projects_windows(): def build_vcproj(vcproj): + if not os.path.exists(CEFPYTHON_H): + print("[build.py] INFO: Looks like first run, as cefpython.h" + " is missing. Skip building C++ project.") + global FIRST_RUN + FIRST_RUN = True + return + if PYVERSION == "27": args = list() - args.append("%LocalAppData%\\Programs\\Common\\" - "Microsoft\\Visual C++ for Python\\9.0\\" - "VC\\bin\\amd64\\vcbuild.exe") + args.append(VS2008_VCVARS) + args.append(VS_PLATFORM_ARG) + args.append("&&") + args.append(VS2008_BUILD) args.append("/nocolor") args.append("/nologo") args.append("/nohtmllog") @@ -250,20 +310,17 @@ def build_vcproj(vcproj): ret = subprocess.call(args, shell=True) if ret != 0: compile_ask_to_continue() + return ret else: raise Exception("Only Python 2.7 32-bit is currently supported") - """ - In VS2010 vcbuild was replaced by msbuild.exe. - /clp:disableconsolecolor - msbuild /p:BuildProjectReferences=false project.proj - MSBuild.exe MyProject.proj /t:build - """ + # In VS2010 vcbuild was replaced by msbuild.exe. + # /clp:disableconsolecolor + # msbuild /p:BuildProjectReferences=false project.proj + # MSBuild.exe MyProject.proj /t:build def compile_ask_to_continue(): - print("[build.py] **INFO**: On first run you should continue despite" - " errors and after completion re-run the build.py script again") # noinspection PyUnboundLocalVariable what = input("[build.py] make failed, 'y' to continue, Enter to stop: ") if what != "y": @@ -280,17 +337,8 @@ def compile_cpp_projects_unix(): # fails and then run the compile.py script again and this time # make should succeed. - # -------- CPP_UTILS_DIR - - os.chdir(CPP_UTILS_DIR) - if not FAST_FLAG: - subprocess.call("rm -f *.o *.a", shell=True) - - ret = subprocess.call("make -f Makefile", shell=True) - if ret != 0: - compile_ask_to_continue() - - # -------- CLIENT_HANDLER_DIR + # -- CLIENT_HANDLER + print("[build.py] ~~ Build CLIENT_HANDLER project") os.chdir(CLIENT_HANDLER_DIR) if not FAST_FLAG: @@ -300,7 +348,8 @@ def compile_cpp_projects_unix(): if ret != 0: compile_ask_to_continue() - # -------- SUBPROCESS_DIR + # -- LIBCEFPYTHONAPP + print("[build.py] ~~ Build LIBCEFPYTHONAPP project") os.chdir(SUBPROCESS_DIR) if not FAST_FLAG: @@ -311,14 +360,29 @@ def compile_cpp_projects_unix(): if ret != 0: compile_ask_to_continue() + # -- SUBPROCESS + print("[build.py] ~~ Build SUBPROCESS project") ret = subprocess.call("make -f Makefile", shell=True) if ret != 0: compile_ask_to_continue() - subprocess_exe = os.path.join(CEFPYTHON_BINARY, "subprocess") - if os.path.exists("./subprocess"): - # .copy() will also copy Permission bits - shutil.copy("./subprocess", subprocess_exe) + # Copy subprocess executable + subprocess_from = os.path.join(SUBPROCESS_DIR, "subprocess") + subprocess_to = os.path.join(CEFPYTHON_BINARY, "subprocess") + if os.path.exists(subprocess_from): + # shutil.copy() will also copy Permission bits + shutil.copy(subprocess_from, subprocess_to) + + # -- CPP_UTILS + print("[build.py] ~~ Build CPP_UTILS project") + + os.chdir(CPP_UTILS_DIR) + if not FAST_FLAG: + subprocess.call("rm -f *.o *.a", shell=True) + + ret = subprocess.call("make -f Makefile", shell=True) + if ret != 0: + compile_ask_to_continue() def clear_cache(): @@ -510,7 +574,19 @@ def build_cefpython_module(): # Check if built succeeded after pyx files were removed if ret != 0: - print("[build.py] FAILED to build the cefpython module") + if FIRST_RUN and os.path.exists(CEFPYTHON_H): + print("[build.py] INFO: looks like this was first run and" + " linking is expected to fail in such case. Will re-run" + " the build.py script programmatically now.") + args = list() + args.append(sys.executable) + args.append(os.path.join(TOOLS_DIR, os.path.basename(__file__))) + assert __file__ in sys.argv[0] + args.extend(sys.argv[1:]) + ret = subprocess.call(args, shell=True) + sys.exit(ret) + else: + print("[build.py] ERROR: failed to build the cefpython module") sys.exit(1) # Move the cefpython module @@ -521,7 +597,7 @@ def build_cefpython_module(): .format(pyver=PYVERSION, ext=MODULE_EXT))) - print("[build.py] Done building the cefpython module") + print("[build.py] DONE building the cefpython module") def move_file_by_pattern(pattern, move_to): @@ -553,60 +629,44 @@ def delete_directories_by_pattern(pattern): def install_and_run(): - os.chdir(BUILD_CEFPYTHON) - # if DEBUG_FLAG: # os.chdir("./binaries_%s" % BITS) # subprocess.call("cygdb . --args python-dbg wxpython.py", shell=True) print("[build.py] Install and run...") + os.chdir(BUILD_DIR) - # Clean installer directory from previous run - try: - delete_directories_by_pattern("./cefpython3-{ver}-*-setup/" - .format(ver=VERSION)) - except: - if LINUX: - os.system("sudo rm -rf ./cefpython3-{ver}-*-setup/" - .format(ver=VERSION)) - else: - raise + # Setup installer directory + setup_installer_dir = ("./cefpython3-{version}-{os}-setup/" + .format(version=VERSION, os=OS_POSTFIX2)) + setup_installer_dir = os.path.join(BUILD_DIR, setup_installer_dir) - # System python requires sudo when installing package - if sys.executable in ["/usr/bin/python", "/usr/bin/python3"]: - sudo = "sudo" - else: - sudo = "" - - os.chdir(BUILD_CEFPYTHON) + # Delete setup installer directory if exists + if os.path.exists(setup_installer_dir): + delete_directory_reliably(setup_installer_dir) # Make setup installer print("[build.py] Make setup installer") - os.system("{python} ../../tools/setup/make.py --version {ver}" - .format(python=sys.executable, ver=VERSION)) - - # Enter setup installer directory - os.chdir("cefpython3-{ver}-{os_postfix2}-setup/" - .format(ver=VERSION, os_postfix2=OS_POSTFIX2)) + make_tool = os.path.join(TOOLS_DIR, "make_installer.py") + os.system("{python} {make_tool} --version {version}" + .format(python=sys.executable, + make_tool=make_tool, + version=VERSION)) # Install print("[build.py] Install the cefpython package") + os.chdir(setup_installer_dir) os.system("{sudo} {python} setup.py install" - .format(sudo=sudo, python=sys.executable)) - - # Delete setup installer directory - print("[build.py] Delete the setup installer directory") - if WINDOWS: - shutil.rmtree("./cefpython3-{ver}-{os_postfix2}-setup/" - .format(ver=VERSION, os_postfix2=OS_POSTFIX2)) - else: - os.system("{sudo} rm -rf ./cefpython3-{ver}-*-setup/" - .format(sudo=sudo, ver=VERSION)) + .format(sudo=get_sudo(), python=sys.executable)) + os.chdir(BUILD_DIR) # Run unittests print("[build.py] Run unittests") - os.system("cd {unittests_dir} && {python} _test_runner.py" - .format(unittests_dir=UNITTESTS_DIR, python=sys.executable)) + test_runner = os.path.join(UNITTESTS_DIR, "_test_runner.py") + ret = os.system("{python} {test_runner}" + .format(python=sys.executable, test_runner=test_runner)) + if ret != 0: + sys.exit(ret) # Run examples print("[build.py] Run examples") @@ -625,14 +685,38 @@ def install_and_run(): if LINUX: run_examples += (" && {python}" " {linux_dir}/deprecated_64bit/kivy_.py") - run_examples.format(linux_dir=LINUX_DIR, examples_dir=EXAMPLES_DIR) + run_examples.format( + python=sys.executable, + linux_dir=LINUX_DIR, + examples_dir=EXAMPLES_DIR) os.system(run_examples) - # Enter tools dir - os.system("cd {tools_dir}".format(tools_dir=TOOLS_DIR)) - print("[build.py] DONE") +def get_sudo(): + # System Python requires sudo when installing package + if sys.executable in ["/usr/bin/python", "/usr/bin/python3"]: + sudo = "sudo" + else: + sudo = "" + return sudo + + +def delete_directory_reliably(adir): + assert len(adir) > 2 + assert os.path.isdir(adir) + print("[build.py] Delete directory: {dir}" + .format(dir=adir.replace(ROOT_DIR, ""))) + if WINDOWS: + shutil.rmtree(adir) + else: + # On Linux sudo might be required to delete directory, as this + # might be a setup installer directory with package installed + # using sudo and in such case files were created with sudo. + os.system("{sudo} rm -rf {dir}" + .format(sudo=get_sudo(), dir=adir)) + + if __name__ == "__main__": main() diff --git a/tools/build_module.py b/tools/build_module.py index bb99eb2f..0d1524df 100644 --- a/tools/build_module.py +++ b/tools/build_module.py @@ -1,5 +1,10 @@ -# For internal use only - called by build.py. -# This is Cython's setup for building the cefpython module. +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under the BSD 3-clause license. + +""" +build_module.py is for internal use only - called by build.py. +This is Cython's setup for building the cefpython module +""" # Use setuptools so that "Visual C++ compiler for Python 2.7" tools # can be used. Otherwise "Unable to find vcvarsall.bat" error occurs. @@ -171,7 +176,7 @@ def get_include_dirs(): def get_library_dirs(): print("[build_module.py] Prepare library directories") library_dirs = [ - os.path.join(CEF_BINARY, "lib"), + os.path.join(CEF_BINARIES_LIBRARIES, "lib"), ] if WINDOWS: library_dirs.extend([ @@ -182,8 +187,11 @@ def get_library_dirs(): os.path.join(SRC_DIR, "subprocess", "Release_{os}" .format(os=OS_POSTFIX2)), + os.path.join(SRC_DIR, "subprocess", + "Release_py{pyver}_{os}" + .format(pyver=PYVERSION, os=OS_POSTFIX2)), os.path.join(SRC_DIR, "cpp_utils", - "Release_py{os}" + "Release_{os}" .format(os=OS_POSTFIX2)) ]) if MAC or LINUX: @@ -280,6 +288,9 @@ def compile_time_constants(): def main(): + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) print("[build_module.py] Cython version: %s" % Cython.__version__) compile_time_constants() options = dict() diff --git a/tools/common.py b/tools/common.py index f42a2667..fb0a733b 100644 --- a/tools/common.py +++ b/tools/common.py @@ -29,32 +29,55 @@ # Python version eg. 27 PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) -# Directories -TOOLS_DIR = os.path.abspath(os.path.dirname(__file__)) -SRC_DIR = os.path.abspath(os.path.join(TOOLS_DIR, "../src")) -WINDOWS_DIR = os.path.abspath(os.path.join(SRC_DIR, "windows")) -MAC_DIR = os.path.abspath(os.path.join(SRC_DIR, "mac")) -LINUX_DIR = os.path.abspath(os.path.join(SRC_DIR, "linux")) -CPP_UTILS_DIR = os.path.abspath(os.path.join(SRC_DIR, "cpp_utils")) -CLIENT_HANDLER_DIR = os.path.abspath(os.path.join(SRC_DIR, "client_handler")) -SUBPROCESS_DIR = os.path.abspath(os.path.join(SRC_DIR, "subprocess")) -CEFPYTHON_DIR = os.path.abspath(os.path.join(SRC_DIR, "..")) -EXAMPLES_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "examples")) -UNITTESTS_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "unittests")) -BUILD_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "build")) -BUILD_CEFPYTHON = os.path.abspath(os.path.join(BUILD_DIR, "build_cefpython")) -# CEF_BINARY may be auto-overwritten through detect_cef_binary_directory() -CEF_BINARY = os.path.abspath(os.path.join(BUILD_DIR, "cef_"+OS_POSTFIX2)) -CEFPYTHON_BINARY = os.path.abspath(os.path.join(BUILD_DIR, - "cefpython_"+OS_POSTFIX2)) - - -def detect_cef_binary_directory(): - # Detect cef binary directory created by automate.py - # eg. build/cef55_3.2883.1553.g80bd606_win32/ - # and set CEF_BINARY to it. Otherwise CEF_BINARY will - # indicate to build/cef_{os_postfix2}/. - if not os.path.exists(CEF_BINARY): +# Root directory +assert __file__ +ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + +# Other directories +BUILD_DIR = os.path.join(ROOT_DIR, "build") +if BUILD_DIR: + BUILD_CEFPYTHON = os.path.join(BUILD_DIR, "build_cefpython") + # -- Auto-detected directories. + # May be auto-overwritten through detect_cef_binaries_libraries_dir() + CEF_BINARIES_LIBRARIES = os.path.join(BUILD_DIR, "cef_"+OS_POSTFIX2) + # Will be overwritten through detect_cefpython_binary_dir() + CEFPYTHON_BINARY = "CEFPYTHON_BINARY" +EXAMPLES_DIR = os.path.join(ROOT_DIR, "examples") +SRC_DIR = os.path.join(ROOT_DIR, "src") +if SRC_DIR: + CLIENT_HANDLER_DIR = os.path.join(SRC_DIR, "client_handler") + CPP_UTILS_DIR = os.path.join(SRC_DIR, "cpp_utils") + LINUX_DIR = os.path.join(SRC_DIR, "linux") + MAC_DIR = os.path.join(SRC_DIR, "mac") + SUBPROCESS_DIR = os.path.join(SRC_DIR, "subprocess") + WINDOWS_DIR = os.path.abspath(os.path.join(SRC_DIR, "windows")) +TOOLS_DIR = os.path.join(ROOT_DIR, "tools") +if TOOLS_DIR: + INSTALLER_DIR = os.path.join(TOOLS_DIR, "installer") +UNITTESTS_DIR = os.path.abspath(os.path.join(ROOT_DIR, "unittests")) + +# Visual Studio constants +VS_PLATFORM_ARG = "x86" if ARCH32 else "amd64" +VS2015_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0" + "\\VC\\vcvarsall.bat") +VS2015_BUILD = "" # TODO +VS2013_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 12.0" + "\\VC\\vcvarsall.bat") +VS2013_BUILD = "" # TODO +VS2008_VCVARS = ("%LocalAppData%\\Programs\\Common\\Microsoft" + "\\Visual C++ for Python\\9.0\\vcvarsall.bat") +VS2008_BUILD = ("%LocalAppData%\\Programs\\Common\\" + "Microsoft\\Visual C++ for Python\\9.0\\" + "VC\\bin\\amd64\\vcbuild.exe") + + +def detect_cef_binaries_libraries_dir(): + """Detect cef binary directory created by automate.py + eg. build/cef55_3.2883.1553.g80bd606_win32/ + and set CEF_BINARIES_LIBRARIES to it, otherwise it will + point to eg. build/cef_win32/ .""" + global CEF_BINARIES_LIBRARIES + if not os.path.exists(CEF_BINARIES_LIBRARIES): version = get_cefpython_version() dirs = glob.glob(os.path.join( BUILD_DIR, @@ -64,10 +87,19 @@ def detect_cef_binary_directory(): os=OS_POSTFIX2, sep=os.sep))) if len(dirs) == 1: - print("[common.py] Auto detected CEF_BINARY directory: {dir}" - .format(dir=dirs[0])) - global CEF_BINARY - CEF_BINARY = dirs[0] + CEF_BINARIES_LIBRARIES = os.path.normpath(dirs[0]) + + +def detect_cefpython_binary_dir(): + """Detect cefpython binary directory where cefpython modules + will be put. Eg. buil/cefpython55_win32/.""" + version = get_cefpython_version() + binary_dir = "cefpython{major}_{os}".format( + major=version["CHROME_VERSION_MAJOR"], + os=OS_POSTFIX2) + binary_dir = os.path.join(BUILD_DIR, binary_dir) + global CEFPYTHON_BINARY + CEFPYTHON_BINARY = binary_dir def get_cefpython_version(): @@ -88,4 +120,5 @@ def get_version_from_file(header_file): return ret -detect_cef_binary_directory() +detect_cef_binaries_libraries_dir() +detect_cefpython_binary_dir() diff --git a/tools/installer/cefpython3.README.txt b/tools/installer/cefpython3.README.txt new file mode 100644 index 00000000..d23dea09 --- /dev/null +++ b/tools/installer/cefpython3.README.txt @@ -0,0 +1,13 @@ +1. To install CEF Python 3 package type: + + python setup.py install + + On Linux/Mac if using system Python then you need to add sudo: + + sudo python setup.py install + +2. To run examples enter the examples/ directory, + start with the hello_world.py example: + + cd examples/ + python hello_world.py diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py new file mode 100644 index 00000000..ca54b44b --- /dev/null +++ b/tools/installer/cefpython3.__init__.py @@ -0,0 +1,62 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under BSD 3-clause license. + +# NOTE: Template variables like {{VERSION}} are replaced with actual +# values when make.py tool generates this package installer. + +import os +import sys +import ctypes +import platform + +__all__ = ["cefpython", "wx"] +__version__ = "{{VERSION}}" +__author__ = "The CEF Python authors" + +# If package was installed using PIP or setup.py then package +# dir is here: +# /usr/local/lib/python2.7/dist-packages/cefpython3/ + +# If this is a debian package then package_dir returns: +# /usr/lib/pymodules/python2.7/cefpython3 +# The above path consists of symbolic links to the real directory: +# /usr/share/pyshared/cefpython3 + +package_dir = os.path.dirname(os.path.abspath(__file__)) + +# This loads the libcef.so library for the subprocess executable. +# On Mac it works without setting library paths, but let's set it +# just to be sure. +os.environ["LD_LIBRARY_PATH"] = package_dir +os.environ["DYLD_LIBRARY_PATH"] = package_dir + +# This env variable will be returned by cefpython.GetModuleDirectory(). +os.environ["CEFPYTHON3_PATH"] = package_dir + +# This loads the libcef library for the main python executable. +# This is required only on linux and Mac. +# The libffmpegsumo.so library does not need to be loaded here, +# it may cause issues to load it here in the browser process. +libcef = None +if platform.system() == "Darwin": + libcef = os.path.join(package_dir, "libcef.dylib") +elif platform.system() == "Linux": + libcef = os.path.join(package_dir, "libcef.so") +if libcef: + ctypes.CDLL(libcef, ctypes.RTLD_GLOBAL) + +# Load the cefpython module for proper Python version +if (2, 7) <= sys.version_info < (2, 8): + # noinspection PyUnresolvedReferences + from . import cefpython_py27 as cefpython +elif (3, 4) <= sys.version_info < (3, 5): + # noinspection PyUnresolvedReferences + from . import cefpython_py34 as cefpython +elif (3, 5) <= sys.version_info < (3, 6): + # noinspection PyUnresolvedReferences + from . import cefpython_py35 as cefpython +elif (3, 6) <= sys.version_info < (3, 7): + # 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 new file mode 100644 index 00000000..646c5fb3 --- /dev/null +++ b/tools/installer/cefpython3.setup.py @@ -0,0 +1,225 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under BSD 3-clause license. + +""" +cefpython3 package setup.py file. + +Usage: + setup.py install + setup.py bdist_wheel [--universal] + +Options: + install Install package + bdist_wheel Generate wheel package. Use the --universal flag when + you have built cefpython modules for multiple Python + versions. +""" + +# NOTE: Template variables like {{VERSION}} are replaced with actual +# values when make.py tool generates this package installer. + +import copy +import os +import platform +import subprocess +import sys + +# The setuptools package is not installed by default on a clean +# Ubuntu. Might be also a case on Windows. Also Python Eggs +# and Wheels can be created only with setuptools. +try: + from setuptools import setup + from setuptools.command.install import install + from setuptools.dist import Distribution + print("[setup.py] Using setuptools") +except ImportError: + from distutils.core import setup + from distutils.command.install import install + from distutils.dist import Distribution + print("[setup.py] Using distutils") + if "bdist_wheel" in sys.argv: + print("[setup.py] ERROR: You must install setuptools package using" + " pip tool to be able to create a wheel package. Type" + " 'pip install setuptools'.") + sys.exit(1) + + +# Need to know which files are executables to set appropriate execute +# permissions during post_install_hook. On Windows .exe postfix will +# be added to these automatically. +EXECUTABLES_NOEXT = [ + "cefclient", + "cefsimple", + "ceftests", + "subprocess", +] + + +class custom_install(install): + def __init__(self, *args, **kwargs): + install.__init__(self, *args, **kwargs) + + def run(self): + install.run(self) + post_install_hook() + + +class BinaryDistribution(Distribution): + def is_pure(self): + return False + + +# Provide a custom install command +print("[setup.py] Overload install command to enable execution of" + " post install hook") +cmdclass = {"install": custom_install} + +# Set custom platform tags on Mac when generating wheel package. See: +# http://lepture.com/en/2014/python-on-a-hard-wheel +if platform.system() == "Darwin" and "bdist_wheel" in sys.argv: + print("[setup.py] Overload bdist_wheel command to add custom" + " platform tags") + from wheel.bdist_wheel import bdist_wheel + + class custom_bdist_wheel(bdist_wheel): + def get_tag(self): + tag = bdist_wheel.get_tag(self) + platform_tag = ("macosx_10_6_intel" + ".macosx_10_9_intel.macosx_10_9_x86_64" + ".macosx_10_10_intel.macosx_10_10_x86_64") + tag = (tag[0], tag[1], platform_tag) + return tag + + cmdclass["bdist_wheel"] = custom_bdist_wheel + + +def main(): + setup( + distclass=BinaryDistribution, + cmdclass=cmdclass, + name="cefpython3", # No spaces here, so that it works with deb pkg + version="{{VERSION}}", + description="GUI toolkit for embedding a Chromium widget" + " in desktop applications", + license="BSD 3-clause", + author="Czarek Tomczak", + author_email="czarek.tomczak@@gmail.com", + url="https://github.com/cztomczak/cefpython", + download_url="https://github.com/cztomczak/cefpython/releases", + platforms=["{{SYSCONFIG_PLATFORM}}"], + packages=["cefpython3"], # Disabled: "cefpython3.wx" + package_data=get_package_data(), + classifiers=[ + "Development Status :: 6 - Mature", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Topic :: Desktop Environment", + "Topic :: Internet", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: Browsers", + "Topic :: Multimedia", + ("Topic :: Software Development :: Libraries" + ":: Application Frameworks"), + "Topic :: Software Development :: User Interfaces", + ], + ) + print("[setup.py] OK installed") + + +def get_package_data(): + package_data = {"cefpython3": get_package_files()} + return package_data + + +def get_package_files(relative_dir=".", recursive=False): + """Finds files recursively in the cefpython3/ local directory. + Includes only files and their paths are relative to the cefpython3/ + local directory. Empty directories are not included.""" + old_dir = None + if not recursive: + old_dir = os.getcwd() + setup_dir = os.path.abspath(os.path.dirname(__file__)) + local_pkg_dir = os.path.join(setup_dir, "cefpython3") + os.chdir(local_pkg_dir) + files = os.listdir(relative_dir) + ret = list() + for fpath in files: + fpath = os.path.join(relative_dir, fpath) + if os.path.isdir(fpath): + ret.extend(get_package_files(relative_dir=fpath, recursive=True)) + else: + ret.append(fpath) + if not recursive: + os.chdir(old_dir) + return ret + + +def get_executables(): + data = copy.copy(EXECUTABLES_NOEXT) + if platform.system() == "Windows": + for key, executable in enumerate(data): + data[key] += ".exe" + return data + + +def post_install_hook(): + """Post install hook to chmod files on Linux and Mac.""" + + # Nothing extra required to do on Windows + if platform.system() == "Windows": + print("[setup.py] post_install_hook is ignored on Windows") + return + + # If this a wheel package generation then do not execute the hook + if "bdist_wheel" in sys.argv: + print("[setup.py] Ignoring post_install_hook as this is bdist_wheel") + return + + print("[setup.py] Execute post_install_hook") + + # Find the installed package directory. Do not import from + # the local cefpython3/ directory. + print("[setup.py] Overload sys.path to facilitate finding correct" + " directory for the installed package") + del sys.path[0] + sys.path.append("") + import cefpython3 + installed_package_dir = os.path.dirname(cefpython3.__file__) + + # Make sure that the imported package wasn't the local cefptyhon3/ + # directory. + print("[setup.py] Installed package directory: {dir}" + .format(dir=installed_package_dir)) + assert not installed_package_dir.startswith( + os.path.dirname(os.path.abspath(__file__))) + + # Set permissions on executables + print("[setup.py] Set execute permissions on executables") + for executable in get_executables(): + executable = os.path.join(installed_package_dir, executable) + command = "chmod +x {executable}".format(executable=executable) + print("[setup.py] {command}".format(command=command)) + subprocess.call(command, shell=True) + + # Set write permissions on log files + print("[setup.py] Set write permissions on log files") + package_data = get_package_data() + for pkgfile in package_data: + if not pkgfile.endswith(".log"): + continue + logfile = os.path.join(installed_package_dir, pkgfile) + command = "chmod 666 {logfile}".format(logfile=logfile) + print("[setup.py] {command}".format(command=command)) + subprocess.call(command, shell=True) + + +if __name__ == "__main__": + main() diff --git a/tools/make_installer.py b/tools/make_installer.py new file mode 100644 index 00000000..87f1a12d --- /dev/null +++ b/tools/make_installer.py @@ -0,0 +1,309 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under BSD 3-clause license. + +""" +Create setup.py package installer. + +Usage: + make.py VERSION + +Options: + VERSION Version number eg. 50.0 +""" + +from common import * + +import glob +import os +import re +import shutil +import subprocess +import sys +import sysconfig + +# Command line args +VERSION = "" + +# Globals +SETUP_DIR = "" +PKG_DIR = "" + +# Config +IGNORE_EXT = [".log", ".pyc", ".pdb", ] +IGNORE_DIRS = ["__pycache__"] + + +def main(): + command_line_args() + + # Setup and package directories + global SETUP_DIR, PKG_DIR + setup_dir_name = ("cefpython3-{version}-{os}-setup" + .format(version=VERSION, os=OS_POSTFIX2)) + SETUP_DIR = os.path.join(BUILD_DIR, setup_dir_name) + PKG_DIR = os.path.join(SETUP_DIR, "cefpython3") + + # Print src and dest for file operations + print("[make_installer.py] Src: {src}".format(src=ROOT_DIR)) + print("[make_installer.py] Dst: {dst}".format(dst=SETUP_DIR)) + + # Make directories + if os.path.exists(SETUP_DIR): + print("[make_installer.py] Delete: {dir}" + .format(dir=SETUP_DIR.replace(ROOT_DIR, ""))) + shutil.rmtree(SETUP_DIR) + os.makedirs(SETUP_DIR) + os.makedirs(os.path.join(SETUP_DIR, "examples/")) + os.makedirs(PKG_DIR) + os.makedirs(os.path.join(PKG_DIR, "examples/")) + + # Copy files from tools/installer/ + copy_tools_installer_files(SETUP_DIR, PKG_DIR) + + # Multiple copy operations using glob patterns + copy_operations = [ + (ROOT_DIR, "License"), (PKG_DIR,), + (CEF_BINARIES_LIBRARIES, "*.txt"), (PKG_DIR,), + (CEF_BINARIES_LIBRARIES, "bin/*"), (PKG_DIR,), + (CEFPYTHON_BINARY, "*"), (PKG_DIR,), + (EXAMPLES_DIR, "*"), (PKG_DIR, "examples/"), + (EXAMPLES_DIR, "*"), (SETUP_DIR, "examples/"), + ] + perform_copy_operations(copy_operations) + + # Linux only operations + if LINUX: + copy_operations_linux = [ + (LINUX_DIR, "binaries_64bit/kivy_.py"), + (PKG_DIR, "examples/"), + (LINUX_DIR, "binaries_64bit/kivy-select-boxes/*"), + (PKG_DIR, "examples/") + ] + perform_copy_operations(copy_operations_linux) + + # Create empty debug.log files so that package uninstalls cleanly + # in case examples or CEF tests were launched. See Issue #149. + create_empty_log_file(os.path.join(PKG_DIR, "debug.log")) + create_empty_log_file(os.path.join(PKG_DIR, "examples/debug.log")) + + print("[make_installer.py] DONE. Installer package created: {setup_dir}" + .format(setup_dir=SETUP_DIR)) + + +def command_line_args(): + args = " ".join(sys.argv) + match = re.search(r"\d+\.\d+", args) + if match: + global VERSION + VERSION = match.group(0) + else: + print(__doc__) + sys.exit(1) + + +def copy_tools_installer_files(setup_dir, pkg_dir): + variables = dict() + variables["VERSION"] = VERSION + variables["SYSCONFIG_PLATFORM"] = sysconfig.get_platform() + + shutil.copy( + os.path.join(INSTALLER_DIR, "cefpython3.README.txt"), + os.path.join(setup_dir, "README.txt")) + + copy_template_file( + os.path.join(INSTALLER_DIR, "cefpython3.setup.py"), + os.path.join(setup_dir, "setup.py"), + variables) + + copy_template_file( + os.path.join(INSTALLER_DIR, "cefpython3.__init__.py"), + os.path.join(pkg_dir, "__init__.py"), + variables) + + +def copy_template_file(src, dst, variables): + """Copy file and replaces template variables in that file.""" + print("[make_installer.py] Copy_t: {src} ==> {dst}" + .format(src=short_src_path(src), dst=short_dst_path(dst))) + with open(src, "rb") as fo: + contents = fo.read() + contents = replace_template_vars(contents, variables) + with open(dst, "wb") as fo: + fo.write(contents) + return contents + + +def replace_template_vars(string, dictionary): + """Replaces template variables like {{SOME}} in the string + using the dictionary values.""" + orig_string = string + for key, value in dictionary.items(): + string = string.replace("{{"+key+"}}", value) + if string == orig_string: + raise Exception("Nothing to format") + if re.search(r"\{\{[a-zA-Z0-9_]+\}\}", string): + raise Exception("Not all strings were formatted") + return string + + +def perform_copy_operations(operations): + assert len(operations) % 2 == 0 + count_ops = int(len(operations) / 2) + for op_i in range(count_ops): + # Refer to values by index + pattern = operations[op_i*2] + dst_dir = operations[op_i*2+1] + # Convert tuples to lists + pattern = list(pattern) + dst_dir = list(dst_dir) + # Join paths + pattern = os.path.join(*pattern) + dst_dir = os.path.join(*dst_dir) + dst_dir = os.path.abspath(dst_dir) + # Normalize unix slashes on Windows + pattern = pattern.replace("/", os.path.sep) + # dst_dir must be a directory + if not os.path.isdir(dst_dir): + raise Exception("Not a directory: {dst_dir}" + .format(dst_dir=dst_dir)) + # Is pattern a file or a directory + if os.path.isfile(pattern): + if is_ignored_path(pattern): + raise Exception("Copy operation pattern is in ignore list:" + " {pattern}".format(pattern=pattern)) + print("[make_installer.py] Copy: {file} ==> {dir}" + .format(file=short_src_path(pattern), + dir=short_dst_path(dst_dir))) + # Destination file must not exist + assert not os.path.exists(os.path.join(dst_dir, + os.path.basename(pattern))) + shutil.copy(pattern, dst_dir) + else: + # pattern is a glob pattern + base_dir = os.path.dirname(pattern) + assert base_dir + assert base_dir == os.path.abspath(base_dir) + paths = glob.glob(pattern) + if not len(paths): + raise Exception("No paths found in: {pattern}" + .format(pattern=pattern)) + for path in paths: + # "path" variable contains absolute path + assert path == os.path.abspath(path) + if os.path.isfile(path): + if is_ignored_path(path): + continue + print("[make_installer.py] Copy: {file} ==> {dir}" + .format(file=short_src_path(path), + dir=short_dst_path(dst_dir))) + # Destination file must not exist + assert not os.path.exists( + os.path.join(dst_dir, os.path.basename(path))) + shutil.copy(path, dst_dir) + elif os.path.isdir(path): + if is_ignored_path(path): + continue + relative_dir = path.replace(base_dir, "") + assert relative_dir[0] == os.path.sep + relative_dir = relative_dir[1:] + perform_copy_recursively(base_dir, relative_dir, dst_dir) + else: + raise Exception("Unknown path: {path}".format(path=path)) + + +def perform_copy_recursively(base_dir, relative_dir, new_dir): + real_dir = os.path.join(base_dir, relative_dir) + assert os.path.exists(real_dir) and os.path.isdir(real_dir) + assert os.path.exists(new_dir) and os.path.isdir(new_dir) + + # Create subdirectory + new_subdir = os.path.join(new_dir, relative_dir) + if not os.path.exists(new_subdir): + print("[make_installer.py] Create: {dir}" + .format(dir=short_dst_path(new_subdir))) + os.makedirs(new_subdir) + + # List directory + paths = os.listdir(real_dir) + for path in paths: + # "path" variable contains relative path + real_path = os.path.join(real_dir, path) + path = os.path.join(relative_dir, path) + if os.path.isdir(real_path): + if is_ignored_path(real_path): + continue + perform_copy_recursively(base_dir, path, new_dir) + elif os.path.isfile(real_path): + if is_ignored_path(real_path): + continue + new_file = os.path.join(new_dir, path) + new_subdir = os.path.dirname(new_file) + if os.path.exists(new_file): + raise Exception("Path aready exists: {new_file}" + .format(new_file=short_dst_path(new_file))) + print("[make_installer.py] Copy: {file} ==> {dir}" + .format(file=short_src_path(real_path), + dir=short_dst_path(new_subdir))) + shutil.copy(real_path, new_subdir) + else: + raise Exception("Unknown path: {path}".format(path=real_path)) + + +def is_ignored_path(path): + basename = os.path.basename(path) + if basename in IGNORE_DIRS: + print("[make_installer.py] Ignore: {dir}" + .format(dir=short_src_path(path))) + return True + for ext in IGNORE_EXT: + if path.endswith(ext): + print("[make_installer.py] Ignore: {file}" + .format(file=short_src_path(path))) + return True + return False + + +def delete_files_by_pattern(pattern): + assert len(pattern) > 2 + # Normalize unix slashes on Windows + pattern = pattern.replace("/", os.path.sep) + print("[make_installer.py] Delete: {pattern}" + .format(pattern=short_dst_path(pattern))) + files = glob.glob(pattern) + for f in files: + os.remove(f) + + +def create_empty_log_file(log_file): + # Normalize unix slashes on Windows + log_file = log_file.replace("/", os.path.sep) + print("[make_installer.py] Create: {file}" + .format(file=short_dst_path(log_file))) + with open(log_file, "wb") as fo: + fo.write("") + # On Linux and Mac chmod so that for cases when package is + # installed using sudo. When wheel package is created it + # will remember file permissions set. + if LINUX or MAC: + command = "chmod 666 {file}".format(file=log_file) + print("[make_installer.py] {command}" + .format(command=command.replace(SETUP_DIR, ""))) + subprocess.call(command, shell=True) + + +def short_src_path(path): + # Very long: \build\cef55_3.2883.1553.g80bd606_win32\ + find = os.path.basename(CEF_BINARIES_LIBRARIES) + if len(find) > 12: + path = path.replace(find, find[:12] + "*") + path = path.replace(ROOT_DIR, "") + return path + + +def short_dst_path(path): + path = path.replace(SETUP_DIR, "") + return path + + +if __name__ == "__main__": + main() diff --git a/tools/requirements.txt b/tools/requirements.txt index c5ba053d..abdc1828 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,2 +1,4 @@ -cython == 0.25.2 +Cython == 0.25.2 docopt >= 0.6.2 +setuptools +wheel diff --git a/unittests/_test_runner.py b/unittests/_test_runner.py index f25f48cb..8b970aca 100644 --- a/unittests/_test_runner.py +++ b/unittests/_test_runner.py @@ -3,9 +3,10 @@ """Run unit tests. With no arguments all tests are run. Read notes below. Usage: - _test_runner.py [FILE | _TESTCASE] + _test_runner.py [--debug] [FILE | _TESTCASE] Options: + --debug Enable debug info FILE Run tests from single file _TESTCASE Test cases matching pattern to run eg "file.TestCase". Calling with this argument is for internal use only. @@ -27,6 +28,9 @@ import re import subprocess +# Command line args +CUSTOM_CMDLINE_ARG = "" + def main(file_arg=""): # type: (str) -> None @@ -37,7 +41,12 @@ def main(file_arg=""): # Script arguments testcase_arg = "" - if len(sys.argv) > 1: + if len(sys.argv) > 1 and sys.argv[1].startswith("--"): + # Will allow to pass custom args like --debug to isolated tests + # (main_test.py for example). + global CUSTOM_CMDLINE_ARG + CUSTOM_CMDLINE_ARG = sys.argv[1] + elif len(sys.argv) > 1: if ".py" in sys.argv[1]: file_arg = sys.argv[1] else: @@ -141,7 +150,8 @@ def _run_suites_in_isolation(self, suites): # Run test using new instance of Python interpreter try: output = subprocess.check_output( - [sys.executable, "_test_runner.py", testcase_id], + [sys.executable, "_test_runner.py", testcase_id, + CUSTOM_CMDLINE_ARG], stderr=subprocess.STDOUT) exit_code = 0 except subprocess.CalledProcessError as exc: diff --git a/unittests/main_test.py b/unittests/main_test.py index a20af1da..05f34158 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -101,11 +101,15 @@ def test_main(self): print("Python {ver}".format(ver=sys.version[:6])) # Test initialization of CEF - cef.Initialize({ + 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 From 97712b3a213b591b63c2f230f3af158f626b3b0d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Feb 2017 21:11:31 +0100 Subject: [PATCH 10/16] Fix crashes in unit tests and examples on Windows with v55+ (#294)... Fixes to wxpython.py example, works great. The hello_world.py example also works good. Unit tests are working fine now. When not providing parent window handle (NULL) during browser creation, so that CEF creates top-level window itself, in such case must call SetAsPopup on Windows. Fixes to build.py tool to run all examples smoothly. --- examples/wxpython.py | 34 +++++++------ src/window_info.pyx | 2 +- tools/build.py | 66 +++++++++++++++----------- tools/installer/cefpython3.__init__.py | 3 +- tools/installer/cefpython3.setup.py | 3 +- tools/make_installer.py | 2 +- 6 files changed, 63 insertions(+), 47 deletions(-) diff --git a/examples/wxpython.py b/examples/wxpython.py index 2de7a752..8e7026fc 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -3,8 +3,11 @@ # To install wxPython on Linux type "sudo apt-get install python-wxtools". -# Tested with wxPython 2.8 on Linux, wxPython 3.0 on Windows/Mac -# and CEF Python v55.3+. +# Tested configurations: +# - wxPython 2.8 on Linux +# - wxPython 3.0.2.0 msw (classic) on Windows +# - wxPython 3.0 on Mac +# - CEF Python v55.3+ import wx from cefpython3 import cefpython as cef @@ -28,7 +31,7 @@ def main(): settings["auto_zooming"] = "system_dpi" # Embed DPI awareness xml manifest inside .exe (recommended, # most reliable) or call the SetProcessDpiAware function. - # noinspection PyUnresolvedReferences + # noinspection PyUnresolvedReferences, PyArgumentList cef.DpiAware.SetProcessDpiAware() cef.Initialize(settings=settings) app = CefApp(False) @@ -40,7 +43,7 @@ def main(): def check_versions(): print("[wxpython.py] CEF Python {ver}".format(ver=cef.__version__)) print("[wxpython.py] Python {ver}".format(ver=sys.version[:6])) - print("[wxpython.py] wx {ver}".format(ver=wx.version())) + 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" @@ -75,11 +78,10 @@ def setup_icon(self): def create_menu(self): filemenu = wx.Menu() - filemenu.Append(1, "Open") - exit_ = filemenu.Append(2, "Exit") - self.Bind(wx.EVT_MENU, self.OnClose, exit_) + filemenu.Append(1, "Some option") + exit_ = filemenu.Append(2, "Another option") aboutmenu = wx.Menu() - aboutmenu.Append(1, "CEF Python") + aboutmenu.Append(1, "Yet another option") menubar = wx.MenuBar() menubar.Append(filemenu, "&File") menubar.Append(aboutmenu, "&About") @@ -93,11 +95,12 @@ def embed_browser(self): self.browser.SetClientHandler(FocusHandler()) def OnSetFocus(self, _): - if not self.brower: + if not self.browser: return if WINDOWS: # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSetFocus(self.GetHandleForBrowser(), 0, 0, 0) + cef.WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), + 0, 0, 0) self.browser.SetFocus(True) def OnSize(self, _): @@ -105,11 +108,11 @@ def OnSize(self, _): return if WINDOWS: # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(self.GetHandleForBrowser(), 0, 0, 0) + cef.WindowUtils.OnSize(self.browser_panel.GetHandle(), + 0, 0, 0) elif LINUX: (x, y) = (0, 0) (width, height) = self.browser_panel.GetSizeTuple() - # noinspection PyUnresolvedReferences self.browser.SetBounds(x, y, width, height) self.browser.NotifyMoveOrResizeStarted() @@ -154,9 +157,10 @@ def OnGotFocus(self, browser, **_): # window (alt+tab) and then back to this example, keyboard # focus becomes broken, you can't type anything, even # though a type cursor blinks in web view. - print("[wxpython.py] FocusHandler.OnGotFocus:" - " keyboard focus fix (#284)") - browser.SetFocus(True) + if LINUX: + print("[wxpython.py] FocusHandler.OnGotFocus:" + " keyboard focus fix (#284)") + browser.SetFocus(True) class CefApp(wx.App): diff --git a/src/window_info.pyx b/src/window_info.pyx index 988c665e..27c6cf16 100644 --- a/src/window_info.pyx +++ b/src/window_info.pyx @@ -87,7 +87,7 @@ cdef class WindowInfo: # On Windows when parent window handle is 0 then SetAsPopup() # must be called instead. if parentWindowHandle == 0: - self.SetAsPopup(parentWindowHandle, "Popup") + self.SetAsPopup(parentWindowHandle, "") return if parentWindowHandle != 0\ and not WindowUtils.IsWindowHandle(parentWindowHandle): diff --git a/tools/build.py b/tools/build.py index 6c424a78..52551837 100644 --- a/tools/build.py +++ b/tools/build.py @@ -648,16 +648,22 @@ def install_and_run(): # Make setup installer print("[build.py] Make setup installer") make_tool = os.path.join(TOOLS_DIR, "make_installer.py") - os.system("{python} {make_tool} --version {version}" - .format(python=sys.executable, - make_tool=make_tool, - version=VERSION)) + ret = os.system("{python} {make_tool} --version {version}" + .format(python=sys.executable, + make_tool=make_tool, + version=VERSION)) + if ret != 0: + print("[build.py] ERROR while making installer package") + sys.exit(ret) # Install print("[build.py] Install the cefpython package") os.chdir(setup_installer_dir) - os.system("{sudo} {python} setup.py install" - .format(sudo=get_sudo(), python=sys.executable)) + ret = os.system("{sudo} {python} setup.py install" + .format(sudo=get_sudo(), python=sys.executable)) + if ret != 0: + print("[build.py] ERROR while installing package") + sys.exit(ret) os.chdir(BUILD_DIR) # Run unittests @@ -666,32 +672,36 @@ def install_and_run(): ret = os.system("{python} {test_runner}" .format(python=sys.executable, test_runner=test_runner)) if ret != 0: + print("[build.py] ERROR while running unit tests") sys.exit(ret) # Run examples print("[build.py] Run examples") - if KIVY_FLAG: - run_examples = "{python} {linux_dir}/deprecated_64bit/kivy_.py" - else: - run_examples = ("cd {examples_dir}" - " && {python} hello_world.py" - " && {python} wxpython.py" - " && {python} gtk2.py" - " && {python} gtk2.py --message-loop-timer" - # " && {python} gtk3.py" - " && {python} tkinter_.py" - " && {python} qt.py pyqt" - " && {python} qt.py pyside") - if LINUX: - run_examples += (" && {python}" - " {linux_dir}/deprecated_64bit/kivy_.py") - run_examples.format( - python=sys.executable, - linux_dir=LINUX_DIR, - examples_dir=EXAMPLES_DIR) - os.system(run_examples) - - print("[build.py] DONE") + os.chdir(EXAMPLES_DIR) + examples = list() + if not KIVY_FLAG: + examples.extend([ + "hello_world.py", + "wxpython.py", + "gtk2.py", + "gtk2.py --message-loop-timer", + "gtk3.py", + "tkinter_.py", + "qt.py pyqt", + "qt.py pyside", + ]) + if LINUX: + examples.append("{linux_dir}/deprecated_64bit/kivy_.py" + .format(linux_dir=LINUX_DIR)) + for example in examples: + ret = os.system("{python} {example}" + .format(python=sys.executable, example=example)) + if ret != 0: + print("[build.py] ERROR while running example: {example}" + .format(example=example)) + sys.exit(1) + + print("[build.py] Everything OK") def get_sudo(): diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index ca54b44b..5e794a0a 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -2,7 +2,8 @@ # Licensed under BSD 3-clause license. # NOTE: Template variables like {{VERSION}} are replaced with actual -# values when make.py tool generates this package installer. +# values when make_installer.py tool generates this package +# installer. import os import sys diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 646c5fb3..292b2ee3 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -16,7 +16,8 @@ """ # NOTE: Template variables like {{VERSION}} are replaced with actual -# values when make.py tool generates this package installer. +# values when make_installer.py tool generates this package +# installer. import copy import os diff --git a/tools/make_installer.py b/tools/make_installer.py index 87f1a12d..5d4f7501 100644 --- a/tools/make_installer.py +++ b/tools/make_installer.py @@ -5,7 +5,7 @@ Create setup.py package installer. Usage: - make.py VERSION + make_installer.py VERSION Options: VERSION Version number eg. 50.0 From 9bd3accd5eee4411dd8b74bf5ff7b1520bf4b4b7 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Feb 2017 15:21:55 +0100 Subject: [PATCH 11/16] Fix issues with examples on Windows with v55+ (#294)... All examples do run fine on Windows now. Add run_examples.py tool to run all examples that can be run on current configuration. --- examples/gtk2.py | 60 ++++++++++----- examples/gtk3.py | 59 +++++++++++++-- examples/hello_world.py | 2 +- examples/qt.py | 104 ++++++++++++++++++-------- examples/tkinter_.py | 21 ++++-- examples/wxpython.py | 27 ++++--- src/window_utils_linux.pyx | 19 +++++ src/window_utils_mac.pyx | 19 +++++ src/window_utils_win.pyx | 14 ++-- tools/build.py | 28 ++----- tools/common.py | 2 +- tools/run_examples.py | 147 +++++++++++++++++++++++++++++++++++++ 12 files changed, 396 insertions(+), 106 deletions(-) create mode 100644 tools/run_examples.py diff --git a/examples/gtk2.py b/examples/gtk2.py index 092c82c8..c7b4c84b 100644 --- a/examples/gtk2.py +++ b/examples/gtk2.py @@ -1,5 +1,5 @@ # Example of embedding CEF Python browser using PyGTK library (GTK 2). -# Tested with GTK 2.24 and CEF Python v55.3+, only on Linux. +# Tested with GTK 2.24 and CEF Python v55.3+, on Windows/Linux. # Known issue on Linux: Keyboard focus problem (Issue #284). from cefpython3 import cefpython as cef @@ -8,16 +8,25 @@ import gobject import sys import os +import platform + +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms +WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") # In CEF you can run message loop in two ways (see API docs for more details): -# 1. By calling cef.MessageLoop() instead of an application-provided +# 1. By calling cef.MessageLoopWork() in a timer - each call performs +# a single iteration of CEF message loop processing. +# 2. By calling cef.MessageLoop() instead of an application-provided # message loop to get the best balance between performance and CPU # usage. This function will block until a quit message is received by -# the system. -# 2. By calling cef.MessageLoopWork() in a timer - each call performs -# a single iteration of CEF message loop processing. -MESSAGE_LOOP_BEST = 1 -MESSAGE_LOOP_TIMER = 2 # Pass --message-loop-timer flag to script to use this +# the system. This seem to work only on Linux in GTK example. +MESSAGE_LOOP_TIMER = 1 +MESSAGE_LOOP_CEF = 2 # Pass --message-loop-cef flag to script on Linux g_message_loop = None @@ -28,7 +37,7 @@ def main(): cef.Initialize() gobject.threads_init() Gtk2Example() - if g_message_loop == MESSAGE_LOOP_BEST: + if g_message_loop == MESSAGE_LOOP_CEF: cef.MessageLoop() else: gtk.main() @@ -46,13 +55,13 @@ def check_versions(): def configure_message_loop(): global g_message_loop - if "--message-loop-timer" in sys.argv: + if "--message-loop-cef" in sys.argv: + print("[gkt2.py] Message loop mode: CEF (best performance)") + g_message_loop = MESSAGE_LOOP_CEF + sys.argv.remove("--message-loop-cef") + else: print("[gkt2.py] Message loop mode: TIMER") g_message_loop = MESSAGE_LOOP_TIMER - sys.argv.remove("--message-loop-timer") - else: - print("[gkt2.py] Message loop mode: BEST") - g_message_loop = MESSAGE_LOOP_BEST if len(sys.argv) > 1: print("[gkt2.py] ERROR: unknown argument passed") sys.exit(1) @@ -83,7 +92,7 @@ def __init__(self): self.main_window.add(self.vbox) windowInfo = cef.WindowInfo() - windowInfo.SetAsChild(self.main_window.window.xid) + windowInfo.SetAsChild(self.get_handle()) self.browser = cef.CreateBrowserSync(windowInfo, settings={}, url="https://www.google.com/") self.browser.SetClientHandler(LoadHandler()) @@ -95,6 +104,12 @@ def __init__(self): if g_message_loop == MESSAGE_LOOP_TIMER: gobject.timeout_add(10, self.on_timer) + def get_handle(self): + if LINUX: + return self.main_window.window.xid + else: + return self.main_window.window.handle + def create_menu(self): item1 = gtk.MenuItem('MenuBar') item1.show() @@ -131,16 +146,22 @@ def on_vbox_size_allocate(self, _, data): y = data.y + self.menubar_height width = data.width height = data.height - self.menubar_height - self.browser.SetBounds(x, y, width, height) + if WINDOWS: + WindowUtils.OnSize(self.get_handle(), 0, 0, 0) + elif LINUX: + self.browser.SetBounds(x, y, width, height) def on_menubar_size_allocate(self, _, data): self.menubar_height = data.height def on_exit(self, *_): + if self.exiting: + print("[gtk2.py] on_exit() called, but already exiting") + return self.exiting = True self.browser.CloseBrowser(True) self.browser = None - if g_message_loop == MESSAGE_LOOP_BEST: + if g_message_loop == MESSAGE_LOOP_CEF: cef.QuitMessageLoop() else: gtk.main_quit() @@ -158,9 +179,10 @@ def OnLoadStart(self, browser, **_): # sometimes during initial loading, keyboard focus may # break and it is not possible to type anything, even # though a type cursor blinks in web view. - print("[gtk2.py] LoadHandler.OnLoadStart:" - " keyboard focus fix (#284)") - browser.SetFocus(True) + if LINUX: + print("[gtk2.py] LoadHandler.OnLoadStart:" + " keyboard focus fix (#284)") + browser.SetFocus(True) self.initial_app_loading = False diff --git a/examples/gtk3.py b/examples/gtk3.py index 5820dcd7..2223362b 100644 --- a/examples/gtk3.py +++ b/examples/gtk3.py @@ -1,12 +1,33 @@ -# ! CURRENTLY BROKEN ! with v54+ (Issue #261). -# Example of embedding CEF Python browser using PyGObject library (GTK 3). -# Tested with GTK 3.10 and CEF Python v53.1+, only on Linux. +# Example of embedding CEF Python browser using PyGObject/PyGI (GTK 3). + +# Linux note: This example is currently broken in v54+ on Linux (Issue #261). +# It works fine with cefpython v53. + +# Tested configurations: +# - GTK 3.18 on Windows +# - GTK 3.10 on Linux +# - CEF Python v53.1+ from cefpython3 import cefpython as cef +import ctypes # noinspection PyUnresolvedReferences -from gi.repository import GdkX11, Gtk, GObject, GdkPixbuf +from gi.repository import Gtk, GObject, Gdk, GdkPixbuf import sys import os +import platform + +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms +WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + +# Linux imports +if LINUX: + # noinspection PyUnresolvedReferences + from gi.repository import GdkX11 def main(): @@ -25,9 +46,10 @@ def main(): class Gtk3Example(Gtk.Application): def __init__(self): - super(Gtk3Example, self).__init__(application_id='cefpython.gtk') + super(Gtk3Example, self).__init__(application_id='cefpython.gtk3') self.browser = None self.window = None + self.win32_handle = None def run(self, argv): GObject.threads_init() @@ -36,6 +58,22 @@ def run(self, argv): self.connect("shutdown", self.on_shutdown) return super(Gtk3Example, self).run(argv) + def get_handle(self): + if LINUX: + return self.window.get_property("window").get_xid() + elif WINDOWS: + Gdk.threads_enter() + ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p + ctypes.pythonapi.PyCapsule_GetPointer.argtypes = \ + [ctypes.py_object] + gpointer = ctypes.pythonapi.PyCapsule_GetPointer( + self.window.get_property("window").__gpointer__, None) + gdk_dll = ctypes.CDLL("libgdk-3-0.dll") + self.win32_handle = gdk_dll.gdk_win32_window_get_handle( + gpointer) + Gdk.threads_leave() + return self.win32_handle + def on_timer(self): cef.MessageLoopWork() return True @@ -51,10 +89,13 @@ def on_activate(self, *_): self.setup_icon() self.window.realize() window_info = cef.WindowInfo() - window_info.SetAsChild(self.window.get_property("window").get_xid()) + window_info.SetAsChild(self.get_handle()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") self.window.show_all() + # Must set size of the window again after it was shown, + # otherwise browser occupies only part of the window area. + self.window.resize(*self.window.get_default_size()) def on_configure(self, *_): if self.browser: @@ -63,7 +104,11 @@ def on_configure(self, *_): def on_size_allocate(self, _, data): if self.browser: - self.browser.SetBounds(data.x, data.y, data.width, data.height) + if WINDOWS: + WindowUtils.OnSize(self.win32_handle, 0, 0, 0) + elif LINUX: + self.browser.SetBounds(data.x, data.y, + data.width, data.height) def on_focus_in(self, *_): if self.browser: diff --git a/examples/hello_world.py b/examples/hello_world.py index 5417bcfd..0c64ab84 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+, only on Linux. +# Tested with CEF Python v55.3+. from cefpython3 import cefpython as cef import sys diff --git a/examples/qt.py b/examples/qt.py index 1877272f..995f637f 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -1,21 +1,30 @@ # Example of embedding CEF Python browser using PyQt/PySide libraries. # This example has two widgets: a navigation bar and a browser. # -# Tested with PyQt 4.10.4 (4.8.6), PySide 1.2.1 (4.8.6) -# and CEF Python v55.3+, only on Linux. +# Tested configurations: +# - PyQt 4.11.4 (4.8.7) on Windows +# - PySide 1.2.4 (4.8.7) on Windows +# - PyQt 4.10.4 (4.8.6) on Linux +# - PySide 1.2.1 (4.8.6) on Linux +# - CEF Python v55.4+ +from cefpython3 import cefpython as cef +import ctypes import os -import sys import platform -from cefpython3 import cefpython as cef +import sys # PyQt imports if "pyqt" in sys.argv: + # noinspection PyUnresolvedReferences from PyQt4.QtGui import * + # noinspection PyUnresolvedReferences from PyQt4.QtCore import * # PySide imports elif "pyside" in sys.argv: + # noinspection PyUnresolvedReferences import PySide + # noinspection PyUnresolvedReferences from PySide import QtCore # noinspection PyUnresolvedReferences from PySide.QtGui import * @@ -27,9 +36,15 @@ print(" qt.py pyside") sys.exit(1) -# Constants -LINUX = (platform.system() == "Linux") +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + +# Configuration WIDTH = 800 HEIGHT = 600 @@ -59,6 +74,7 @@ def check_versions(): print("[qt.py] Python {ver}".format(ver=sys.version[:6])) # PyQt version if "pyqt" in sys.argv: + # noinspection PyUnresolvedReferences print("[qt.py] PyQt {v1} ({v2})".format( v1=PYQT_VERSION_STR, v2=qVersion())) # PySide version @@ -91,6 +107,8 @@ def setupLayout(self): layout.addWidget(self.cef_widget, 1, 0) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) + layout.setRowStretch(0, 0) + layout.setRowStretch(1, 1) frame = QFrame() frame.setLayout(layout) self.setCentralWidget(frame) @@ -98,20 +116,20 @@ def setupLayout(self): self.cef_widget.embedBrowser() def focusInEvent(self, event): - # This event seems to never get called, as CEF is stealing all - # focus due to Issue #284. - if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSetFocus(int(self.centralWidget().winId()), - 0, 0, 0) - print("[qt.py] focusInEvent") + # This event seems to never get called on Linux, as CEF is + # stealing all focus due to Issue #284. + # print("[qt.py] focusInEvent") if self.cef_widget.browser: + if WINDOWS: + WindowUtils.OnSetFocus(self.cef_widget.getHandle(), + 0, 0, 0) self.cef_widget.browser.SetFocus(True) def focusOutEvent(self, event): - # This event seems to never get called, as CEF is stealing all - # focus due to Issue #284. - print("[qt.py] focusOutEvent") + # This event seems to never get called on Linux, as CEF is + # stealing all focus due to Issue #284. + # print("[qt.py] focusOutEvent") + pass def closeEvent(self, event): # Close browser (force=True) and free CEF reference @@ -212,12 +230,34 @@ def embedBrowser(self): self.width = 0 self.height = 0 window_info = cef.WindowInfo() - window_info.SetAsChild(int(self.winId())) + window_info.SetAsChild(self.getHandle()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar)) self.browser.SetClientHandler(FocusHandler()) + def getHandle(self): + # PySide bug: QWidget.winId() returns + # There is no easy way to convert it to int. + try: + return int(self.winId()) + except: + if sys.version_info[0] == 2: + # Python 2 + ctypes.pythonapi.PyCObject_AsVoidPtr.restype = ( + ctypes.c_void_p) + ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = ( + [ctypes.py_object]) + return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId()) + else: + # Python 3 + ctypes.pythonapi.PyCapsule_GetPointer.restype = ( + ctypes.c_void_p) + ctypes.pythonapi.PyCapsule_GetPointer.argtypes = ( + [ctypes.py_object]) + return ctypes.pythonapi.PyCapsule_GetPointer( + self.winId(), None) + def moveEvent(self, _): # pos = event.pos() # self.x = pos.x() @@ -226,11 +266,11 @@ def moveEvent(self, _): self.y = 0 if self.browser: if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) + WindowUtils.OnSize(self.getHandle(), 0, 0, 0) elif LINUX: - # noinspection PyUnresolvedReferences - self.browser.SetBounds(self.x, self.y, self.width, self.height) + self.browser.SetBounds(self.x, self.y, + self.width, self.height) + self.browser.NotifyMoveOrResizeStarted() def resizeEvent(self, event): size = event.size() @@ -238,11 +278,11 @@ def resizeEvent(self, event): self.height = size.height() if self.browser: if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) + WindowUtils.OnSize(self.getHandle(), 0, 0, 0) elif LINUX: - # noinspection PyUnresolvedReferences - self.browser.SetBounds(self.x, self.y, self.width, self.height) + self.browser.SetBounds(self.x, self.y, + self.width, self.height) + self.browser.NotifyMoveOrResizeStarted() class CefApplication(QApplication): @@ -298,9 +338,10 @@ def OnLoadStart(self, browser, **_): # sometimes during initial loading, keyboard focus may # break and it is not possible to type anything, even # though a type cursor blinks in web view. - print("[qt.py] LoadHandler.OnLoadStart:" - " keyboard focus fix no. 2 (#284)") - browser.SetFocus(True) + if LINUX: + print("[qt.py] LoadHandler.OnLoadStart:" + " keyboard focus fix no. 2 (#284)") + browser.SetFocus(True) self.initial_app_loading = False @@ -328,9 +369,10 @@ def OnGotFocus(self, browser, **_): # window (alt+tab) and then back to this example, keyboard # focus becomes broken, you can't type anything, even # though a type cursor blinks in web view. - print("[qt.py] FocusHandler.OnGotFocus:" - " keyboard focus fix no. 1 (#284)") - browser.SetFocus(True) + if LINUX: + print("[qt.py] FocusHandler.OnGotFocus:" + " keyboard focus fix no. 1 (#284)") + browser.SetFocus(True) if __name__ == '__main__': diff --git a/examples/tkinter_.py b/examples/tkinter_.py index c8385992..dc5abf24 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -1,7 +1,9 @@ # Example of embedding CEF Python browser using Tkinter toolkit. # This example has two widgets: a navigation bar and a browser. # -# Tested with Tk 8.6 and CEF Python v55.3+, only on Linux. +# Tested configurations: +# - Tk 8.6 and CEF Python v55.3+ on Linux +# - Tk 8.5 and CEF Python v55.4+ on Windows # # Known issue on Linux: When typing url, mouse must be over url # entry widget otherwise keyboard focus is lost (Issue #255 @@ -17,10 +19,18 @@ import platform import logging as _logging +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms +WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + # Globals logger = _logging.getLogger("tkinter_.py") # Python 2.7 on Windows comes with Tk 8.5 which doesn't support PNG images -IMAGE_EXT = ".gif" if platform.system() == "Windows" else ".png" +IMAGE_EXT = ".gif" if WINDOWS else ".png" def main(): @@ -278,10 +288,9 @@ def on_root_configure(self): def on_mainframe_configure(self, width, height): if self.browser: - if platform.system() == "Windows": - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(self.winfo_id(), 0, 0, 0) - elif platform.system() == "Linux": + if WINDOWS: + WindowUtils.OnSize(self.winfo_id(), 0, 0, 0) + elif LINUX: self.browser.SetBounds(0, 0, width, height) self.browser.NotifyMoveOrResizeStarted() diff --git a/examples/wxpython.py b/examples/wxpython.py index 8e7026fc..226642c2 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -5,9 +5,8 @@ # Tested configurations: # - wxPython 2.8 on Linux -# - wxPython 3.0.2.0 msw (classic) on Windows -# - wxPython 3.0 on Mac -# - CEF Python v55.3+ +# - wxPython 3.0 on Windows +# - CEF Python v55.4+ import wx from cefpython3 import cefpython as cef @@ -15,9 +14,15 @@ import sys import os -# Constants -LINUX = (platform.system() == "Linux") +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + +# Configuration WIDTH = 800 HEIGHT = 600 @@ -79,7 +84,7 @@ def setup_icon(self): def create_menu(self): filemenu = wx.Menu() filemenu.Append(1, "Some option") - exit_ = filemenu.Append(2, "Another option") + filemenu.Append(2, "Another option") aboutmenu = wx.Menu() aboutmenu.Append(1, "Yet another option") menubar = wx.MenuBar() @@ -98,18 +103,16 @@ def OnSetFocus(self, _): if not self.browser: return if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), - 0, 0, 0) + WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), + 0, 0, 0) self.browser.SetFocus(True) def OnSize(self, _): if not self.browser: return if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(self.browser_panel.GetHandle(), - 0, 0, 0) + WindowUtils.OnSize(self.browser_panel.GetHandle(), + 0, 0, 0) elif LINUX: (x, y) = (0, 0) (width, height) = self.browser_panel.GetSizeTuple() diff --git a/src/window_utils_linux.pyx b/src/window_utils_linux.pyx index 885f82f8..da4030cc 100644 --- a/src/window_utils_linux.pyx +++ b/src/window_utils_linux.pyx @@ -11,6 +11,25 @@ class WindowUtils: # You have to overwrite this class and provide implementations # for these methods. + @staticmethod + def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnSize(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnEraseBackground(long windowHandle, long msg, long wparam, + long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + @staticmethod def GetParentHandle(WindowHandle windowHandle): Debug("WindowUtils::GetParentHandle() not implemented (returns 0)") diff --git a/src/window_utils_mac.pyx b/src/window_utils_mac.pyx index 742a9b8f..08eb7b85 100644 --- a/src/window_utils_mac.pyx +++ b/src/window_utils_mac.pyx @@ -8,6 +8,25 @@ class WindowUtils: # You have to overwrite this class and provide implementations # for these methods. + @staticmethod + def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnSize(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnEraseBackground(long windowHandle, long msg, long wparam, + long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + @staticmethod def GetParentHandle(WindowHandle windowHandle): Debug("WindowUtils::GetParentHandle() not implemented (returns 0)") diff --git a/src/window_utils_win.pyx b/src/window_utils_win.pyx index 81d80143..c40bd383 100644 --- a/src/window_utils_win.pyx +++ b/src/window_utils_win.pyx @@ -4,10 +4,10 @@ include "cefpython.pyx" -class WindowUtils: +class WindowUtils(object): @staticmethod - def OnSetFocus(int windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return 0 @@ -15,7 +15,7 @@ class WindowUtils: return 0 @staticmethod - def OnSize(int windowHandle, long msg, long wparam, long lparam): + def OnSize(long windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return DefWindowProc(windowHandle, msg, wparam, lparam) @@ -35,7 +35,7 @@ class WindowUtils: return DefWindowProc(windowHandle, msg, wparam, lparam) @staticmethod - def OnEraseBackground(int windowHandle, long msg, long wparam, + def OnEraseBackground(long windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: @@ -134,11 +134,11 @@ class WindowUtils: ICON_SMALL, parentIconSmall) @staticmethod - def GetParentHandle(int windowHandle): - return GetParent(windowHandle) + def GetParentHandle(long windowHandle): + return GetParent(windowHandle) @staticmethod - def IsWindowHandle(int windowHandle): + def IsWindowHandle(long windowHandle): IF UNAME_SYSNAME == "Windows": return bool(IsWindow(windowHandle)) ELSE: diff --git a/tools/build.py b/tools/build.py index 52551837..c7d742ec 100644 --- a/tools/build.py +++ b/tools/build.py @@ -678,28 +678,12 @@ def install_and_run(): # Run examples print("[build.py] Run examples") os.chdir(EXAMPLES_DIR) - examples = list() - if not KIVY_FLAG: - examples.extend([ - "hello_world.py", - "wxpython.py", - "gtk2.py", - "gtk2.py --message-loop-timer", - "gtk3.py", - "tkinter_.py", - "qt.py pyqt", - "qt.py pyside", - ]) - if LINUX: - examples.append("{linux_dir}/deprecated_64bit/kivy_.py" - .format(linux_dir=LINUX_DIR)) - for example in examples: - ret = os.system("{python} {example}" - .format(python=sys.executable, example=example)) - if ret != 0: - print("[build.py] ERROR while running example: {example}" - .format(example=example)) - sys.exit(1) + run_examples = os.path.join(TOOLS_DIR, "run_examples.py") + ret = os.system("{python} {run_examples}" + .format(python=sys.executable, run_examples=run_examples)) + if ret != 0: + print("[build.py] ERROR while running examples") + sys.exit(1) print("[build.py] Everything OK") diff --git a/tools/common.py b/tools/common.py index fb0a733b..9a5c5b61 100644 --- a/tools/common.py +++ b/tools/common.py @@ -22,8 +22,8 @@ OS_POSTFIX2 = "linux32" if ARCH32 else "linux64" # Platforms -LINUX = (platform.system() == "Linux") WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") MAC = (platform.system() == "Darwin") # Python version eg. 27 diff --git a/tools/run_examples.py b/tools/run_examples.py new file mode 100644 index 00000000..a89084e7 --- /dev/null +++ b/tools/run_examples.py @@ -0,0 +1,147 @@ +""" +Run all examples that can be run on current configuration. + +Note on GTK 2 / GTK 3 on Windows: + Installing both PyGTK and PyGI on Windows will cause errors. + You can install only one of these packages. +""" + +from common import * + +import importlib +import os +import sys + + +def main(): + os.chdir(EXAMPLES_DIR) + + packages = check_installed_packages() + examples = list() + examples.append("hello_world.py") + succeeded = list() + failed = list() + passed = list() + + # wxpython + if packages["wx"]: + examples.append("wxpython.py") + else: + print("[run_examples.py] PASS: wxpython.py (wxPython not installed)") + passed.append("wxpython.py") + + # gtk2 + if packages["gtk"]: + examples.append("gtk2.py") + if LINUX: + examples.append("gtk2.py --message-loop-cef") + else: + print("[run_examples.py] PASS: gtk2.py (Gtk 2 not installed") + passed.append("gtk2.py") + + # gtk3 + if packages["gi"]: + if not LINUX: + examples.append("gtk3.py") + else: + # Gtk 3 example is currently broken on Linux (Issue #261) + print("[run_examples.py] PASS: gtk3.py (Issue #261)") + passed.append("gtk3.py") + else: + print("[run_examples.py] PASS: gtk3.py (Gtk 3 not installed)") + passed.append("gtk3.py") + + # pyqt + if packages["PyQt4"]: + examples.append("qt.py pyqt") + else: + print("[run_examples.py] PASS: qt.py pyqt (PyQt4 not installed)") + passed.append("qt.py pyqt") + + # pyside + if packages["PySide"]: + examples.append("qt.py pyside") + else: + print("[run_examples.py] PASS: qt.py pyside (PySide not installed)") + passed.append("qt.py pyside") + + # tkinter + if packages["tkinter"] or packages["Tkinter"]: + examples.append("tkinter_.py") + else: + print(["run_examples.py] PASS: tkinter_.py (tkinter not installed)"]) + passed.append("tkinter_.py") + + # kivy + if LINUX and packages["kivy"] and packages["gtk"]: + if "--kivy" in sys.argv: + # When --kivy flag passed run only Kivy example + examples = list() + passed = list() + examples.append("{linux_dir}/binaries_64bit/kivy_.py" + .format(linux_dir=LINUX_DIR)) + + # Run all + for example in examples: + print("[run_examples.py] Running '{example}'..." + .format(example=example)) + ret = os.system("{python} {example}" + .format(python=sys.executable, example=example)) + if ret == 0: + succeeded.append(example) + else: + print("[run_examples.py] ERROR while running example: {example}" + .format(example=example)) + failed.append(example) + + # Summary + summary = "" + for example in succeeded: + summary += " OK {example}{nl}"\ + .format(example=example, nl=os.linesep) + for example in failed: + summary += " ERROR {example}{nl}"\ + .format(example=example, nl=os.linesep) + for example in passed: + summary += " PASS {example}{nl}"\ + .format(example=example, nl=os.linesep) + summary = summary[:-(len(os.linesep))] + print("[run_examples.py] SUMMARY:") + print(summary.format()) + + # OK or error message + passed_msg = "" + if passed: + passed_msg = ". Passed: {passed}.".format(passed=len(passed)) + if len(failed): + print("[run_examples.py] ERRORS({failed}) while running examples" + "{passed_msg}" + .format(failed=len(failed), passed_msg=passed_msg)) + sys.exit(1) + else: + print("[run_examples.py] OK({succeeded}){passed_msg}" + .format(succeeded=len(succeeded), passed_msg=passed_msg)) + + +def check_installed_packages(): + packages = { + "gtk": False, + "gi": False, + "kivy": False, + "PyQt4": False, + "PySide": False, + "tkinter": False, + "Tkinter": False, + "wx": False, + } + for package in packages: + try: + importlib.import_module(package) + packages[package] = True + except ImportError: + packages[package] = False + return packages + + +if __name__ == "__main__": + main() From 6f7c52da12704313009b834e086f0cd102b3d450 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Feb 2017 16:59:14 +0100 Subject: [PATCH 12/16] Update installer to include platform tag when generating .whl file. --- tools/build.py | 7 +++++-- tools/installer/cefpython3.setup.py | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/build.py b/tools/build.py index c7d742ec..0c2a84a4 100644 --- a/tools/build.py +++ b/tools/build.py @@ -678,9 +678,12 @@ def install_and_run(): # Run examples print("[build.py] Run examples") os.chdir(EXAMPLES_DIR) + kivy_flag = "--kivy" if KIVY_FLAG else "" run_examples = os.path.join(TOOLS_DIR, "run_examples.py") - ret = os.system("{python} {run_examples}" - .format(python=sys.executable, run_examples=run_examples)) + ret = os.system("{python} {run_examples} {kivy_flag}" + .format(python=sys.executable, + run_examples=run_examples, + kivy_flag=kivy_flag)) if ret != 0: print("[build.py] ERROR while running examples") sys.exit(1) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 292b2ee3..bfdc570d 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -24,6 +24,7 @@ import platform import subprocess import sys +import sysconfig # The setuptools package is not installed by default on a clean # Ubuntu. Might be also a case on Windows. Also Python Eggs @@ -92,6 +93,19 @@ def get_tag(self): return tag cmdclass["bdist_wheel"] = custom_bdist_wheel +elif platform.system() in ["Windows", "Linux"] and "bdist_wheel" in sys.argv: + # On Windows and Linux platform tag is always "any". + print("[setup.py] Overload bdist_wheel command to fix platform tag") + from wheel.bdist_wheel import bdist_wheel + + class custom_bdist_wheel(bdist_wheel): + def get_tag(self): + tag = bdist_wheel.get_tag(self) + platform_tag = sysconfig.get_platform() + tag = (tag[0], tag[1], platform_tag) + return tag + + cmdclass["bdist_wheel"] = custom_bdist_wheel def main(): @@ -132,7 +146,12 @@ def main(): "Topic :: Software Development :: User Interfaces", ], ) - print("[setup.py] OK installed") + if "install" in sys.argv: + print("[setup.py] OK installed") + elif "bdist_wheel" in sys.argv: + print("[setup.py] OK created wheel package in dist/ directory") + else: + print("[setup.py] Unknown command line arguments") def get_package_data(): From 5ce5c368abaf973d0c65fad4d7f136c18cb06279 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Feb 2017 12:36:24 +0100 Subject: [PATCH 13/16] Fix DevTools window not showing on Windows (#303)... Add "Quick build instructions for Windows" section indocs/Build-instructions. Fix invalid types for window handles on Windows 64-bit (#302). Also fix long long for other pointers such as in PaintBuffer. Also fix long long for uint types. Get rid of invalid/unnecessary long conversions eg. from int64 type. --- docs/Build-instructions.md | 82 +++++++++++++++++++++------ docs/Tutorial.md | 8 +++ src/browser.pyx | 31 ++++++---- src/cefpython.pyx | 15 +++-- src/client_handler/render_handler.cpp | 4 +- src/extern/cef/cef_types.pxd | 11 ++-- src/frame.pyx | 3 +- src/handlers/render_handler.pyx | 4 +- src/handlers/request_handler.pyx | 2 +- src/handlers/resource_handler.pyx | 2 +- src/paint_buffer.pyx | 4 +- src/process_message_utils.pyx | 8 +-- src/utils.pyx | 4 +- src/web_request.pyx | 8 +-- src/window_utils_linux.pyx | 13 +++-- src/window_utils_mac.pyx | 7 ++- src/window_utils_win.pyx | 17 +++--- tools/build.py | 4 +- unittests/_test_runner.py | 2 +- 19 files changed, 152 insertions(+), 77 deletions(-) diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index b0c4a049..179a970d 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -1,27 +1,33 @@ # Build instructions -__IMPORTANT NOTE__: These instructions are for the new releases of CEF Python -(Chrome v51 and later). Currently only Linux platform is being tested. -For the old v31 release that is supported on all platforms, see build -instructions on the wiki pages. +These instructions are for the new releases of CEF Python v50+. +For the old v31 release that is supported on all platforms, see +the build instructions on the wiki pages. + +If you would like to quickly build cefpython then see the +[Quick build instructions for Windows](#quick-build-instructions-for-windows) +and [Quick build instructions for Linux](#quick-build-instructions-for-linux) +sections for complete instructions. There are several types of builds you can perform: -1. You can build CEF Python using prebuilt CEF binaries that were - uploaded to GH releases (tagged eg. v51-upstream) -2. You can build both CEF Python and CEF from sources, but note +1. You can build CEF Python using prebuilt CEF binaries and libraries + that were uploaded to GH releases (tagged eg. v55-upstream) +2. You can build CEF Python using prebuilt CEF binaries from + Spotify Automated Builds. +3. You can build both CEF and CEF Python from sources, but note that Building CEF is a long process that can take hours. - In the tools/ directory there is the automate.py script that - automates building CEF. -3. You may also use prebuilt binaries from Spotify automated builds, - see the CEF automated builds section. -Before you can build CEF Python or CEF you must satisfy requirements -listed on this page. +Detailed instructions for building can be found in module doc comments +in the `automate.py` and `build.py` tools (the tools/ directory). + +Before you can build CEF Python or CEF you must satisfy some +[requirements](#requirements) listed on this page. Table of contents: -* [Build CEF Python on Linux](#build-cef-python-on-linux) +* [Quick build instructions for Windows](#quick-build-instructions-for-windows) +* [Quick build instructions for Linux](#quick-build-instructions-for-linux) * [Requirements](#requirements) * [Build CEF Python using prebuilt CEF binaries](#build-cef-python-using-prebuilt-cef-binaries) * [Build both CEF Python and CEF from sources](#build-both-cef-python-and-cef-from-sources) @@ -30,7 +36,44 @@ Table of contents: * [How to patch](#how-to-patch) -## Build CEF Python on Linux +## Quick build instructions for Windows + +Complete steps for building CEF Python v50+ using prebuilt binaries +and libraries from GitHub releases: + +1) Tested and works fine on Windows 7 64-bit + +2) Download [ninja](https://github.com/ninja-build/ninja) 1.7.2 or later + and add it to PATH. + +3) Download [cmake](https://cmake.org/download/) 3.7.2 or later and add + it to PATH. + +4) For Python 2.7 Install "Visual C++ Compiler for Python 2.7" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=44266) + +5) For Python 2.7 and when using using "Visual C++ compiler for Python 2.7" + you have to install "Visual C++ 2008 Redistributable Package (x64)" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) + +6) Install python dependencies by running: + `cd cefpython/tools/ && pip install -r requirements.txt` + (Cython version from requirements.txt must match exactly) + +7) Download 32-bit Windows binaries and libraries from + [GH releases](https://github.com/cztomczak/cefpython/tags) + tagged e.g. 'v55-upstream' when building v55. + +8) Extract the archive it in the "cefpython/build/" directory. + +9) Build cefpython and run examples (xx.x is version number eg. 55.4): +``` +cd cefpython/build/ +python ../tools/build.py xx.x +``` + + +## Quick build instructions for Linux Complete steps for building CEF Python v50+ using prebuilt binaries from GitHub releases: @@ -47,18 +90,19 @@ binaries from GitHub releases: (Cython version from requirements.txt must match exactly) 5) Download 64-bit Linux binaries and libraries from - [GH releases](https://github.com/cztomczak/cefpython/releases) - tagged e.g. 'v50-upstream' when building v50. + [GH releases](https://github.com/cztomczak/cefpython/tags) + tagged e.g. 'v55-upstream' when building v55. 6) Extract it in the cefpython/build/ directory and rename the extracted directory to "cef_linux64". -8) Build cefpython and run examples (xx.x is version e.g. 50.0): +7) Build cefpython and run examples (xx.x is version e.g. 50.0): ``` cd cefpython/src/linux/ python compile.py xx.x ``` + ## Requirements Below are platform specific requirements. Do these first before @@ -69,6 +113,8 @@ __Windows__ * Install an appropriate MS compiler for a specific Python version: https://wiki.python.org/moin/WindowsCompilers +* For Python 2.7 install "Microsoft Visual C++ Compiler for Python 2.7" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=44266) * When using "Visual C++ compiler for Python 2.7" you have to install "Microsoft Visual C++ 2008 Redistributable Package (x64)" from [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 04ecf74e..e659b263 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -9,6 +9,14 @@ Table of contents: * [Hello world](#hello-world) * [CEF's multiprocess architecture](#cefs-multiprocess-architecture) * [Handling Python exceptions](#handling-python-exceptions) +* [Message loop](#message-loop) +* [Settings](#settings) +* [Handlers](#handlers) +* [Javascript integration](#javascript-integration) +* [Plugins](#plugins) +* [Helper functions](#helper-functions) +* [Build executable](#build-executable) +* [What's next?](#whats-next) ## Install and download examples diff --git a/src/browser.pyx b/src/browser.pyx index 5907dd72..4a38f0d8 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -107,7 +107,7 @@ cpdef PyBrowser GetBrowserByWindowHandle(WindowHandle windowHandle): for browserId in g_pyBrowsers: pyBrowser = g_pyBrowsers[browserId] if (pyBrowser.GetWindowHandle() == windowHandle or - pyBrowser.GetUserData("__outerWindowHandle") == long(windowHandle)): + pyBrowser.GetUserData("__outerWindowHandle") == windowHandle): return pyBrowser return None @@ -312,7 +312,7 @@ cdef class PyBrowser: cpdef object GetFrameByIdentifier(self, object identifier): return GetPyFrame(self.GetCefBrowser().get().GetFrame( - long(identifier))) + identifier)) cpdef list GetFrameNames(self): assert IsThread(TID_UI), ( @@ -418,7 +418,7 @@ cdef class PyBrowser: IF UNAME_SYSNAME == "Linux": x11.SetX11WindowBounds(self.GetCefBrowser(), x, y, width, height) ELSE: - raise Exception("SetBounds() not implemented on this platform") + NonCriticalError("SetBounds() not implemented on this platform") cpdef py_void SetFocus(self, enable): self.GetCefBrowserHost().get().SetFocus(bool(enable)) @@ -430,13 +430,19 @@ cdef class PyBrowser: self.GetCefBrowserHost().get().SetZoomLevel(zoomLevel) cpdef py_void ShowDevTools(self): - cdef CefWindowInfo windowInfo - cdef CefRefPtr[ClientHandler] clientHandler =\ + cdef CefWindowInfo window_info + IF UNAME_SYSNAME == "Windows": + # On Windows with empty window_info structure the devtools + # window doesn't appear. + window_info.SetAsPopup( + self.GetOpenerWindowHandle(), + PyToCefStringValue("DevTools")) + cdef CefRefPtr[ClientHandler] client_handler =\ new ClientHandler() cdef CefBrowserSettings settings cdef CefPoint inspect_element_at self.GetCefBrowserHost().get().ShowDevTools( - windowInfo, clientHandler, settings, + window_info, client_handler, settings, inspect_element_at) cpdef py_void StopLoad(self): @@ -454,7 +460,8 @@ cdef class PyBrowser: cpdef py_void ToggleFullscreen_Windows(self): cdef WindowHandle windowHandle if self.GetUserData("__outerWindowHandle"): - windowHandle = self.GetUserData("__outerWindowHandle") + windowHandle = \ + self.GetUserData("__outerWindowHandle") else: windowHandle = self.GetWindowHandle() @@ -463,7 +470,7 @@ cdef class PyBrowser: "Browser.ToggleFullscreen() failed: no window handle " "found") - cdef HWND hwnd = int(windowHandle) + cdef HWND hwnd = windowHandle cdef RECT rect cdef HMONITOR monitor cdef MONITORINFO monitorInfo @@ -533,7 +540,7 @@ cdef class PyBrowser: if "type" in pyEvent: cefEvent.type = int(pyEvent["type"]) if "modifiers" in pyEvent: - cefEvent.modifiers = long(pyEvent["modifiers"]) + cefEvent.modifiers = pyEvent["modifiers"] # Always set CefKeyEvent.windows_key_code in SendKeyEvent, even on # Linux. When sending key event for 'backspace' on Linux and setting # "native_key_code", "character", "unmodified_character" it doesn't @@ -639,7 +646,7 @@ cdef class PyBrowser: # ------------------------------------------------------------------------- cpdef py_void DragTargetDragEnter(self, DragData drag_data, int x, int y, - long long allowed_ops): + uint32 allowed_ops): cdef CefMouseEvent mouse_event mouse_event.x = x mouse_event.y = y @@ -647,7 +654,7 @@ cdef class PyBrowser: drag_data.cef_drag_data, mouse_event, allowed_ops) - cpdef py_void DragTargetDragOver(self, int x, int y, long long allowed_ops): + cpdef py_void DragTargetDragOver(self, int x, int y, uint32 allowed_ops): cdef CefMouseEvent mouse_event mouse_event.x = x mouse_event.y = y @@ -663,7 +670,7 @@ cdef class PyBrowser: mouse_event.y = y self.GetCefBrowserHost().get().DragTargetDrop(mouse_event) - cpdef py_void DragSourceEndedAt(self, int x, int y, long long operation): + cpdef py_void DragSourceEndedAt(self, int x, int y, uint32 operation): self.GetCefBrowserHost().get().DragSourceEndedAt( x, y, operation) diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 634a9930..ffbcde96 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -312,11 +312,11 @@ import weakref # would be a bad idea. ctypedef object py_string -# You can't use "void" along with cpdef function returning None, it is planned to be -# added to Cython in the future, creating this virtual type temporarily. If you -# change it later to "void" then don't forget to add "except *". +# You can't use "void" along with cpdef function returning None, it is +# planned to be added to Cython in the future, creating this virtual +# type temporarily. If you change it later to "void" then don't forget +# to add "except *". ctypedef object py_void -ctypedef long long WindowHandle # noinspection PyUnresolvedReferences from cpython cimport PyLong_FromVoidPtr @@ -363,6 +363,9 @@ from libc.stdint cimport uint64_t # noinspection PyUnresolvedReferences from libc.stdint cimport uintptr_t +# noinspection PyUnresolvedReferences +ctypedef uintptr_t WindowHandle + # noinspection PyUnresolvedReferences cimport ctime @@ -388,6 +391,7 @@ from cef_types cimport ( CefSettings, CefBrowserSettings, CefRect, CefPoint, CefKeyEvent, CefMouseEvent, CefScreenInfo, PathKey, PK_DIR_EXE, PK_DIR_MODULE, + int32, uint32, int64, uint64, ) from cef_task cimport * @@ -938,7 +942,8 @@ def Shutdown(): # If the the two code blocks above, that tried to close browsers # and free CEF references, failed, then display an error about it! if len(g_pyBrowsers): - Error("Shutdown called, but there are still browser references alive!") + NonCriticalError("Shutdown called, but there are still browser" + " references alive") Debug("Shutdown()") with nogil: diff --git a/src/client_handler/render_handler.cpp b/src/client_handler/render_handler.cpp index 812f2ce9..fcbbbfeb 100644 --- a/src/client_handler/render_handler.cpp +++ b/src/client_handler/render_handler.cpp @@ -94,7 +94,7 @@ bool RenderHandler::StartDragging(CefRefPtr browser, { REQUIRE_UI_THREAD(); return RenderHandler_StartDragging(browser, drag_data, - static_cast(allowed_ops), x, y); + allowed_ops, x, y); } @@ -102,5 +102,5 @@ void RenderHandler::UpdateDragCursor(CefRefPtr browser, DragOperation operation) { REQUIRE_UI_THREAD(); - RenderHandler_UpdateDragCursor(browser, static_cast(operation)); + RenderHandler_UpdateDragCursor(browser, operation); } diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index 9769e0b6..1b244521 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -7,16 +7,19 @@ include "compile_time_constants.pxi" from libcpp cimport bool as cpp_bool # noinspection PyUnresolvedReferences from libc.stddef cimport wchar_t +# noinspection PyUnresolvedReferences +from libc.stdint cimport int32_t, uint32_t, int64_t, uint64_t from cef_string cimport cef_string_t # noinspection PyUnresolvedReferences from libc.limits cimport UINT_MAX cdef extern from "include/internal/cef_types.h": - ctypedef unsigned int uint32 - ctypedef int int32 - ctypedef long long int64 - ctypedef unsigned long long uint64 + ctypedef int32_t int32 + ctypedef uint32_t uint32 + ctypedef int64_t int64 + ctypedef uint64_t uint64 + IF UNAME_SYSNAME == "Windows": # noinspection PyUnresolvedReferences ctypedef wchar_t char16 diff --git a/src/frame.pyx b/src/frame.pyx index a8502427..ab22ba4f 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -21,8 +21,7 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): Debug("GetPyFrame(): returning None") return cdef PyFrame pyFrame - # long long - cdef object frameId = cefFrame.get().GetIdentifier() + cdef object frameId = cefFrame.get().GetIdentifier() # int64 cdef int browserId = cefFrame.get().GetBrowser().get().GetIdentifier() assert (frameId and browserId), "frameId or browserId empty" cdef object uniqueFrameId = GetUniqueFrameId(browserId, frameId) diff --git a/src/handlers/render_handler.pyx b/src/handlers/render_handler.pyx index a332ff18..3204ca3b 100644 --- a/src/handlers/render_handler.pyx +++ b/src/handlers/render_handler.pyx @@ -247,7 +247,7 @@ cdef public void RenderHandler_OnScrollOffsetChanged( cdef public cpp_bool RenderHandler_StartDragging( CefRefPtr[CefBrowser] cef_browser, CefRefPtr[CefDragData] cef_drag_data, - long long allowed_ops, + uint32 allowed_ops, int x, int y ) except * with gil: cdef PyBrowser browser @@ -276,7 +276,7 @@ cdef public cpp_bool RenderHandler_StartDragging( cdef public void RenderHandler_UpdateDragCursor( CefRefPtr[CefBrowser] cef_browser, - long long operation, + uint32 operation, ) except * with gil: cdef PyBrowser browser try: diff --git a/src/handlers/request_handler.pyx b/src/handlers/request_handler.pyx index 74b3c23d..a4709d98 100644 --- a/src/handlers/request_handler.pyx +++ b/src/handlers/request_handler.pyx @@ -267,7 +267,7 @@ cdef public cpp_bool RequestHandler_OnQuotaRequest( returnValue = clientCallback( browser=pyBrowser, origin_url=pyOriginUrl, - new_size=long(newSize), + new_size=newSize, callback=CreatePyRequestCallback(cefRequestCallback)) return bool(returnValue) else: diff --git a/src/handlers/resource_handler.pyx b/src/handlers/resource_handler.pyx index c7e20929..c4f1d6cd 100644 --- a/src/handlers/resource_handler.pyx +++ b/src/handlers/resource_handler.pyx @@ -122,7 +122,7 @@ cdef public void ResourceHandler_GetResponseHeaders( if userCallback: returnValue = userCallback(pyResponse, responseLengthOut, redirectUrlOut) - (&cefResponseLength)[0] = long(responseLengthOut[0]) + (&cefResponseLength)[0] = responseLengthOut[0] if redirectUrlOut[0]: PyToCefString(redirectUrlOut[0], cefRedirectUrl) return diff --git a/src/paint_buffer.pyx b/src/paint_buffer.pyx index 3ce59d9e..0ea480d1 100644 --- a/src/paint_buffer.pyx +++ b/src/paint_buffer.pyx @@ -18,8 +18,8 @@ cdef class PaintBuffer: cdef int height cdef Py_ssize_t length - cpdef long long GetIntPointer(self) except *: - return self.buffer + cpdef uintptr_t GetIntPointer(self) except *: + return self.buffer cpdef object GetString(self, str mode="bgra", str origin="top-left"): cdef void* dest diff --git a/src/process_message_utils.pyx b/src/process_message_utils.pyx index 80e91781..314871d5 100644 --- a/src/process_message_utils.pyx +++ b/src/process_message_utils.pyx @@ -49,8 +49,8 @@ cdef list CefListValueToPyList( cdef cef_types.cef_value_type_t valueType cdef list ret = [] cdef CefRefPtr[CefBinaryValue] binaryValue - cdef cef_types.uint32 uint32_value - cdef cef_types.int64 int64_value + cdef uint32 uint32_value + cdef int64 int64_value cdef object originallyString for index in range(0, size): valueType = cefListValue.get().GetType(index) @@ -111,8 +111,8 @@ cdef dict CefDictionaryValueToPyDict( cdef CefString cefKey cdef py_string pyKey cdef CefRefPtr[CefBinaryValue] binaryValue - cdef cef_types.uint32 uint32_value - cdef cef_types.int64 int64_value + cdef uint32 uint32_value + cdef int64 int64_value cdef object originallyString while iterator != keyList.end(): cefKey = deref(iterator) diff --git a/src/utils.pyx b/src/utils.pyx index 86ced211..a3ef03aa 100644 --- a/src/utils.pyx +++ b/src/utils.pyx @@ -42,6 +42,7 @@ cpdef py_bool IsThread(int threadID): # This change is required to work with Cython 0.20. cpdef object Debug(py_string msg): + """Print debug message. Will be shown only when settings.debug=True.""" if not g_debug: return # In Python 3 str or bytes may be passed @@ -59,7 +60,8 @@ cpdef object Debug(py_string msg): print("[CEF Python] WARNING: failed writing to debug file: %s" % ( g_debugFile)) -cdef void Error(py_string msg) except *: +cdef void NonCriticalError(py_string msg) except *: + """Notify about error gently. Does not terminate application.""" # In Python 3 str or bytes may be passed if type(msg) != str and type(msg) == bytes: msg = msg.decode("utf-8", "replace") diff --git a/src/web_request.pyx b/src/web_request.pyx index d5558882..cd591ebc 100644 --- a/src/web_request.pyx +++ b/src/web_request.pyx @@ -122,8 +122,8 @@ cdef class PyWebRequest: cdef public void WebRequestClient_OnUploadProgress( int webRequestId, CefRefPtr[CefURLRequest] cefWebRequest, - cef_types.int64 current, - cef_types.int64 total + int64 current, + int64 total ) except * with gil: cdef PyWebRequest webRequest cdef object userCallback @@ -143,8 +143,8 @@ cdef public void WebRequestClient_OnUploadProgress( cdef public void WebRequestClient_OnDownloadProgress( int webRequestId, CefRefPtr[CefURLRequest] cefWebRequest, - cef_types.int64 current, - cef_types.int64 total + int64 current, + int64 total ) except * with gil: cdef PyWebRequest webRequest cdef object userCallback diff --git a/src/window_utils_linux.pyx b/src/window_utils_linux.pyx index da4030cc..9427ebb0 100644 --- a/src/window_utils_linux.pyx +++ b/src/window_utils_linux.pyx @@ -12,19 +12,20 @@ class WindowUtils: # for these methods. @staticmethod - def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(WindowHandle windowHandle, long msg, long wparam, + long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnSize(long windowHandle, long msg, long wparam, long lparam): + def OnSize(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnEraseBackground(long windowHandle, long msg, long wparam, + def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. @@ -41,11 +42,11 @@ class WindowUtils: return True @staticmethod - def gtk_plug_new(long long gdkNativeWindow): - return gtk_plug_new(gdkNativeWindow) + def gtk_plug_new(WindowHandle gdkNativeWindow): + return gtk_plug_new(gdkNativeWindow) @staticmethod - def gtk_widget_show(long long gtkWidgetPtr): + def gtk_widget_show(WindowHandle gtkWidgetPtr): with nogil: gtk_widget_show(gtkWidgetPtr) diff --git a/src/window_utils_mac.pyx b/src/window_utils_mac.pyx index 08eb7b85..7b8e8569 100644 --- a/src/window_utils_mac.pyx +++ b/src/window_utils_mac.pyx @@ -9,19 +9,20 @@ class WindowUtils: # for these methods. @staticmethod - def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(WindowHandle windowHandle, long msg, long wparam, + long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnSize(long windowHandle, long msg, long wparam, long lparam): + def OnSize(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnEraseBackground(long windowHandle, long msg, long wparam, + def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. diff --git a/src/window_utils_win.pyx b/src/window_utils_win.pyx index c40bd383..5f8e8019 100644 --- a/src/window_utils_win.pyx +++ b/src/window_utils_win.pyx @@ -7,7 +7,7 @@ include "cefpython.pyx" class WindowUtils(object): @staticmethod - def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(WindowHandle windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return 0 @@ -15,7 +15,7 @@ class WindowUtils(object): return 0 @staticmethod - def OnSize(long windowHandle, long msg, long wparam, long lparam): + def OnSize(WindowHandle windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return DefWindowProc(windowHandle, msg, wparam, lparam) @@ -35,7 +35,7 @@ class WindowUtils(object): return DefWindowProc(windowHandle, msg, wparam, lparam) @staticmethod - def OnEraseBackground(long windowHandle, long msg, long wparam, + def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: @@ -58,7 +58,8 @@ class WindowUtils(object): cdef WindowHandle windowHandle if pyBrowser.GetUserData("__outerWindowHandle"): - windowHandle = pyBrowser.GetUserData("__outerWindowHandle") + windowHandle = \ + pyBrowser.GetUserData("__outerWindowHandle") else: windowHandle = pyBrowser.GetWindowHandle() @@ -102,7 +103,7 @@ class WindowUtils(object): iconSmall = SendMessage( windowHandle, WM_GETICON, ICON_SMALL, 0) - cdef long long parentWindowHandle + cdef WindowHandle parentWindowHandle if not iconBig and not iconSmall: parentWindowHandle = pyBrowser.GetOpenerWindowHandle() @@ -134,11 +135,11 @@ class WindowUtils(object): ICON_SMALL, parentIconSmall) @staticmethod - def GetParentHandle(long windowHandle): - return GetParent(windowHandle) + def GetParentHandle(WindowHandle windowHandle): + return GetParent(windowHandle) @staticmethod - def IsWindowHandle(long windowHandle): + def IsWindowHandle(WindowHandle windowHandle): IF UNAME_SYSNAME == "Windows": return bool(IsWindow(windowHandle)) ELSE: diff --git a/tools/build.py b/tools/build.py index 0c2a84a4..7f89faf7 100644 --- a/tools/build.py +++ b/tools/build.py @@ -504,7 +504,9 @@ def except_all_missing(content): patterns.append( r"\bcp?def\s+" "((int|short|long|double|char|unsigned|float|double|cpp_bool" - "|cpp_string|cpp_wstring|uint64_t|uintptr_t|void" + "|cpp_string|cpp_wstring|uintptr_t|void" + "|int32|uint32|int64|uint64" + "|int32_t|uint32_t|int64_t|uint64_t" "|CefString)\s+)+" "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") patterns.append( diff --git a/unittests/_test_runner.py b/unittests/_test_runner.py index 8b970aca..3968a52c 100644 --- a/unittests/_test_runner.py +++ b/unittests/_test_runner.py @@ -266,7 +266,7 @@ def _print_summary(self): failed_str += ")" print(failed_str) else: - print("[_test_runner.py] OK") + print("[_test_runner.py] OK all unit tests succeeded") self._exit() def _exit(self): From 101a223464eab744dcca421d2fca188bb6a9c992 Mon Sep 17 00:00:00 2001 From: Czarek Date: Thu, 23 Feb 2017 13:41:21 +0100 Subject: [PATCH 14/16] Up-to-date complete build instructions for all three types of builds. 1. Build using prebuilt binaries and libraries from GH Releases 2. Build using binaries from Spotify Automated Builds 3. Build upstream CEF from sources --- docs/Build-instructions.md | 295 +++++++++++++++++++++++++++---------- 1 file changed, 214 insertions(+), 81 deletions(-) diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 179a970d..72ca54e8 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -7,21 +7,21 @@ the build instructions on the wiki pages. If you would like to quickly build cefpython then see the [Quick build instructions for Windows](#quick-build-instructions-for-windows) and [Quick build instructions for Linux](#quick-build-instructions-for-linux) -sections for complete instructions. +sections. These instructions are complete meaning you don't need +to read anything more from this document. Using these quick +instructions you should be able to build cefpython in less than +10 minutes. -There are several types of builds you can perform: +There are several types of builds described in this document: 1. You can build CEF Python using prebuilt CEF binaries and libraries - that were uploaded to GH releases (tagged eg. v55-upstream) + that were uploaded to GH releases 2. You can build CEF Python using prebuilt CEF binaries from Spotify Automated Builds. -3. You can build both CEF and CEF Python from sources, but note - that Building CEF is a long process that can take hours. +3. You can build upstream CEF from sources, but note that building CEF + is a long process that can take hours. -Detailed instructions for building can be found in module doc comments -in the `automate.py` and `build.py` tools (the tools/ directory). - -Before you can build CEF Python or CEF you must satisfy some +Before you can build CEF Python or CEF you must satisfy [requirements](#requirements) listed on this page. @@ -29,17 +29,23 @@ Table of contents: * [Quick build instructions for Windows](#quick-build-instructions-for-windows) * [Quick build instructions for Linux](#quick-build-instructions-for-linux) * [Requirements](#requirements) -* [Build CEF Python using prebuilt CEF binaries](#build-cef-python-using-prebuilt-cef-binaries) -* [Build both CEF Python and CEF from sources](#build-both-cef-python-and-cef-from-sources) + * [Windows](#windows) + * [Linux](#linux) + * [Mac](#mac) + * [All platforms](#all-platforms) +* [Build using prebuilt CEF binaries and libraries](#build-using-prebuilt-cef-binaries-and-libraries) +* [Build using CEF binaries from Spotify Automated Builds](#build-using-cef-binaries-from-spotify-automated-builds) +* [Build upstream CEF from sources](#build-upstream-cef-from-sources) * [Build CEF manually](#build-cef-manually) -* [CEF automated builds](#cef-automated-builds) -* [How to patch](#how-to-patch) +* [CEF Automated Builds (Spotify and Adobe)](#cef-automated-builds-spotify-and-adobe) +* [Notes](#notes) +* [How to patch mini tutorial](#how-to-patch-mini-tutorial) ## Quick build instructions for Windows Complete steps for building CEF Python v50+ using prebuilt binaries -and libraries from GitHub releases: +and libraries from GitHub Releases: 1) Tested and works fine on Windows 7 64-bit @@ -56,19 +62,30 @@ and libraries from GitHub releases: you have to install "Visual C++ 2008 Redistributable Package (x64)" from [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) -6) Install python dependencies by running: - `cd cefpython/tools/ && pip install -r requirements.txt` - (Cython version from requirements.txt must match exactly) +6) Clone cefpython and create a build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` + +7) Install python dependencies: +``` +pip install -r ../tools/requirements.txt +``` -7) Download 32-bit Windows binaries and libraries from +8) Download Windows binaries and libraries from [GH releases](https://github.com/cztomczak/cefpython/tags) - tagged e.g. 'v55-upstream' when building v55. + tagged e.g. 'v55-upstream' when building v55. The version + of the binaries must match exactly the CEF version from + the "cefpython/src/version/cef_version_win.h" file + (the CEF_VERSION constant). -8) Extract the archive it in the "cefpython/build/" directory. +8) Extract the archive in the "build/" directory. -9) Build cefpython and run examples (xx.x is version number eg. 55.4): +9) Build cefpython and run examples (xx.x is version number): ``` -cd cefpython/build/ python ../tools/build.py xx.x ``` @@ -76,29 +93,45 @@ python ../tools/build.py xx.x ## Quick build instructions for Linux Complete steps for building CEF Python v50+ using prebuilt -binaries from GitHub releases: +binaries and libraries from GitHub Releases: -1) Tested and works fine on Ubuntu 14.04 64-bit (cmake 2.8.12 and g++ 4.8.4) +1) Tested and works fine on Ubuntu 14.04 64-bit 2) Download [ninja](https://github.com/ninja-build/ninja) 1.7.1 or later and copy it to /usr/bin and chmod 755. -3) Install packages: `sudo apt-get install python-dev cmake g++ libgtk2.0-dev` +3) Install required packages (tested and works with: cmake 2.8.12 + and g++ 4.8.4): +``` +sudo apt-get install python-dev cmake g++ libgtk2.0-dev +``` -4) Install python dependencies by executing: - `cd cefpython/tools/ && sudo pip install -r requirements.txt` - (Cython version from requirements.txt must match exactly) +4) Clone cefpython and create build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` + +5) Install python dependencies: +``` +sudo pip install -r ../tools/requirements.txt +``` -5) Download 64-bit Linux binaries and libraries from +6) Download Linux binaries and libraries from [GH releases](https://github.com/cztomczak/cefpython/tags) - tagged e.g. 'v55-upstream' when building v55. + tagged e.g. 'v55-upstream' when building v55. The version + of the binaries must match exactly the CEF version from + the "cefpython/src/version/cef_version_linux.h" file + (the CEF_VERSION constant). -6) Extract it in the cefpython/build/ directory and rename the extracted - directory to "cef_linux64". +7) Extract the archive in the build/ directory and rename + the extracted directory to "cef_linux64/". -7) Build cefpython and run examples (xx.x is version e.g. 50.0): +8) Build cefpython and run examples (xx.x is version number): ``` -cd cefpython/src/linux/ +cd ../src/linux/ python compile.py xx.x ``` @@ -109,7 +142,7 @@ Below are platform specific requirements. Do these first before following instructions in the "All platforms" section that lists requirements common for all platforms. -__Windows__ +### Windows * Install an appropriate MS compiler for a specific Python version: https://wiki.python.org/moin/WindowsCompilers @@ -133,7 +166,7 @@ __Windows__ "%LocalAppData%\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\include\" -__Linux__ +### Linux * Install packages: `sudo apt-get install python-dev cmake g++ libgtk2.0-dev` * If using prebuilt binaries from Spotify automated builds and want to @@ -159,62 +192,118 @@ __Linux__ [cef/#1804](https://bitbucket.org/chromiumembedded/cef/issues/1804). -__All platforms__ +### Mac + +* MacOS 10.9+, Xcode5+ and Xcode command line tools. Only 64-bit builds + are supported. + + + +### All platforms * Install dependencies for the automate.py tool by executing: `cd tools/ && pip install -r requirements.txt` (on Linux use `sudo`). - This will install some PyPI packages including Cython. On Windows - installing Cython requires a VS compiler - see instructions above - for Windows. + This will install some PyPI packages including Cython. -## Build CEF Python using prebuilt CEF binaries +## Build using prebuilt CEF binaries and libraries -__NOT WORKING YET__ +1) Clone cefpython and create a build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` -Prebuilt binaries are available on -[GitHub Releases](https://github.com/cztomczak/cefpython/releases) -and tagged eg. 'v51-upstream'. +2) Download binaries and libraries from + [GH releases](https://github.com/cztomczak/cefpython/tags) + tagged eg. 'v55-upstream' when building v55. The version + of the binaries must match exactly the CEF version from + the "cefpython/src/version/" directory (look for CEF_VERSION + constant in .h file). -Run the automate.py tool using the --prebuilt-cef flag that will download -prebuilt binaries from GitHub Releases using version information from -the "src/version/" directory. +3) Extract the downloaded archive eg. "cef55_3.2883.1553.g80bd606_win32.zip" + in the "build/" directory (using "extract here" option) +4) Run the build.py tool (xx.x is version number): ``` -cd tools/ -python automate.py --prebuilt-cef +python ../tools/build.py xx.x ``` -You should be fine by running automate.py with the default options, but if you -need to customize the build then use the --help flag to see more. +## Build using CEF binaries from Spotify Automated Builds -## Build both CEF Python and CEF from sources +1) Clone cefpython and create a build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` + +2) Download CEF binaries from [Spotify Automated Builds](http://opensource.spotify.com/cefbuilds/index.html). + The version of the binaries must match exactly the CEF version + from the "cefpython/src/version/" directory (look for CEF_VERSION + constant in .h file). -Run the automate.py tool using the --build-cef flag. You can optionally -set how many parallel ninja jobs to run (by default cores/2) with -the --ninja-jobs flag. +3) Extract the downloaded archive eg. + "cef_binary_3.2883.1553.g80bd606_windows32.tar.bz2" + in the build/ directory (using "extract here" option) -The automate script will use version information from the "src/version/" -directory. If you would like to use a custom CEF branch then you can -use the --cef-branch flag - but note that this is only for advanced +4) Run the automate.py tool. After it completes you should see a new + directory eg. "cef55_3.2883.1553.g80bd606_win32/". +``` +python ../tools/automate.py --prebuilt-cef +``` + +5) Run the build.py tool (xx.x is version number): +``` +python ../tools/build.py xx.x +``` + + +## Build upstream CEF from sources + +Building CEF from sources is a very long process that can take several +hours depending on your CPU speed and the platform you're building on. +If you would like to speed up the process you could modify automate.py +tool and remove the `is_official_build=true` flag which slows down +builds significantly (PR to add an option for that is welcome). +You can optionally set how many parallel ninja jobs to run (by default cores/2) with the --ninja-jobs flag passed to automate.py. + +To build CEF from sources run the automate.py tool using the --build-cef +flag. The automate script will use version information from the +"cefpython/src/version/" directory. If you would like to use +a custom CEF branch +then use the --cef-branch flag, but note that this is only for advanced users as this will require updating cefpython's C++/Cython code. -If building on Linux and there are errors, see the MISSING PACKAGES -note futher down. +If building on Linux and there are errors, see the +"MISSING PACKAGES (Linux)" note futher down. -You should be fine by running automate.py with the default options, but if you -need to customize the build then use the --help flag to see more. +You should be fine by running automate.py with the default options, +but if you need to customize the build then use the --help flag to +see more options. +The commands below will build CEF from sources with custom CEF Python +patches applied and then build the CEF Python package (xx.x is version +number): ``` -cd ~/cefpython/ -mkdir build/ && cd build +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ python ../tools/automate.py --build-cef --ninja-jobs 6 -mv cef*_*_linux64/ cef_linux64/ -cd ../../../src/linux/ -python compile.py xx.x +python ../tools/build.py xx.x ``` +The automate.py tool should create eg. "cef55_3.2883.1553.g80bd606_win32/" +directory when it's done. Then the build.py tool will build the cefpython +module, make installer package, install the package and run unit tests +and examples. See the notes for commands for creating package installer +and/or wheel package for distribution. + __MISSING PACKAGES (Linux)__: After the chromium sources are downloaded, it will try to build cef projects and if it fails due to missing packages make sure you've installed all the required packages listed in the @@ -235,8 +324,8 @@ After dependencies are satisifed re-run automate.py. ## Build CEF manually CEF Python official binaries come with custom CEF binaries with -a few patches applied for our use case. These patches are in the -patches/ directory. +a few patches applied for our use case, see the Notes section further +down on this page. On Linux before running any of CEF tools apply the issue73 patch first. @@ -245,16 +334,16 @@ To build CEF follow the instructions on the Branches and Building CEF wiki page: https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding -After it is successfully built - apply patches, rebuild and remake +After it is successfully built, apply patches, rebuild and remake distribs. Note that CEF patches must be applied in the "download_dir/chromium/src/cef/" directory, not in the "download_dir/cef/" directory. -## CEF automated builds +## CEF Automated Builds (Spotify and Adobe) -There are two sites that provide latest builds of CEF: +There are two sites that provide automated CEF builds: * Spotify - http://opensource.spotify.com/cefbuilds/index.html * This is the new build system * Since June 2016 all builds are without tcmalloc, see @@ -264,25 +353,69 @@ There are two sites that provide latest builds of CEF: * This is the old build system. Not tested whether it builds without tcmalloc. + +## Notes + +If you would like to update CEF version in cefpython then +see complete instructions provided in +[Issue #264](https://github.com/cztomczak/cefpython/issues/264). + +When building for multiple Python versions on Linux/Mac use +pyenv to manage multiple Python installations, see +[Issue #249](https://github.com/cztomczak/cefpython/issues/249) +for details. + +Command for making installer package is (xx.x is version number): +``` +cd cefpython/build/ +python ../tools/make_installer.py xx.x +``` + +To create a wheel package from that installer package type: +``` +cd *-setup/ +python setup.py bdist_wheel +cd dist/ +ls *.whl +``` + +Optional flags for the setup.py script above: +* `--python-tag cp27` to generate Python 2.7 only package +* `--universal` to build package for multiple Python versions + (in such case you must first build multiple cefpython modules + for each Python version) + +CEF Python binaries are build using similar configuration as described +on the ["Automated Build Setup"](https://bitbucket.org/chromiumembedded/cef/wiki/AutomatedBuildSetup.md#markdown-header-platform-build-configurations) wiki page in upstream CEF. The automate.py tool incorporates most of +of the flags from these configurations. + To build the "libcef_dll_wrapper" library type these commands: ``` -cd cef_binary/ +cd cef_binary*/ mkdir build cd build/ cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release .. ninja libcef_dll_wrapper ``` -To build CEF sample applications type `ninja cefclient cefsimple`. +To build CEF sample applications type: +``` +ninja cefclient cefsimple ceftests +``` + +Official CEF Python binaries may come with additional patches applied +to CEF/Chromium depending on platform. These patches can be found +in the "cefpython/patches/" directory. Whether you need these patches +depends on your use case, they may not be required and thus you could +use the Spotify Automated Builds. Spotify builds have the issue73 patch +(no tcmalloc) applied. -Official CEF Python binaries come with additional patches to CEF/Chromium, -see the [patches/](../../../tree/master/patches) directory. Whether you -need these patches depends on your use case, they may not be required -and thus you could use the Spotify binaries. Spotify builds have the -issue73 patch (no tcmalloc) applied. +Currently (February 2017) only Linux releases have the custom +patches applied. Windows and Mac releases use CEF binaries from +Spotify Automated Builds. -## How to patch +## How to patch mini tutorial Create a patch from unstaged changes in current directory: ``` From 6b24eb198159a1e18c6902bb3f219a58276bbd21 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 24 Feb 2017 22:38:55 +0100 Subject: [PATCH 15/16] Build v55 on Mac Part 1 (#295)... Successfully built the cefpython module. There are still some warnings and issues to resolve, but it looks good. Update makefiles. Update installer setup for Mac. Update Mac requirements in build instructions. Add --fast-build option to automate.py. --- docs/Build-instructions.md | 9 +-- src/client_handler/Makefile | 12 ++- src/client_handler/lifespan_handler.cpp | 2 + src/compile_time_constants.pxi | 2 +- src/extern/cef/cef_mac.pxd | 2 +- src/linux/setup/setup.py | 4 +- src/subprocess/Makefile | 26 +++--- src/subprocess/Makefile-libcefpythonapp | 2 +- src/version/cef_version_mac.h | 102 ++++++++++++++++++++++++ tools/automate.py | 12 ++- tools/build.py | 83 +++++++++---------- tools/build_module.py | 29 +++---- tools/installer/cefpython3.__init__.py | 32 +++++--- 13 files changed, 226 insertions(+), 91 deletions(-) create mode 100644 src/version/cef_version_mac.h diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 72ca54e8..d22d0263 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -198,7 +198,6 @@ requirements common for all platforms. are supported. - ### All platforms * Install dependencies for the automate.py tool by executing: @@ -267,10 +266,10 @@ python ../tools/build.py xx.x Building CEF from sources is a very long process that can take several hours depending on your CPU speed and the platform you're building on. -If you would like to speed up the process you could modify automate.py -tool and remove the `is_official_build=true` flag which slows down -builds significantly (PR to add an option for that is welcome). -You can optionally set how many parallel ninja jobs to run (by default cores/2) with the --ninja-jobs flag passed to automate.py. +To speed up the process you can pass the --fast-build flag, however +in such case result binaries won't be optimized. +You can optionally set how many parallel ninja jobs to run (by default +cores/2) with the --ninja-jobs flag passed to automate.py. To build CEF from sources run the automate.py tool using the --build-cef flag. The automate script will use version information from the diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile index 08abf696..5340da3d 100644 --- a/src/client_handler/Makefile +++ b/src/client_handler/Makefile @@ -10,17 +10,21 @@ UNAME_S = $(shell uname -s) CC = g++ -CCFLAGS = -fPIC -std=gnu++11 -Wall -Werror $(CEF_CCFLAGS) +CCFLAGS = -fPIC -std=c++11 -Wall -Werror $(CEF_CCFLAGS) SRC = client_handler.cpp cookie_visitor.cpp resource_handler.cpp \ web_request_client.cpp string_visitor.cpp request_context_handler.cpp \ - task.cpp x11.cpp context_menu_handler.cpp display_handler.cpp \ + task.cpp context_menu_handler.cpp display_handler.cpp \ download_handler.cpp focus_handler.cpp js_dialog_handler.cpp \ keyboard_handler.cpp lifespan_handler.cpp load_handler.cpp \ render_handler.cpp request_handler.cpp OBJ = $(SRC:.cpp=.o) +ifeq ($(UNAME_S), Linux) + OBJ += x11.o +endif + ifeq ($(UNAME_S), Darwin) OBJ += util_mac.o endif @@ -56,6 +60,10 @@ $(OUT): $(OBJ) @echo [CLIENT HANDLER] Creating library $(OUT) from $(OBJ)... ar rcs $(OUT) $(OBJ) +x11.o: x11.cpp + @echo [CLIENT HANDLER] Building $@ from $<... + $(CC) $(CCFLAGS) $(INC) -c $< -o $@ + util_mac.o: util_mac.mm @echo [CLIENT HANDLER] Building $@ from $<... $(CC) $(CCFLAGS) $(INC) -c $< -o $@ diff --git a/src/client_handler/lifespan_handler.cpp b/src/client_handler/lifespan_handler.cpp index 4b9f24a8..d77a1f0e 100644 --- a/src/client_handler/lifespan_handler.cpp +++ b/src/client_handler/lifespan_handler.cpp @@ -1,7 +1,9 @@ // Copyright (c) 2016 CEF Python. See the Authors and License files. #include "lifespan_handler.h" +#if defined(OS_WIN) #include "dpi_aware.h" +#endif #include "LOG_DEBUG.h" diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 10ec798a..05306be6 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -1,3 +1,3 @@ # This file was generated by setup.py -DEF UNAME_SYSNAME = "Windows" +DEF UNAME_SYSNAME = "Darwin" DEF PY_MAJOR_VERSION = 2 diff --git a/src/extern/cef/cef_mac.pxd b/src/extern/cef/cef_mac.pxd index 60216fcc..2639eb39 100644 --- a/src/extern/cef/cef_mac.pxd +++ b/src/extern/cef/cef_mac.pxd @@ -6,7 +6,7 @@ include "compile_time_constants.pxi" from libcpp cimport bool as cpp_bool -cdef extern from "include/internal/cef_linux.h": +cdef extern from "include/internal/cef_mac.h": ctypedef void* CefWindowHandle ctypedef void* CefCursorHandle diff --git a/src/linux/setup/setup.py b/src/linux/setup/setup.py index d06a688f..c9a4494d 100644 --- a/src/linux/setup/setup.py +++ b/src/linux/setup/setup.py @@ -16,7 +16,7 @@ # Fast mode disables optimization flags FAST = True print("FAST mode On") - COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=gnu++11'] + COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=c++11'] LINK_OPTIMIZE_FLAGS = ['-flto'] else: FAST = False @@ -25,7 +25,7 @@ # prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/6687630/ COMPILE_OPTIMIZE_FLAGS = ['-flto', '-fdata-sections', '-ffunction-sections', - '-std=gnu++11'] + '-std=c++11'] LINK_OPTIMIZE_FLAGS = ['-flto', '-Wl,--gc-sections'] diff --git a/src/subprocess/Makefile b/src/subprocess/Makefile index fcaad726..75fdeb36 100644 --- a/src/subprocess/Makefile +++ b/src/subprocess/Makefile @@ -32,27 +32,33 @@ ifeq ($(UNAME_S), Linux) -L./../../build/cef_linux64/lib \ -L./../../build/cef_linux32/lib else ifeq ($(UNAME_S), Darwin) - LIB_DIRS = -L./../../build/cef_mac64/bin \ - -L./../../build/cef_mac32/bin \ - -L./../../build/cef_mac64/lib \ - -L./../../build/cef_mac32/lib + LIB_DIRS = -F$(CEF_BIN) \ + -L$(CEF_LIB) endif ifeq ($(UNAME_S), Linux) CPP_FILES = print_handler_gtk.cpp - LIBS = -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 -else + LIBS = -lcef -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 +else ifeq ($(UNAME_S), Darwin) CPP_FILES = - LIBS = + LIBS = -framework Chromium\ Embedded\ Framework endif -CCFLAGS = -g -std=gnu++11 -Wall -Werror -DRENDERER_PROCESS $(CEF_CCFLAGS) + +CCFLAGS = -g -std=c++11 -Wall -Werror -DRENDERER_PROCESS $(CEF_CCFLAGS) + +ifeq ($(UNAME_S), Darwin) + MACFLAGS = -O3 -DNDEBUG -stdlib=libstdc++ \ + -Wl,-search_paths_first -Wl,-ObjC -Wl,-pie -Wl,-dead_strip +else + MACFLAGS = +endif subprocess: # -fPIC is required only for libraries included by Cython. @echo [SUBPROCESS] Building the 'subprocess' executable - g++ $(CCFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ + g++ $(CCFLAGS) $(MACFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ v8function_handler.cpp v8utils.cpp javascript_callback.cpp \ $(CPP_FILES) \ - $(LIBS) -lcef -lcef_dll_wrapper -o subprocess -Wl,-rpath,. + $(LIBS) -lcef_dll_wrapper -o subprocess -Wl,-rpath,. diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index b52157b7..5264a9c5 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -10,7 +10,7 @@ UNAME_S = $(shell uname -s) CC = g++ -CCFLAGS = -fPIC -std=gnu++11 -Wall -Werror -DBROWSER_PROCESS \ +CCFLAGS = -fPIC -std=c++11 -Wall -Werror -DBROWSER_PROCESS \ $(CEF_CCFLAGS) ifeq ($(UNAME_S), Linux) diff --git a/src/version/cef_version_mac.h b/src/version/cef_version_mac.h new file mode 100644 index 00000000..3a20c3f4 --- /dev/null +++ b/src/version/cef_version_mac.h @@ -0,0 +1,102 @@ +// 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.2883.1553.g80bd606" +#define CEF_VERSION_MAJOR 3 +#define CEF_COMMIT_NUMBER 1553 +#define CEF_COMMIT_HASH "80bd6062d7ac4c5fd1d7bc7ee78e8e59d4a040aa" +#define COPYRIGHT_YEAR 2017 + +#define CHROME_VERSION_MAJOR 55 +#define CHROME_VERSION_MINOR 0 +#define CHROME_VERSION_BUILD 2883 +#define CHROME_VERSION_PATCH 87 + +#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 "87b7eefcb86c87b28f86bfd7919f7d7a6cffc0d8" +#if defined(OS_WIN) +#define CEF_API_HASH_PLATFORM "00823905486d7b7222da5654fe35d2d15f65543a" +#elif defined(OS_MACOSX) +#define CEF_API_HASH_PLATFORM "f0180f006643782254250f34e858b98110a40e6e" +#elif defined(OS_LINUX) +#define CEF_API_HASH_PLATFORM "14b19454a4231fa10a77b8955954dc95f073af6b" +#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/tools/automate.py b/tools/automate.py index 0d966067..f6dd9fb0 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -20,6 +20,7 @@ Usage: automate.py (--prebuilt-cef | --build-cef) + [--fast-build FAST_BUILD] [--force-chromium-update FORCE_CHROMIUM_UPDATE] [--no-cef-update NO_CEF_UPDATE] [--cef-branch BRANCH] [--cef-commit COMMIT] @@ -34,6 +35,7 @@ binaries for Linux are built on Ubuntu. --build-cef Whether to build CEF from sources with the cefpython patches applied. + --fast-build Fast build with is_official_build=False --force-chromium-update Force Chromium update (gclient sync etc). --no-cef-update Do not update CEF sources (by default both cef/ directories are deleted on every run). @@ -73,6 +75,7 @@ class Options(object): # From command-line prebuilt_cef = False build_cef = False + fast_build = False force_chromium_update = False no_cef_update = False cef_branch = "" @@ -354,8 +357,11 @@ def build_cef_projects(): print("[automate.py] Build cefclient, cefsimple, ceftests") # Cmake command = prepare_build_command() - command.extend(["cmake", "-G", "Ninja", - "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) + command.extend(["cmake", "-G", "Ninja"]) + command.append("-DCMAKE_BUILD_TYPE="+Options.build_type) + if MAC: + command.append("-DPROJECT_ARCH=x86_64") + command.append("..") run_command(command, Options.build_cefclient_dir) print("[automate.py] OK") # Ninja @@ -695,7 +701,7 @@ def getenv(): # To perform an official build set GYP_DEFINES=buildtype=Official. # This will disable debugging code and enable additional link-time # optimizations in Release builds. - if Options.release_build: + if Options.release_build and not Options.fast_build: env["GN_DEFINES"] += " is_official_build=true" # Modifications to automate-git.py env["CEFPYTHON_NINJA_JOBS"] = str(Options.ninja_jobs) diff --git a/tools/build.py b/tools/build.py index 7f89faf7..6fcbb5f4 100644 --- a/tools/build.py +++ b/tools/build.py @@ -35,10 +35,10 @@ # 6. More commands: http://docs.cython.org/src/userguide/debugging.html # This will not show "Segmentation fault" error message: -# | subprocess.call(["python", "./wxpython.py"]) -# You need to call it with shell=True for this kind of -# error message to be shown: -# | subprocess.call("python wxpython.py", shell=True) +# > subprocess.call(["python", "./wxpython.py"]) +# You need to call it with command as string and shell=True +# for this kind of error message to be shown: +# > subprocess.call("python wxpython.py", shell=True) from common import * import sys @@ -84,11 +84,17 @@ def main(): check_cython_version() command_line_args() check_directories() - fix_cefpython_h() - if WINDOWS: - compile_cpp_projects_windows() - elif MAC or LINUX: - compile_cpp_projects_unix() + if os.path.exists(CEFPYTHON_H): + fix_cefpython_h() + if WINDOWS: + compile_cpp_projects_windows() + elif MAC or LINUX: + compile_cpp_projects_unix() + else: + print("[build.py] INFO: Looks like first run, as cefpython.h" + " is missing. Skip building C++ projects.") + global FIRST_RUN + FIRST_RUN = True clear_cache() copy_and_fix_pyx_files() create_version_pyx_file() @@ -103,9 +109,6 @@ def setup_environ(): include path. Set Mac compiler options. Etc.""" print("[build.py] Setup environment variables") - if not WINDOWS: - return - # PATH if WINDOWS: path = [ @@ -134,6 +137,11 @@ def setup_environ(): print("[build.py] environ AdditionalLibraryDirectories: {lib}" .format(lib=os.environ["AdditionalLibraryDirectories"])) + # Mac env variables for makefiles + if MAC: + os.environ["CEF_BIN"] = os.path.join(CEF_BINARIES_LIBRARIES, "bin") + os.environ["CEF_LIB"] = os.path.join(CEF_BINARIES_LIBRARIES, "lib") + # Mac compiler options if MAC: os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] @@ -235,14 +243,13 @@ def fix_cefpython_h(): print("[build.py] cefpython.h was not yet generated") return with open("cefpython.h", "r") as fo: - content = fo.read() - pragma = "#pragma warning(disable:4190)" - if pragma in content: - print("[build.py] cefpython.h is already fixed") - return - content = ("%s\n\n" % pragma) + content + contents = fo.read() + # Error/warning depending on compiler: + # > has C-linkage specified, but returns user-defined type + contents = contents.replace("#define __PYX_EXTERN_C extern \"C\"", + "#define __PYX_EXTERN_C extern") with open("cefpython.h", "w") as fo: - fo.write(content) + fo.write(contents) print("[build.py] Save build_cefpython/cefpython.h") @@ -288,13 +295,6 @@ def compile_cpp_projects_windows(): def build_vcproj(vcproj): - if not os.path.exists(CEFPYTHON_H): - print("[build.py] INFO: Looks like first run, as cefpython.h" - " is missing. Skip building C++ project.") - global FIRST_RUN - FIRST_RUN = True - return - if PYVERSION == "27": args = list() args.append(VS2008_VCVARS) @@ -451,7 +451,7 @@ def copy_and_fix_pyx_files(): os.remove(pyxfile) # Copying pyxfiles and reading its contents. - print("[build.py] Copying pyx files to build_cefpython/: %s" % pyxfiles) + print("[build.py] Copying pyx files to build_cefpython/") # Copy cefpython.pyx and fix includes in cefpython.pyx, eg.: # include "handlers/focus_handler.pyx" becomes include "focus_handler.pyx" @@ -467,7 +467,7 @@ def copy_and_fix_pyx_files(): print("[build.py] %s includes fixed in %s" % (subs, mainfile)) # Copy the rest of the files - print("[build.py] Fixing includes in .pyx files:") + print("[build.py] Fix includes in other .pyx files") for pyxfile in pyxfiles: newfile = "./%s" % os.path.basename(pyxfile) shutil.copy(pyxfile, newfile) @@ -489,8 +489,9 @@ def copy_and_fix_pyx_files(): content, flags=re.MULTILINE) if subs: - print("[build.py] %s includes removed in: %s" - % (subs, os.path.basename(pyxfile))) + # print("[build.py] %s includes removed in: %s" + # % (subs, os.path.basename(pyxfile))) + pass with open(pyxfile, "w") as pyxfileopened: pyxfileopened.write(content) @@ -540,24 +541,26 @@ def create_version_pyx_file(): def build_cefpython_module(): - os.chdir(BUILD_CEFPYTHON) # if DEBUG_FLAG: # ret = subprocess.call("python-dbg setup.py build_ext --inplace" # " --cython-gdb", shell=True) print("[build.py] Execute build_module.py script") print("") + + os.chdir(BUILD_CEFPYTHON) + if FAST_FLAG: - ret = subprocess.call([sys.executable, - "{tools_dir}/build_module.py" - .format(tools_dir=TOOLS_DIR), - "build_ext", "--inplace", "--fast"], + ret = subprocess.call("{python} {tools_dir}/build_module.py" + " build_ext --fast" + .format(python=sys.executable, + tools_dir=TOOLS_DIR), shell=True) else: - ret = subprocess.call([sys.executable, - "{tools_dir}/build_module.py" - .format(tools_dir=TOOLS_DIR), - "build_ext", "--inplace"], + ret = subprocess.call("{python} {tools_dir}/build_module.py" + " build_ext" + .format(python=sys.executable, + tools_dir=TOOLS_DIR), shell=True) # if DEBUG_FLAG: @@ -585,7 +588,7 @@ def build_cefpython_module(): args.append(os.path.join(TOOLS_DIR, os.path.basename(__file__))) assert __file__ in sys.argv[0] args.extend(sys.argv[1:]) - ret = subprocess.call(args, shell=True) + ret = subprocess.call(" ".join(args), shell=True) sys.exit(ret) else: print("[build.py] ERROR: failed to build the cefpython module") diff --git a/tools/build_module.py b/tools/build_module.py index 0d1524df..e5c72668 100644 --- a/tools/build_module.py +++ b/tools/build_module.py @@ -77,36 +77,31 @@ def set_compiler_options(options): # # The above warning LNK4217 is caused by the warning below which occurs # when building the client_handler.lib static library: - # - # cefpython.h(36): warning C4190: 'RequestHandler_GetResourceHandler' - # has C-linkage specified, but returns UDT 'CefRefPtr' which is - # incompatible with C - # - # The C4190 warning is disabled with pragma in cefpython.h, see the - # fix_cefpython_h() in the build.py script. - extra_compile_args.extend(['/EHsc']) - extra_link_args.extend(['/ignore:4217']) + extra_compile_args.extend(["/EHsc"]) + extra_link_args.extend(["/ignore:4217"]) if LINUX: if len(sys.argv) > 1 and "--fast" in sys.argv: sys.argv.remove("--fast") # Fast mode disables optimization flags print("[build_module.py] FAST mode On") - extra_compile_args.extend(['-flto', '-std=gnu++11']) - extra_link_args.extend(['-flto']) + extra_compile_args.extend(["-flto", "-std=c++11"]) + extra_link_args.extend(["-flto"]) else: # Fix "ImportError ... undefined symbol ..." caused by CEF's # include/base/ headers by adding the -flto flag (Issue #230). # Unfortunately -flto prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/ # 6687630/ . - extra_compile_args.extend(['-flto', '-fdata-sections', - '-ffunction-sections', '-std=gnu++11']) - extra_link_args.extend(['-flto', '-Wl,--gc-sections']) + extra_compile_args.extend(["-flto", "-fdata-sections", + "-ffunction-sections", "-std=c++11"]) + extra_link_args.extend(["-flto", "-Wl,--gc-sections"]) if MAC: - os.environ["CC"] = "gcc" - os.environ["CXX"] = "g++" + extra_compile_args.extend(["-std=c++11"]) + # extra_link_args.extend([]) + # os.environ["CC"] = "gcc" + # os.environ["CXX"] = "g++" options["extra_compile_args"] = extra_compile_args options["extra_link_args"] = extra_link_args @@ -254,7 +249,7 @@ def get_ext_modules(options): "c_string_encoding": "utf-8", }, - language='c++', + language="c++", include_dirs=options["include_dirs"], library_dirs=options["library_dirs"], diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index 5e794a0a..67ce48ba 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -10,7 +10,7 @@ import ctypes import platform -__all__ = ["cefpython", "wx"] +__all__ = ["cefpython"] # Disabled: "wx" __version__ = "{{VERSION}}" __author__ = "The CEF Python authors" @@ -26,10 +26,12 @@ package_dir = os.path.dirname(os.path.abspath(__file__)) # This loads the libcef.so library for the subprocess executable. -# On Mac it works without setting library paths, but let's set it -# just to be sure. os.environ["LD_LIBRARY_PATH"] = package_dir -os.environ["DYLD_LIBRARY_PATH"] = package_dir + +# On Mac it works without setting library paths. Better not set it, +# as maybe user's app will set it itself. +# > os.environ["DYLD_LIBRARY_PATH"] = package_dir +# > os.environ["DYLD_FRAMEWORK_PATH"] = package_dir # This env variable will be returned by cefpython.GetModuleDirectory(). os.environ["CEFPYTHON3_PATH"] = package_dir @@ -40,23 +42,35 @@ # it may cause issues to load it here in the browser process. libcef = None if platform.system() == "Darwin": - libcef = os.path.join(package_dir, "libcef.dylib") + cef_framework = "Chromium Embedded Framework.framework" + libcef_name = "Chromium Embedded Framework" + # Search for it in current directory or in ../Frameworks/ dir + # in case this is user's app packaged for distribution. + libcef1 = os.path.join(package_dir, cef_framework, libcef_name) + libcef2 = os.path.join(package_dir, "..", "Frameworks", cef_framework, + libcef_name) + if os.path.exists(libcef1): + libcef = libcef1 + elif os.path.exists(libcef2): + libcef = libcef2 + else: + raise Exception("Can't find: " + cef_framework) elif platform.system() == "Linux": libcef = os.path.join(package_dir, "libcef.so") if libcef: ctypes.CDLL(libcef, ctypes.RTLD_GLOBAL) # Load the cefpython module for proper Python version -if (2, 7) <= sys.version_info < (2, 8): +if sys.version_info[:2] == (2, 8): # noinspection PyUnresolvedReferences from . import cefpython_py27 as cefpython -elif (3, 4) <= sys.version_info < (3, 5): +elif sys.version_info[:2] == (3, 4): # noinspection PyUnresolvedReferences from . import cefpython_py34 as cefpython -elif (3, 5) <= sys.version_info < (3, 6): +elif sys.version_info[:2] == (3, 5): # noinspection PyUnresolvedReferences from . import cefpython_py35 as cefpython -elif (3, 6) <= sys.version_info < (3, 7): +elif sys.version_info[:2] == (3, 6): # noinspection PyUnresolvedReferences from . import cefpython_py36 as cefpython else: From eeef09ab8e3d398e5dc6714efdbcde921d33704d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 27 Feb 2017 16:00:14 +0100 Subject: [PATCH 16/16] Build v55 on Mac Part 2 (#295)... Update build tools and makefiles. Link to libc++ and libc++abi to avoid undefined symbol error. Fix visibility: PyMODINIT_FUNC. Minimum Mac version: 10.7. Do not use ctypes.CDLL on Mac, load CEF framework statically. Both Mac and Linux: Add -DNDEBUG and -O3 optimization flags. libcef_dll_wrapper needs to be built using this command: cmake -G "Ninja" -DPROJECT_ARCH="x86_64" \ -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DCMAKE_BUILD_TYPE=Release .. ninja libcef_dll_wrapper --- docs/Build-instructions.md | 2 + src/__version__.pyx | 2 - src/cefpython.pyx | 12 +- src/client_handler/Makefile | 37 ++--- src/cpp_utils/Makefile | 5 +- src/linux/setup/setup.py | 4 +- src/subprocess/Makefile | 20 +-- src/subprocess/Makefile-libcefpythonapp | 33 ++-- tools/build.py | 109 +++++++++---- tools/build_module.py | 200 +++++++++++++++++++----- tools/common.py | 12 ++ tools/installer/cefpython3.__init__.py | 31 +--- 12 files changed, 312 insertions(+), 155 deletions(-) delete mode 100644 src/__version__.pyx diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index d22d0263..2ee531ae 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -196,6 +196,8 @@ requirements common for all platforms. * MacOS 10.9+, Xcode5+ and Xcode command line tools. Only 64-bit builds are supported. +* Upgrade setuptools package to latest version otherwise there will be + problems with Cython: `sudo pip install --upgrade setuptools` ### All platforms diff --git a/src/__version__.pyx b/src/__version__.pyx deleted file mode 100644 index 52d26ce1..00000000 --- a/src/__version__.pyx +++ /dev/null @@ -1,2 +0,0 @@ -# A dummy file. The compile script generates a real __version__.pyx -# in the setup/ directory. diff --git a/src/cefpython.pyx b/src/cefpython.pyx index ffbcde96..9d641ebe 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -246,12 +246,8 @@ END OF: CHANGES in CEF since v31..v47. # Includes being made in other .pyx files are allowed to help # IDE completion, but will be removed during cython compilation. -# Version file is generated by the compile.bat/compile.py script. -include "__version__.pyx" - include "compile_time_constants.pxi" - # ----------------------------------------------------------------------------- # IMPORTS @@ -503,7 +499,11 @@ include "command_line.pyx" include "app.pyx" include "drag_data.pyx" include "helpers.pyx" -include "image.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" @@ -645,7 +645,7 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): if "resources_dir_path" not in applicationSettings: applicationSettings["resources_dir_path"] = module_dir if platform.system() == "Darwin": - applicationSettings["resources_dir_path"] = module_dir+"/Resources" + pass # TODO: Check if this needs to be set in v56+ if "browser_subprocess_path" not in applicationSettings: applicationSettings["browser_subprocess_path"] = os.path.join( module_dir, "subprocess") diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile index 5340da3d..f0b9a5a4 100644 --- a/src/client_handler/Makefile +++ b/src/client_handler/Makefile @@ -4,30 +4,29 @@ # -Wall - show important warnings # -Werror - treat warnings as errors -# Cython compiler options: +# Cython compiler options on Linux: # -fPIC -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions \ # -Wl,-z,relro UNAME_S = $(shell uname -s) -CC = g++ -CCFLAGS = -fPIC -std=c++11 -Wall -Werror $(CEF_CCFLAGS) +CCFLAGS = -fPIC $(CEF_CCFLAGS) + +ifeq ($(UNAME_S), Linux) + SRC_MORE = x11.cpp +else ifeq ($(UNAME_S), Darwin) + SRC_MORE = util_mac.mm +endif SRC = client_handler.cpp cookie_visitor.cpp resource_handler.cpp \ web_request_client.cpp string_visitor.cpp request_context_handler.cpp \ task.cpp context_menu_handler.cpp display_handler.cpp \ download_handler.cpp focus_handler.cpp js_dialog_handler.cpp \ keyboard_handler.cpp lifespan_handler.cpp load_handler.cpp \ - render_handler.cpp request_handler.cpp - -OBJ = $(SRC:.cpp=.o) - -ifeq ($(UNAME_S), Linux) - OBJ += x11.o -endif + render_handler.cpp request_handler.cpp \ + $(SRC_MORE) -ifeq ($(UNAME_S), Darwin) - OBJ += util_mac.o -endif +OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o)) +.SUFFIXES: .cpp .mm .o OUT = libclient_handler.a @@ -52,21 +51,19 @@ INC = -I./../ -I./../common/ -I/usr/include/python2.7 \ -I/usr/lib/gtk-2.0/gtk-unix-print-2.0 \ -I/usr/lib/glib-2.0/include -.cpp.o: - @echo [CLIENT HANDLER] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ $(OUT): $(OBJ) @echo [CLIENT HANDLER] Creating library $(OUT) from $(OBJ)... ar rcs $(OUT) $(OBJ) -x11.o: x11.cpp +.cpp.o: @echo [CLIENT HANDLER] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ -util_mac.o: util_mac.mm +.mm.o: @echo [CLIENT HANDLER] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ + clean: @echo [CLIENT HANDLER] Cleaning... diff --git a/src/cpp_utils/Makefile b/src/cpp_utils/Makefile index 9e589e80..338fd960 100644 --- a/src/cpp_utils/Makefile +++ b/src/cpp_utils/Makefile @@ -1,5 +1,4 @@ -CC = g++ -CCFLAGS = -g $(CEF_CCFLAGS) +CCFLAGS = -fPIC $(CEF_CCFLAGS) SRC = PaintBuffer.cpp OBJ = $(SRC:.cpp=.o) @@ -15,7 +14,7 @@ INC = -I./../ -I/usr/include/gtk-2.0 \ -I/usr/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include .cpp.o: - $(CC) -fPIC $(INC) $(CCFLAGS) -c $< -o $@ + $(CXX) $(INC) $(CCFLAGS) -c $< -o $@ $(OUT): $(OBJ) ar rcs $(OUT) $(OBJ) diff --git a/src/linux/setup/setup.py b/src/linux/setup/setup.py index c9a4494d..d06a688f 100644 --- a/src/linux/setup/setup.py +++ b/src/linux/setup/setup.py @@ -16,7 +16,7 @@ # Fast mode disables optimization flags FAST = True print("FAST mode On") - COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=c++11'] + COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=gnu++11'] LINK_OPTIMIZE_FLAGS = ['-flto'] else: FAST = False @@ -25,7 +25,7 @@ # prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/6687630/ COMPILE_OPTIMIZE_FLAGS = ['-flto', '-fdata-sections', '-ffunction-sections', - '-std=c++11'] + '-std=gnu++11'] LINK_OPTIMIZE_FLAGS = ['-flto', '-Wl,--gc-sections'] diff --git a/src/subprocess/Makefile b/src/subprocess/Makefile index 75fdeb36..c4d73a5b 100644 --- a/src/subprocess/Makefile +++ b/src/subprocess/Makefile @@ -41,24 +41,18 @@ ifeq ($(UNAME_S), Linux) LIBS = -lcef -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 else ifeq ($(UNAME_S), Darwin) CPP_FILES = - LIBS = -framework Chromium\ Embedded\ Framework -endif - - -CCFLAGS = -g -std=c++11 -Wall -Werror -DRENDERER_PROCESS $(CEF_CCFLAGS) - -ifeq ($(UNAME_S), Darwin) - MACFLAGS = -O3 -DNDEBUG -stdlib=libstdc++ \ - -Wl,-search_paths_first -Wl,-ObjC -Wl,-pie -Wl,-dead_strip -else - MACFLAGS = + # Include framework before libcef_dll_wrapper + LIBS = -framework "Chromium Embedded Framework" endif +CCFLAGS = -DRENDERER_PROCESS $(CEF_CCFLAGS) subprocess: # -fPIC is required only for libraries included by Cython. @echo [SUBPROCESS] Building the 'subprocess' executable - g++ $(CCFLAGS) $(MACFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ + $(CXX) $(CCFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ v8function_handler.cpp v8utils.cpp javascript_callback.cpp \ $(CPP_FILES) \ - $(LIBS) -lcef_dll_wrapper -o subprocess -Wl,-rpath,. + $(CEF_LINK_FLAGS) \ + $(LIBS) -lcef_dll_wrapper \ + -o subprocess -Wl,-rpath,. diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index 5264a9c5..bd8186da 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -9,28 +9,25 @@ # -Wl,-z,relro UNAME_S = $(shell uname -s) -CC = g++ -CCFLAGS = -fPIC -std=c++11 -Wall -Werror -DBROWSER_PROCESS \ - $(CEF_CCFLAGS) +CCFLAGS = -fPIC -DBROWSER_PROCESS $(CEF_CCFLAGS) ifeq ($(UNAME_S), Linux) - CPP_FILES = print_handler_gtk.cpp \ - main_message_loop/main_message_loop_external_pump_linux.cpp + SRC_MORE = print_handler_gtk.cpp \ + main_message_loop/main_message_loop_external_pump_linux.cpp else ifeq ($(UNAME_S), Darwin) - CPP_FILES = \ - main_message_loop/main_message_loop_external_pump_mac.mm -else - CPP_FILES = + SRC_MORE = main_message_loop/main_message_loop_external_pump_mac.mm 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 \ - $(CPP_FILES) -OBJ = $(SRC:.cpp=.o) + $(SRC_MORE) + +OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o)) +.SUFFIXES: .cpp .mm .o + OUT = libcefpythonapp.a INC = -I./../ -I./../common/ -I/usr/include/python2.7 \ @@ -54,10 +51,14 @@ INC = -I./../ -I./../common/ -I/usr/include/python2.7 \ -I/usr/lib/gtk-2.0/gtk-unix-print-2.0 \ -I/usr/lib/glib-2.0/include -.cpp.o: - @echo [CEFPYTHONAPP] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ - $(OUT): $(OBJ) @echo [CEFPYTHONAPP] Creating library $(OUT) from $(OBJ)... ar rcs $(OUT) $(OBJ) + +.cpp.o: + @echo [CEFPYTHONAPP] Building $@ from $<... + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ + +.mm.o: + @echo [CEFPYTHONAPP] Building $@ from $<... + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ diff --git a/tools/build.py b/tools/build.py index 6fcbb5f4..3d070d25 100644 --- a/tools/build.py +++ b/tools/build.py @@ -63,12 +63,6 @@ REBUILD_CPP = False VERSION = "" -# Module extension -if WINDOWS: - MODULE_EXT = "pyd" -else: - MODULE_EXT = "so" - # First run FIRST_RUN = False CEFPYTHON_H = os.path.join(BUILD_CEFPYTHON, "cefpython.h") @@ -97,7 +91,6 @@ def main(): FIRST_RUN = True clear_cache() copy_and_fix_pyx_files() - create_version_pyx_file() build_cefpython_module() fix_cefpython_h() install_and_run() @@ -137,6 +130,20 @@ def setup_environ(): print("[build.py] environ AdditionalLibraryDirectories: {lib}" .format(lib=os.environ["AdditionalLibraryDirectories"])) + if LINUX or MAC: + # Used in makefiles + os.environ["CEF_CCFLAGS"] = "-std=gnu++11 -DNDEBUG -Wall -Werror" + if FAST_FLAG: + os.environ["CEF_CCFLAGS"] += " -O0" + else: + os.environ["CEF_CCFLAGS"] += " -O3" + os.environ["CEF_LINK_FLAGS"] = "" + + if LINUX: + # TODO: Set CEF_CCFLAGS and CEF_LINK_FLAGS according to what is + # in upstream cefclient, see cef/cmake/cef_variables.cmake. + pass + # Mac env variables for makefiles if MAC: os.environ["CEF_BIN"] = os.path.join(CEF_BINARIES_LIBRARIES, "bin") @@ -145,12 +152,42 @@ def setup_environ(): # Mac compiler options if MAC: os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] - os.environ["CC"] = "gcc" - os.environ["CXX"] = "g++" - os.environ["CEF_CCFLAGS"] = "-arch x86_64" - os.environ["ARCHFLAGS"] = "-arch x86_64" + os.environ["CC"] = "c++" + os.environ["CXX"] = "c++" + if ARCH32: raise Exception("Python 32-bit is not supported on Mac") + os.environ["ARCHFLAGS"] = "-arch x86_64" + os.environ["CEF_CCFLAGS"] += " -arch x86_64" + os.environ["CEF_LINK_FLAGS"] += " -mmacosx-version-min=10.7" + + # -Wno-return-type-c-linkage to ignore: + # > warning: 'somefunc' has C-linkage specified, but returns + # > user-defined type 'sometype' which is incompatible with C + os.environ["CEF_CCFLAGS"] += " -Wno-return-type-c-linkage" + + # Compile against libc++ otherwise error "symbol not found" + # with cef::logging::LogMessage symbol. Also include -lc++ + # and -lc++abi libraries. + os.environ["CEF_CCFLAGS"] += " -stdlib=libc++" + + # See compile/link flags in upstream cefclient + os.environ["CEF_CCFLAGS"] += ( + " -fno-strict-aliasing" + " -fno-rtti" + " -fno-threadsafe-statics" + " -fobjc-call-cxx-cdtors" + " -fvisibility=hidden" + " -fvisibility-inlines-hidden" + ) + os.environ["CEF_LINK_FLAGS"] += ( + " -lc++" + " -lc++abi" + " -Wl,-search_paths_first" + " -Wl,-ObjC" + " -Wl,-pie" + " -Wl,-dead_strip" + ) def get_python_path(): @@ -237,6 +274,13 @@ def check_directories(): def fix_cefpython_h(): + # Fix cefpython.h to disable this warning: + # > warning: 'somefunc' has C-linkage specified, but returns + # > user-defined type 'sometype' which is incompatible with C + # On Mac this warning must be disabled using -Wno-return-type-c-linkage + # flag in makefiles. + if MAC: + return os.chdir(BUILD_CEFPYTHON) print("[build.py] Fix cefpython.h to disable warnings") if not os.path.exists("cefpython.h"): @@ -244,10 +288,11 @@ def fix_cefpython_h(): return with open("cefpython.h", "r") as fo: contents = fo.read() - # Error/warning depending on compiler: - # > has C-linkage specified, but returns user-defined type - contents = contents.replace("#define __PYX_EXTERN_C extern \"C\"", - "#define __PYX_EXTERN_C extern") + pragma = "#pragma warning(disable:4190)" + if pragma in contents: + print("[build.py] cefpython.h is already fixed") + return + contents = ("%s\n\n" % pragma) + contents with open("cefpython.h", "w") as fo: fo.write(contents) print("[build.py] Save build_cefpython/cefpython.h") @@ -389,12 +434,14 @@ def clear_cache(): print("[build.py] Clean build cache") # Cache in CEFPYTHON_BINARY directory (eg. cefpython_linux64/) os.chdir(CEFPYTHON_BINARY) - delete_files_by_pattern("./cefpython_py*.{ext}".format(ext=MODULE_EXT)) + delete_files_by_pattern("./"+MODULE_NAME_TEMPLATE + .format(pyversion="*", ext=MODULE_EXT)) # Cache in build_cefpython/ directory os.chdir(BUILD_CEFPYTHON) - delete_files_by_pattern("./cefpython_py*.{ext}".format(ext=MODULE_EXT)) + delete_files_by_pattern("./"+MODULE_NAME_TEMPLATE + .format(pyversion="*", ext=MODULE_EXT)) delete_files_by_pattern("./*.pyx") try: @@ -451,7 +498,7 @@ def copy_and_fix_pyx_files(): os.remove(pyxfile) # Copying pyxfiles and reading its contents. - print("[build.py] Copying pyx files to build_cefpython/") + print("[build.py] Copy pyx files to build_cefpython/") # Copy cefpython.pyx and fix includes in cefpython.pyx, eg.: # include "handlers/focus_handler.pyx" becomes include "focus_handler.pyx" @@ -462,9 +509,12 @@ def copy_and_fix_pyx_files(): "include \"", content, flags=re.MULTILINE) + # Add __version__ variable in cefpython.pyx + print("[build.py] Add __version__ variable to %s" % mainfile) + content = ('__version__ = "{}"\n'.format(VERSION)) + content with open("./%s" % mainfile, "w") as fo: fo.write(content) - print("[build.py] %s includes fixed in %s" % (subs, mainfile)) + print("[build.py] Fix %s includes in %s" % (subs, mainfile)) # Copy the rest of the files print("[build.py] Fix includes in other .pyx files") @@ -533,13 +583,6 @@ def except_all_missing(content): return lineNumber -def create_version_pyx_file(): - os.chdir(BUILD_CEFPYTHON) - print("[build.py] Create __version__.pyx file") - with open("__version__.pyx", "w") as fo: - fo.write('__version__ = "{}"\n'.format(VERSION)) - - def build_cefpython_module(): # if DEBUG_FLAG: # ret = subprocess.call("python-dbg setup.py build_ext --inplace" @@ -595,12 +638,14 @@ def build_cefpython_module(): sys.exit(1) # Move the cefpython module - move_file_by_pattern("./cefpython_py{pyver}*.{ext}" - .format(pyver=PYVERSION, ext=MODULE_EXT), - os.path.join(CEFPYTHON_BINARY, - "cefpython_py{pyver}.{ext}" - .format(pyver=PYVERSION, - ext=MODULE_EXT))) + module_pattern = MODULE_NAME_TEMPLATE.format(pyversion=PYVERSION+"*", + ext=MODULE_EXT) + if MAC: + module_pattern = "./build/lib*/"+module_pattern + else: + module_pattern = "./"+module_pattern + move_file_by_pattern(module_pattern, os.path.join(CEFPYTHON_BINARY, + MODULE_NAME)) print("[build.py] DONE building the cefpython module") diff --git a/tools/build_module.py b/tools/build_module.py index e5c72668..f347bc87 100644 --- a/tools/build_module.py +++ b/tools/build_module.py @@ -18,17 +18,88 @@ # Use "Extension" from Cython.Distutils so that "cython_directives" works from Cython.Distutils import build_ext, Extension from Cython.Compiler import Options +# noinspection PyUnresolvedReferences +from Cython.Compiler.ModuleNode import ModuleNode from common import * import sys import platform import Cython import os +# Must monkey patch Cython's ModuleNode to inject custom C++ code +# in the generated cefpython.cpp. This is a fix for an error on Mac: +# > ImportError: dynamic module does not define init function +# To get rid of CEF's undefined symbol error when importing module +# it was required to pass "-fvisibility=hidden" and "-Wl,-dead_strip" +# flags. However these flags cause the "initcefpython_py27" Python +# Module Initialization function to be hidden as well. To workaround +# this it is required to add default visibility attribute to the +# signature of that init function. +# +# Original definition in Python 2.7: +# | https://github.com/python/cpython/blob/2.7/Include/pyport.h +# > define PyMODINIT_FUNC extern "C" __declspec(dllexport) void +# +# Original definition in Python 3.4 / 3.5 / 3.6: +# > define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject* + +if MAC: + g_generate_extern_c_macro_definition_old = ( + ModuleNode.generate_extern_c_macro_definition) + + def generate_extern_c_macro_definition(self, code): + # This code is written to both cefpython.h and cefpython.cpp + g_generate_extern_c_macro_definition_old(self, code) + code.putln("// Added by: cefpython/tools/build_module.py") + code.putln("#undef PyMODINIT_FUNC") + if sys.version_info[:2] == (2, 7): + code.putln("#define PyMODINIT_FUNC extern \"C\"" + " __attribute__((visibility(\"default\"))) void") + else: + code.putln("#define PyMODINIT_FUNC extern \"C\"" + " __attribute__((visibility(\"default\"))) PyObject*") + # Overwrite Cython function + ModuleNode.generate_extern_c_macro_definition = ( + generate_extern_c_macro_definition) + + +# Constants +FAST_FLAG = False + # Cython options. Stop on first error, otherwise hundreds # of errors appear in the console. Options.fast_fail = True +def main(): + global FAST_FLAG + if len(sys.argv) > 1 and "--fast" in sys.argv: + # Fast mode disables optimization flags + print("[build_module.py] FAST mode On") + FAST_FLAG = True + sys.argv.remove("--fast") + + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) + + print("[build_module.py] Cython version: %s" % Cython.__version__) + + compile_time_constants() + options = dict() + set_compiler_options(options) + options["include_dirs"] = get_include_dirs() + options["library_dirs"] = get_library_dirs() + options["libraries"] = get_libraries() + + print("[build_module.py] Execute setup()") + setup( + name='cefpython_py%s' % PYVERSION, + cmdclass={'build_ext': build_ext}, + ext_modules=get_ext_modules(options) + ) + + def get_winsdk_lib(): print("[build_module.py] Detect Windows SDK library directory") ret = "" @@ -80,12 +151,25 @@ def set_compiler_options(options): extra_compile_args.extend(["/EHsc"]) extra_link_args.extend(["/ignore:4217"]) + if LINUX or MAC: + # Compiler flags + if FAST_FLAG: + extra_compile_args.append("-O0") + else: + extra_compile_args.append("-O3") + + extra_compile_args.extend([ + "-DNDEBUG", + "-std=gnu++11", + ]) + if LINUX: - if len(sys.argv) > 1 and "--fast" in sys.argv: - sys.argv.remove("--fast") - # Fast mode disables optimization flags - print("[build_module.py] FAST mode On") - extra_compile_args.extend(["-flto", "-std=c++11"]) + os.environ["CC"] = "g++" + os.environ["CXX"] = "g++" + + if FAST_FLAG: + extra_compile_args.extend(["-flto", + "-std=gnu++11"]) extra_link_args.extend(["-flto"]) else: # Fix "ImportError ... undefined symbol ..." caused by CEF's @@ -93,15 +177,72 @@ def set_compiler_options(options): # Unfortunately -flto prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/ # 6687630/ . - extra_compile_args.extend(["-flto", "-fdata-sections", - "-ffunction-sections", "-std=c++11"]) - extra_link_args.extend(["-flto", "-Wl,--gc-sections"]) + extra_compile_args.extend(["-flto", + "-fdata-sections", + "-ffunction-sections", + "-std=gnu++11"]) + extra_link_args.extend(["-flto", + "-Wl,--gc-sections"]) if MAC: - extra_compile_args.extend(["-std=c++11"]) - # extra_link_args.extend([]) - # os.environ["CC"] = "gcc" - # os.environ["CXX"] = "g++" + # Compiler environment variables + os.environ["CC"] = "c++" + os.environ["CXX"] = "c++" + + # COMPILER ARGS + + # -Wno-return-type-c-linkage to ignore: + # > warning: 'somefunc' has C-linkage specified, but returns + # > user-defined type 'sometype' which is incompatible with C + # + # -Wno-constant-logical-operand to ignore: + # > warning: use of logical '||' with constant operand + + extra_compile_args.extend([ + # Compile against libc++ otherwise error "symbol not found" + # with cef::logging::LogMessage symbol. Also include -lc++ + # and -lc++abi libraries. + "-stdlib=libc++", + "-Wno-return-type-c-linkage", + "-Wno-constant-logical-operand", + ]) + # From upstream CEF cefclient + extra_compile_args.extend([ + "-fno-strict-aliasing", + "-fno-rtti", + "-fno-threadsafe-statics", + "-fobjc-call-cxx-cdtors", + # Visibility of symbols: + "-fvisibility=hidden", + "-fvisibility-inlines-hidden", + ]) + # Visibility of symbols + extra_compile_args.extend([ + # "-flto", + # "-fdata-sections", + # "-ffunction-sections", + ]) + + # Build libcef_dll_wrapper: + # cmake -G "Ninja" -DPROJECT_ARCH="x86_64" + # -DCMAKE_CXX_FLAGS="-stdlib=libc++" + # -DCMAKE_BUILD_TYPE=Release .. + # ninja libcef_dll_wrapper + + # LINKER ARGS + extra_link_args.extend([ + "-mmacosx-version-min=10.7", + "-Wl,-search_paths_first", + "-F"+os.path.join(CEF_BINARIES_LIBRARIES, "bin"), + "-framework", "Chromium Embedded Framework", + "-Wl,-rpath,@loader_path", + ]) + if not FAST_FLAG: + extra_link_args.extend([ + # "-force_flat_namespace", + # "-flto", + "-Wl,-dead_strip", + ]) options["extra_compile_args"] = extra_compile_args options["extra_link_args"] = extra_link_args @@ -189,6 +330,8 @@ def get_library_dirs(): "Release_{os}" .format(os=OS_POSTFIX2)) ]) + if MAC: + library_dirs.append(os.path.join(CEF_BINARIES_LIBRARIES, "bin")) if MAC or LINUX: library_dirs.extend([ os.path.join(SRC_DIR, "client_handler"), @@ -215,10 +358,12 @@ def get_libraries(): ]) elif MAC: libraries.extend([ - 'client_handler', - 'cef_dll_wrapper', - 'cefpythonapp', - 'cpp_utils' + "c++", + "c++abi", + "cef_dll_wrapper", + "cefpythonapp", + "client_handler", + "cpp_utils", ]) elif LINUX: libraries.extend([ @@ -237,8 +382,8 @@ def get_libraries(): def get_ext_modules(options): ext_modules = [Extension( - "cefpython_py%s" % PYVERSION, - ["cefpython.pyx"], + name=MODULE_NAME_NOEXT, + sources=["cefpython.pyx"], # Ignore the warning in the console: # > C:\Python27\lib\distutils\extension.py:133: UserWarning: @@ -282,24 +427,5 @@ def compile_time_constants(): fd.write('DEF PY_MAJOR_VERSION = %s\n' % sys.version_info.major) -def main(): - if len(sys.argv) <= 1: - print(__doc__) - sys.exit(1) - print("[build_module.py] Cython version: %s" % Cython.__version__) - compile_time_constants() - options = dict() - set_compiler_options(options) - options["include_dirs"] = get_include_dirs() - options["library_dirs"] = get_library_dirs() - options["libraries"] = get_libraries() - print("[build_module.py] Execute setup()") - setup( - name='cefpython_py%s' % PYVERSION, - cmdclass={'build_ext': build_ext}, - ext_modules=get_ext_modules(options) - ) - - if __name__ == "__main__": main() diff --git a/tools/common.py b/tools/common.py index 9a5c5b61..bb161553 100644 --- a/tools/common.py +++ b/tools/common.py @@ -29,6 +29,18 @@ # Python version eg. 27 PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) +# Module extension +if WINDOWS: + MODULE_EXT = "pyd" +else: + MODULE_EXT = "so" + +# CEF Python module name +MODULE_NAME_TEMPLATE = "cefpython_py{pyversion}.{ext}" +MODULE_NAME_TEMPLATE_NOEXT = "cefpython_py{pyversion}" +MODULE_NAME = MODULE_NAME_TEMPLATE.format(pyversion=PYVERSION, ext=MODULE_EXT) +MODULE_NAME_NOEXT = MODULE_NAME_TEMPLATE_NOEXT.format(pyversion=PYVERSION) + # Root directory assert __file__ ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index 67ce48ba..3d821b89 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -26,42 +26,25 @@ package_dir = os.path.dirname(os.path.abspath(__file__)) # This loads the libcef.so library for the subprocess executable. +# On Mac it works without setting library paths. os.environ["LD_LIBRARY_PATH"] = package_dir -# On Mac it works without setting library paths. Better not set it, -# as maybe user's app will set it itself. -# > os.environ["DYLD_LIBRARY_PATH"] = package_dir -# > os.environ["DYLD_FRAMEWORK_PATH"] = package_dir - # This env variable will be returned by cefpython.GetModuleDirectory(). os.environ["CEFPYTHON3_PATH"] = package_dir # This loads the libcef library for the main python executable. -# This is required only on linux and Mac. +# Loading library dynamically using ctypes.CDLL is required on Linux. +# TODO: Check if on Linux libcef.so can be linked like on Mac. +# On Mac the CEF framework dependency information is added to +# the cefpython*.so module by linking to CEF framework. # The libffmpegsumo.so library does not need to be loaded here, # it may cause issues to load it here in the browser process. -libcef = None -if platform.system() == "Darwin": - cef_framework = "Chromium Embedded Framework.framework" - libcef_name = "Chromium Embedded Framework" - # Search for it in current directory or in ../Frameworks/ dir - # in case this is user's app packaged for distribution. - libcef1 = os.path.join(package_dir, cef_framework, libcef_name) - libcef2 = os.path.join(package_dir, "..", "Frameworks", cef_framework, - libcef_name) - if os.path.exists(libcef1): - libcef = libcef1 - elif os.path.exists(libcef2): - libcef = libcef2 - else: - raise Exception("Can't find: " + cef_framework) -elif platform.system() == "Linux": +if platform.system() == "Linux": libcef = os.path.join(package_dir, "libcef.so") -if libcef: ctypes.CDLL(libcef, ctypes.RTLD_GLOBAL) # Load the cefpython module for proper Python version -if sys.version_info[:2] == (2, 8): +if sys.version_info[:2] == (2, 7): # noinspection PyUnresolvedReferences from . import cefpython_py27 as cefpython elif sys.version_info[:2] == (3, 4):