From 357a7fbaac184205e7016076d120ec369006e107 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 11 Jun 2018 18:38:53 +0200 Subject: [PATCH 001/177] Update dialog_handler_gtk files with changes from upstream (#403). Generate a patch file with changes. --- src/cef_v59..v66_changes.txt | 10 +- src/client_handler/dialog_handler_gtk.cpp | 72 ++++------- src/client_handler/dialog_handler_gtk.h | 16 ++- src/client_handler/dialog_handler_gtk.patch | 127 ++++++++++++++++++++ 4 files changed, 163 insertions(+), 62 deletions(-) create mode 100644 src/client_handler/dialog_handler_gtk.patch diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 4b57eba2..36ce50d5 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -60,12 +60,12 @@ internal/cef_types.h - + Remove: UR_FLAG_ALLOW_CACHED_CREDENTIALS -TODO +MISC ---- -1. Compare src/handler/dialog_handler_gtk.cpp (and .h) with upstream - cefclient files -2. In subprocess/print_handler_gtk.cpp use GetWindow implementation - from x11.cpp ++ Compare src/client_handler/dialog_handler_gtk.cpp (and .h) with upstream + cefclient files +- In subprocess/print_handler_gtk.cpp use GetWindow implementation + from x11.cpp NEW FEATURES ------------ diff --git a/src/client_handler/dialog_handler_gtk.cpp b/src/client_handler/dialog_handler_gtk.cpp index 6c119910..ce5a4d4e 100644 --- a/src/client_handler/dialog_handler_gtk.cpp +++ b/src/client_handler/dialog_handler_gtk.cpp @@ -1,8 +1,5 @@ -// Default dialog handler implementation on Linux. -// Copied from upstream cefclient with changes: -// - Rewrote GetWindow() func -// - Removed "client" namespace -// - Changed titles of JS alerts, removed URL and "Javascript" word +// COPIED from upstream "cef/tests/cefclient/browser/" directory +// with minor modifications. See the .patch file in current directory. // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that @@ -10,16 +7,12 @@ #include #include -#include -#include -#include #include "include/cef_browser.h" #include "include/cef_parser.h" #include "include/wrapper/cef_helpers.h" #include "include/base/cef_logging.h" - #include "dialog_handler_gtk.h" #include "x11.h" @@ -43,10 +36,10 @@ std::string GetDescriptionFromMimeType(const std::string& mime_type) { const char* mime_type; const char* label; } kWildCardMimeTypes[] = { - { "audio", "Audio Files" }, - { "image", "Image Files" }, - { "text", "Text Files" }, - { "video", "Video Files" }, + {"audio", "Audio Files"}, + {"image", "Image Files"}, + {"text", "Text Files"}, + {"video", "Video Files"}, }; for (size_t i = 0; @@ -140,11 +133,7 @@ void AddFilters(GtkFileChooser* chooser, } // namespace - - -ClientDialogHandlerGtk::ClientDialogHandlerGtk() - : gtk_dialog_(NULL) { -} +ClientDialogHandlerGtk::ClientDialogHandlerGtk() : gtk_dialog_(NULL) {} bool ClientDialogHandlerGtk::OnFileDialog( CefRefPtr browser, @@ -161,7 +150,7 @@ bool ClientDialogHandlerGtk::OnFileDialog( // Remove any modifier flags. FileDialogMode mode_type = - static_cast(mode & FILE_DIALOG_TYPE_MASK); + static_cast(mode & FILE_DIALOG_TYPE_MASK); if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_MULTIPLE) { action = GTK_FILE_CHOOSER_ACTION_OPEN; @@ -204,20 +193,15 @@ bool ClientDialogHandlerGtk::OnFileDialog( return false; GtkWidget* dialog = gtk_file_chooser_dialog_new( - title_str.c_str(), - GTK_WINDOW(window), - action, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - accept_button, GTK_RESPONSE_ACCEPT, - NULL); + title_str.c_str(), GTK_WINDOW(window), action, GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, accept_button, GTK_RESPONSE_ACCEPT, NULL); if (mode_type == FILE_DIALOG_OPEN_MULTIPLE) gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); if (mode_type == FILE_DIALOG_SAVE) { gtk_file_chooser_set_do_overwrite_confirmation( - GTK_FILE_CHOOSER(dialog), - !!(mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG)); + GTK_FILE_CHOOSER(dialog), !!(mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG)); } gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), @@ -230,8 +214,7 @@ bool ClientDialogHandlerGtk::OnFileDialog( struct stat sb; if (stat(file_path.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)) { // Use the directory and name of the existing file. - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), - file_path.data()); + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), file_path.data()); exists = true; } @@ -298,14 +281,13 @@ bool ClientDialogHandlerGtk::OnFileDialog( return true; } -bool ClientDialogHandlerGtk::OnJSDialog( - CefRefPtr browser, - const CefString& origin_url, - JSDialogType dialog_type, - const CefString& message_text, - const CefString& default_prompt_text, - CefRefPtr callback, - bool& suppress_message) { +bool ClientDialogHandlerGtk::OnJSDialog(CefRefPtr browser, + const CefString& origin_url, + JSDialogType dialog_type, + const CefString& message_text, + const CefString& default_prompt_text, + CefRefPtr callback, + bool& suppress_message) { CEF_REQUIRE_UI_THREAD(); GtkButtonsType buttons = GTK_BUTTONS_NONE; @@ -343,22 +325,16 @@ bool ClientDialogHandlerGtk::OnJSDialog( if (!window) return false; - gtk_dialog_ = gtk_message_dialog_new(GTK_WINDOW(window), - GTK_DIALOG_MODAL, - gtk_message_type, - buttons, - "%s", + gtk_dialog_ = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, + gtk_message_type, buttons, "%s", message_text.ToString().c_str()); - g_signal_connect(gtk_dialog_, - "delete-event", - G_CALLBACK(gtk_widget_hide_on_delete), - NULL); + g_signal_connect(gtk_dialog_, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); gtk_window_set_title(GTK_WINDOW(gtk_dialog_), title.c_str()); GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), - GTK_STOCK_OK, - GTK_RESPONSE_OK); + GTK_STOCK_OK, GTK_RESPONSE_OK); if (dialog_type != JSDIALOGTYPE_PROMPT) gtk_widget_grab_focus(ok_button); diff --git a/src/client_handler/dialog_handler_gtk.h b/src/client_handler/dialog_handler_gtk.h index aba4857a..59f65eda 100644 --- a/src/client_handler/dialog_handler_gtk.h +++ b/src/client_handler/dialog_handler_gtk.h @@ -1,6 +1,5 @@ -// Default dialog handler implementation on Linux. -// Copied from upstream cefclient with changes: -// - Removed "client" namespace +// COPIED from upstream "cef/tests/cefclient/browser/" directory +// with minor modifications. See the .patch file in current directory. // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that @@ -37,15 +36,14 @@ class ClientDialogHandlerGtk : public CefDialogHandler, const CefString& default_prompt_text, CefRefPtr callback, bool& suppress_message) OVERRIDE; - bool OnBeforeUnloadDialog( - CefRefPtr browser, - const CefString& message_text, - bool is_reload, - CefRefPtr callback) OVERRIDE; + bool OnBeforeUnloadDialog(CefRefPtr browser, + const CefString& message_text, + bool is_reload, + CefRefPtr callback) OVERRIDE; void OnResetDialogState(CefRefPtr browser) OVERRIDE; private: - static void OnDialogResponse(GtkDialog *dialog, + static void OnDialogResponse(GtkDialog* dialog, gint response_id, ClientDialogHandlerGtk* handler); diff --git a/src/client_handler/dialog_handler_gtk.patch b/src/client_handler/dialog_handler_gtk.patch new file mode 100644 index 00000000..78492921 --- /dev/null +++ b/src/client_handler/dialog_handler_gtk.patch @@ -0,0 +1,127 @@ +diff --git dialog_handler_gtk.cc dialog_handler_gtk.cc +index 042be2ca..ce5a4d4e 100644 +--- dialog_handler_gtk.cc ++++ dialog_handler_gtk.cc +@@ -1,18 +1,20 @@ ++// COPIED from upstream "cef/tests/cefclient/browser/" directory ++// with minor modifications. See the .patch file in current directory. ++ + // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights + // reserved. Use of this source code is governed by a BSD-style license that + // can be found in the LICENSE file. + +-#include "tests/cefclient/browser/dialog_handler_gtk.h" +- + #include + #include + + #include "include/cef_browser.h" + #include "include/cef_parser.h" + #include "include/wrapper/cef_helpers.h" +-#include "tests/cefclient/browser/root_window.h" + +-namespace client { ++#include "include/base/cef_logging.h" ++#include "dialog_handler_gtk.h" ++#include "x11.h" + + namespace { + +@@ -129,18 +131,6 @@ void AddFilters(GtkFileChooser* chooser, + } + } + +-GtkWindow* GetWindow(CefRefPtr browser) { +- scoped_refptr root_window = +- RootWindow::GetForBrowser(browser->GetIdentifier()); +- if (root_window) { +- GtkWindow* window = GTK_WINDOW(root_window->GetWindowHandle()); +- if (!window) +- LOG(ERROR) << "No GtkWindow for browser"; +- return window; +- } +- return NULL; +-} +- + } // namespace + + ClientDialogHandlerGtk::ClientDialogHandlerGtk() : gtk_dialog_(NULL) {} +@@ -198,7 +188,7 @@ bool ClientDialogHandlerGtk::OnFileDialog( + } + } + +- GtkWindow* window = GetWindow(browser); ++ GtkWindow* window = CefBrowser_GetGtkWindow(browser); + if (!window) + return false; + +@@ -308,30 +298,30 @@ bool ClientDialogHandlerGtk::OnJSDialog(CefRefPtr browser, + case JSDIALOGTYPE_ALERT: + buttons = GTK_BUTTONS_NONE; + gtk_message_type = GTK_MESSAGE_WARNING; +- title = "JavaScript Alert"; ++ title = "Alert"; + break; + + case JSDIALOGTYPE_CONFIRM: + buttons = GTK_BUTTONS_CANCEL; + gtk_message_type = GTK_MESSAGE_QUESTION; +- title = "JavaScript Confirm"; ++ title = "Confirm"; + break; + + case JSDIALOGTYPE_PROMPT: + buttons = GTK_BUTTONS_CANCEL; + gtk_message_type = GTK_MESSAGE_QUESTION; +- title = "JavaScript Prompt"; ++ title = "Prompt"; + break; + } + + js_dialog_callback_ = callback; + + if (!origin_url.empty()) { +- title += " - "; +- title += CefFormatUrlForSecurityDisplay(origin_url).ToString(); ++ // title += " - "; ++ // title += CefFormatUrlForSecurityDisplay(origin_url).ToString(); + } + +- GtkWindow* window = GetWindow(browser); ++ GtkWindow* window = CefBrowser_GetGtkWindow(browser); + if (!window) + return false; + +@@ -413,5 +403,3 @@ void ClientDialogHandlerGtk::OnDialogResponse(GtkDialog* dialog, + + handler->OnResetDialogState(NULL); + } +- +-} // namespace client +diff --git dialog_handler_gtk.h dialog_handler_gtk.h +index 163d0a35..59f65eda 100644 +--- dialog_handler_gtk.h ++++ dialog_handler_gtk.h +@@ -1,3 +1,6 @@ ++// COPIED from upstream "cef/tests/cefclient/browser/" directory ++// with minor modifications. See the .patch file in current directory. ++ + // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights + // reserved. Use of this source code is governed by a BSD-style license that + // can be found in the LICENSE file. +@@ -11,8 +14,6 @@ + #include "include/cef_dialog_handler.h" + #include "include/cef_jsdialog_handler.h" + +-namespace client { +- + class ClientDialogHandlerGtk : public CefDialogHandler, + public CefJSDialogHandler { + public: +@@ -53,6 +54,4 @@ class ClientDialogHandlerGtk : public CefDialogHandler, + DISALLOW_COPY_AND_ASSIGN(ClientDialogHandlerGtk); + }; + +-} // namespace client +- + #endif // CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_HANDLER_GTK_H_ From d688f4297da48d6ef2abc7629cae59a276e63d6b Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 11 Jun 2018 19:45:30 +0200 Subject: [PATCH 002/177] Update print handler on Linux (#403) and fix print dialog error (#435) --- src/cef_v59..v66_changes.txt | 2 +- src/client_handler/x11.cpp | 9 +++++++++ src/subprocess/Makefile | 4 ++-- src/subprocess/Makefile-libcefpythonapp | 2 +- src/subprocess/cefpython_app.cpp | 5 +++++ src/subprocess/print_handler_gtk.cpp | 16 +++------------- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 36ce50d5..600f194d 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -64,7 +64,7 @@ MISC ---- + Compare src/client_handler/dialog_handler_gtk.cpp (and .h) with upstream cefclient files -- In subprocess/print_handler_gtk.cpp use GetWindow implementation ++ In subprocess/print_handler_gtk.cpp use GetWindow implementation from x11.cpp NEW FEATURES diff --git a/src/client_handler/x11.cpp b/src/client_handler/x11.cpp index 9ebfe2c8..2e97a6e9 100644 --- a/src/client_handler/x11.cpp +++ b/src/client_handler/x11.cpp @@ -2,6 +2,9 @@ // All rights reserved. Licensed under BSD 3-clause license. // Project website: https://github.com/cztomczak/cefpython +// NOTE: This file is also used by "subprocess" and "libcefpythonapp" +// targets during build. + #include "x11.h" #include "include/base/cef_logging.h" @@ -24,6 +27,7 @@ void InstallX11ErrorHandlers() { // Copied from upstream cefclient. // Install xlib error handlers so that the application won't be terminated // on non-fatal errors. Must be done after initializing GTK. + LOG(INFO) << "[Browser process] Install X11 error handlers"; XSetErrorHandler(XErrorHandlerImpl); XSetIOErrorHandler(XIOErrorHandlerImpl); } @@ -48,6 +52,7 @@ void SetX11WindowTitle(CefRefPtr browser, char* title) { } GtkWindow* CefBrowser_GetGtkWindow(CefRefPtr browser) { + // TODO: Should return NULL when using the Views framework // -- REWRITTEN FOR CEF PYTHON USE CASE -- // X11 window handle ::Window xwindow = browser->GetHost()->GetWindowHandle(); @@ -68,6 +73,10 @@ GtkWindow* CefBrowser_GetGtkWindow(CefRefPtr browser) { // internally, so GTK wasn't yet initialized and must do it // now, so that display is available. Also must install X11 // error handlers to avoid 'BadWindow' errors. + // -- + // A similar code is in cefpython_app.cpp and it might already + // been executed. If making changes here, make changes there + // as well. LOG(INFO) << "[Browser process] Initialize GTK"; gtk_init(0, NULL); InstallX11ErrorHandlers(); diff --git a/src/subprocess/Makefile b/src/subprocess/Makefile index b6f003be..f52b72df 100644 --- a/src/subprocess/Makefile +++ b/src/subprocess/Makefile @@ -35,8 +35,8 @@ else ifeq ($(UNAME_S), Darwin) endif ifeq ($(UNAME_S), Linux) - CPP_FILES = print_handler_gtk.cpp - LIBS = -lcef -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 + CPP_FILES = print_handler_gtk.cpp ../client_handler/x11.cpp + LIBS = -lcef -lX11 -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 -lgdk-x11-2.0 else ifeq ($(UNAME_S), Darwin) CPP_FILES = # Include framework before libcef_dll_wrapper diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index e811a32f..57f3dcce 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -12,7 +12,7 @@ UNAME_S = $(shell uname -s) CCFLAGS = -fPIC -DBROWSER_PROCESS $(CEF_CCFLAGS) ifeq ($(UNAME_S), Linux) - SRC_MORE = print_handler_gtk.cpp \ + SRC_MORE = print_handler_gtk.cpp ../client_handler/x11.cpp \ main_message_loop/main_message_loop_external_pump_linux.cpp else ifeq ($(UNAME_S), Darwin) SRC_MORE = main_message_loop/main_message_loop_external_pump_mac.mm diff --git a/src/subprocess/cefpython_app.cpp b/src/subprocess/cefpython_app.cpp index 9105da3c..a02a7cd5 100644 --- a/src/subprocess/cefpython_app.cpp +++ b/src/subprocess/cefpython_app.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "client_handler/x11.h" #include "print_handler_gtk.h" #endif // OS_LINUX #endif // BROWSER_PROCESS @@ -221,10 +222,14 @@ CefRefPtr CefPythonApp::GetPrintHandler() { #if defined(OS_LINUX) // For print handler to work GTK must be initialized. This is // required for some of the examples. + // -- + // A similar code is in client_handler/x11.cpp. If making changes here, + // make changes there as well. GdkDisplay* gdk_display = gdk_display_get_default(); if (!gdk_display) { LOG(INFO) << "[Browser process] Initialize GTK"; gtk_init(0, NULL); + InstallX11ErrorHandlers(); } #endif #endif diff --git a/src/subprocess/print_handler_gtk.cpp b/src/subprocess/print_handler_gtk.cpp index 18c8c1c4..3bfc30c5 100644 --- a/src/subprocess/print_handler_gtk.cpp +++ b/src/subprocess/print_handler_gtk.cpp @@ -19,6 +19,8 @@ #include "include/wrapper/cef_helpers.h" #include "include/wrapper/cef_closure_task.h" +#include "client_handler/x11.h" + namespace { // CUPS Duplex attribute and values. @@ -374,7 +376,7 @@ struct ClientPrintHandlerGtk::PrintHandler { CefRefPtr callback) { dialog_callback_ = callback; - GtkWindow* parent = GetWindow(); + GtkWindow* parent = CefBrowser_GetGtkWindow(browser_); // TODO(estade): We need a window title here. dialog_ = gtk_print_unix_dialog_new(NULL, parent); g_signal_connect(dialog_, "delete-event", @@ -429,18 +431,6 @@ struct ClientPrintHandlerGtk::PrintHandler { } private: - // Returns the GtkWindow* for the browser. Will return NULL when using the - // Views framework. - GtkWindow* GetWindow() { - // TODO(cefpython): Test the code that is commented out whether it works - /* - CefWindowHandle hwnd = browser_->GetWindowHandle(); - if (hwnd) - return GTK_WINDOW(hwnd); - */ - return NULL; - } - void OnDialogResponse(GtkDialog* dialog, gint response_id) { int num_matched_handlers = g_signal_handlers_disconnect_by_func( dialog_, reinterpret_cast(&OnDialogResponseThunk), this); From ced25bf284d523e0d883b0d8fada8cfb3261cd41 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 11 Jun 2018 19:51:38 +0200 Subject: [PATCH 003/177] Update print handler gtk patch (#403) --- src/subprocess/print_handler_gtk.patch | 46 ++++++++++++++++---------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/subprocess/print_handler_gtk.patch b/src/subprocess/print_handler_gtk.patch index 75dcff42..8ee62a2a 100644 --- a/src/subprocess/print_handler_gtk.patch +++ b/src/subprocess/print_handler_gtk.patch @@ -1,8 +1,8 @@ diff --git print_handler_gtk.cc print_handler_gtk.cc -index 9a822b7a..18c8c1c4 100644 +index 9a822b7a..3bfc30c5 100644 --- print_handler_gtk.cc +++ print_handler_gtk.cc -@@ -1,22 +1,23 @@ +@@ -1,22 +1,25 @@ +// COPIED from upstream "cef/tests/cefclient/browser/" directory +// with minor modifications. See the .patch file in current directory. + @@ -23,32 +23,42 @@ index 9a822b7a..18c8c1c4 100644 #include "include/base/cef_logging.h" #include "include/base/cef_macros.h" #include "include/wrapper/cef_helpers.h" -- ++#include "include/wrapper/cef_closure_task.h" + -#include "tests/cefclient/browser/root_window.h" - -namespace client { -+#include "include/wrapper/cef_closure_task.h" ++#include "client_handler/x11.h" namespace { -@@ -431,10 +432,12 @@ struct ClientPrintHandlerGtk::PrintHandler { - // Returns the GtkWindow* for the browser. Will return NULL when using the - // Views framework. - GtkWindow* GetWindow() { +@@ -373,7 +376,7 @@ struct ClientPrintHandlerGtk::PrintHandler { + CefRefPtr callback) { + dialog_callback_ = callback; + +- GtkWindow* parent = GetWindow(); ++ GtkWindow* parent = CefBrowser_GetGtkWindow(browser_); + // TODO(estade): We need a window title here. + dialog_ = gtk_print_unix_dialog_new(NULL, parent); + g_signal_connect(dialog_, "delete-event", +@@ -428,16 +431,6 @@ struct ClientPrintHandlerGtk::PrintHandler { + } + + private: +- // Returns the GtkWindow* for the browser. Will return NULL when using the +- // Views framework. +- GtkWindow* GetWindow() { - scoped_refptr root_window = - RootWindow::GetForBrowser(browser_->GetIdentifier()); - if (root_window) - return GTK_WINDOW(root_window->GetWindowHandle()); -+ // TODO(cefpython): Test the code that is commented out whether it works -+ /* -+ CefWindowHandle hwnd = browser_->GetWindowHandle(); -+ if (hwnd) -+ return GTK_WINDOW(hwnd); -+ */ - return NULL; - } - -@@ -626,5 +629,3 @@ ClientPrintHandlerGtk::PrintHandler* ClientPrintHandlerGtk::GetPrintHandler( +- return NULL; +- } +- + void OnDialogResponse(GtkDialog* dialog, gint response_id) { + int num_matched_handlers = g_signal_handlers_disconnect_by_func( + dialog_, reinterpret_cast(&OnDialogResponseThunk), this); +@@ -626,5 +619,3 @@ ClientPrintHandlerGtk::PrintHandler* ClientPrintHandlerGtk::GetPrintHandler( DCHECK(it != print_handler_map_.end()); return it->second; } From 0634b90b0f04c729866b3138e3dc418121eaf68a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 12 Jun 2018 14:47:28 +0200 Subject: [PATCH 004/177] Update log severity constants (#403) --- api/ApplicationSettings.md | 1 - src/cef_v59..v66_changes.txt | 3 ++- src/extern/cef/cef_types.pxd | 1 + src/settings.pyx | 5 +++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index 5a2f664d..87bad879 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -314,7 +314,6 @@ Accepted values - constants available in the cefpython module: * LOGSEVERITY_INFO * LOGSEVERITY_WARNING * LOGSEVERITY_ERROR (default) -* LOGSEVERITY_ERROR_REPORT * LOGSEVERITY_DISABLE diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 600f194d..1f1deac4 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -71,7 +71,8 @@ NEW FEATURES ------------ internal/cef_types.h -- cef_log_severity_t: new key LOGSEVERITY_DEBUG ++ cef_log_severity_t: new key LOGSEVERITY_DEBUG (no need to expose, + same as LOGSEVERITY_VERBOSE, see code comments in setting.pyx - cef_settings_t: - background_color: OSR windows can set an ARGB background color - cef_path_key_t (informational only): diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index e70ec5ac..5fcd3fa7 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -115,6 +115,7 @@ cdef extern from "include/internal/cef_types.h": ctypedef enum cef_log_severity_t: LOGSEVERITY_DEFAULT, LOGSEVERITY_VERBOSE, + LOGSEVERITY_DEBUG = LOGSEVERITY_VERBOSE, LOGSEVERITY_INFO, LOGSEVERITY_WARNING, LOGSEVERITY_ERROR, diff --git a/src/settings.pyx b/src/settings.pyx index 154a40d0..08204491 100644 --- a/src/settings.pyx +++ b/src/settings.pyx @@ -12,6 +12,11 @@ cimport cef_types LOGSEVERITY_DEFAULT = cef_types.LOGSEVERITY_DEFAULT LOGSEVERITY_VERBOSE = cef_types.LOGSEVERITY_VERBOSE +# LOGSEVERITY_DEBUG is not exposed, as it is the same +# as LOGSEVERITY_VERBOSE, and because it would be confusing +# as currently passing --debug arg to app causes it to +# set logseverity to LOGSEVERITY_INFO. Verbose logseverity +# contains too much information. LOGSEVERITY_INFO = cef_types.LOGSEVERITY_INFO LOGSEVERITY_WARNING = cef_types.LOGSEVERITY_WARNING LOGSEVERITY_ERROR = cef_types.LOGSEVERITY_ERROR From bd63e306eb8739adde51dbe6de547d91868acbdc Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 12 Jun 2018 14:52:19 +0200 Subject: [PATCH 005/177] Update background_color option in app/browser settings (#403) --- api/ApplicationSettings.md | 2 +- api/BrowserSettings.md | 4 ++++ api/WindowInfo.md | 2 +- src/cef_v59..v66_changes.txt | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index 87bad879..b5a81900 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -118,7 +118,7 @@ Description from upstream CEF: > will be enabled. 32-bit ARGB color value, not premultiplied. The color components are always -in a known order. Equivalent to the `SkColor` type. +in a known order. Equivalent to the `SkColor` type in Chromium. ### browser_subprocess_path diff --git a/api/BrowserSettings.md b/api/BrowserSettings.md index 9fd1a380..4b1b13af 100644 --- a/api/BrowserSettings.md +++ b/api/BrowserSettings.md @@ -86,6 +86,10 @@ Description from upstream CEF: > fully transparent for a windowless (off-screen) browser then transparent > painting will be enabled. +32-bit ARGB color value, not premultiplied. The color components are always +in a known order. Equivalent to the `SkColor` type in Chromium. + + ### databases_disabled (bool) Controls whether databases can be used. Also configurable using the --disable-databases switch. diff --git a/api/WindowInfo.md b/api/WindowInfo.md index 091eb3f1..957b8757 100644 --- a/api/WindowInfo.md +++ b/api/WindowInfo.md @@ -50,7 +50,7 @@ Available only on Windows. | parentWindowHandle | int | | __Return__ | void | -Upstream CEF description: +Description from upstream CEF: > Create the browser using windowless (off-screen) rendering. No window will be created for the browser and all rendering will occur via the CefRenderHandler interface. The |parent| value will be used to identify diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 1f1deac4..1feb6f1c 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -73,8 +73,8 @@ NEW FEATURES internal/cef_types.h + cef_log_severity_t: new key LOGSEVERITY_DEBUG (no need to expose, same as LOGSEVERITY_VERBOSE, see code comments in setting.pyx -- cef_settings_t: - - background_color: OSR windows can set an ARGB background color ++ cef_settings_t: + + background_color: OSR windows can set an ARGB background color - cef_path_key_t (informational only): - PK_DIR_RESOURCES new key - cef_urlrequest_flags_t (expose in cefpython.Request.Flags): From 1ff451429d33fec293d36931992833545f515e7c Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 12 Jun 2018 14:59:46 +0200 Subject: [PATCH 006/177] Update cef_path_key_t (#403). PK_DIR_RESOURCES should not be included in CefOverridePath patch (Issue #231), since there is already a way to set resources dir path using CefSettings.resources_dir_path. --- src/extern/cef/cef_types.pxd | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index 5fcd3fa7..70263780 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -344,6 +344,7 @@ cdef extern from "include/internal/cef_types.h": PK_FILE_MODULE, PK_LOCAL_APP_DATA, PK_USER_DATA, + PK_DIR_RESOURCES, ctypedef cef_path_key_t PathKey ctypedef enum cef_plugin_policy_t: From 0e85431f7d6d0cf5ef541660bb8f629804155ccf Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 18 Jun 2018 13:48:24 +0200 Subject: [PATCH 007/177] Update build instructions for 32-bit (#403) --- docs/Build-instructions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 507cbc92..5e698af4 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -196,13 +196,14 @@ requirements common for all platforms. * Official binaries are built on Ubuntu 14.04 (cmake 2.8.12, g++ 4.8.4) * Download [ninja](https://github.com/ninja-build/ninja/releases) 1.7.1 or later and copy it to /usr/bin and chmod 755. - * Install/upgrade required packages using one of the three methods below + * Install/upgrade required packages using one of the four methods below (these packages should be upgraded each time you update to newer CEF): - 1. Type command: `sudo apt-get install bison build-essential cdbs curl devscripts dpkg-dev elfutils fakeroot flex g++ git-core git-svn gperf libapache2-mod-php5 libasound2-dev libav-tools libbrlapi-dev libbz2-dev libcairo2-dev libcap-dev libcups2-dev libcurl4-gnutls-dev libdrm-dev libelf-dev libexif-dev libffi-dev libgconf2-dev libgconf-2-4 libgl1-mesa-dev libglib2.0-dev libglu1-mesa-dev libgnome-keyring-dev libgtk2.0-dev libkrb5-dev libnspr4-dev libnss3-dev libpam0g-dev libpci-dev libpulse-dev libsctp-dev libspeechd-dev libsqlite3-dev libssl-dev libudev-dev libwww-perl libxslt1-dev libxss-dev libxt-dev libxtst-dev mesa-common-dev openbox patch perl php5-cgi pkg-config python python-cherrypy3 python-crypto python-dev python-psutil python-numpy python-opencv python-openssl python-yaml rpm ruby subversion ttf-dejavu-core ttf-indic-fonts ttf-kochi-gothic ttf-kochi-mincho fonts-thai-tlwg wdiff wget zip` - 2. See the list of packages on the + 1. For 64-bit build, type this command: `sudo apt-get install bison build-essential cdbs curl devscripts dpkg-dev elfutils fakeroot flex g++ git-core git-svn gperf libapache2-mod-php5 libasound2-dev libav-tools libbrlapi-dev libbz2-dev libcairo2-dev libcap-dev libcups2-dev libcurl4-gnutls-dev libdrm-dev libelf-dev libexif-dev libffi-dev libgconf2-dev libgconf-2-4 libgl1-mesa-dev libglib2.0-dev libglu1-mesa-dev libgnome-keyring-dev libgtk2.0-dev libkrb5-dev libnspr4-dev libnss3-dev libpam0g-dev libpci-dev libpulse-dev libsctp-dev libspeechd-dev libsqlite3-dev libssl-dev libudev-dev libwww-perl libxslt1-dev libxss-dev libxt-dev libxtst-dev mesa-common-dev openbox patch perl php5-cgi pkg-config python python-cherrypy3 python-crypto python-dev python-psutil python-numpy python-opencv python-openssl python-yaml rpm ruby subversion ttf-dejavu-core ttf-indic-fonts ttf-kochi-gothic ttf-kochi-mincho fonts-thai-tlwg wdiff wget zip` + 2. For 32-bit build, type this command: `bison build-essential cdbs curl devscripts dpkg-dev elfutils fakeroot flex g++ git-core git-svn gperf libapache2-mod-php5 libasound2-dev libav-tools libbrlapi-dev libbz2-dev libcairo2-dev libcap-dev libcups2-dev libcurl4-gnutls-dev libdrm-dev libelf-dev libexif-dev libffi-dev libgconf2-dev libgl1-mesa-dev libglib2.0-dev libglu1-mesa-dev libgnome-keyring-dev libgtk2.0-dev libkrb5-dev libnspr4-dev libnss3-dev libpam0g-dev libpci-dev libpulse-dev libsctp-dev libspeechd-dev libsqlite3-dev libssl-dev libudev-dev libwww-perl libxslt1-dev libxss-dev libxt-dev libxtst-dev mesa-common-dev openbox patch perl php5-cgi pkg-config python python-cherrypy3 python-crypto python-dev python-psutil python-numpy python-opencv python-openssl python-yaml rpm ruby subversion ttf-dejavu-core ttf-indic-fonts ttf-kochi-gothic ttf-kochi-mincho fonts-thai-tlwg wdiff wget zip lib32gcc1 lib32stdc++6 libc6-i386 linux-libc-dev:i386 libasound2:i386 libcap2:i386 libelf-dev:i386 libfontconfig1:i386 libgconf-2-4:i386 libglib2.0-0:i386 libgpm2:i386 libgtk2.0-0:i386 libgtk-3-0:i386 libncurses5:i386 libnss3:i386 libpango1.0-0:i386 libssl1.0.0:i386 libtinfo-dev:i386 libxcomposite1:i386 libxcursor1:i386 libxdamage1:i386 libxi6:i386 libxrandr2:i386 libxss1:i386 libxtst6:i386` + 3. See the list of packages on the [cef/AutomatedBuildSetup.md](https://bitbucket.org/chromiumembedded/cef/wiki/AutomatedBuildSetup.md#markdown-header-linux-configuration) wiki page. - 2. Run the install-build-deps.sh script - + 4. Run the install-build-deps.sh script - instructions provided further down on this page. * To build on Debian 7 see [cef/BuildingOnDebian7.md](https://bitbucket.org/chromiumembedded/cef/wiki/BuildingOnDebian7.md) and From 1c616296538fd2526961fe989f70cc735372b13f Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 15:00:13 +0200 Subject: [PATCH 008/177] Support for Python 3.7 (#433). Still needs testing. --- README.md | 6 +++--- docs/Knowledge-Base.md | 4 ++-- src/cef_v59..v66_changes.txt | 15 ++++----------- src/common/cefpython_public_api.h | 2 ++ tools/build_distrib.py | 17 +++++++++++++---- tools/common.py | 2 ++ tools/installer/cefpython3.__init__.py | 5 ++++- tools/installer/cefpython3.setup.py | 3 ++- 8 files changed, 32 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ddb39381..f478a96c 100644 --- a/README.md +++ b/README.md @@ -218,9 +218,9 @@ support old operating systems then choose the v31 release. OS | Py2 | Py3 | 32bit | 64bit | Requirements --- | --- | --- | --- | --- | --- -Windows | 2.7 | 3.4 / 3.5 / 3.6 | Yes | Yes | Windows 7+ -Linux | 2.7 | 3.4 / 3.5 / 3.6 | Yes | Yes | Debian 7+ / Ubuntu 12.04+ -Mac | 2.7 | 3.4 / 3.5 / 3.6 | No | Yes | MacOS 10.9+ +Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Windows 7+ +Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 7+ / Ubuntu 12.04+ +Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ These platforms are not supported yet: - ARM - see [Issue #267](../../issues/267) diff --git a/docs/Knowledge-Base.md b/docs/Knowledge-Base.md index cf1deba5..5e49162f 100644 --- a/docs/Knowledge-Base.md +++ b/docs/Knowledge-Base.md @@ -33,9 +33,9 @@ ImportError: DLL load failed: The specified module could not be found. ``` Then most probably this is caused, because you are missing -"msvcp140.dll" dependency (for Python 3.5/3.6 for example). This +"msvcp140.dll" dependency (for Python 3.5/3.6/3.7 for example). This is a dependency for Python C++ extensions (eg. cefpython_py36.pyd -depends on it). For Python 3.5/3.6 to fix this download +depends on it). For Python 3.5/3.6/3.7 to fix this download [Visual C++ Redistributable for VS2015](https://www.microsoft.com/en-us/download/details.aspx?id=52685) (13 MB) - this is just a VC++ redistributable, not a Visual Studio package. For 32-bit download "vc_redist.x86.exe" file and for diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 1feb6f1c..38a071e4 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -75,17 +75,10 @@ internal/cef_types.h same as LOGSEVERITY_VERBOSE, see code comments in setting.pyx + cef_settings_t: + background_color: OSR windows can set an ARGB background color -- cef_path_key_t (informational only): - - PK_DIR_RESOURCES new key -- cef_urlrequest_flags_t (expose in cefpython.Request.Flags): - - UR_FLAG_ONLY_FROM_CACHE new flag - - UR_FLAG_STOP_ON_REDIRECT new flag - - enum values have changed due to new key -- cef_thread_id_t: - - TID_FILE_BACKGROUND (deprecated TID_FILE) - - TID_FILE_USER_VISIBLE -- cef_popup_features_t: some keys removed (not exposed, informational only) -- cef_referrer_policy_t changes (not exposed) ++ cef_path_key_t (informational only): + + PK_DIR_RESOURCES new key ++ cef_popup_features_t: some keys removed (not exposed, informational only) ++ cef_referrer_policy_t changes (not exposed, info only) cef_accessibility_handler.h - CefAccessibilityHandler diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h index 0fbd60a5..53ad62c6 100644 --- a/src/common/cefpython_public_api.h +++ b/src/common/cefpython_public_api.h @@ -44,6 +44,8 @@ #include "../../build/build_cefpython/cefpython_py35_fixed.h" #elif PY_MINOR_VERSION == 6 #include "../../build/build_cefpython/cefpython_py36_fixed.h" +#elif PY_MINOR_VERSION == 7 +#include "../../build/build_cefpython/cefpython_py37_fixed.h" #endif // PY_MINOR_VERSION #endif // PY_MAJOR_VERSION diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 3c2abef8..08af1ec5 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -20,6 +20,9 @@ allows to use CEF prebuilt binaries and libraries downloaded from CEF Python's Github releases to build distribution pacakges. + --allow-partial Do not require all supported Python versions to + be installed. If some are missing they just won't + be included in distribution. This script does the following: @@ -73,9 +76,10 @@ NO_RUN_EXAMPLES = False NO_REBUILD = False NO_AUTOMATE = False +ALLOW_PARTIAL = False # Python versions -SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6)] +SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6), (3, 7)] # Python search paths. It will use first Python found for specific version. # Supports replacement of one environment variable in path eg.: %ENV_KEY%. @@ -148,7 +152,7 @@ def main(): def command_line_args(): - global VERSION, NO_RUN_EXAMPLES, NO_REBUILD, NO_AUTOMATE + global VERSION, NO_RUN_EXAMPLES, NO_REBUILD, NO_AUTOMATE, ALLOW_PARTIAL version = get_version_from_command_line_args(__file__) if not version or "--help" in sys.argv: print(__doc__) @@ -163,6 +167,9 @@ def command_line_args(): if "--no-automate" in sys.argv: NO_AUTOMATE = True sys.argv.remove("--no-automate") + if "--allow-partial" in sys.argv: + ALLOW_PARTIAL = True + sys.argv.remove("--allow-partial") args = sys.argv[1:] for arg in args: if arg == version: @@ -295,7 +302,8 @@ def check_pythons(pythons_32bit, pythons_64bit): if pythons_32bit: print("[build_distrib.py] Pythons 32-bit found:") pp.pprint(pythons_32bit) - if check_32bit and len(pythons_32bit) != len(SUPPORTED_PYTHON_VERSIONS): + if check_32bit and len(pythons_32bit) != len(SUPPORTED_PYTHON_VERSIONS) \ + and not ALLOW_PARTIAL: print("[build_distrib.py] ERROR: Couldn't find all supported" " python 32-bit installations. Found: {found}." .format(found=len(pythons_32bit))) @@ -303,7 +311,8 @@ def check_pythons(pythons_32bit, pythons_64bit): if pythons_64bit: print("[build_distrib.py] Pythons 64-bit found:") pp.pprint(pythons_64bit) - if check_64bit and len(pythons_64bit) != len(SUPPORTED_PYTHON_VERSIONS): + if check_64bit and len(pythons_64bit) != len(SUPPORTED_PYTHON_VERSIONS) \ + and not ALLOW_PARTIAL: print("[build_distrib.py] ERROR: Couldn't find all supported" " python 64-bit installations. Found: {found}." .format(found=len(pythons_64bit))) diff --git a/tools/common.py b/tools/common.py index 4ca34d73..6e44dc02 100644 --- a/tools/common.py +++ b/tools/common.py @@ -424,6 +424,8 @@ def get_msvs_for_python(vs_prefix=False): return "VS2015" if vs_prefix else "2015" elif sys.version_info[:2] == (3, 6): return "VS2015" if vs_prefix else "2015" + elif sys.version_info[:2] == (3, 7): + return "VS2015" if vs_prefix else "2015" else: print("ERROR: This version of Python is not supported") sys.exit(1) diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index d7463248..fc00000d 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -44,7 +44,7 @@ libcef = os.path.join(package_dir, "libcef.so") ctypes.CDLL(libcef, ctypes.RTLD_GLOBAL) -# Load the cefpython module for proper Python version +# Load the cefpython module for given Python version if sys.version_info[:2] == (2, 7): # noinspection PyUnresolvedReferences from . import cefpython_py27 as cefpython @@ -57,5 +57,8 @@ elif sys.version_info[:2] == (3, 6): # noinspection PyUnresolvedReferences from . import cefpython_py36 as cefpython +elif sys.version_info[:2] == (3, 7): + # noinspection PyUnresolvedReferences + from . import cefpython_py37 as cefpython else: raise Exception("Python version not supported: " + sys.version) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 08065ebd..c8e52699 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -129,7 +129,7 @@ def main(): "https://github.com/cztomczak/cefpython", license="BSD 3-clause", author="Czarek Tomczak", - author_email="czarek.tomczak@@gmail.com", + author_email="czarek.tomczak2@@gmail.com", url="https://github.com/cztomczak/cefpython", download_url="https://github.com/cztomczak/cefpython/releases", platforms=["{{SYSCONFIG_PLATFORM}}"], @@ -147,6 +147,7 @@ def main(): "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Topic :: Desktop Environment", "Topic :: Internet", "Topic :: Internet :: WWW/HTTP", From 732fbc98aca28072ab43fd587454183c6b4304ed Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 15:35:16 +0200 Subject: [PATCH 009/177] Update to Cython 0.28.4 (#433, #250). Tested only on Linux. --- src/extern/cef/cef_task.pxd | 1 - tools/requirements.txt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/extern/cef/cef_task.pxd b/src/extern/cef/cef_task.pxd index 806cbe89..fbc0181e 100644 --- a/src/extern/cef/cef_task.pxd +++ b/src/extern/cef/cef_task.pxd @@ -9,7 +9,6 @@ from cef_types cimport int64 from cef_ptr cimport CefRefPtr cdef extern from "include/cef_task.h": - ctypedef int CefThreadId ctypedef cef_types.cef_thread_id_t CefThreadId cdef cpp_bool CefCurrentlyOn(CefThreadId) diff --git a/tools/requirements.txt b/tools/requirements.txt index e8f345b1..81ebc0c5 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,4 +1,4 @@ -Cython == 0.25.2 +Cython == 0.28.4 docopt >= 0.6.2 setuptools wheel From b34600d5e224e9c1e07bed2dc66e26f4688c8773 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 15:46:36 +0200 Subject: [PATCH 010/177] Update automate.py --- tools/automate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/automate.py b/tools/automate.py index a618cee5..655e7c4c 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -786,7 +786,8 @@ def copy_app(app): shutil.copy(app, bindir) if not MAC: - # Currently do not copy apps on Mac + # Currently do not copy apps on Mac, as they take lots of + # additional space (cefsimple is 157 MB). copy_app(cefclient) copy_app(cefsimple) copy_app(ceftests) From d1c72668c2080b444fec4c62c30ca88610da4adf Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 16:12:25 +0200 Subject: [PATCH 011/177] Fix warnings in windows.pxd --- src/extern/windows.pxd | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/extern/windows.pxd b/src/extern/windows.pxd index 82355a60..b6e1c772 100644 --- a/src/extern/windows.pxd +++ b/src/extern/windows.pxd @@ -23,6 +23,8 @@ cdef extern from "Windows.h" nogil: ctypedef HICON HCURSOR ctypedef unsigned int UINT + ctypedef unsigned int UINT_PTR + # noinspection PyUnresolvedReferences ctypedef wchar_t* LPCTSTR # noinspection PyUnresolvedReferences @@ -49,7 +51,11 @@ cdef extern from "Windows.h" nogil: cdef size_t mbstowcs(wchar_t *wcstr, const_char *mbstr, size_t count) ctypedef void* HDWP + cdef int SWP_NOZORDER + cdef int SWP_NOACTIVATE + cdef int SWP_FRAMECHANGED + cdef HDWP BeginDeferWindowPos(int nNumWindows) cdef HDWP DeferWindowPos( HDWP hWinPosInfo, HWND hWnd, HWND hWndInsertAfter, @@ -64,8 +70,6 @@ cdef extern from "Windows.h" nogil: cdef BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) - ctypedef unsigned int UINT_PTR - ctypedef unsigned int UINT ctypedef struct TIMERPROC: pass cdef UINT_PTR SetTimer( @@ -108,9 +112,6 @@ cdef extern from "Windows.h" nogil: cdef BOOL SetWindowPos( HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags) - cdef int SWP_NOZORDER - cdef int SWP_NOACTIVATE - cdef int SWP_FRAMECHANGED cdef DWORD GetLastError() cdef BOOL IsWindow(HWND hWnd) From 18c873556b7b59f1b8d7e68f8e8a3114b66b194c Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 16:25:04 +0200 Subject: [PATCH 012/177] Fix run_examples.py on Windows --- tools/run_examples.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/run_examples.py b/tools/run_examples.py index 2948012c..5ec43fcb 100644 --- a/tools/run_examples.py +++ b/tools/run_examples.py @@ -14,6 +14,7 @@ import importlib import os +import subprocess import sys @@ -189,9 +190,10 @@ def check_installed_packages(): def check_gi_installed(): # Cannot import both gtk and gi in the same script, thus # need another way of checking if gi package is installed. - command = ("\"{python}\" -c \"import gi\"" - .format(python=sys.executable)) - code = os.system(command) + code = subprocess.call([sys.executable, "-c", "import gi"]) + if code != 0: + print("[run_examples.py] gi module not found (PyGI / GTK 3).") + print(" Import error above can be safely ignored.") return True if code == 0 else False From 6fdadb4f38e79b29052bc0589abfc36f3eb54ba7 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 16:58:50 +0200 Subject: [PATCH 013/177] Fix C4305 warnings on Windows with Cython 0.28.4 (#250) --- tools/cython_setup.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/cython_setup.py b/tools/cython_setup.py index ea9443e5..99b567a4 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -151,9 +151,18 @@ def set_compiler_options(options): # function "public: virtual bool __thiscall # ClientHandler::OnProcessMessageReceived # + # /wd4305 - disable warnnings such as this: + # + # warning C4305: '=' : truncation from 'int' to 'bool' + # Code: > cdef public cpp_bool ExecutePythonCallback(...) except *: + # > return True + # Discussed on cython group error 4800 which is similar to this, + # but there is no other way to fix this warning: + # https://groups.google.com/d/topic/cython-users/X_X0lfIBCqo/discussion + # # The above warning LNK4217 is caused by the warning below which occurs # when building the client_handler.lib static library: - extra_compile_args.extend(["/EHsc"]) + extra_compile_args.extend(["/EHsc", "/wd4305"]) extra_link_args.extend(["/ignore:4217"]) if LINUX or MAC: From 0867dbeff3882e5f766ea4cc706a1e3a441f414b Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 20:40:31 +0200 Subject: [PATCH 014/177] Create tools/requirements.py --- tools/requirements.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tools/requirements.py diff --git a/tools/requirements.py b/tools/requirements.py new file mode 100644 index 00000000..e1637904 --- /dev/null +++ b/tools/requirements.py @@ -0,0 +1,22 @@ +""" +Installs Python dependencies using the pip tool. +See the requirements.txt file. +pip install --upgrade -r ../tools/requirements.txt +""" + +from common import * +import subprocess + + +def main(): + args = [] + if sys.executable.startswith("/usr/"): + args.append("sudo") + requirements = os.path.join(TOOLS_DIR, "requirements.txt") + args.extend(["pip", "install", "--upgrade", "-r", requirements]) + retcode = subprocess.call(args) + sys.exit(retcode) + + +if __name__ == "__main__": + main() From 72adaf27739c2a227374b1e3f9ae6e3166c91750 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 31 Jul 2018 21:04:03 +0200 Subject: [PATCH 015/177] Fix GPUCache/ folder creation when using in-memory cache (#419) --- src/cefpython.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cefpython.pyx b/src/cefpython.pyx index dd3f9f49..96e1b46a 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -583,6 +583,15 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): application_settings["single_process"] = False # ------------------------------------------------------------------------ + # ------------------------------------------------------------------------ + # Fix GPUCache/ folder creation when using in-memory cache (Issue #419) + # ------------------------------------------------------------------------ + if not "cache_path" in application_settings: + application_settings["cache_path"] = "" + if not application_settings["cache_path"]: + g_commandLineSwitches["disable-gpu-shader-disk-cache"] = "" + + cdef CefRefPtr[CefApp] cefApp = new CefPythonApp() IF UNAME_SYSNAME == "Windows": From bf0e6e3f7a4fda5ecd66c3c77805ff534a4f5c7c Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 1 Aug 2018 18:02:16 +0200 Subject: [PATCH 016/177] Update README - Debian 7 and Ubuntu 12/13 no more supported since v66 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f478a96c..c51d7d4d 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ support old operating systems then choose the v31 release. OS | Py2 | Py3 | 32bit | 64bit | Requirements --- | --- | --- | --- | --- | --- Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Windows 7+ -Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 7+ / Ubuntu 12.04+ +Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 8+ / Ubuntu 14.04+ Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ These platforms are not supported yet: From ef76811e003b98c46cfe977c76a851bab041ee5c Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 1 Aug 2018 18:44:37 +0200 Subject: [PATCH 017/177] Add flags to build.py: --enable-profiling and --enable-line-tracing (#424) --- tools/build.py | 66 ++++++++++++++++++++++++++++++++++--------- tools/cython_setup.py | 20 +++++++++++-- 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/tools/build.py b/tools/build.py index 1ae47b73..0f9512ca 100644 --- a/tools/build.py +++ b/tools/build.py @@ -22,12 +22,14 @@ [--hello-world] Options: - VERSION Version number eg. 50.0 - --no-run-examples Do not run examples after build, only unit tests - --fast Fast mode - --clean Clean C++ projects build files on Linux/Mac - --kivy Run only Kivy example - --hello-world Run only Hello World example + VERSION Version number eg. 50.0 + --no-run-examples Do not run examples after build, only unit tests + --fast Fast mode + --clean Clean C++ projects build files on Linux/Mac + --kivy Run only Kivy example + --hello-world Run only Hello World example + --enable-profiling Enable cProfile profiling + --enable-line-tracing Enable cProfile line tracing """ # --rebuild-cpp Force rebuild of .vcproj C++ projects (DISABLED) @@ -82,6 +84,8 @@ KIVY_FLAG = False HELLO_WORLD_FLAG = False REBUILD_CPP = False +ENABLE_PROFILING = False +ENABLE_LINE_TRACING = False # First run FIRST_RUN = False @@ -122,48 +126,72 @@ def command_line_args(): REBUILD_CPP, VERSION, NO_RUN_EXAMPLES VERSION = get_version_from_command_line_args(__file__) + # Other scripts called by this script expect that version number + # is available in sys.argv, so don't remove it like it's done + # for all other args starting with "--". if not VERSION: print(__doc__) sys.exit(1) print("[build.py] Parse command line arguments") - # --no-run-examples if "--no-run-examples" in sys.argv: NO_RUN_EXAMPLES = True print("[build.py] Running examples disabled (--no-run-examples)") + sys.argv.remove("--no-run-examples") - # -- debug if "--debug" in sys.argv: DEBUG_FLAG = True print("[build.py] DEBUG mode On") + sys.argv.remove("--debug") - # --fast if "--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") + sys.argv.remove("--fast") - # --clean if "--clean" in sys.argv: CLEAN_FLAG = True + sys.argv.remove("--clean") - # --kivy if "--kivy" in sys.argv: KIVY_FLAG = True print("[build.py] KIVY example") + sys.argv.remove("--kivy") - # --kivy if "--hello-world" in sys.argv: HELLO_WORLD_FLAG = True print("[build.py] HELLO WORLD example") + sys.argv.remove("--hello-world") - # --rebuild-cpp # Rebuild c++ projects if "--rebuild-cpp" in sys.argv: REBUILD_CPP = True print("[build.py] REBUILD_CPP mode enabled") + sys.argv.remove("--rebuild-cpp") + + global ENABLE_PROFILING + if "--enable-profiling" in sys.argv: + print("[build.py] cProfile profiling enabled") + ENABLE_PROFILING = True + sys.argv.remove("--enable-profiling") + + global ENABLE_LINE_TRACING + if "--enable-line-tracing" in sys.argv: + print("[build.py] cProfile line tracing enabled") + ENABLE_LINE_TRACING = True + sys.argv.remove("--enable-line-tracing") + + for arg in sys.argv: + if arg.startswith("--"): + print("ERROR: invalid arg {0}".format(arg)) + sys.exit(1) + + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) print("[build.py] VERSION=%s" % VERSION) @@ -726,8 +754,18 @@ def build_cefpython_module(): os.chdir(BUILD_CEFPYTHON) + enable_profiling = "" + if ENABLE_PROFILING: + enable_profiling = "--enable-profiling" + enable_line_tracing = "" + if ENABLE_LINE_TRACING: + enable_line_tracing = "--enable-line-tracing" + command = ("\"{python}\" {tools_dir}/cython_setup.py build_ext" - .format(python=sys.executable, tools_dir=TOOLS_DIR)) + " {enable_profiling} {enable_line_tracing}" + .format(python=sys.executable, tools_dir=TOOLS_DIR, + enable_profiling=enable_profiling, + enable_line_tracing=enable_line_tracing)) if FAST_FLAG: command += " --fast" ret = subprocess.call(command, shell=True) diff --git a/tools/cython_setup.py b/tools/cython_setup.py index 99b567a4..0d2cb5b7 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -65,8 +65,10 @@ def generate_extern_c_macro_definition(self, code): generate_extern_c_macro_definition) -# Constants +# Command line args FAST_FLAG = False +ENABLE_PROFILING = False +ENABLE_LINE_TRACING = False # Cython options. Stop on first error, otherwise hundreds # of errors appear in the console. @@ -80,12 +82,24 @@ def main(): print("[cython_setup.py] Cython version: %s" % Cython.__version__) global FAST_FLAG - if len(sys.argv) > 1 and "--fast" in sys.argv: + if "--fast" in sys.argv: # Fast mode disables optimization flags print("[cython_setup.py] FAST mode enabled") FAST_FLAG = True sys.argv.remove("--fast") + global ENABLE_PROFILING + if "--enable-profiling" in sys.argv: + print("[cython_setup.py] cProfile profiling enabled") + ENABLE_PROFILING = True + sys.argv.remove("--enable-profiling") + + global ENABLE_LINE_TRACING + if "--enable-line-tracing" in sys.argv: + print("[cython_setup.py] cProfile line tracing enabled") + ENABLE_LINE_TRACING = True + sys.argv.remove("--enable-line-tracing") + if len(sys.argv) <= 1: print(__doc__) sys.exit(1) @@ -393,6 +407,8 @@ def get_ext_modules(options): # Any conversion to unicode must be explicit using .decode(). "c_string_type": "bytes", "c_string_encoding": "utf-8", + "profile": ENABLE_PROFILING, + "linetrace": ENABLE_LINE_TRACING, }, language="c++", From 5030b89ef5f3b17e6ea780f74b194ef1b8b354eb Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 1 Aug 2018 21:54:46 +0200 Subject: [PATCH 018/177] Include C++ extension dependencies (msvcpxx.dll) with binaries (#359) --- tools/build_distrib.py | 35 +++++++++++++++--- tools/common.py | 6 ++- tools/make_installer.py | 82 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 8 deletions(-) diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 08af1ec5..3d0a8105 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -131,22 +131,22 @@ def main(): run_automate_prebuilt_cef(pythons_32bit[0]) pack_prebuilt_cef("32bit") if LINUX: - reduce_package_size_issue_262("32bit") + reduce_package_size_issue262("32bit") remove_unnecessary_package_files("32bit") if pythons_64bit: if not NO_AUTOMATE: run_automate_prebuilt_cef(pythons_64bit[0]) pack_prebuilt_cef("64bit") if LINUX: - reduce_package_size_issue_262("64bit") + reduce_package_size_issue262("64bit") remove_unnecessary_package_files("64bit") if not NO_REBUILD: build_cefpython_modules(pythons_32bit, "32bit") build_cefpython_modules(pythons_64bit, "64bit") if pythons_32bit: - make_packages(pythons_32bit[0], "32bit") + make_packages(pythons_32bit[0], "32bit", pythons_32bit) if pythons_64bit: - make_packages(pythons_64bit[0], "64bit") + make_packages(pythons_64bit[0], "64bit", pythons_64bit) test_wheel_packages(pythons_32bit + pythons_64bit) show_summary(pythons_32bit, pythons_64bit) @@ -432,7 +432,7 @@ def zip_directory(path, base_path, archive): os.chdir(original_dir) -def reduce_package_size_issue_262(arch): +def reduce_package_size_issue262(arch): """Linux only: libcef.so is huge (500 MB) in Chrome v54+. Issue #262.""" print("[build_distrib.py] Reduce package size for {arch} (Issue #262)" .format(arch=arch)) @@ -533,7 +533,7 @@ def restore_subprocess_executable_issue342(arch): shutil.copy(src, dst) -def make_packages(python, arch): +def make_packages(python, arch, all_pythons): """Make setup and wheel packages.""" print("[build_distrib.py] Make setup package for {arch}..." .format(arch=arch)) @@ -556,6 +556,7 @@ def make_packages(python, arch): setup_basename = get_setup_installer_basename( VERSION, get_os_postfix2_for_arch(arch)) setup_dir = os.path.join(BUILD_DIR, setup_basename) + check_cpp_extension_dependencies_issue359(setup_dir, all_pythons) archive = pack_directory(setup_dir, BUILD_DIR) shutil.move(archive, DISTRIB_DIR) @@ -583,6 +584,28 @@ def make_packages(python, arch): shutil.rmtree(setup_dir) +def check_cpp_extension_dependencies_issue359(setup_dir, all_pythons): + """Windows only: check if msvcpXX.dll exist for all Python versions. + Issue #359.""" + if not WINDOWS: + return + checked_any = False + for python in all_pythons: + if python["version2"] in ((3, 5), (3, 6), (3, 7)): + checked_any = True + if not os.path.exists(os.path.join(setup_dir, "msvcp140.dll")): + raise Exception("C++ ext dependency missing: msvcp140.dll") + elif python["version2"] == (3, 4): + checked_any = True + if not os.path.exists(os.path.join(setup_dir, "msvcp100.dll")): + raise Exception("C++ ext dependency missing: msvcp100.dll") + elif python["version2"] == (2, 7): + if not os.path.exists(os.path.join(setup_dir, "msvcp90.dll")): + raise Exception("C++ ext dependency missing: msvcp100.dll") + checked_any = True + assert checked_any + + def test_wheel_packages(pythons): """Test wheel packages installation and run unit tests.""" uninstall_cefpython3_packages(pythons) diff --git a/tools/common.py b/tools/common.py index 6e44dc02..b0c21b90 100644 --- a/tools/common.py +++ b/tools/common.py @@ -15,7 +15,7 @@ # These sample apps will be deleted when creating setup/wheel packages CEF_SAMPLE_APPS = ["cefclient", "cefsimple", "ceftests", "chrome-sandbox"] -# Architecture and OS postfixes +# Python architecture and OS postfixes ARCH32 = (8 * struct.calcsize('P') == 32) ARCH64 = (8 * struct.calcsize('P') == 64) # Make sure platform.architecture()[0] shows correctly 32bit when @@ -26,6 +26,10 @@ assert platform.architecture()[0] == "64bit" ARCH_STR = platform.architecture()[0] +# Operating system architecture +SYSTEM64 = platform.machine().endswith('64') +SYSTEM32 = not SYSTEM64 + # OS_POSTFIX is for directories/files names in cefpython sources # and doesn't include architecture type, just OS name. diff --git a/tools/make_installer.py b/tools/make_installer.py index 4220710f..bc3b5cef 100644 --- a/tools/make_installer.py +++ b/tools/make_installer.py @@ -102,6 +102,8 @@ def main(): create_empty_log_file(os.path.join(PKG_DIR, "debug.log")) create_empty_log_file(os.path.join(PKG_DIR, "examples/debug.log")) + copy_cpp_extension_dependencies_issue359(PKG_DIR) + print("[make_installer.py] Done. Installer package created: {setup_dir}" .format(setup_dir=SETUP_DIR)) @@ -184,7 +186,7 @@ def replace_template_vars(string, dictionary): string = string.replace("{{"+key+"}}", value) if string == orig_string: raise Exception("Nothing to format") - if re.search(r"\{\{[a-zA-Z0-9_]+\}\}", string): + if re.search(r"{{[a-zA-Z0-9_]+}}", string): raise Exception("Not all strings were formatted") return string @@ -334,6 +336,84 @@ def create_empty_log_file(log_file): subprocess.check_call(command, shell=True) +def copy_cpp_extension_dependencies_issue359(pkg_dir): + """CEF Python module is written in Cython and is a Python C++ + extension and depends on msvcpXX.dll. For Python 3.5 / 3.6 / 3.7 + msvcp140.dll is required. See Issue #359. For Python 2.7 + msvcp90.dll is required. Etc. These dependencies are not included + with Python binaries from Python.org.""" + if not WINDOWS: + return + + windows_dir = os.environ["SYSTEMROOT"] + if SYSTEM64: + system32 = os.path.join(windows_dir, "SysWOW64") + system64 = os.path.join(windows_dir, "System32") + else: + system32 = os.path.join(windows_dir, "") + system64 = None + if ARCH64: + system = system64 + else: + system = system32 + + root_search_paths = [] + + # Need to check for .pyd files for all Python version, because + # the builder/installer work in a way that previous cefpython + # module builds for other Python versions are also included + # in the package. Thus if included, msvcpxx.dll dependency is + # required as well. + + # Python 3.5 / 3.6 / 3.7 + if os.path.exists(os.path.join(pkg_dir, "cefpython_py35.pyd")) \ + or os.path.exists(os.path.join(pkg_dir, "cefpython_py36.pyd")) \ + or os.path.exists(os.path.join(pkg_dir, "cefpython_py37.pyd")): + search_paths = [ + # This is where Microsoft Visual C++ 2015 Update 3 installs + # (14.00.24212). + os.path.join(system, "msvcp140.dll"), + ] + root_search_paths.append(search_paths) + + # Python 3.4 + if os.path.exists(os.path.join(pkg_dir, "cefpython_py34.pyd")): + search_paths = [ + # 10.00.40219.325 installs here on my system. + os.path.join(system, "msvcp100.dll"), + ] + root_search_paths.append(search_paths) + + # Python 2.7 + if os.path.exists(os.path.join(pkg_dir, "cefpython_py27.pyd")): + if ARCH32: + search_paths = [ + # This runtime version is shipped with Python 2.7.14 + r"c:\Windows\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b" + r"_9.0.30729.1_none_e163563597edeada\msvcp90.dll", + ] + else: + search_paths = [ + # This runtime version is shipped with Python 2.7.14 + r"c:\Windows\winsxs\amd64_microsoft.vc90.crt_1fc8b3b9a1e18e3b" + r"_9.0.30729.1_none_99b61f5e8371c1d4\msvcp90.dll", + ] + root_search_paths.append(search_paths) + + assert len(root_search_paths) + + for search_paths in root_search_paths: + found = False + for path in search_paths: + if os.path.exists(path): + shutil.copy(path, pkg_dir) + found = True + if not found: + raise Exception("C++ extension dll dependency not found." + " Search paths: {0}" + .format(", ".join(search_paths))) + + def short_src_path(path): # Very long: \build\cef55_3.2883.1553.g80bd606_win32\ find = os.path.basename(CEF_BINARIES_LIBRARIES) From c8526856b3d22e7b97d71d745ad65075b9cb35ad Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 1 Aug 2018 21:56:42 +0200 Subject: [PATCH 019/177] Update Knowledge Base doc (#359) --- docs/Knowledge-Base.md | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/docs/Knowledge-Base.md b/docs/Knowledge-Base.md index 5e49162f..91d5a516 100644 --- a/docs/Knowledge-Base.md +++ b/docs/Knowledge-Base.md @@ -1,7 +1,6 @@ # Knowledge Base Table of contents: -* [ImportError: DLL load failed (Windows)](#importerror-dll-load-failed-windows) * [Notifications about new releases / commits](#notifications-about-new-releases--commits) * [Changes in API after CEF updates](#changes-in-api-after-cef-updates) * [Differences between Python 2 and Python 3](#differences-between-python-2-and-python-3) @@ -20,40 +19,6 @@ Table of contents: * [Security](#security) -## ImportError: DLL load failed (Windows) - -If you get such an error on Windows: -```Text -import cefpython3 -Traceback (most recent call last): - File "", line 1, in - File "C:\Python3\lib\site-packages\cefpython3\__init__.py", line 59, in - from . import cefpython_py36 as cefpython -ImportError: DLL load failed: The specified module could not be found. -``` - -Then most probably this is caused, because you are missing -"msvcp140.dll" dependency (for Python 3.5/3.6/3.7 for example). This -is a dependency for Python C++ extensions (eg. cefpython_py36.pyd -depends on it). For Python 3.5/3.6/3.7 to fix this download -[Visual C++ Redistributable for VS2015](https://www.microsoft.com/en-us/download/details.aspx?id=52685) -(13 MB) - this is just a VC++ redistributable, not a Visual Studio -package. For 32-bit download "vc_redist.x86.exe" file and for -64-bit download "vc_redist.x64.exe" file. - -Explanation: msvcp140.dll is the DLL for the C++ runtime library. -This dependency is added by Cython when building CEF Python -module. It seems that Python 3.6 only ships "vcruntime140.dll" -which is the DLL for the C runtime library. - -On a side note, when using pyinstaller/py2exe tools for -freezing application into exe then these tools should -automatically detect the msvcp140.dll dependency and ship it -with your application. - -Created [Issue #359](../../../issues/359) to track this problem. - - ## Notifications about new releases / commits To be notified of new releases subscribe to this [RSS/Atom feed](../../../releases.atom). From 4a1d759ca13b4ca28572f8e07c4c15b847d00221 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 1 Aug 2018 23:41:32 +0200 Subject: [PATCH 020/177] Expose PaintBuffer attrs: length, width, height (#443) --- src/paint_buffer.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/paint_buffer.pyx b/src/paint_buffer.pyx index 8e7be430..d99f19d0 100644 --- a/src/paint_buffer.pyx +++ b/src/paint_buffer.pyx @@ -14,9 +14,9 @@ cdef PaintBuffer CreatePaintBuffer(const void* buffer_, int width, int height): cdef class PaintBuffer: cdef const void* buffer - cdef int width - cdef int height - cdef Py_ssize_t length + cdef public int width + cdef public int height + cdef public Py_ssize_t length cpdef uintptr_t GetPointer(self) except *: # BEFORE MODIFYING CODE: From 5525fbbd6a4c450d6e3979e60caf99fcc6d5b85d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 1 Aug 2018 23:46:41 +0200 Subject: [PATCH 021/177] Rename PaintBuffer.GetString to GetBytes. Update docs. --- api/PaintBuffer.md | 4 ++-- examples/screenshot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/PaintBuffer.md b/api/PaintBuffer.md index edd9376d..c6509db2 100644 --- a/api/PaintBuffer.md +++ b/api/PaintBuffer.md @@ -9,7 +9,7 @@ This object used in: [RenderHandler](RenderHandler.md).OnPaint(). Table of contents: * [Methods](#methods) * [GetIntPointer](#getintpointer) - * [GetString](#getstring) + * [GetBytes](#getbytes) ## Methods @@ -28,7 +28,7 @@ Description from upstream CEF: > image with an upper-left origin. -### GetString +### GetBytes | Parameter | Type | | --- | --- | diff --git a/examples/screenshot.py b/examples/screenshot.py index e99c2fe6..c27e72d0 100644 --- a/examples/screenshot.py +++ b/examples/screenshot.py @@ -205,8 +205,8 @@ def OnPaint(self, browser, element_type, paint_buffer, **_): # reasons it would be better not to copy this string. # I think that Python makes a copy of that string when # passing it to SetUserData. - buffer_string = paint_buffer.GetString(mode="rgba", - origin="top-left") + buffer_string = paint_buffer.GetBytes(mode="rgba", + origin="top-left") # Browser object provides GetUserData/SetUserData methods # for storing custom data associated with browser. browser.SetUserData("OnPaint.buffer_string", buffer_string) From 92b1895acd9a294e67cbe28c40110d6094c3e5a9 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 2 Aug 2018 13:49:55 +0200 Subject: [PATCH 022/177] Update build_distrib - restore sample apps in cef prebuilt directories --- tools/automate.py | 13 +++++++++---- tools/common.py | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/tools/automate.py b/tools/automate.py index 655e7c4c..a9992f38 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -744,7 +744,9 @@ def create_prebuilt_binaries(): "build_cefclient", "tests", "cefclient", Options.build_type, "cefclient" + APP_EXT) - if LINUX and os.path.exists(cefclient): + if not MAC: + assert os.path.exists(cefclient) + if LINUX: # On Windows resources/*.html files are embedded inside exe cefclient_files = os.path.join( src, @@ -759,6 +761,8 @@ def create_prebuilt_binaries(): "build_cefclient", "tests", "cefsimple", Options.build_type, "cefsimple" + APP_EXT) + if not MAC: + assert os.path.exists(cefsimple) # ceftests ceftests = os.path.join( @@ -766,7 +770,9 @@ def create_prebuilt_binaries(): "build_cefclient", "tests", "ceftests", Options.build_type, "ceftests" + APP_EXT) - if LINUX and os.path.exists(ceftests): + if not MAC: + assert os.path.exists(ceftests) + if LINUX: # On Windows resources/*.html files are embedded inside exe ceftests_files = os.path.join( src, @@ -776,8 +782,7 @@ def create_prebuilt_binaries(): cpdir(ceftests_files, os.path.join(bindir, "ceftests_files")) def copy_app(app): - if os.path.exists(app): - if os.path.isdir(app): + if MAC: # On Mac app is a directory shutil.copytree(app, os.path.join(bindir, diff --git a/tools/common.py b/tools/common.py index b0c21b90..ab51af8a 100644 --- a/tools/common.py +++ b/tools/common.py @@ -4,6 +4,7 @@ # Common stuff for tools such as automate.py, build.py, etc. +import atexit import glob import os import platform @@ -11,6 +12,7 @@ import shutil import struct import sys +import tempfile # These sample apps will be deleted when creating setup/wheel packages CEF_SAMPLE_APPS = ["cefclient", "cefsimple", "ceftests", "chrome-sandbox"] @@ -268,9 +270,12 @@ def get_python_include_path(): return results[0] return ".\\" if WINDOWS else "./" +g_deleted_sample_apps = [] + def delete_cef_sample_apps(caller_script, bin_dir): """Delete CEF sample apps to reduce package size.""" + atexit.register(restore_cef_sample_apps, caller_script) for sample_app_name in CEF_SAMPLE_APPS: sample_app = os.path.join(bin_dir, sample_app_name + APP_EXT) # Not on all platforms sample apps may be available @@ -278,16 +283,34 @@ def delete_cef_sample_apps(caller_script, bin_dir): print("[{script}] Delete {sample_app}" .format(script=os.path.basename(caller_script), sample_app=os.path.basename(sample_app))) - if os.path.isdir(sample_app): - shutil.rmtree(sample_app) - else: - os.remove(sample_app) + tmpdir = tempfile.mkdtemp() + g_deleted_sample_apps.append((bin_dir, + os.path.basename(sample_app), + tmpdir)) + shutil.move(sample_app, tmpdir) # Also delete subdirs eg. cefclient_files/, ceftests_files/ files_subdir = os.path.join(bin_dir, sample_app_name + "_files") if os.path.isdir(files_subdir): - print("[build_distrib.py] Delete directory: {dir}/" - .format(dir=os.path.basename(files_subdir))) - shutil.rmtree(files_subdir) + print("[{script}] Delete directory: {dir}/" + .format(script=os.path.basename(caller_script), + dir=os.path.basename(files_subdir))) + tmpdir = tempfile.mkdtemp() + g_deleted_sample_apps.append((bin_dir, + os.path.basename(files_subdir), + tmpdir)) + shutil.move(files_subdir, tmpdir) + + +def restore_cef_sample_apps(caller_script): + for deleted in g_deleted_sample_apps: + bin_dir = deleted[0] + tmp = os.path.join(deleted[2], deleted[1]) + print("[{script}] Restore: {path}" + .format(script=os.path.basename(caller_script), + path=os.path.join(bin_dir, deleted[1]))) + shutil.move(tmp, bin_dir) + shutil.rmtree(deleted[2]) + del g_deleted_sample_apps[0:len(g_deleted_sample_apps)] def _detect_cef_binaries_libraries_dir(): From 7aeee773321930c62c5053a17b2e8072510585d8 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 2 Aug 2018 16:41:30 +0200 Subject: [PATCH 023/177] Update to handling of browser and frame references (#365) There are still some strange issues with CEF. For example I can see that OnContextReleased is executed twice for that same frame. --- src/browser.pyx | 7 ++++++- src/cefpython.pyx | 11 ++++++----- src/frame.pyx | 27 ++++++++++++++++++++++++--- src/handlers/lifespan_handler.pyx | 17 ++++++++++++----- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/browser.pyx b/src/browser.pyx index 03e43e39..8c57460e 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -74,7 +74,8 @@ cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser, cdef JavascriptBindings javascriptBindings cdef PyBrowser tempPyBrowser - if browserId in g_unreferenced_browsers: + if browserId in g_unreferenced_browsers \ + or browserId in g_closed_browsers: # This browser was already unreferenced due to OnBeforeClose # was already called. An incomplete new instance of Browser # object is created. This instance doesn't have the client @@ -119,9 +120,13 @@ cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser, cdef void RemovePyBrowser(int browserId) except *: # Called from LifespanHandler_OnBeforeClose(). global g_pyBrowsers, g_unreferenced_browsers + cdef PyBrowser pyBrowser if browserId in g_pyBrowsers: # noinspection PyUnresolvedReferences Debug("del g_pyBrowsers[%s]" % browserId) + pyBrowser = g_pyBrowsers[browserId] + pyBrowser.cefBrowser.Assign(NULL) + del pyBrowser del g_pyBrowsers[browserId] g_unreferenced_browsers.append(browserId) else: diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 96e1b46a..3deebbc1 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -843,11 +843,6 @@ def QuitMessageLoop(): def Shutdown(): Debug("Shutdown()") - # Release shared request context. This is sometimes causing - # segmentation fault, so disabling it for now. See Issue #333: - # https://github.com/cztomczak/cefpython/issues/333 - # OFF: g_shared_request_context.Assign(NULL) - # Run some message loop work, force closing browsers and then run # some message loop work again for the browsers to close cleanly. # @@ -929,6 +924,12 @@ def Shutdown(): NonCriticalError("Shutdown called, but there are still browser" " references alive") + # Release shared request context. In the past this was sometimes + # causing segmentation fault. See Issue #333: + # https://github.com/cztomczak/cefpython/issues/333 + # Debug("Free g_shared_request_context") + # g_shared_request_context.Assign(NULL) + Debug("CefShutdown()") with nogil: CefShutdown() diff --git a/src/frame.pyx b/src/frame.pyx index 6535791d..504637ab 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -7,6 +7,10 @@ include "browser.pyx" cdef dict g_pyFrames = {} +# If a frame was unreferenced (browser closed or OnContextReleased) +# it shouldn't be kept global anymore. +cdef list g_unreferenced_frames = [] # [str unique identifier, ..] + cdef object GetUniqueFrameId(int browserId, object frameId): return str(browserId) +"#"+ str(frameId) @@ -24,6 +28,11 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): cdef PyFrame pyFrame cdef object frameId = cefFrame.get().GetIdentifier() # int64 + if frameId < 0: + # Underlying frame does not yet exist. + Debug("GetPyFrame(): underlying frame does not yet exist" + ", frameId = {0}".format(frameId)) + return None cdef int browserId = cefFrame.get().GetBrowser().get().GetIdentifier() assert (frameId and browserId), "frameId or browserId empty" cdef object uniqueFrameId = GetUniqueFrameId(browserId, frameId) @@ -46,9 +55,12 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): pyFrame = PyFrame(browserId, frameId) pyFrame.cefFrame = cefFrame - if browserId in g_unreferenced_browsers: + if uniqueFrameId in g_unreferenced_frames \ + or browserId in g_unreferenced_browsers \ + or browserId in g_closed_browsers: # Browser was already globally unreferenced in OnBeforeClose, - # thus all frames are globally unreferenced too. Create a new + # thus all frames are globally unreferenced too, or frame + # was unreferenced in OnContextReleased. Create a new # incomplete instance of PyFrame object. Read comments in # browser.pyx > GetPyBrowser and in Browser.md for what # "incomplete" means. @@ -60,17 +72,22 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): # SIDE EFFECT: two calls to GetPyFrame for the same frame object # may return two different PyFrame objects. Compare # frame objects always using GetIdentifier(). - # Debug("GetPyFrame(): create new PyFrame, frameId=%s" % frameId) + Debug("GetPyFrame(): create new PyFrame, frameId=%s" % frameId) g_pyFrames[uniqueFrameId] = pyFrame return pyFrame cdef void RemovePyFrame(int browserId, object frameId) except *: # Called from V8ContextHandler_OnContextReleased(). global g_pyFrames + cdef PyFrame pyFrame cdef object uniqueFrameId = GetUniqueFrameId(browserId, frameId) if uniqueFrameId in g_pyFrames: Debug("del g_pyFrames[%s]" % uniqueFrameId) + pyFrame = g_pyFrames[uniqueFrameId] + pyFrame.cefFrame.Assign(NULL) + del pyFrame del g_pyFrames[uniqueFrameId] + g_unreferenced_frames.append(uniqueFrameId) else: Debug("RemovePyFrame() FAILED: uniqueFrameId = %s" % uniqueFrameId) @@ -85,7 +102,11 @@ cdef void RemovePyFramesForBrowser(int browserId) except *: toRemove.append(uniqueFrameId) for uniqueFrameId in toRemove: Debug("del g_pyFrames[%s]" % uniqueFrameId) + pyFrame = g_pyFrames[uniqueFrameId] + pyFrame.cefFrame.Assign(NULL) + del pyFrame del g_pyFrames[uniqueFrameId] + g_unreferenced_frames.append(uniqueFrameId) cdef class PyFrame: cdef CefRefPtr[CefFrame] cefFrame diff --git a/src/handlers/lifespan_handler.pyx b/src/handlers/lifespan_handler.pyx index 32a3f0c4..40ab8f9a 100644 --- a/src/handlers/lifespan_handler.pyx +++ b/src/handlers/lifespan_handler.pyx @@ -110,6 +110,7 @@ cdef public void LifespanHandler_OnBeforeClose( CefRefPtr[CefBrowser] cefBrowser ) except * with gil: cdef PyBrowser pyBrowser + cdef int browserId cdef object callback try: Debug("LifespanHandler_OnBeforeClose") @@ -126,14 +127,20 @@ cdef public void LifespanHandler_OnBeforeClose( callback = pyBrowser.GetClientCallback("OnBeforeClose") if callback: callback(browser=pyBrowser) - RemovePythonCallbacksForBrowser(pyBrowser.GetIdentifier()) - RemovePyFramesForBrowser(pyBrowser.GetIdentifier()) - RemovePyBrowser(pyBrowser.GetIdentifier()) + + browserId = pyBrowser.GetIdentifier() + pyBrowser.cefBrowser.Assign(NULL) + cefBrowser.Assign(NULL) + del pyBrowser + + RemovePythonCallbacksForBrowser(browserId) + RemovePyFramesForBrowser(browserId) + RemovePyBrowser(browserId) + if g_MessageLoop_called and not len(g_pyBrowsers): # Automatically quit message loop when last browser was closed. # This is required for hello_world.py example to work. - QuitMessageLoop() - + PostTask(TID_UI, QuitMessageLoop) except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) From c78619aa8ef7c38b486f9f245cb9c518276f892c Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 2 Aug 2018 17:19:37 +0200 Subject: [PATCH 024/177] Fix flushing cookies to disk when closing app immediately (#365). Expose CookieManager.FlushStore method. --- api/CookieManager.md | 6 +++--- src/browser.pyx | 14 ++++++++++++++ src/cookie.pyx | 5 +++++ src/extern/cef/cef_browser.pxd | 3 +++ src/extern/cef/cef_cookie.pxd | 2 +- src/extern/cef/cef_cookie_manager_namespace.pxd | 4 ++++ src/extern/cef/cef_request_context.pxd | 4 ++++ src/handlers/lifespan_handler.pyx | 9 +++++++++ 8 files changed, 43 insertions(+), 4 deletions(-) diff --git a/api/CookieManager.md b/api/CookieManager.md index db53c9e0..6c70b153 100644 --- a/api/CookieManager.md +++ b/api/CookieManager.md @@ -174,11 +174,11 @@ browsers do not persist them. Returns false if cookies cannot be accessed. | Parameter | Type | | --- | --- | -| handler | CompletionHandler | +| callback | CompletionHandler (optional) | | __Return__ | bool | -Not yet implemented. - Flush the backing store (if any) to disk. If |callback| is non-NULL it will be executed asnychronously on the IO thread after the flush is complete. Returns false if cookies cannot be accessed. + +The callback arg is not implemented. diff --git a/src/browser.pyx b/src/browser.pyx index 8c57460e..87d8047b 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -328,7 +328,21 @@ cdef class PyBrowser: pass cpdef py_void CloseBrowser(self, py_bool forceClose=False): + # Browser can be closed in two ways. Either by calling + # CloseBrowser explicitilly or by destroying window + # object and in such case lifespanHandler.OnBeforeClose + # will be called. Debug("CefBrowser::CloseBrowser(%s)" % forceClose) + + # Flush cookies to disk. Temporary solution for Issue #365. + # A similar call is made in LifespanHandler_OnBeforeClose. + # If using GetCookieManager to implement custom cookie managers + # then flushing of cookies would need to be handled manually. + self.GetCefBrowserHost().get().GetRequestContext().get() \ + .GetDefaultCookieManager( + NULL) \ + .get().FlushStore(NULL) + cdef int browserId = self.GetCefBrowser().get().GetIdentifier() self.GetCefBrowserHost().get().CloseBrowser(bool(forceClose)) global g_closed_browsers diff --git a/src/cookie.pyx b/src/cookie.pyx index 45b692cc..4ccf8844 100644 --- a/src/cookie.pyx +++ b/src/cookie.pyx @@ -268,6 +268,11 @@ cdef class PyCookieManager: PyToCefStringValue(path), bool(persistSessionCookies), NULL) + cpdef py_bool FlushStore(self, callback=None): + return self.cefCookieManager.get().FlushStore( + NULL) + + # ------------------------------------------------------------------------------ # PyCookieVisitor # ------------------------------------------------------------------------------ diff --git a/src/extern/cef/cef_browser.pxd b/src/extern/cef/cef_browser.pxd index 50a70393..d0b16483 100644 --- a/src/extern/cef/cef_browser.pxd +++ b/src/extern/cef/cef_browser.pxd @@ -15,6 +15,7 @@ from cef_types cimport int64 from cef_types cimport CefBrowserSettings, CefPoint from cef_drag_data cimport CefDragData from cef_types cimport CefMouseEvent +from cef_request_context cimport CefRequestContext from cef_process_message cimport CefProcessMessage, CefProcessId @@ -62,6 +63,8 @@ cdef extern from "include/cef_browser.h": const CefPoint& inspect_element_at) void CloseDevTools() + CefRefPtr[CefRequestContext] GetRequestContext() + void Find(int identifier, const CefString& searchText, cpp_bool forward, cpp_bool matchCase, cpp_bool findNext) void StopFinding(cpp_bool clearSelection) diff --git a/src/extern/cef/cef_cookie.pxd b/src/extern/cef/cef_cookie.pxd index 94263a6b..e1778786 100644 --- a/src/extern/cef/cef_cookie.pxd +++ b/src/extern/cef/cef_cookie.pxd @@ -47,7 +47,7 @@ cdef extern from "include/cef_cookie.h": cpp_bool SetStoragePath(const CefString& path, cpp_bool persist_session_cookies, CefRefPtr[CefCompletionCallback] callback) - # cpp_bool FlushStore(CefRefPtr[CefCompletionCallback] handler) + cpp_bool FlushStore(CefRefPtr[CefCompletionCallback] callback) cdef cppclass CefCookieVisitor: pass diff --git a/src/extern/cef/cef_cookie_manager_namespace.pxd b/src/extern/cef/cef_cookie_manager_namespace.pxd index bcc8a1b3..daf1a0f3 100644 --- a/src/extern/cef/cef_cookie_manager_namespace.pxd +++ b/src/extern/cef/cef_cookie_manager_namespace.pxd @@ -10,6 +10,8 @@ from cef_cookie cimport CefCookie # noinspection PyUnresolvedReferences from cef_cookie cimport CefSetCookieCallback, CefDeleteCookiesCallback from cef_ptr cimport CefRefPtr +# noinspection PyUnresolvedReferences +from cef_callback cimport CefCompletionCallback # We need to pass C++ class methods by reference to a function, # it is not possible with such syntax: @@ -28,3 +30,5 @@ cdef extern from "include/cef_cookie.h" namespace "CefCookieManager": cpp_bool DeleteCookies(const CefString& url, const CefString& cookie_name, CefRefPtr[CefDeleteCookiesCallback] callback) + + cpp_bool FlushStore(CefRefPtr[CefCompletionCallback] callback) diff --git a/src/extern/cef/cef_request_context.pxd b/src/extern/cef/cef_request_context.pxd index 811cf72f..11674174 100644 --- a/src/extern/cef/cef_request_context.pxd +++ b/src/extern/cef/cef_request_context.pxd @@ -5,6 +5,8 @@ from cef_ptr cimport CefRefPtr # noinspection PyUnresolvedReferences from cef_request_context_handler cimport CefRequestContextHandler +from cef_callback cimport CefCompletionCallback +from cef_cookie cimport CefCookieManager cdef extern from "include/cef_request_context.h": cdef cppclass CefRequestContext: @@ -14,3 +16,5 @@ cdef extern from "include/cef_request_context.h": CefRefPtr[CefRequestContext] CreateContext( CefRefPtr[CefRequestContext] other, CefRefPtr[CefRequestContextHandler] handler) + CefRefPtr[CefCookieManager] GetDefaultCookieManager( + CefRefPtr[CefCompletionCallback] callback) diff --git a/src/handlers/lifespan_handler.pyx b/src/handlers/lifespan_handler.pyx index 40ab8f9a..bfe22bd1 100644 --- a/src/handlers/lifespan_handler.pyx +++ b/src/handlers/lifespan_handler.pyx @@ -128,6 +128,15 @@ cdef public void LifespanHandler_OnBeforeClose( if callback: callback(browser=pyBrowser) + # Flush cookies to disk. Temporary solution for Issue #365. + # A similar call is made in Browser.CloseBrowser. If using + # GetCookieManager to implement custom cookie managers then + # flushing of cookies would need to be handled manually. + cefBrowser.get().GetHost().get().GetRequestContext().get() \ + .GetDefaultCookieManager( + NULL) \ + .get().FlushStore(NULL) + browserId = pyBrowser.GetIdentifier() pyBrowser.cefBrowser.Assign(NULL) cefBrowser.Assign(NULL) From bff458e3bb20068c7027b583452f0c7b14bed59b Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 2 Aug 2018 17:33:29 +0200 Subject: [PATCH 025/177] Fix frame.ExecuteFunction throwing error in tutorial.py example --- src/frame.pyx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/frame.pyx b/src/frame.pyx index 504637ab..6ff7873c 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -28,17 +28,21 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): cdef PyFrame pyFrame cdef object frameId = cefFrame.get().GetIdentifier() # int64 - if frameId < 0: - # Underlying frame does not yet exist. - Debug("GetPyFrame(): underlying frame does not yet exist" - ", frameId = {0}".format(frameId)) - return None cdef int browserId = cefFrame.get().GetBrowser().get().GetIdentifier() assert (frameId and browserId), "frameId or browserId empty" cdef object uniqueFrameId = GetUniqueFrameId(browserId, frameId) - if uniqueFrameId in g_pyFrames: - return g_pyFrames[uniqueFrameId] + if frameId < 0: + # Underlying frame does not yet exist. In such case PyFrame + # is not stored in g_pyFrames since frameId is invalid. + # However even though frame is not supposed to exist, you + # can still call CefFrame.ExecuteFunction and it works fine + # in tutorial.py example. + Debug("GetPyFrame(): underlying frame does not yet exist:" + " browserId = {0}, frameId = {1}".format(browserId, frameId)) + else: + if uniqueFrameId in g_pyFrames: + return g_pyFrames[uniqueFrameId] # This code probably ain't needed. # ---- @@ -56,6 +60,7 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): pyFrame.cefFrame = cefFrame if uniqueFrameId in g_unreferenced_frames \ + or frameId < 0 \ or browserId in g_unreferenced_browsers \ or browserId in g_closed_browsers: # Browser was already globally unreferenced in OnBeforeClose, From 9f86c427ea2e60eb8c0ab3ded58552449649b46e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 3 Aug 2018 00:01:02 +0200 Subject: [PATCH 026/177] When implementing custom cookie managers you must call FlushStore() (#365) --- api/RequestHandler.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/RequestHandler.md b/api/RequestHandler.md index 7d7423cf..e7f48224 100644 --- a/api/RequestHandler.md +++ b/api/RequestHandler.md @@ -86,7 +86,7 @@ false. Called on the IO thread before a resource is loaded. To allow the resource to load normally return None. To specify a handler for the resource return -a [ResourceHandler](ResourceHandler.md) object. The |request| object should +a [ResourceHandler](ResourceHandler.md) object. The |request| object should not be modified in this callback. The [ResourceHandler](ResourceHandler.md) object is a python class that @@ -215,6 +215,12 @@ to True. Otherwise the browser param passed to this callback will always be the same first browser that was created using [cefpython](cefpython.md).`CreateBrowserSync`. +**NOTE**: If implementing custom cookie managers you will encounter +problems similar to [Issue #365](../../../issues/365) ("Cookies not +flushed to disk when closing app immediately"). To resolve +it you have to call CookieManager.[FlushStore](CookieManager.md#flushstore) +method when closing associated browser. + 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 From 7c614c3f0b3363f0bf87e379003df5e22d72477e Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Fri, 3 Aug 2018 11:47:24 +0200 Subject: [PATCH 027/177] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index c51d7d4d..6940fade 100644 --- a/README.md +++ b/README.md @@ -113,10 +113,6 @@ also download packages for offline installation available on the pip install cefpython3==57.0 ``` -If you get an error when importing the cefpython3 package on -Windows then see this section in the Knowledge Base document: -[ImportError: DLL load failed (Windows)](docs/Knowledge-Base.md#importerror-dll-load-failed-windows). - ## Tutorial See the [Tutorial.md](docs/Tutorial.md) file. From 2a0b06082b70694d98be57334dc598d443324300 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sun, 5 Aug 2018 12:41:12 +0200 Subject: [PATCH 028/177] Update Tutorial.md --- docs/Tutorial.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index e79815a4..71dc6b57 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -431,18 +431,15 @@ however these APIs were not yet exposed to CEF Python. ## Javascript exceptions and Python exceptions -There are cases when executing Javascript code may end up with -Python exception being thrown: +When a Python function is invoked from Javascript and it fails, +a Python exception will be thrown. When Python executes a Javascript +callback and it fails, a Javascript exception will be thrown. -1. When a Python function is invoked from Javascript and it fails, - a Python exception will be thrown -2. When Python executes a Javascript callback and it fails, - a Python exception will be thrown - -In other cases to see Javascript exceptions open Developer Tools +To see Javascript exceptions open Developer Tools window using mouse context menu and switch to Console tab. -There are multiple ways to catch/intercept javascript exceptions: +There are multiple ways to intercept javascript exceptions programmaticaly +in CEF: 1. In Javascript you can register "window.onerror" event to catch all Javascript exceptions From 3e6eaf0a6e745fc29bffb21a919c87b26368c84f Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sun, 5 Aug 2018 12:57:47 +0200 Subject: [PATCH 029/177] Update Tutorial.md --- docs/Tutorial.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 71dc6b57..c6c0fcf7 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -409,7 +409,8 @@ html_to_data_uri("test", js_callback_1); Python and Javascript can also communicate using http requests by running an internal web-server. See for example [SimpleHTTPServer](https://docs.python.org/2/library/simplehttpserver.html) -in Python docs. +in Python docs. In upstream CEF there is available a fast built-in +web server and [Issue #445](../../../issues/445) is to expose its API. With http requests it is possible for synchronous communication from Javascript to Python by performing From 5bf5d194209cff433ec272e9f091361046be7374 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 6 Aug 2018 13:24:27 +0200 Subject: [PATCH 030/177] Add --use-gtk3 flag to automate.py (#446) --- tools/automate.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/automate.py b/tools/automate.py index a9992f38..63aa55d7 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -34,6 +34,7 @@ [--ninja-jobs JOBS] [--gyp-generators GENERATORS] [--gyp-msvs-version MSVS] [--use-system-freetype USE_SYSTEM_FREETYPE] + [--use-gtk3 USE_GTK3] [--no-depot-tools-update NO_DEPOT_TOOLS_UPDATE] automate.py (-h | --help) [type -h to show full description for options] @@ -64,6 +65,7 @@ --gyp-generators= Set GYP_GENERATORS [default: ninja]. --gyp-msvs-version= Set GYP_MSVS_VERSION. --use-system-freetype Use system Freetype library on Linux (Issue #402) + --use-gtk3 Link CEF with GTK 3 libraries (Issue #446) --no-depot-tools-update Do not update depot_tools/ directory. When building old unsupported versions of Chromium you want to manually checkout an old version @@ -111,6 +113,7 @@ class Options(object): gyp_generators = "ninja" # Even though CEF uses now GN, still some GYP gyp_msvs_version = "" # env variables are being used. use_system_freetype = False + use_gtk3 = False no_depot_tools_update = False # Internal options @@ -897,14 +900,18 @@ def getenv(): env["CEF_USE_GN"] = "1" # Issue #73 patch applied here with "use_allocator=none" env["GN_DEFINES"] = "use_sysroot=true use_allocator=none symbol_level=1" - # env["GN_DEFINES"] += " use_gtk3=false" + + # Link with GTK 3 (Issue #446) + if Options.use_gtk3: + env["GN_DEFINES"] += " use_gtk3=true" + # 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 and not Options.fast_build: env["GN_DEFINES"] += " is_official_build=true" - # Isssue #402 - Blurry font rendering on Linux + # Blurry font rendering on Linux (Isssue #402) if Options.use_system_freetype: env["GN_DEFINES"] += " use_system_freetype=true" From 1a3ca6bf748f2068410f0126621d36128c225e72 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Tue, 7 Aug 2018 09:28:56 +0200 Subject: [PATCH 031/177] Update ApplicationSettings.md --- api/ApplicationSettings.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index b5a81900..d0ec2a7d 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -187,15 +187,16 @@ Default: False EXPERIMENTAL: So far this was tested only on Linux and actually made app significantly slower. Windows and Mac platforms were not - tested yet. Reported issue in upstream, see [Issue #246] - (https://github.com/cztomczak/cefpython/issues/246) for details. + tested yet. Reported issue in upstream, see + [Issue #246](https://github.com/cztomczak/cefpython/issues/246) + for details. It is recommended to use this option as a replacement for calls to cefpython.MessageLoopWork(). CEF Python will do these calls automatically using CEF's OnScheduleMessagePumpWork. This results in improved performance on Windows and Mac and resolves some bugs with missing keyboard events -on these platforms. See [Issue #246] -(https://github.com/cztomczak/cefpython/issues/246) for more details. +on these platforms. See [Issue #246](https://github.com/cztomczak/cefpython/issues/246) +for more details. Description from upstream CEF: > Set to true (1) to control browser process main (UI) thread message pump From c760eb5c1470a92746f92cfbc80c324dfd8bd519 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 7 Aug 2018 12:36:20 +0200 Subject: [PATCH 032/177] Fix external message pump implementation (#246). Update CEF to branch 3359 on Mac. --- .gitignore | 1 + src/cefpython.pyx | 18 ++++++++------ src/extern/cef/cef_scoped_ptr.pxd | 2 ++ src/subprocess/Makefile-libcefpythonapp | 2 +- .../main_message_loop_external_pump.cpp | 4 +++- .../main_message_loop_external_pump_mac.mm | 1 + src/version/cef_version_mac.h | 24 +++++++++---------- 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index ed3648c1..b786188b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ examples/webrtc_event_logs/ unittests/GPUCache/ unittests/blob_storage/ unittests/webrtc_event_logs/ +.DS_Store diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 3deebbc1..369f8f2e 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -622,8 +622,11 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): # External message pump if GetAppSetting("external_message_pump")\ and not g_external_message_pump.get(): - g_external_message_pump.reset( - MainMessageLoopExternalPump.Create().get()) + Debug("Create external message pump") + # Using .reset() here to assign new instance was causing + # MainMessageLoopExternalPump destructor to be called. Strange. + g_external_message_pump.Assign( + MainMessageLoopExternalPump.Create()) Debug("CefInitialize()") cdef cpp_bool ret @@ -930,15 +933,16 @@ def Shutdown(): # Debug("Free g_shared_request_context") # g_shared_request_context.Assign(NULL) - Debug("CefShutdown()") - with nogil: - CefShutdown() - - # Release external message pump, as in cefclient after Shutdown + # Release external message pump before CefShutdown, so that + # message pump timer is killed. if g_external_message_pump.get(): + Debug("Reset external message pump") # Reset will set it to NULL g_external_message_pump.reset() + Debug("CefShutdown()") + with nogil: + CefShutdown() def SetOsModalLoop(py_bool modalLoop): cdef cpp_bool cefModalLoop = bool(modalLoop) diff --git a/src/extern/cef/cef_scoped_ptr.pxd b/src/extern/cef/cef_scoped_ptr.pxd index 4498360c..2e770a5b 100644 --- a/src/extern/cef/cef_scoped_ptr.pxd +++ b/src/extern/cef/cef_scoped_ptr.pxd @@ -13,3 +13,5 @@ cdef extern from "include/base/cef_scoped_ptr.h": void reset(T* p) # noinspection PyUnresolvedReferences T* get() + # noinspection PyUnresolvedReferences + scoped_ptr[T]& Assign "operator="(scoped_ptr[T] p) diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index 57f3dcce..23fbde39 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -15,7 +15,7 @@ ifeq ($(UNAME_S), Linux) SRC_MORE = print_handler_gtk.cpp ../client_handler/x11.cpp \ main_message_loop/main_message_loop_external_pump_linux.cpp else ifeq ($(UNAME_S), Darwin) - SRC_MORE = main_message_loop/main_message_loop_external_pump_mac.mm + SRC_MORE = main_message_loop/main_message_loop_external_pump_mac.mm endif SRC = cefpython_app.cpp v8function_handler.cpp v8utils.cpp \ diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp b/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp index abd81a84..18b14289 100644 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp +++ b/src/subprocess/main_message_loop/main_message_loop_external_pump.cpp @@ -22,7 +22,7 @@ const int32 kTimerDelayPlaceholder = INT_MAX; // DoWork(). const int64 kMaxTimerDelay = 1000 / 30; // 30fps -MainMessageLoopExternalPump* g_external_message_pump = NULL; +::MainMessageLoopExternalPump* g_external_message_pump = NULL; } // namespace @@ -43,6 +43,8 @@ MainMessageLoopExternalPump* MainMessageLoopExternalPump::Get() { void MainMessageLoopExternalPump::OnScheduleWork(int64 delay_ms) { REQUIRE_MAIN_THREAD(); + // LOG(INFO) << "MainMessageLoopExternalPump::OnScheduleWork"; + // LOG(INFO) << delay_ms << " ms"; if (delay_ms == kTimerDelayPlaceholder && IsTimerPending()) { // Don't set the maximum timer requested from DoWork() if a timer event is diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm b/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm index f77c4d27..c446b59b 100644 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm +++ b/src/subprocess/main_message_loop/main_message_loop_external_pump_mac.mm @@ -92,6 +92,7 @@ - (void)timerTimeout:(id)obj { } int MainMessageLoopExternalPumpMac::Run() { + LOG(INFO) << "MainMessageLoopExternalPumpMac::Run"; // Run the message loop. [NSApp run]; diff --git a/src/version/cef_version_mac.h b/src/version/cef_version_mac.h index 11454273..50e9b4ef 100644 --- a/src/version/cef_version_mac.h +++ b/src/version/cef_version_mac.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. +// Copyright (c) 2018 Marshall A. Greenblatt. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -35,16 +35,16 @@ #ifndef CEF_INCLUDE_CEF_VERSION_H_ #define CEF_INCLUDE_CEF_VERSION_H_ -#define CEF_VERSION "3.2987.1601.gf035232" +#define CEF_VERSION "3.3359.1774.gd49d25f" #define CEF_VERSION_MAJOR 3 -#define CEF_COMMIT_NUMBER 1601 -#define CEF_COMMIT_HASH "f035232c082f837d2b85bd7821a93a54fc742775" -#define COPYRIGHT_YEAR 2017 +#define CEF_COMMIT_NUMBER 1774 +#define CEF_COMMIT_HASH "d49d25f881b68f418e243e12801cbbb7caebb563" +#define COPYRIGHT_YEAR 2018 -#define CHROME_VERSION_MAJOR 57 +#define CHROME_VERSION_MAJOR 66 #define CHROME_VERSION_MINOR 0 -#define CHROME_VERSION_BUILD 2987 -#define CHROME_VERSION_PATCH 133 +#define CHROME_VERSION_BUILD 3359 +#define CHROME_VERSION_PATCH 181 #define DO_MAKE_STRING(p) #p #define MAKE_STRING(p) DO_MAKE_STRING(p) @@ -63,13 +63,13 @@ extern "C" { // universal hash value will change if any platform is affected whereas the // platform hash values will change only if that particular platform is // affected. -#define CEF_API_HASH_UNIVERSAL "b0a24e3e202f3d8b72f2fbc1ebc5864f96ec16ae" +#define CEF_API_HASH_UNIVERSAL "84263345b0b1143139aba560e5e5ed16fb6a9628" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "1c6a27f840ac87c8c971350c907edbe2c5fa0387" +#define CEF_API_HASH_PLATFORM "cf092ef692a2ff18b0fc732b58bde9b8b8655fcc" #elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "1567db600ee83cc2a59bb8c17ca416d11a7c9b8a" +#define CEF_API_HASH_PLATFORM "34f636bde2f02cb43c10061f384af4fc51c26e1f" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "1f9f9e15bf7cf13de2557ddd411dfc9f694503b0" +#define CEF_API_HASH_PLATFORM "6b57a640612f8d459042917ad2568b1526a70af4" #endif // Returns CEF version information for the libcef library. The |entry| From b20c79b35a522380b6aac2f4e84668128a7bd74e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 7 Aug 2018 14:18:59 +0200 Subject: [PATCH 033/177] Fix qt.py and wxpython.py examples on Mac (#442). Enable external message pump to temporarily fix Issue #442. --- api/ApplicationSettings.md | 20 +++++++++++++++----- examples/qt.py | 9 ++++++++- examples/wxpython.py | 6 ++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index d0ec2a7d..82ca5b23 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -185,11 +185,13 @@ Downloads are handled automatically. A default `SaveAs` file dialog provided by (bool) Default: False -EXPERIMENTAL: So far this was tested only on Linux and actually made app - significantly slower. Windows and Mac platforms were not - tested yet. Reported issue in upstream, see - [Issue #246](https://github.com/cztomczak/cefpython/issues/246) - for details. +This option is for use on Mac and Linux only. On Windows for best +performance you should use a multi-threaded message loop instead +of calling CefDoMessageLoopWork in a timer. + +EXPERIMENTAL (Linux): There are issues with this option on Linux. See + [Issue #246](https://github.com/cztomczak/cefpython/issues/246) + for details. It is recommended to use this option as a replacement for calls to cefpython.MessageLoopWork(). CEF Python will do these calls automatically @@ -198,6 +200,14 @@ on Windows and Mac and resolves some bugs with missing keyboard events on these platforms. See [Issue #246](https://github.com/cztomczak/cefpython/issues/246) for more details. +IMPORTANT: Currently there are issues on Mac with both message loop work + and external message pump. The working solution is to call + a message loop work in a timer and enable external message pump + both at the same time (an incorrect approach, but it works). + This is just a temporary solution and how this affects + performance was not tested. See [Issue #442](../../../issues/442) + for more details. + Description from upstream CEF: > Set to true (1) to control browser process main (UI) thread message pump > scheduling via the CefBrowserProcessHandler::OnScheduleMessagePumpWork() diff --git a/examples/qt.py b/examples/qt.py index 731ac2e0..dc8f944a 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -76,7 +76,14 @@ def main(): check_versions() sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error - cef.Initialize() + settings = {} + if MAC: + # Issue #442 requires enabling message pump on Mac + # and calling message loop work in a timer both at + # the same time. This is an incorrect approach + # and only a temporary solution. + settings["external_message_pump"] = True + cef.Initialize(settings) app = CefApplication(sys.argv) main_window = MainWindow() main_window.show() diff --git a/examples/wxpython.py b/examples/wxpython.py index 6adfa9c4..bafcdaec 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -33,6 +33,12 @@ def main(): check_versions() sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error settings = {} + if MAC: + # Issue #442 requires enabling message pump on Mac + # and calling message loop work in a timer both at + # the same time. This is an incorrect approach + # and only a temporary solution. + settings["external_message_pump"] = True if WINDOWS: # noinspection PyUnresolvedReferences, PyArgumentList cef.DpiAware.EnableHighDpiSupport() From 4e77c936b88caf3efc3984878ba4b03f865e9155 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 7 Aug 2018 21:04:59 +0200 Subject: [PATCH 034/177] Update Migration Guide document. Changes to Mac apps that integrate into existing message loops (Qt, wxPython) (#442). --- README.md | 12 ++++++------ api/API-index.md | 2 +- docs/Migration-guide.md | 19 ++++++++++++++++++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6940fade..de38333b 100644 --- a/README.md +++ b/README.md @@ -254,8 +254,8 @@ Additional information for v31.2 release: - [Tutorial](docs/Tutorial.md) -### API categories - +### API categories + #### Modules * [cefpython](api/cefpython.md#cefpython) module @@ -310,9 +310,9 @@ Additional information for v31.2 release: * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface - -### API index - + +### API index + * [Application settings](api/ApplicationSettings.md#application-settings) * [accept_language_list](api/ApplicationSettings.md#accept_language_list) * [app_user_model_id](api/ApplicationSettings.md#app_user_model_id) @@ -652,7 +652,7 @@ Additional information for v31.2 release: * [ERR_UNSAFE_REDIRECT](api/NetworkError.md#err_unsafe_redirect) * [PaintBuffer (object)](api/PaintBuffer.md#paintbuffer-object) * [GetIntPointer](api/PaintBuffer.md#getintpointer) - * [GetString](api/PaintBuffer.md#getstring) + * [GetBytes](api/PaintBuffer.md#getbytes) * [RenderHandler (interface)](api/RenderHandler.md#renderhandler-interface) * [GetRootScreenRect](api/RenderHandler.md#getrootscreenrect) * [GetViewRect](api/RenderHandler.md#getviewrect) diff --git a/api/API-index.md b/api/API-index.md index 3382f363..a6f81ba0 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -341,7 +341,7 @@ * [ERR_UNSAFE_REDIRECT](NetworkError.md#err_unsafe_redirect) * [PaintBuffer (object)](PaintBuffer.md#paintbuffer-object) * [GetIntPointer](PaintBuffer.md#getintpointer) - * [GetString](PaintBuffer.md#getstring) + * [GetBytes](PaintBuffer.md#getbytes) * [RenderHandler (interface)](RenderHandler.md#renderhandler-interface) * [GetRootScreenRect](RenderHandler.md#getrootscreenrect) * [GetViewRect](RenderHandler.md#getviewrect) diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index 3e419176..b211f355 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -44,7 +44,8 @@ Table of contents: * [v66+ BrowserSettings.javascript_open_windows_disallowed option was removed](#v66-browsersettingsjavascript_open_windows_disallowed-option-was-removed) * [v66+ Threads removed: TID_DB, TID_PROCESS_LAUNCHER, TID_CACHE](#v66-threads-removed-tid_db-tid_process_launcher-tid_cache) * [v66+ cef.Request.Flags changed](#v66-cefrequestflags-changed) -* [v66+ RequestHandler.GetCookieManager 'browser' param may be None](#v66-requesthandlergetcookiemanager-browser-param-may-be-none) +* [v66+ RequestHandler.GetCookieManager not getting called in some cases](#v66-requesthandlergetcookiemanager-not-getting-called-in-some-cases) +* [v66+ Changes to Mac apps that integrate into existing message loops (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loops-qt-wxpython) @@ -384,3 +385,19 @@ In some cases the RequestHandler.[GetCookieManager](../api/RequestHandler.md#get callback is not getting called due to a race condition. This bug is to be fixed in Issue [#429](../../../issues/429). + +## v66+ Changes to Mac apps that integrate into existing message loops (Qt, wxPython) + +The `qt.py` and `wxpython.py` examples were modified to set +ApplicationSettings.[external_message_pump](../api/ApplicationSettings.md#external_message_pump) +to `True`. Due to Issue [#442](../../../issues/442) it is required +to implement both approaches to integrating with existing message +loops at the same time: +1. Call `cef.DoMessageLoopWork` in a 10ms timer +2. Set `ApplicationSettings.external_message_pump` to True + +This is not a correct approach and is only a temporary fix. More +testing is required to check if that resolves all the issues with message +loop freezing. Only basic testing was performed. It was not tested of +how this change affects performance. + From d32f4b628a5d46bbfbb14462f62efb132938dd85 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Wed, 8 Aug 2018 10:13:39 +0200 Subject: [PATCH 035/177] Update compile_time_constants.pxi --- src/compile_time_constants.pxi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 632aab59..609470c1 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -1,3 +1,7 @@ # This file was generated by setup.py + +# Type this command to ignore changes to this file: +# git update-index --assume-unchanged src/compile_time_constants.pxi + DEF UNAME_SYSNAME = "Windows" DEF PY_MAJOR_VERSION = 3 From 18c4a5860af24770d3f304ea5421d2579143ed5a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 8 Aug 2018 12:44:40 +0200 Subject: [PATCH 036/177] Further changes to message loop issues on Mac (#442). Minor fix to CefAppProtocol implementation, see CEF Forum topic for details. Updated Qt example so that message loop work is not called in a timer when message pump is enabled. This is not required. In wxPython apps it is still required to enable both message pump and message looper timer work. --- api/ApplicationSettings.md | 6 ++++-- docs/Migration-guide.md | 26 ++++++++++++++++---------- examples/qt.py | 12 +++++++----- examples/wxpython.py | 33 ++++++++++++++++++--------------- src/cefpython.pyx | 8 +++++--- src/client_handler/util_mac.h | 1 + src/client_handler/util_mac.mm | 24 +++++++++++++++++++++--- src/extern/mac.pxd | 1 + 8 files changed, 73 insertions(+), 38 deletions(-) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index 82ca5b23..a9ad20f1 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -201,8 +201,10 @@ on these platforms. See [Issue #246](https://github.com/cztomczak/cefpython/issu for more details. IMPORTANT: Currently there are issues on Mac with both message loop work - and external message pump. The working solution is to call - a message loop work in a timer and enable external message pump + and external message pump. In Qt apps calling message loop + work in a timer doesn't work anymore, you have to use external + message pump. In wxPython apps it is required to call a message + loop work in a timer and enable external message pump both at the same time (an incorrect approach, but it works). This is just a temporary solution and how this affects performance was not tested. See [Issue #442](../../../issues/442) diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index b211f355..3063ada1 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -45,7 +45,7 @@ Table of contents: * [v66+ Threads removed: TID_DB, TID_PROCESS_LAUNCHER, TID_CACHE](#v66-threads-removed-tid_db-tid_process_launcher-tid_cache) * [v66+ cef.Request.Flags changed](#v66-cefrequestflags-changed) * [v66+ RequestHandler.GetCookieManager not getting called in some cases](#v66-requesthandlergetcookiemanager-not-getting-called-in-some-cases) -* [v66+ Changes to Mac apps that integrate into existing message loops (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loops-qt-wxpython) +* [v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loop-qt-wxpython) @@ -386,18 +386,24 @@ callback is not getting called due to a race condition. This bug is to be fixed in Issue [#429](../../../issues/429). -## v66+ Changes to Mac apps that integrate into existing message loops (Qt, wxPython) +## v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython) -The `qt.py` and `wxpython.py` examples were modified to set +In Qt apps calling message loop work in a timer doesn't work anymore. +You have to enable external message pump by setting ApplicationSettings.[external_message_pump](../api/ApplicationSettings.md#external_message_pump) -to `True`. Due to Issue [#442](../../../issues/442) it is required -to implement both approaches to integrating with existing message -loops at the same time: +to `True`. The `qt.py` example was updated to disable calling +message loop work in a timer. External message pump +is a recommended way over calling message loop work in a timer on Mac, +so this should make Qt apps work smoothly. + +In wxPython apps you have to implement both approaches for +integrating with existing message loop at the same time: 1. Call `cef.DoMessageLoopWork` in a 10ms timer 2. Set `ApplicationSettings.external_message_pump` to True -This is not a correct approach and is only a temporary fix. More -testing is required to check if that resolves all the issues with message -loop freezing. Only basic testing was performed. It was not tested of -how this change affects performance. +This is not a correct approach and is only a temporary fix for wxPython +apps. More testing is required to check if that resolves all the issues +with message loop freezing. Only basic testing was performed. It was not +tested of how this change affects performance. +See Issue [#442](../../../issues/442) for more details on the issues. diff --git a/examples/qt.py b/examples/qt.py index dc8f944a..d0ed3300 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -79,10 +79,10 @@ def main(): settings = {} if MAC: # Issue #442 requires enabling message pump on Mac - # and calling message loop work in a timer both at - # the same time. This is an incorrect approach - # and only a temporary solution. + # in Qt example. Calling cef.DoMessageLoopWork in a timer + # doesn't work anymore. settings["external_message_pump"] = True + cef.Initialize(settings) app = CefApplication(sys.argv) main_window = MainWindow() @@ -90,7 +90,8 @@ def main(): main_window.activateWindow() main_window.raise_() app.exec_() - app.stopTimer() + if not cef.GetAppSetting("external_message_pump"): + app.stopTimer() del main_window # Just to be safe, similarly to "del app" del app # Must destroy app object before calling Shutdown cef.Shutdown() @@ -266,7 +267,8 @@ def resizeEvent(self, event): class CefApplication(QApplication): def __init__(self, args): super(CefApplication, self).__init__(args) - self.timer = self.createTimer() + if not cef.GetAppSetting("external_message_pump"): + self.timer = self.createTimer() self.setupIcon() def createTimer(self): diff --git a/examples/wxpython.py b/examples/wxpython.py index bafcdaec..91adea2d 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -21,6 +21,17 @@ LINUX = (platform.system() == "Linux") MAC = (platform.system() == "Darwin") +if MAC: + try: + # noinspection PyUnresolvedReferences + from AppKit import NSApp + except ImportError: + print("[wxpython.py] Error: PyObjC package is missing, " + "cannot fix Issue #371") + print("[wxpython.py] To install PyObjC type: " + "pip install -U pyobjc") + sys.exit(1) + # Configuration WIDTH = 800 HEIGHT = 600 @@ -37,7 +48,7 @@ def main(): # Issue #442 requires enabling message pump on Mac # and calling message loop work in a timer both at # the same time. This is an incorrect approach - # and only a temporary solution. + # and only a temporary fix. settings["external_message_pump"] = True if WINDOWS: # noinspection PyUnresolvedReferences, PyArgumentList @@ -87,20 +98,12 @@ def __init__(self): self.browser_panel.Bind(wx.EVT_SIZE, self.OnSize) if MAC: - try: - # noinspection PyUnresolvedReferences - from AppKit import NSApp - # Make the content view for the window have a layer. - # This will make all sub-views have layers. This is - # necessary to ensure correct layer ordering of all - # child views and their layers. This fixes Window - # glitchiness during initial loading on Mac (Issue #371). - NSApp.windows()[0].contentView().setWantsLayer_(True) - except ImportError: - print("[wxpython.py] Warning: PyObjC package is missing, " - "cannot fix Issue #371") - print("[wxpython.py] To install PyObjC type: " - "pip install -U pyobjc") + # Make the content view for the window have a layer. + # This will make all sub-views have layers. This is + # necessary to ensure correct layer ordering of all + # child views and their layers. This fixes Window + # glitchiness during initial loading on Mac (Issue #371). + NSApp.windows()[0].contentView().setWantsLayer_(True) if LINUX: # On Linux must show before embedding browser, so that handle diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 369f8f2e..05a12217 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -499,9 +499,7 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): Debug("Initialize() called") - # Mac initialization. Need to call NSApplication.sharedApplication() - # and do NSApplication methods swizzling to implement - # CrAppControlProtocol. See Issue 156. + # Additional initialization on Mac, see util_mac.mm. IF UNAME_SYSNAME == "Darwin": MacInitialize() @@ -944,6 +942,10 @@ def Shutdown(): with nogil: CefShutdown() + # Additional cleanup on Mac, see util_mac.mm. + IF UNAME_SYSNAME == "Darwin": + MacShutdown() + def SetOsModalLoop(py_bool modalLoop): cdef cpp_bool cefModalLoop = bool(modalLoop) with nogil: diff --git a/src/client_handler/util_mac.h b/src/client_handler/util_mac.h index 37ec7266..63ee1dff 100644 --- a/src/client_handler/util_mac.h +++ b/src/client_handler/util_mac.h @@ -11,6 +11,7 @@ #include "include/cef_browser.h" void MacInitialize(); +void MacShutdown(); void MacSetWindowTitle(CefRefPtr browser, char* title); #endif // CEFPYTHON_UTIL_MAC_H_ diff --git a/src/client_handler/util_mac.mm b/src/client_handler/util_mac.mm index f2e14cd1..4916b253 100644 --- a/src/client_handler/util_mac.mm +++ b/src/client_handler/util_mac.mm @@ -2,6 +2,16 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. +// Copyright (c) 2015 CEF Python, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/cefpython + +// Some code was copied from here: +// java-cef: src/master/native/util_mac.mm +// upstream cef: src/tests/ceftests/run_all_unittests_mac.mm +// upstream cef: src/tests/cefclient/cefclient_mac.mm +// upstream cef: src/tests/cefsimple/cefsimple_mac.mm + #import "util_mac.h" #import #include @@ -11,13 +21,14 @@ namespace { +// static NSAutoreleasePool* g_autopool = nil; BOOL g_handling_send_event = false; } // namespace -// Add the necessary CrAppControlProtocol -// functionality to NSApplication using categories and swizzling. -@interface NSApplication (CEFPythonApplication) +// Add the necessary CefAppProtocol functionality to NSApplication +// using categories and swizzling (Issue #442, Issue #156). +@interface NSApplication (CEFPythonApplication) - (BOOL)isHandlingSendEvent; - (void)setHandlingSendEvent:(BOOL)handlingSendEvent; @@ -63,9 +74,16 @@ - (void)_swizzled_terminate:(id)sender { @end void MacInitialize() { + // OFF: it's causing a crash during shutdown release + // g_autopool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; } +void MacShutdown() { + // OFF: it's causing a crash during shutdown release + // [g_autopool release]; +} + void MacSetWindowTitle(CefRefPtr browser, char* title) { NSView* view = browser->GetHost()->GetWindowHandle(); NSString* nstitle = [NSString stringWithFormat:@"%s" , title]; diff --git a/src/extern/mac.pxd b/src/extern/mac.pxd index cca038b9..e23b4b98 100644 --- a/src/extern/mac.pxd +++ b/src/extern/mac.pxd @@ -7,4 +7,5 @@ from cef_browser cimport CefBrowser cdef extern from "client_handler/util_mac.h": void MacInitialize() + void MacShutdown() void MacSetWindowTitle(CefRefPtr[CefBrowser] browser, char* title) From 4c5cf09c453b27808938ad50d1ddfc32d359c111 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 13 Aug 2018 11:27:38 +0200 Subject: [PATCH 037/177] Fix Request.SetPostData in Python 3 (#228) --- api/Request.md | 11 ++++++----- src/cefpython.pyx | 18 +++++++++++------- src/extern/cef/cef_string.pxd | 2 +- src/request.pyx | 21 ++++++++++----------- src/string_utils.pyx | 8 ++++++++ unittests/main_test.py | 21 +++++++++++++++++++++ 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/api/Request.md b/api/Request.md index 3fcf5032..d9d0ece3 100644 --- a/api/Request.md +++ b/api/Request.md @@ -95,9 +95,10 @@ Set the request method type. | --- | --- | | __Return__ | list/dict | -Get the post data. If the form content type is "multipart/form-data" -then the post data will be returned as a list. If the form content -type is "application/x-www-form-urlencoded" then the post data will +Get the post data. All strings are byte strings. If the form content +type is "multipart/form-data" then the post data will be returned +as a list. If the form content type is +"application/x-www-form-urlencoded" then the post data will be returned as a dict. @@ -108,8 +109,8 @@ be returned as a dict. | postData | list/dict | | __Return__ | void | -Set the post data. See GetPostData() for an explanation of the -postData type. +Set the post data. All strings are expected to be byte strings. +See GetPostData() for an explanation of the postData type. ### GetHeaderMap diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 05a12217..493a53d9 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -135,19 +135,23 @@ import random # noinspection PyUnresolvedReferences import struct -if sys.version_info.major == 2: +# Must use compile-time condition instead of checking sys.version_info.major +# otherwise results in "ImportError: cannot import name urlencode" strange +# error in Python 3.6. +IF PY_MAJOR_VERSION == 2: # noinspection PyUnresolvedReferences import urlparse -else: - # noinspection PyUnresolvedReferences - from urllib import parse as urlparse - -if sys.version_info.major == 2: # noinspection PyUnresolvedReferences from urllib import pathname2url as urllib_pathname2url -else: + # noinspection PyUnresolvedReferences + from urllib import urlencode as urllib_urlencode +ELSE: + # noinspection PyUnresolvedReferences + from urllib import parse as urlparse # noinspection PyUnresolvedReferences from urllib.request import pathname2url as urllib_pathname2url + # noinspection PyUnresolvedReferences + from urllib.parse import urlencode as urllib_urlencode # noinspection PyUnresolvedReferences from cpython.version cimport PY_MAJOR_VERSION diff --git a/src/extern/cef/cef_string.pxd b/src/extern/cef/cef_string.pxd index 74f2e342..0986d7a6 100644 --- a/src/extern/cef/cef_string.pxd +++ b/src/extern/cef/cef_string.pxd @@ -21,5 +21,5 @@ cdef extern from "include/internal/cef_string.h": cpp_bool FromString(cpp_string& str) cpp_string ToString() cpp_wstring ToWString() - char* c_str() + const char* c_str() size_t length() diff --git a/src/request.pyx b/src/request.pyx index 0f770b20..ed097638 100644 --- a/src/request.pyx +++ b/src/request.pyx @@ -99,10 +99,10 @@ cdef class PyRequest: # pyData is really of type "str", but Cython will throw # an error if we use that type: "Cannot convert 'bytes' # object to str implicitly. This is not portable to Py3." - cdef object pyData + cdef bytes pyData cdef size_t bytesCount cdef void* voidData - cdef str pyFile + cdef bytes pyFile while iterator != elementVector.end(): postDataElement = deref(iterator) if postDataElement.get().GetType() == cef_types.PDE_TYPE_EMPTY: @@ -112,9 +112,9 @@ cdef class PyRequest: bytesCount = postDataElement.get().GetBytesCount() voidData = malloc(bytesCount) postDataElement.get().GetBytes(bytesCount, voidData) - pyData = VoidPtrToString(voidData, bytesCount) + pyData = VoidPtrToBytes(voidData, bytesCount) free(voidData) - if pyData.startswith('--') or retMultipart: + if pyData.startswith(b'--') or retMultipart: # Content-Type: multipart/form-data retMultipart.append(pyData) else: @@ -122,8 +122,8 @@ cdef class PyRequest: retUrlEncoded.update(urlparse.parse_qsl(qs=pyData, keep_blank_values=True)) elif postDataElement.get().GetType() == cef_types.PDE_TYPE_FILE: - pyFile = CefToPyString(postDataElement.get().GetFile()) - retMultipart.append("@"+pyFile) + pyFile = CefToPyBytes(postDataElement.get().GetFile()) + retMultipart.append(b"@"+pyFile) else: raise Exception("Invalid type of CefPostDataElement") preinc(iterator) @@ -135,15 +135,15 @@ cdef class PyRequest: cpdef py_void SetPostData(self, object pyPostData): cdef CefRefPtr[CefPostData] postData = CefPostData_Create() cdef CefRefPtr[CefPostDataElement] postDataElement - cdef py_string pyElement + cdef bytes pyElement cdef CefString sfile if type(pyPostData) == list: for pyElement in pyPostData: - if pyElement.startswith('--'): + if pyElement.startswith(b'--'): postDataElement = CefPostDataElement_Create() postDataElement.get().SetToBytes(len(pyElement), pyElement) - elif pyElement.startswith('@'): + elif pyElement.startswith(b'@'): postDataElement = CefPostDataElement_Create() PyToCefString(pyElement[1:], sfile) postDataElement.get().SetToFile(sfile) @@ -156,8 +156,7 @@ cdef class PyRequest: postData.get().AddElement(postDataElement) self.GetCefRequest().get().SetPostData(postData) elif type(pyPostData) == dict: - pyElement = urllib.urlencode(pyPostData) - pyElement = str(pyElement) + pyElement = urllib_urlencode(pyPostData).encode() postDataElement = CefPostDataElement_Create() postDataElement.get().SetToBytes(len(pyElement), pyElement) postData.get().AddElement(postDataElement) diff --git a/src/string_utils.pyx b/src/string_utils.pyx index 3129b1a3..c5980b94 100644 --- a/src/string_utils.pyx +++ b/src/string_utils.pyx @@ -94,6 +94,10 @@ cdef py_string CefToPyString( g_applicationSettings["string_encoding"], errors=BYTES_DECODE_ERRORS)) +cdef bytes CefToPyBytes( + ConstCefString& cefString): + return cefString.ToString() + cdef void PyToCefString( py_string pyString, CefString& cefString @@ -148,3 +152,7 @@ cdef py_string VoidPtrToString(const void* data, size_t dataLength): return (((data)[:dataLength]).decode( g_applicationSettings["string_encoding"], errors=BYTES_DECODE_ERRORS)) + +cdef bytes VoidPtrToBytes(const void* data, size_t dataLength): + return ((data)[:dataLength]) + diff --git a/unittests/main_test.py b/unittests/main_test.py index ecc0f9c3..766e3552 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -11,6 +11,7 @@ from cefpython3 import cefpython as cef import time import base64 +import os import sys # To show the window for an extended period of time increase this number. @@ -159,6 +160,26 @@ def test_main(self): browser.SetJavascriptBindings(bindings) subtest_message("browser.SetJavascriptBindings() ok") + # Test Request.SetPostData(list) + req = cef.Request.CreateRequest() + req_file = os.path.dirname(os.path.abspath(__file__)) + req_file = os.path.join(req_file, "main_test.py") + if sys.version_info.major > 2: + req_file = req_file.encode() + req_data = [b"--key=value", b"@"+req_file] + req.SetMethod("POST") + req.SetPostData(req_data) + self.assertEqual(req_data, req.GetPostData()) + subtest_message("cef.Request.SetPostData(list) ok") + + # Test Request.SetPostData(dict) + req = cef.Request.CreateRequest() + req_data = {b"key": b"value"} + req.SetMethod("POST") + req.SetPostData(req_data) + self.assertEqual(req_data, req.GetPostData()) + subtest_message("cef.Request.SetPostData(dict) ok") + # Run message loop for some time. # noinspection PyTypeChecker for i in range(MESSAGE_LOOP_RANGE): From e0bdca3d246daeff8aa1835bef87efe2564a6c16 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 14 Aug 2018 10:18:47 +0200 Subject: [PATCH 038/177] Support for Visual Studio 2008 build tools in automate.py. Previously only Visual C++ for Python 2.7 was supported. --- tools/build.py | 6 ++++++ tools/common.py | 24 +++++++++++------------- tools/cython_setup.py | 4 ++++ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/tools/build.py b/tools/build.py index 0f9512ca..4ad74015 100644 --- a/tools/build.py +++ b/tools/build.py @@ -469,6 +469,12 @@ def build_vcproj_DEPRECATED(vcproj): # msbuild /p:BuildProjectReferences=false project.proj # MSBuild.exe MyProject.proj /t:build + VS2008_BUILD = ("%LocalAppData%\\Programs\\Common\\" + "Microsoft\\Visual C++ for Python\\9.0\\" + "VC\\bin\\amd64\\vcbuild.exe") + VS2008_BUILD = VS2008_BUILD.replace("%LocalAppData%", + os.environ["LOCALAPPDATA"]) + if PYVERSION == "27": args = list() args.append(VS2008_VCVARS) diff --git a/tools/common.py b/tools/common.py index ab51af8a..d4336b03 100644 --- a/tools/common.py +++ b/tools/common.py @@ -191,30 +191,28 @@ SUBPROCESS_EXE = os.path.join(BUILD_SUBPROCESS, "subprocess" + EXECUTABLE_EXT) -# Visual Studio constants +# These Visual Studio constants are used by automate.py tool +# to build upstream C++ projects. CEF Python C++ code is built +# with setuptools/distutils in the build_cpp_projects.py tool. +# ----------------------------------------------------------------------------- + VS_PLATFORM_ARG = "x86" if ARCH32 else "amd64" VS2015_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0" "\\VC\\vcvarsall.bat") -# For CEF build VS2013_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 12.0" "\\VC\\vcvarsall.bat") -# VS2010 vcvarsall not used, using detection with setuptools instead VS2010_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0" "\\VC\\vcvarsall.bat") -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") -if "LOCALAPPDATA" in os.environ: - VS2008_VCVARS = VS2008_VCVARS.replace("%LocalAppData%", - os.environ["LOCALAPPDATA"]) - VS2008_BUILD = VS2008_BUILD.replace("%LocalAppData%", - os.environ["LOCALAPPDATA"]) +VS2008_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 9.0" + "\\VC\\vcvarsall.bat") + +if not os.path.exists(VS2008_VCVARS): + VS2008_VCVARS = (os.environ["LOCALAPPDATA"]+"\\Programs\\Common\\Microsoft" + "\\Visual C++ for Python\\9.0\\vcvarsall.bat") # ----------------------------------------------------------------------------- diff --git a/tools/cython_setup.py b/tools/cython_setup.py index 0d2cb5b7..e033cf84 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -127,11 +127,15 @@ def get_winsdk_lib(): winsdk_libs = [ r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Lib", r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0\\Lib", + # Visual Studio 2008 installation + r"C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\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", + # Visual Studio 2008 installation + r"C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib\\x64", ] else: raise Exception("Unknown architecture") From cc5695711c7a28600eb2ebeb6206944924aa6ab6 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 14:43:13 +0200 Subject: [PATCH 039/177] Fix common.py in tools --- tools/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/common.py b/tools/common.py index d4336b03..da24e8f6 100644 --- a/tools/common.py +++ b/tools/common.py @@ -210,7 +210,7 @@ VS2008_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 9.0" "\\VC\\vcvarsall.bat") -if not os.path.exists(VS2008_VCVARS): +if WINDOWS and not os.path.exists(VS2008_VCVARS): VS2008_VCVARS = (os.environ["LOCALAPPDATA"]+"\\Programs\\Common\\Microsoft" "\\Visual C++ for Python\\9.0\\vcvarsall.bat") From 8a20fdb0881322964e7db3de5b7a2a5661731ff8 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 15:25:01 +0200 Subject: [PATCH 040/177] Fix and handle more keyboard shortcuts on Mac (#161) Handle: copy, paste, cut, undo, redo, select all. --- src/handlers/keyboard_handler.pyx | 67 ++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/src/handlers/keyboard_handler.pyx b/src/handlers/keyboard_handler.pyx index c895b9b5..30c04e2f 100644 --- a/src/handlers/keyboard_handler.pyx +++ b/src/handlers/keyboard_handler.pyx @@ -3,6 +3,7 @@ # Project website: https://github.com/cztomczak/cefpython include "../cefpython.pyx" +include "../browser.pyx" # noinspection PyUnresolvedReferences cimport cef_types @@ -72,39 +73,67 @@ cdef public cpp_bool KeyboardHandler_OnPreKeyEvent( (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) + +cdef py_bool HandleKeyboardShortcuts(PyBrowser browser, dict event): + """Default implementation for keyboard shortcuts on Mac (Issue #161).""" + if platform.system() == "Darwin": + if event["modifiers"] == 128 \ + and event["type"] != KEYEVENT_RAWKEYDOWN: + # Copy and paste was handled in RAWKEYDOWN, return True + if event["native_key_code"] in [8, 9]: + return True + if event["modifiers"] == 128 \ + and event["type"] == KEYEVENT_RAWKEYDOWN: + # Select all: command + a + if event["native_key_code"] == 0: + browser.GetMainFrame().SelectAll() + return True + # Copy: command + c + elif event["native_key_code"] == 8: + browser.GetMainFrame().Copy() + return True + # Paste: command + v + elif event["native_key_code"] == 9: + browser.GetMainFrame().Paste() + return True + # Cut: command + x + elif event["native_key_code"] == 7: + browser.GetMainFrame().Cut() + return True + # Undo: command + z + elif event["native_key_code"] == 6: + browser.GetMainFrame().Undo() + return True + elif event["modifiers"] == 130 \ + and event["type"] == KEYEVENT_RAWKEYDOWN: + # Redo: command + shift + z + if event["native_key_code"] == 6: + browser.GetMainFrame().Redo() + return True + return False + cdef public cpp_bool KeyboardHandler_OnKeyEvent( CefRefPtr[CefBrowser] cefBrowser, const cef_types.CefKeyEvent& cefEvent, cef_types.CefEventHandle cefEventHandle ) except * with gil: - cdef PyBrowser pyBrowser - cdef dict pyEvent + cdef PyBrowser browser + cdef dict event cdef py_bool returnValue cdef object callback try: - pyBrowser = GetPyBrowser(cefBrowser, "OnKeyEvent") - pyEvent = CefToPyKeyEvent(cefEvent) - callback = pyBrowser.GetClientCallback("OnKeyEvent") + browser = GetPyBrowser(cefBrowser, "OnKeyEvent") + event = CefToPyKeyEvent(cefEvent) + callback = browser.GetClientCallback("OnKeyEvent") if callback: returnValue = callback( - browser=pyBrowser, - event=pyEvent, + browser=browser, + event=event, event_handle=PyLong_FromVoidPtr(cefEventHandle)) # If returnValue is False then handle copy/paste on Mac if returnValue: return bool(returnValue) - if platform.system() == "Darwin": - # Handle copy: command + c - if pyEvent["modifiers"] == 128 \ - and pyEvent["native_key_code"] == 8: - pyBrowser.GetFocusedFrame().Copy() - return True - # Handle paste: command + v - elif pyEvent["modifiers"] == 128 \ - and pyEvent["native_key_code"] == 9: - pyBrowser.GetFocusedFrame().Paste() - return True - return False + return bool(HandleKeyboardShortcuts(browser, event)) except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) From e6bcf2c3fdf5e34ab7781a2af38ebb517e36695e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 16:59:45 +0200 Subject: [PATCH 041/177] Fix & enhance HighDPI support on Windows in wxPython example (#398, #399) Fix DpiAware.GetSystemDpi, CalculateWindowSize (deprecated now). Add DpiAware.Scale. --- README.md | 109 ++++++++++++++++++----------------- api/API-index.md | 1 + api/DpiAware.md | 30 ++++++++-- examples/tkinter_.py | 2 +- examples/wxpython.py | 39 +++++++++++-- src/dpi_aware_win.pyx | 51 +++++++++++----- src/extern/dpi_aware_win.pxd | 1 - src/extern/windows.pxd | 2 + 8 files changed, 156 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index de38333b..e2883112 100644 --- a/README.md +++ b/README.md @@ -256,60 +256,60 @@ Additional information for v31.2 release: ### API categories -#### Modules - - * [cefpython](api/cefpython.md#cefpython) module - - -#### Settings - - * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary - * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary - * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary - - -#### Classes and objects - - * [Browser](api/Browser.md#browser-object) object - * [Callback](api/Callback.md#callback-object) object - * [Cookie](api/Cookie.md#cookie-class) class - * [CookieManager](api/CookieManager.md#cookiemanager-class) class - * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) - * [DragData](api/DragData.md#dragdata-object) object - * [Frame](api/Frame.md#frame-object) object - * [Image](api/Image.md#image-object) object - * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class - * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object - * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object - * [Request](api/Request.md#request-class) class - * [Response](api/Response.md#response-object) object - * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object - * [WebRequest](api/WebRequest.md#webrequest-class) class - * [WindowInfo](api/WindowInfo.md#windowinfo-class) class - * [WindowUtils](api/WindowUtils.md#windowutils-class) class - - -#### Client handlers (interfaces) - - * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) - * [DownloadHandler](api/DownloadHandler.md#downloadhandler) - * [FocusHandler](api/FocusHandler.md#focushandler-interface) - * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) - * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) - * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) - * [LoadHandler](api/LoadHandler.md#loadhandler-interface) - * [RenderHandler](api/RenderHandler.md#renderhandler-interface) - * [RequestHandler](api/RequestHandler.md#requesthandler-interface) - * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) - * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) - - -#### Other interfaces - - * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface - * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface - * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface - +#### Modules + + * [cefpython](api/cefpython.md#cefpython) module + + +#### Settings + + * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary + * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary + * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary + + +#### Classes and objects + + * [Browser](api/Browser.md#browser-object) object + * [Callback](api/Callback.md#callback-object) object + * [Cookie](api/Cookie.md#cookie-class) class + * [CookieManager](api/CookieManager.md#cookiemanager-class) class + * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) + * [DragData](api/DragData.md#dragdata-object) object + * [Frame](api/Frame.md#frame-object) object + * [Image](api/Image.md#image-object) object + * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class + * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object + * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object + * [Request](api/Request.md#request-class) class + * [Response](api/Response.md#response-object) object + * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object + * [WebRequest](api/WebRequest.md#webrequest-class) class + * [WindowInfo](api/WindowInfo.md#windowinfo-class) class + * [WindowUtils](api/WindowUtils.md#windowutils-class) class + + +#### Client handlers (interfaces) + + * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) + * [DownloadHandler](api/DownloadHandler.md#downloadhandler) + * [FocusHandler](api/FocusHandler.md#focushandler-interface) + * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) + * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) + * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) + * [LoadHandler](api/LoadHandler.md#loadhandler-interface) + * [RenderHandler](api/RenderHandler.md#renderhandler-interface) + * [RequestHandler](api/RequestHandler.md#requesthandler-interface) + * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) + * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) + + +#### Other interfaces + + * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface + * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface + * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface + ### API index @@ -524,6 +524,7 @@ Additional information for v31.2 release: * [GetSystemDpi](api/DpiAware.md#getsystemdpi) * [IsProcessDpiAware](api/DpiAware.md#isprocessdpiaware) * [SetProcessDpiAware](api/DpiAware.md#setprocessdpiaware) + * [Scale](api/DpiAware.md#scale) * [DragData (object)](api/DragData.md#dragdata-object) * [IsLink](api/DragData.md#islink) * [IsFragment](api/DragData.md#isfragment) diff --git a/api/API-index.md b/api/API-index.md index a6f81ba0..74971115 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -213,6 +213,7 @@ * [GetSystemDpi](DpiAware.md#getsystemdpi) * [IsProcessDpiAware](DpiAware.md#isprocessdpiaware) * [SetProcessDpiAware](DpiAware.md#setprocessdpiaware) + * [Scale](DpiAware.md#scale) * [DragData (object)](DragData.md#dragdata-object) * [IsLink](DragData.md#islink) * [IsFragment](DragData.md#isfragment) diff --git a/api/DpiAware.md b/api/DpiAware.md index a86d85d3..5a335b21 100644 --- a/api/DpiAware.md +++ b/api/DpiAware.md @@ -16,6 +16,7 @@ Table of contents: * [GetSystemDpi](#getsystemdpi) * [IsProcessDpiAware](#isprocessdpiaware) * [SetProcessDpiAware](#setprocessdpiaware) + * [Scale](#scale) @@ -37,13 +38,13 @@ Enabling High DPI support in app can be done by embedding a DPI awareness xml ma | height | int | | __Return__ | tuple | +Deprecated. Use `Scale()` method instead which can handle +non standard DPI settings such as '132%' on Windows 10. + 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. -Calculation for DPI < 96 is not yet supported. Use -the `GetSystemDpi` method for that. - ### EnableHighDpiSupport @@ -68,20 +69,29 @@ Description from upstream CEF: Returns tuple(int dpix, int dpiy). +Returns Windows DPI settings ("Custom scaling" on Win10). + Win7 DPI (Control Panel > Appearance and Personalization > Display): * text size Larger 150% => dpix/dpiy 144 * text size Medium 125% => dpix/dpiy 120 * text size Smaller 100% => dpix/dpiy 96 -Example zoom levels based on DPI. For use with the ApplicationSettings.`auto_zooming` option. +Example zoom levels based on DPI. For use with the +ApplicationSettings.`auto_zooming` option. * dpix=96 zoomlevel=0.0 * dpix=120 zoomlevel=1.0 * dpix=144 zoomlevel=2.0 * dpix=72 zoomlevel=-1.0 -If DPI awareness wasn't yet enabled, then `GetSystemDpi` will always return a default 96 DPI. +If DPI awareness wasn't yet enabled, then `GetSystemDpi` will always +return a default 96 DPI. + +DPI settings should not be cached. When `SetProcessDpiAware` +is not yet called, then OS returns 96 DPI, even though it +is set to 144 DPI. After DPI Awareness is enabled for the +running process it will return the correct 144 DPI. ### IsProcessDpiAware @@ -106,3 +116,13 @@ See [Issue #358](../../../issues/358) for how the behavior changed in latest CEF. This method now internally calls `EnableHighDpiSupport()`. Enables DPI awareness for the running process. Embedding a DPI manifest in .exe is the prefered way, as it gives more reliable results, otherwise some display bugs may appear (discussed in the "Introduction" section on this page). + + +### Scale + +| Parameter | Type | +| --- | --- | +| size | int/tuple/list | +| __Return__ | tuple | + +Scale units for high DPI devices. diff --git a/examples/tkinter_.py b/examples/tkinter_.py index 024e0feb..e3d01c9e 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -67,7 +67,7 @@ def __init__(self, root): self.navigation_bar = None # Root - root.geometry("800x600") + root.geometry("900x640") tk.Grid.rowconfigure(root, 0, weight=1) tk.Grid.columnconfigure(root, 0, weight=1) diff --git a/examples/wxpython.py b/examples/wxpython.py index 91adea2d..1f63ab97 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -33,8 +33,8 @@ sys.exit(1) # Configuration -WIDTH = 800 -HEIGHT = 600 +WIDTH = 900 +HEIGHT = 640 # Globals g_count_windows = 0 @@ -71,11 +71,26 @@ def check_versions(): assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" +def scale_window_size_for_high_dpi(width, height): + """Scale window size for high DPI devices. This func can be + called on all operating systems, but scales only for Windows. + If scaled value is bigger than the work area on the display + then it will be reduced.""" + if not WINDOWS: + return width, height + (_, _, max_width, max_height) = wx.GetClientDisplayRect().Get() + # noinspection PyUnresolvedReferences, PyArgumentList + (width, height) = cef.DpiAware.Scale((width, height)) + if width > max_width: + width = max_width + if height > max_height: + height = max_height + return width, height + + 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 # Must ignore X11 errors like 'BadWindow' and others by @@ -87,6 +102,22 @@ def __init__(self): global g_count_windows g_count_windows += 1 + # noinspection PyUnresolvedReferences, PyArgumentList + print("[wxpython.py] System DPI settings: %s" + % str(cef.DpiAware.GetSystemDpi())) + print("[wxpython.py] wx.GetDisplayPPI = %s" % wx.GetDisplayPPI()) + print("[wxpython.py] wx.GetDisplaySize = %s" % wx.GetDisplaySize()) + + print("[wxpython.py] MainFrame declared size: %s" % str((WIDTH, HEIGHT))) + size = scale_window_size_for_high_dpi(WIDTH, HEIGHT) + print("[wxpython.py] MainFrame DPI scaled size: %s" % str(size)) + + wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, + title='wxPython example', size=size) + # wxPython will set a smaller size when it is bigger + # than desktop size. + print("[wxpython.py] MainFrame actual size: %s" % self.GetSize()) + self.setup_icon() self.create_menu() self.Bind(wx.EVT_CLOSE, self.OnClose) diff --git a/src/dpi_aware_win.pyx b/src/dpi_aware_win.pyx index 4a02184c..b6ed169c 100644 --- a/src/dpi_aware_win.pyx +++ b/src/dpi_aware_win.pyx @@ -8,28 +8,51 @@ class DpiAware: @staticmethod def GetSystemDpi(): - # Win7 DPI (Control Panel > Appearance and Personalization > Display): - # text size Larger 150% => dpix/dpiy 144 - # text size Medium 125% => dpix/dpiy 120 - # text size Smaller 100% => dpix/dpiy 96 - # - # dpix=96 zoomlevel=0.0 - # dpix=120 zoomlevel=1.0 - # dpix=144 zoomlevel=2.0 - # dpix=72 zoomlevel=-1.0 - # - # If DPI awareness wasn't yet enabled, then GetSystemDpi - # will always return a default 96 DPI. + """Returns Windows DPI settings ("Custom scaling" on Win10). + + Win7 DPI (Control Panel > Appearance and Personalization > Display): + text size Larger 150% => dpix/dpiy 144 + text size Medium 125% => dpix/dpiy 120 + text size Smaller 100% => dpix/dpiy 96 + + DPI settings should not be cached. When SetProcessDpiAware + is not yet called, then OS returns 96 DPI, even though it + is set to 144 DPI. After DPI Awareness is enabled for the + running process it will return the correct 144 DPI. + """ cdef int dpix = 0 cdef int dpiy = 0 GetSystemDpi(&dpix, &dpiy) - return tuple(dpix, dpiy) + return dpix, dpiy @staticmethod def CalculateWindowSize(int width, int height): + """@DEPRECATED. Use Scale() method instead.""" # Calculation for DPI < 96 is not yet supported. GetDpiAwareWindowSize(&width, &height) - return tuple(width, height) + return width, height + + @staticmethod + def Scale(arg): + """Scale units for high DPI devices. Argument can be an int, + tuple or list.""" + (dpix, dpiy) = DpiAware.GetSystemDpi() + # - Using only "dpix" value to calculate zoom level since all + # modern displays have equal horizontal and vertical resolution. + default_dpix = 96 + scale = MulDiv(dpix, 100, default_dpix) + if isinstance(arg, (int, long)): + v = arg + new_value = MulDiv(v, scale, 100) + return new_value + ret_tuple = isinstance(arg, tuple) + ret = list() + for i,v in enumerate(arg): + v = int(v) + ret.append(MulDiv(v, scale, 100)) + if ret_tuple: + return tuple(ret) + return ret @staticmethod def IsProcessDpiAware(): diff --git a/src/extern/dpi_aware_win.pxd b/src/extern/dpi_aware_win.pxd index 3842062a..fd9a3cbe 100644 --- a/src/extern/dpi_aware_win.pxd +++ b/src/extern/dpi_aware_win.pxd @@ -9,4 +9,3 @@ cdef extern from "client_handler/dpi_aware.h": cdef void GetDpiAwareWindowSize(int* width, int* height) cdef void SetProcessDpiAware() cdef cpp_bool IsProcessDpiAware() - diff --git a/src/extern/windows.pxd b/src/extern/windows.pxd index b6e1c772..5e329b2a 100644 --- a/src/extern/windows.pxd +++ b/src/extern/windows.pxd @@ -128,3 +128,5 @@ cdef extern from "Windows.h" nogil: cdef int ICON_SMALL cdef HWND GetParent(HWND hwnd) + cdef int MulDiv(int number, int numerator, int denominator) + From c0a80111655e05864f8fb441efee01bfc751f039 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 17:37:40 +0200 Subject: [PATCH 042/177] Expose DragData image APIs on all platforms (#251) --- api/Browser.md | 2 +- src/cefpython.pyx | 6 +----- src/drag_data.pyx | 30 +++++++++++++----------------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/api/Browser.md b/api/Browser.md index 47b3752b..768adeca 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -427,7 +427,7 @@ Returns the names of all existing frames. This list does not include the main fr | --- | --- | | __Return__ | tuple(bytes buffer, int width, int height) | -Currently works only on Linux (Issue [#427](../../../issues/427)). +Currently available only on Linux (Issue [#427](../../../issues/427)). Get browser contents as image. Only screen visible contents are returned. diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 493a53d9..2ed752c5 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -368,11 +368,7 @@ include "command_line.pyx" include "app.pyx" include "drag_data.pyx" include "helpers.pyx" - -# Currently used only on Linux via DragData. Do not include on other -# platforms otherwise warning about unused function appears. -IF UNAME_SYSNAME == "Linux": - include "image.pyx" +include "image.pyx" # Handlers include "handlers/browser_process_handler.pyx" diff --git a/src/drag_data.pyx b/src/drag_data.pyx index ddb7d9f9..5da99a6f 100644 --- a/src/drag_data.pyx +++ b/src/drag_data.pyx @@ -36,20 +36,16 @@ cdef class DragData: cpdef py_string GetFragmentHtml(self): return CefToPyString(self.cef_drag_data.get().GetFragmentHtml()) - 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 tuple GetImageHotspot(self): - cdef CefPoint point = self.cef_drag_data.get().GetImageHotspot() - return (point.x, point.y) - - cpdef py_bool HasImage(self): - return self.cef_drag_data.get().HasImage() - - # END 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 tuple GetImageHotspot(self): + cdef CefPoint point = self.cef_drag_data.get().GetImageHotspot() + return point.x, point.y + + cpdef py_bool HasImage(self): + return self.cef_drag_data.get().HasImage() From 8ade567bcc992b909747cc1e9c7c346a39d6dee4 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 17:54:09 +0200 Subject: [PATCH 043/177] Do not auto run tkinter_.py example on Windows with Python 2.7 (#441) --- tools/run_examples.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/run_examples.py b/tools/run_examples.py index 5ec43fcb..a3a4ce84 100644 --- a/tools/run_examples.py +++ b/tools/run_examples.py @@ -103,6 +103,9 @@ def main(): # This example often crashes on Mac (Issue #309) print("[run_examples.py] PASS: tkinter_.py (Issue #309)") passed.append("tkinter_.py (Issue #309)") + elif WINDOWS and sys.version_info.major == 2: + print("[run_examples.py] PASS: tkinter_.py (Issue #441)") + passed.append("tkinter_.py (Issue #441)") elif packages["tkinter"] or packages["Tkinter"]: examples.append("tkinter_.py") else: From 84502b5881639215669d729f1116e1d8c0015fe2 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 18:00:57 +0200 Subject: [PATCH 044/177] Fix wxpython.py example on non-Windows platforms --- examples/wxpython.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/wxpython.py b/examples/wxpython.py index 1f63ab97..50f585fb 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -103,8 +103,9 @@ def __init__(self): g_count_windows += 1 # noinspection PyUnresolvedReferences, PyArgumentList - print("[wxpython.py] System DPI settings: %s" - % str(cef.DpiAware.GetSystemDpi())) + if WINDOWS: + print("[wxpython.py] System DPI settings: %s" + % str(cef.DpiAware.GetSystemDpi())) print("[wxpython.py] wx.GetDisplayPPI = %s" % wx.GetDisplayPPI()) print("[wxpython.py] wx.GetDisplaySize = %s" % wx.GetDisplaySize()) From a1c1981c296dcdc972b7e988e521b3a860949d21 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Aug 2018 21:44:19 +0200 Subject: [PATCH 045/177] Fix wxpython.py example on Linux with wxPython 2.8 --- examples/wxpython.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/wxpython.py b/examples/wxpython.py index 50f585fb..71ab0372 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -106,7 +106,8 @@ def __init__(self): if WINDOWS: print("[wxpython.py] System DPI settings: %s" % str(cef.DpiAware.GetSystemDpi())) - print("[wxpython.py] wx.GetDisplayPPI = %s" % wx.GetDisplayPPI()) + if hasattr(wx, "GetDisplayPPI"): + print("[wxpython.py] wx.GetDisplayPPI = %s" % wx.GetDisplayPPI()) print("[wxpython.py] wx.GetDisplaySize = %s" % wx.GetDisplaySize()) print("[wxpython.py] MainFrame declared size: %s" % str((WIDTH, HEIGHT))) From 89587eb052820959b40c4a7bcfd7d059c2180e06 Mon Sep 17 00:00:00 2001 From: Elliot Woods Date: Fri, 17 Aug 2018 11:36:19 +0900 Subject: [PATCH 046/177] Update README-examples.md add mention of method for passing exceptions from python to js --- examples/README-examples.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README-examples.md b/examples/README-examples.md index 8517e4ef..2135cea6 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -90,6 +90,7 @@ yet ported to latest CEF. Some of them are externally maintained. example in the cefpython31 branch. - Example of using Python network library (urllib3/openssl) instead of Chromium's network library - see [gist by Massimiliano Dal Cero](https://gist.github.com/yattamax/0252a3c5dc54a2f81650d5c0eafabf99) +- Example of passing exceptions from Python to Javascript and using await syntax to receive values from python return values - see [Managed python calls example by Elliot Woods)(https://github.com/elliotwoods/cefpython-tests/tree/0180b22eac10a1bde08820ca192fdc30eb93f00d/6.%20Managed%20python%20calls) ## More examples to come From 6a5b683bd697bd4d61787df10de1e5abb634fd4d Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Fri, 17 Aug 2018 10:42:58 +0200 Subject: [PATCH 047/177] Update README-examples.md Fix link --- examples/README-examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README-examples.md b/examples/README-examples.md index 2135cea6..179b0d69 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -90,7 +90,7 @@ yet ported to latest CEF. Some of them are externally maintained. example in the cefpython31 branch. - Example of using Python network library (urllib3/openssl) instead of Chromium's network library - see [gist by Massimiliano Dal Cero](https://gist.github.com/yattamax/0252a3c5dc54a2f81650d5c0eafabf99) -- Example of passing exceptions from Python to Javascript and using await syntax to receive values from python return values - see [Managed python calls example by Elliot Woods)(https://github.com/elliotwoods/cefpython-tests/tree/0180b22eac10a1bde08820ca192fdc30eb93f00d/6.%20Managed%20python%20calls) +- Example of passing exceptions from Python to Javascript and using await syntax to receive values from python return values - see [Managed python calls example by Elliot Woods](https://github.com/elliotwoods/cefpython-tests/tree/0180b22eac10a1bde08820ca192fdc30eb93f00d/6.%20Managed%20python%20calls) ## More examples to come From f64dff531bf389d21a5215c3bbd10ddbbe9b84c1 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Aug 2018 14:18:32 +0200 Subject: [PATCH 048/177] Update README-examples.md and wxpython.py example --- examples/README-examples.md | 5 ++--- examples/wxpython.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/README-examples.md b/examples/README-examples.md index 8517e4ef..f1477878 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -37,7 +37,6 @@ workarounds. discussed in great details in Tutorial in the [Off-screen rendering](../docs/Tutorial.md#off-screen-rendering) section. - **Embedding using various GUI frameworks** - [gtk2.py](gtk2.py): example for [PyGTK](http://www.pygtk.org/) @@ -55,14 +54,14 @@ workarounds. - [tkinter_.py](tkinter_.py): example for [Tkinter](https://wiki.python.org/moin/TkInter). Currently broken on Mac ([#309](../../../issues/309)). - [wxpython.py](wxpython.py): example for [wxPython](https://wxpython.org/) - toolkit + toolkit. This example implements High DPI support on Windows. **Build executable using Python packagers** - [PyInstaller example](pyinstaller/README-pyinstaller.md): example of packaging app using [PyInstaller](http://www.pyinstaller.org/) - packager + packager (currently example supports only Windows platform) **Unit tests** diff --git a/examples/wxpython.py b/examples/wxpython.py index 71ab0372..c42d3fe7 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -5,7 +5,7 @@ # - wxPython 4.0 on Windows/Mac/Linux # - wxPython 3.0 on Windows/Mac # - wxPython 2.8 on Linux -# - CEF Python v55.4+ +# - CEF Python v66.0+ import wx from cefpython3 import cefpython as cef @@ -68,7 +68,7 @@ def check_versions(): ver=platform.python_version(), arch=platform.architecture()[0])) print("[wxpython.py] wxPython {ver}".format(ver=wx.version())) # CEF Python version requirement - assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" + assert cef.__version__ >= "66.0", "CEF Python v66.0+ required to run this" def scale_window_size_for_high_dpi(width, height): From cf0761e9fc8cfa16c1184c61ff07347177874297 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Aug 2018 14:41:50 +0200 Subject: [PATCH 049/177] Fix style issues in wxpython.py --- examples/wxpython.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/wxpython.py b/examples/wxpython.py index c42d3fe7..915f7dff 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -102,15 +102,16 @@ def __init__(self): global g_count_windows g_count_windows += 1 - # noinspection PyUnresolvedReferences, PyArgumentList if WINDOWS: + # noinspection PyUnresolvedReferences, PyArgumentList print("[wxpython.py] System DPI settings: %s" % str(cef.DpiAware.GetSystemDpi())) if hasattr(wx, "GetDisplayPPI"): print("[wxpython.py] wx.GetDisplayPPI = %s" % wx.GetDisplayPPI()) print("[wxpython.py] wx.GetDisplaySize = %s" % wx.GetDisplaySize()) - print("[wxpython.py] MainFrame declared size: %s" % str((WIDTH, HEIGHT))) + print("[wxpython.py] MainFrame declared size: %s" + % str((WIDTH, HEIGHT))) size = scale_window_size_for_high_dpi(WIDTH, HEIGHT) print("[wxpython.py] MainFrame DPI scaled size: %s" % str(size)) From a010e6819e935888e6d70e3194544fb0f3c99f72 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Aug 2018 17:40:46 +0200 Subject: [PATCH 050/177] Fix errors due to NULL frames sent from Renderer process (#431). Remove Python callback references properly. Fix PyCharm code inspection warnings in main_test.py. --- src/client_handler/client_handler.cpp | 33 +++++++++-------- src/frame.pyx | 3 ++ src/handlers/v8context_handler.pyx | 15 ++++---- src/handlers/v8function_handler.pyx | 52 ++++++++++++++------------- src/python_callback.pyx | 40 ++++++++++----------- src/subprocess/cefpython_app.cpp | 11 ++---- unittests/main_test.py | 6 +++- 7 files changed, 81 insertions(+), 79 deletions(-) diff --git a/src/client_handler/client_handler.cpp b/src/client_handler/client_handler.cpp index f55da812..daf9a4e5 100644 --- a/src/client_handler/client_handler.cpp +++ b/src/client_handler/client_handler.cpp @@ -30,6 +30,7 @@ bool ClientHandler::OnProcessMessageReceived( CefProcessId source_process, CefRefPtr message) { + // Return true if message was handled. if (source_process != PID_RENDERER) { return false; } @@ -42,6 +43,12 @@ bool ClientHandler::OnProcessMessageReceived( if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) { int64 frameId = arguments->GetInt(0); CefRefPtr frame = browser->GetFrame(frameId); + if (!frame.get()) { + // Frame was already destroyed while IPC messaging was + // executing. Issue #431. User callback will not be + // executed in such case. + return true; + } V8ContextHandler_OnContextCreated(browser, frame); return true; } else { @@ -56,6 +63,11 @@ bool ClientHandler::OnProcessMessageReceived( && arguments->GetType(1) == VTYPE_INT) { int browserId = arguments->GetInt(0); int64 frameId = arguments->GetInt(1); + // Even if frame was alrady destroyed (Issue #431) you still + // want to call V8ContextHandler_OnContextReleased as it releases + // some resources. Thus passing IDs instead of actual + // objects. Cython code in V8ContextHandler_OnContextReleased + // will handle a case when frame is already destroyed. V8ContextHandler_OnContextReleased(browserId, frameId); return true; } else { @@ -75,8 +87,13 @@ bool ClientHandler::OnProcessMessageReceived( int64 frameId = arguments->GetInt(0); CefString functionName = arguments->GetString(1); CefRefPtr functionArguments = arguments->GetList(2); - CefRefPtr frame = browser->GetFrame(frameId); - V8FunctionHandler_Execute(browser, frame, functionName, + // Even if frame was already destroyed (Issue #431) you still + // want to call V8FunctionHandler_Execute, as it can run + // Python code without issues and doesn't require an actual + // frame. Thus passing IDs instead of actual objects. Cython + // code in V8FunctionHandler_Execute will handle a case when + // frame is already destroyed. + V8FunctionHandler_Execute(browser, frameId, functionName, functionArguments); return true; } else { @@ -100,18 +117,6 @@ bool ClientHandler::OnProcessMessageReceived( " messageName=ExecutePythonCallback"; return false; } - } else if (messageName == "RemovePythonCallbacksForFrame") { - CefRefPtr arguments = message->GetArgumentList(); - if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) { - int frameId = arguments->GetInt(0); - RemovePythonCallbacksForFrame(frameId); - return true; - } else { - LOG(ERROR) << "[Browser process] OnProcessMessageReceived():" - " invalid arguments," - " messageName=ExecutePythonCallback"; - return false; - } } return false; } diff --git a/src/frame.pyx b/src/frame.pyx index 6ff7873c..ec3ef8bc 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -93,6 +93,7 @@ cdef void RemovePyFrame(int browserId, object frameId) except *: del pyFrame del g_pyFrames[uniqueFrameId] g_unreferenced_frames.append(uniqueFrameId) + RemovePythonCallbacksForFrame(frameId) else: Debug("RemovePyFrame() FAILED: uniqueFrameId = %s" % uniqueFrameId) @@ -112,6 +113,8 @@ cdef void RemovePyFramesForBrowser(int browserId) except *: del pyFrame del g_pyFrames[uniqueFrameId] g_unreferenced_frames.append(uniqueFrameId) + # RemovePythonCallbacksForBrowser already called + # in LifespanHandler_OnBeforeClose. cdef class PyFrame: cdef CefRefPtr[CefFrame] cefFrame diff --git a/src/handlers/v8context_handler.pyx b/src/handlers/v8context_handler.pyx index 704f6ad9..fccdad56 100644 --- a/src/handlers/v8context_handler.pyx +++ b/src/handlers/v8context_handler.pyx @@ -10,6 +10,7 @@ include "../cefpython.pyx" include "../browser.pyx" +include "../frame.pyx" cdef public void V8ContextHandler_OnContextCreated( CefRefPtr[CefBrowser] cefBrowser, @@ -23,7 +24,7 @@ cdef public void V8ContextHandler_OnContextCreated( pyBrowser = GetPyBrowser(cefBrowser, "OnContextCreated") pyBrowser.SetUserData("__v8ContextCreated", True) pyFrame = GetPyFrame(cefFrame) - # User defined callback. + # User defined callback clientCallback = pyBrowser.GetClientCallback("OnContextCreated") if clientCallback: clientCallback(browser=pyBrowser, frame=pyFrame) @@ -52,19 +53,15 @@ cdef public void V8ContextHandler_OnContextReleased( if not pyBrowser: Debug("OnContextReleased: Browser doesn't exist anymore, id={id}" .format(id=str(browserId))) + RemovePyFrame(browserId, frameId) return pyFrame = GetPyFrameById(browserId, frameId) - if pyBrowser and pyFrame: + # Frame may already be destroyed while IPC messaging was executing + # (Issue #431). + if pyFrame: clientCallback = pyBrowser.GetClientCallback("OnContextReleased") if clientCallback: clientCallback(browser=pyBrowser, frame=pyFrame) - else: - if not pyBrowser: - Debug("V8ContextHandler_OnContextReleased() WARNING: " - "PyBrowser not found") - if not pyFrame: - Debug("V8ContextHandler_OnContextReleased() WARNING: " - "PyFrame not found") RemovePyFrame(browserId, frameId) except: (exc_type, exc_value, exc_trace) = sys.exc_info() diff --git a/src/handlers/v8function_handler.pyx b/src/handlers/v8function_handler.pyx index 2c9b3f1c..ee73619f 100644 --- a/src/handlers/v8function_handler.pyx +++ b/src/handlers/v8function_handler.pyx @@ -4,46 +4,48 @@ include "../cefpython.pyx" include "../browser.pyx" +include "../frame.pyx" cdef public void V8FunctionHandler_Execute( CefRefPtr[CefBrowser] cefBrowser, - CefRefPtr[CefFrame] cefFrame, - CefString& cefFunctionName, - CefRefPtr[CefListValue] cefFunctionArguments + int64 frameId, + CefString& cefFuncName, + CefRefPtr[CefListValue] cefFuncArgs ) except * with gil: cdef PyBrowser pyBrowser - cdef PyFrame pyFrame - cdef py_string functionName - cdef object function - cdef list functionArguments + cdef CefRefPtr[CefFrame] cefFrame + cdef PyFrame pyFrame # may be None + cdef py_string funcName + cdef object func + cdef list funcArgs cdef object returnValue - cdef py_string jsErrorMessage + cdef py_string errorMessage try: pyBrowser = GetPyBrowser(cefBrowser, "V8FunctionHandler_Execute") - pyFrame = GetPyFrame(cefFrame) - functionName = CefToPyString(cefFunctionName) - Debug("V8FunctionHandler_Execute(): functionName=%s" % functionName) + cefFrame = cefBrowser.get().GetFrame(frameId) + if cefFrame.get(): + pyFrame = GetPyFrame(cefFrame) + else: + pyFrame = None + funcName = CefToPyString(cefFuncName) + Debug("V8FunctionHandler_Execute(): funcName=%s" % funcName) jsBindings = pyBrowser.GetJavascriptBindings() - function = jsBindings.GetFunctionOrMethod(functionName) - if not function: + func = jsBindings.GetFunctionOrMethod(funcName) + if not func: # The Renderer process already checks whether function # name is valid before calling V8FunctionHandler_Execute(), # but it is possible for the javascript bindings to change # during execution, so it's possible for the Browser/Renderer # bindings to be out of sync due to delay in process messaging. - jsErrorMessage = "V8FunctionHandler_Execute() FAILED: " \ - "python function not found: %s" % functionName - Debug(jsErrorMessage) - # Raise a javascript exception in that frame. - pyFrame.ExecuteJavascript("throw '%s';" % jsErrorMessage) + errorMessage = "V8FunctionHandler_Execute() FAILED: " \ + "python function not found: %s" % funcName + NonCriticalError(errorMessage) + # Raise a javascript exception in that frame if it still exists + if pyFrame: + pyFrame.ExecuteJavascript("throw '%s';" % errorMessage) return - functionArguments = CefListValueToPyList(cefBrowser, - cefFunctionArguments) - returnValue = function(*functionArguments) - if returnValue is not None: - Debug("V8FunctionHandler_Execute() WARNING: function returned" \ - "value, but returning values to javascript is not " \ - "supported, functionName=%s" % functionName) + funcArgs = CefListValueToPyList(cefBrowser, cefFuncArgs) + func(*funcArgs) except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) diff --git a/src/python_callback.pyx b/src/python_callback.pyx index 705e0f54..c76f7ce3 100644 --- a/src/python_callback.pyx +++ b/src/python_callback.pyx @@ -5,10 +5,12 @@ include "cefpython.pyx" cdef int g_pythonCallbackMaxId = 0 +# [callbackId] = (browserId, frameId, func) cdef dict g_pythonCallbacks = {} # TODO: send callbackId using CefBinaryNamedValue, see: -# http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10881 +# http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10881 + cdef struct PythonCallback: int callbackId char uniqueCefBinaryValueSize[16] @@ -16,7 +18,7 @@ cdef struct PythonCallback: cdef CefRefPtr[CefBinaryValue] PutPythonCallback( object browserId, object frameId, - object function + object func ) except *: global g_pythonCallbacks global g_pythonCallbackMaxId @@ -29,12 +31,11 @@ cdef CefRefPtr[CefBinaryValue] PutPythonCallback( pyCallback.callbackId = g_pythonCallbackMaxId cdef CefRefPtr[CefBinaryValue] binaryValue = CefBinaryValue_Create( &pyCallback, sizeof(pyCallback)) - # [0] browserId, [1] frameId, [2] function. - g_pythonCallbacks[g_pythonCallbackMaxId] = (browserId, frameId, function) + g_pythonCallbacks[g_pythonCallbackMaxId] = (browserId, frameId, func) return binaryValue cdef public void RemovePythonCallbacksForFrame( - int frameId + object frameId ) except * with gil: # Cannot remove elements from g_pythonCallbacks (dict) while iterating. cdef list toRemove = [] @@ -46,8 +47,8 @@ cdef public void RemovePythonCallbacksForFrame( for callbackId in toRemove: del g_pythonCallbacks[callbackId] Debug("RemovePythonCallbacksForFrame(): " \ - "removed python callback, callbackId = %s" \ - % callbackId) + "removed python callback, callbackId = %s" \ + % callbackId) except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) @@ -62,33 +63,28 @@ cdef void RemovePythonCallbacksForBrowser( for callbackId in toRemove: del g_pythonCallbacks[callbackId] Debug("RemovePythonCallbacksForBrowser(): " \ - "removed python callback, callbackId = %s" \ - % callbackId) + "removed python callback, callbackId = %s" \ + % callbackId) cdef public cpp_bool ExecutePythonCallback( CefRefPtr[CefBrowser] cefBrowser, int callbackId, - CefRefPtr[CefListValue] cefFunctionArguments, + CefRefPtr[CefListValue] cefFuncArgs, ) except * with gil: - cdef object function - cdef list functionArguments + cdef object func + cdef list funcArgs cdef object returnValue try: global g_pythonCallbacks if callbackId in g_pythonCallbacks: - # [0] browserId, [1] frameId, [2] function. - function = g_pythonCallbacks[callbackId][2] - functionArguments = CefListValueToPyList( - cefBrowser, cefFunctionArguments) - returnValue = function(*functionArguments) - if returnValue is not None: - Debug("ExecutePythonCallback() WARNING: function returned" \ - "value, but returning values to javascript is not " \ - "supported, function name = %s" % function.__name__) + func = g_pythonCallbacks[callbackId][2] + funcArgs = CefListValueToPyList( + cefBrowser, cefFuncArgs) + func(*funcArgs) return True else: Debug("ExecutePythonCallback() FAILED: callback not found, " \ - "callbackId = %s" % callbackId) + "callbackId = %s" % callbackId) return False except: (exc_type, exc_value, exc_trace) = sys.exc_info() diff --git a/src/subprocess/cefpython_app.cpp b/src/subprocess/cefpython_app.cpp index a02a7cd5..786a919d 100644 --- a/src/subprocess/cefpython_app.cpp +++ b/src/subprocess/cefpython_app.cpp @@ -337,15 +337,10 @@ void CefPythonApp::OnContextReleased(CefRefPtr browser, // ------------------------------------------------------------------------ // 2. Remove python callbacks for a frame. // ------------------------------------------------------------------------ - // If this is the main frame then the message won't arrive - // to the browser process, as browser is being destroyed, - // but it doesn't matter because in LifespanHandler_BeforeClose() + // This is already done via RemovePyFrame called from + // V8ContextHandler_OnContextReleased. + // If this is the main frame then in LifespanHandler_BeforeClose() // we're calling RemovePythonCallbacksForBrowser(). - message = CefProcessMessage::Create("RemovePythonCallbacksForFrame"); - arguments = message->GetArgumentList(); - // TODO: int64 precision lost - arguments->SetInt(0, (int)(frame->GetIdentifier())); - browser->SendProcessMessage(PID_BROWSER, message); // ------------------------------------------------------------------------ // 3. Clear javascript callbacks. // ------------------------------------------------------------------------ diff --git a/unittests/main_test.py b/unittests/main_test.py index 766e3552..b0d16f40 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -154,13 +154,15 @@ def test_main(self): # supports passing functions as callbacks when called from # javascript, and as a side effect any value and in this case # a property can also be a function. - bindings.SetProperty("test_property3_function", external.test_property3_function) + bindings.SetProperty("test_property3_function", + external.test_property3_function) bindings.SetProperty("cefpython_version", cef.GetVersion()) bindings.SetObject("external", external) browser.SetJavascriptBindings(bindings) subtest_message("browser.SetJavascriptBindings() ok") # Test Request.SetPostData(list) + # noinspection PyArgumentList req = cef.Request.CreateRequest() req_file = os.path.dirname(os.path.abspath(__file__)) req_file = os.path.join(req_file, "main_test.py") @@ -173,6 +175,7 @@ def test_main(self): subtest_message("cef.Request.SetPostData(list) ok") # Test Request.SetPostData(dict) + # noinspection PyArgumentList req = cef.Request.CreateRequest() req_data = {b"key": b"value"} req.SetMethod("POST") @@ -199,6 +202,7 @@ def test_main(self): time.sleep(0.01) # Automatic check of asserts in handlers and in external + # noinspection PyTypeChecker for obj in [] + client_handlers + [global_handler, external]: test_for_True = False # Test whether asserts are working correctly for key, value in obj.__dict__.items(): From 66107b258657c06e35319d14d1c860f1379b77a6 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Aug 2018 18:02:05 +0200 Subject: [PATCH 051/177] Fix crash on exit when closing browser immediately (#454) --- src/browser.pyx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/browser.pyx b/src/browser.pyx index 87d8047b..f09c3a57 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -334,6 +334,17 @@ cdef class PyBrowser: # will be called. Debug("CefBrowser::CloseBrowser(%s)" % forceClose) + # Fix Issue #454 "Crash on exit when closing browser + # immediately during initial loading". + if not self.cefBrowser.get(): + Debug("cefBrowser.get() failed in CloseBrowser") + return + # From testing it seems that only cefBrowser.get() can fail, + # however let's check the host as well just to be safe. + if not self.cefBrowser.get().GetHost().get(): + Debug("cefBrowser.get().GetHost() failed in CloseBrowser") + return + # Flush cookies to disk. Temporary solution for Issue #365. # A similar call is made in LifespanHandler_OnBeforeClose. # If using GetCookieManager to implement custom cookie managers From a276fd8538e22fce329144f8d494f09ba45f4e86 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Aug 2018 18:43:10 +0200 Subject: [PATCH 052/177] Add safety checks in request handler to possibly fix crash on exit (#455) --- src/browser.pyx | 14 +++++++ src/handlers/request_handler.pyx | 63 ++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/browser.pyx b/src/browser.pyx index f09c3a57..60194e54 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -33,6 +33,20 @@ cdef PyBrowser GetPyBrowserById(int browserId): return g_pyBrowsers[browserId] return None +cdef py_bool IsBrowserClosed(CefRefPtr[CefBrowser] cefBrowser): + """Possibly fix Issue #455 by using this helper function to detect + if browser is closing/closed.""" + # CefBrowser may sometimes be NULL e.g. Issue #429, Issue #454. + if not cefBrowser.get(): + return True + if not cefBrowser.get().GetHost().get(): + return True + cdef int browserId = cefBrowser.get().GetIdentifier() + if browserId in g_unreferenced_browsers \ + or browserId in g_closed_browsers: + return True + return False + cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser, callerIdStr="GetPyBrowser"): """The second argument 'callerIdStr' is so that a debug diff --git a/src/handlers/request_handler.pyx b/src/handlers/request_handler.pyx index 02bbc15d..d89c5a1b 100644 --- a/src/handlers/request_handler.pyx +++ b/src/handlers/request_handler.pyx @@ -68,6 +68,11 @@ cdef public cpp_bool RequestHandler_OnBeforeBrowse( cdef object clientCallback cdef py_bool returnValue try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return False + pyBrowser = GetPyBrowser(cefBrowser, "OnBeforeBrowse") pyFrame = GetPyFrame(cefFrame) pyRequest = CreatePyRequest(cefRequest) @@ -98,6 +103,11 @@ cdef public cpp_bool RequestHandler_OnBeforeResourceLoad( cdef object clientCallback cdef py_bool returnValue try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return False + pyBrowser = GetPyBrowser(cefBrowser, "OnBeforeResourceLoad") pyFrame = GetPyFrame(cefFrame) pyRequest = CreatePyRequest(cefRequest) @@ -126,6 +136,11 @@ cdef public CefRefPtr[CefResourceHandler] RequestHandler_GetResourceHandler( cdef object clientCallback cdef object returnValue try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return NULL + pyBrowser = GetPyBrowser(cefBrowser, "GetResourceHandler") pyFrame = GetPyFrame(cefFrame) pyRequest = CreatePyRequest(cefRequest) @@ -162,6 +177,11 @@ cdef public void RequestHandler_OnResourceRedirect( cdef PyResponse pyResponse cdef object clientCallback try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return + pyBrowser = GetPyBrowser(cefBrowser, "OnResourceRedirect") pyFrame = GetPyFrame(cefFrame) pyOldUrl = CefToPyString(cefOldUrl) @@ -207,6 +227,11 @@ cdef public cpp_bool RequestHandler_GetAuthCredentials( cdef list pyPasswordOut cdef object clientCallback try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return False + pyBrowser = GetPyBrowser(cefBrowser, "GetAuthCredentials") pyFrame = GetPyFrame(cefFrame) pyIsProxy = bool(cefIsProxy) @@ -261,6 +286,11 @@ cdef public cpp_bool RequestHandler_OnQuotaRequest( cdef py_bool returnValue cdef object clientCallback try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return False + pyBrowser = GetPyBrowser(cefBrowser, "OnQuotaRequest") pyOriginUrl = CefToPyString(cefOriginUrl) clientCallback = pyBrowser.GetClientCallback("OnQuotaRequest") @@ -291,10 +321,16 @@ cdef public CefRefPtr[CefCookieManager] RequestHandler_GetCookieManager( cdef object clientCallback cdef PyCookieManager returnValue try: + # Issue #429: in some cases due to a race condition the browser + # may be NULL. if not cefBrowser.get(): - # Bug: In some cases due to a race condition the browser - # may be NULL. Issue #429. return NULL + + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return NULL + pyBrowser = GetPyBrowser(cefBrowser, "GetCookieManager") pyMainUrl = CefToPyString(cefMainUrl) clientCallback = pyBrowser.GetClientCallback("GetCookieManager") @@ -325,6 +361,11 @@ cdef public void RequestHandler_OnProtocolExecution( cdef list pyAllowOSExecutionOut cdef object clientCallback try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return + pyBrowser = GetPyBrowser(cefBrowser, "OnProtocolExecution") pyUrl = CefToPyString(cefUrl) pyAllowOSExecutionOut = [bool(cefAllowOSExecution)] @@ -369,6 +410,12 @@ cdef public cpp_bool RequestHandler_OnBeforePluginLoad( Debug("WARNING: RequestHandler_OnBeforePluginLoad() failed," " Browser object is not available") return False + + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(browser): + return False + py_browser = GetPyBrowser(browser, "OnBeforePluginLoad") py_plugin_info = CreatePyWebPluginInfo(plugin_info) clientCallback = GetGlobalClientCallback("OnBeforePluginLoad") @@ -424,6 +471,11 @@ cdef public void RequestHandler_OnRendererProcessTerminated( cdef PyBrowser pyBrowser cdef object clientCallback try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return + pyBrowser = GetPyBrowser(cefBrowser, "OnRendererProcessTerminated") clientCallback = pyBrowser.GetClientCallback( "OnRendererProcessTerminated") @@ -439,13 +491,18 @@ cdef public void RequestHandler_OnPluginCrashed( const CefString& cefPluginPath ) except * with gil: # TODO: plugin may crash during browser creation. Let this callback - # to be set either through cefpython.SetGlobalClientCallback() + # to be set either through cefpython.SetGlobalClientCallback() # or PyBrowser.SetClientCallback(). Modify the # PyBrowser.GetClientCallback() implementation to return a global # callback first if set. cdef PyBrowser pyBrowser cdef object clientCallback try: + # Issue #455: CefRequestHandler callbacks still executed after + # browser was closed. + if IsBrowserClosed(cefBrowser): + return + pyBrowser = GetPyBrowser(cefBrowser, "OnPluginCrashed") clientCallback = pyBrowser.GetClientCallback("OnPluginCrashed") if clientCallback: From cb9628bd755416013ab2f05eea048eb209f6f914 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Aug 2018 18:49:09 +0200 Subject: [PATCH 053/177] Update run_examples.py (#452) --- tools/run_examples.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/run_examples.py b/tools/run_examples.py index a3a4ce84..d78a1a47 100644 --- a/tools/run_examples.py +++ b/tools/run_examples.py @@ -78,7 +78,10 @@ def main(): passed.append("gtk3.py") # pyqt4 - if packages["PyQt4"]: + if LINUX: + print("[run_examples.py] PASS: qt.py pyqt4 (Issue #452)") + passed.append("qt.py pyqt4 (Issue #452)") + elif packages["PyQt4"]: examples.append("qt.py pyqt4") else: print("[run_examples.py] PASS: qt.py pyqt4 (PyQt4 not installed)") @@ -92,7 +95,10 @@ def main(): passed.append("qt.py pyqt5") # pyside - if packages["PySide"]: + if LINUX: + print("[run_examples.py] PASS: qt.py pyside (Issue #452)") + passed.append("qt.py pyside (Issue #452)") + elif packages["PySide"]: examples.append("qt.py pyside") else: print("[run_examples.py] PASS: qt.py pyside (PySide not installed)") From e72609c2874e38bc58ba54bd114861bf32dab8ce Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 18 Aug 2018 14:46:21 +0200 Subject: [PATCH 054/177] Add accessibility support for both windowed and OSR modes (#449). Add Browser.SetAccessibilityState. Add AccessibilityHandler (OSR mode). Add cef.SetGlobalClientHandler. Add osr_test.py unittest for off-screen rendering mode (#59). Refactor main_test.py. Update tools/apidocs.py - generate TOCs by default Update tools/build_distrib.py - run all unit tests --- README.md | 114 +++++----- api/API-categories.md | 1 + api/API-index.md | 5 + api/AccessibilityHandler.md | 50 +++++ api/Browser.md | 39 ++++ api/cefpython.md | 21 +- src/browser.pyx | 4 + src/cef_types.pyx | 9 + src/cef_v59..v66_changes.txt | 15 +- src/cefpython.pyx | 24 ++- src/client_handler/Makefile | 2 +- src/client_handler/accessibility_handler.cpp | 19 ++ src/client_handler/accessibility_handler.h | 20 ++ src/client_handler/render_handler.h | 9 +- src/extern/cef/cef_browser.pxd | 4 +- src/extern/cef/cef_values.pxd | 13 ++ src/handlers/accessibility_handler.pyx | 34 +++ src/process_message_utils.pyx | 83 ++++++-- tools/apidocs.py | 23 +- tools/build_distrib.py | 2 +- tools/common.py | 3 +- unittests/_common.py | 173 +++++++++++++++ unittests/main_test.py | 183 +++------------- unittests/osr_test.py | 210 +++++++++++++++++++ 24 files changed, 816 insertions(+), 244 deletions(-) create mode 100644 api/AccessibilityHandler.md create mode 100644 src/cef_types.pyx create mode 100644 src/client_handler/accessibility_handler.cpp create mode 100644 src/client_handler/accessibility_handler.h create mode 100644 src/handlers/accessibility_handler.pyx create mode 100644 unittests/_common.py create mode 100644 unittests/osr_test.py diff --git a/README.md b/README.md index e2883112..3b9f1a21 100644 --- a/README.md +++ b/README.md @@ -256,63 +256,67 @@ Additional information for v31.2 release: ### API categories -#### Modules - - * [cefpython](api/cefpython.md#cefpython) module - - -#### Settings - - * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary - * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary - * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary - - -#### Classes and objects - - * [Browser](api/Browser.md#browser-object) object - * [Callback](api/Callback.md#callback-object) object - * [Cookie](api/Cookie.md#cookie-class) class - * [CookieManager](api/CookieManager.md#cookiemanager-class) class - * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) - * [DragData](api/DragData.md#dragdata-object) object - * [Frame](api/Frame.md#frame-object) object - * [Image](api/Image.md#image-object) object - * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class - * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object - * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object - * [Request](api/Request.md#request-class) class - * [Response](api/Response.md#response-object) object - * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object - * [WebRequest](api/WebRequest.md#webrequest-class) class - * [WindowInfo](api/WindowInfo.md#windowinfo-class) class - * [WindowUtils](api/WindowUtils.md#windowutils-class) class - - -#### Client handlers (interfaces) - - * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) - * [DownloadHandler](api/DownloadHandler.md#downloadhandler) - * [FocusHandler](api/FocusHandler.md#focushandler-interface) - * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) - * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) - * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) - * [LoadHandler](api/LoadHandler.md#loadhandler-interface) - * [RenderHandler](api/RenderHandler.md#renderhandler-interface) - * [RequestHandler](api/RequestHandler.md#requesthandler-interface) - * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) - * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) - - -#### Other interfaces - - * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface - * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface - * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface - +#### Modules + + * [cefpython](api/cefpython.md#cefpython) module + + +#### Settings + + * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary + * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary + * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary + + +#### Classes and objects + + * [Browser](api/Browser.md#browser-object) object + * [Callback](api/Callback.md#callback-object) object + * [Cookie](api/Cookie.md#cookie-class) class + * [CookieManager](api/CookieManager.md#cookiemanager-class) class + * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) + * [DragData](api/DragData.md#dragdata-object) object + * [Frame](api/Frame.md#frame-object) object + * [Image](api/Image.md#image-object) object + * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class + * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object + * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object + * [Request](api/Request.md#request-class) class + * [Response](api/Response.md#response-object) object + * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object + * [WebRequest](api/WebRequest.md#webrequest-class) class + * [WindowInfo](api/WindowInfo.md#windowinfo-class) class + * [WindowUtils](api/WindowUtils.md#windowutils-class) class + + +#### Client handlers (interfaces) + + * [AccessibilityHandler](api/AccessibilityHandler.md#accessibilityhandler-interface) + * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) + * [DownloadHandler](api/DownloadHandler.md#downloadhandler) + * [FocusHandler](api/FocusHandler.md#focushandler-interface) + * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) + * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) + * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) + * [LoadHandler](api/LoadHandler.md#loadhandler-interface) + * [RenderHandler](api/RenderHandler.md#renderhandler-interface) + * [RequestHandler](api/RequestHandler.md#requesthandler-interface) + * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) + * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) + + +#### Other interfaces + + * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface + * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface + * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface + ### API index +* [AccessibilityHandler (interface)](api/AccessibilityHandler.md#accessibilityhandler-interface) + * [_OnAccessibilityTreeChange](api/AccessibilityHandler.md#_onaccessibilitytreechange) + * [_OnAccessibilityLocationChange](api/AccessibilityHandler.md#_onaccessibilitylocationchange) * [Application settings](api/ApplicationSettings.md#application-settings) * [accept_language_list](api/ApplicationSettings.md#accept_language_list) * [app_user_model_id](api/ApplicationSettings.md#app_user_model_id) @@ -408,6 +412,7 @@ Additional information for v31.2 release: * [SendMouseWheelEvent](api/Browser.md#sendmousewheelevent) * [SendFocusEvent](api/Browser.md#sendfocusevent) * [SendCaptureLostEvent](api/Browser.md#sendcapturelostevent) + * [SetAccessibilityState](api/Browser.md#setaccessibilitystate) * [SetClientCallback](api/Browser.md#setclientcallback) * [SetClientHandler](api/Browser.md#setclienthandler) * [SetFocus](api/Browser.md#setfocus) @@ -469,6 +474,7 @@ Additional information for v31.2 release: * [PostDelayedTask](api/cefpython.md#postdelayedtask) * [QuitMessageLoop](api/cefpython.md#quitmessageloop) * [SetGlobalClientCallback](api/cefpython.md#setglobalclientcallback) + * [SetGlobalClientHandler](api/cefpython.md#setglobalclienthandler) * [SetOsModalLoop](api/cefpython.md#setosmodalloop) * [Shutdown](api/cefpython.md#shutdown) * [Command line switches](api/CommandLineSwitches.md#command-line-switches) diff --git a/api/API-categories.md b/api/API-categories.md index 1f4fc992..d1d7320e 100644 --- a/api/API-categories.md +++ b/api/API-categories.md @@ -38,6 +38,7 @@ ### Client handlers (interfaces) + * [AccessibilityHandler](AccessibilityHandler.md#accessibilityhandler-interface) * [DisplayHandler](DisplayHandler.md#displayhandler-interface) * [DownloadHandler](DownloadHandler.md#downloadhandler) * [FocusHandler](FocusHandler.md#focushandler-interface) diff --git a/api/API-index.md b/api/API-index.md index 74971115..fb8c5efe 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -2,6 +2,9 @@ # API index +* [AccessibilityHandler (interface)](AccessibilityHandler.md#accessibilityhandler-interface) + * [_OnAccessibilityTreeChange](AccessibilityHandler.md#_onaccessibilitytreechange) + * [_OnAccessibilityLocationChange](AccessibilityHandler.md#_onaccessibilitylocationchange) * [Application settings](ApplicationSettings.md#application-settings) * [accept_language_list](ApplicationSettings.md#accept_language_list) * [app_user_model_id](ApplicationSettings.md#app_user_model_id) @@ -97,6 +100,7 @@ * [SendMouseWheelEvent](Browser.md#sendmousewheelevent) * [SendFocusEvent](Browser.md#sendfocusevent) * [SendCaptureLostEvent](Browser.md#sendcapturelostevent) + * [SetAccessibilityState](Browser.md#setaccessibilitystate) * [SetClientCallback](Browser.md#setclientcallback) * [SetClientHandler](Browser.md#setclienthandler) * [SetFocus](Browser.md#setfocus) @@ -158,6 +162,7 @@ * [PostDelayedTask](cefpython.md#postdelayedtask) * [QuitMessageLoop](cefpython.md#quitmessageloop) * [SetGlobalClientCallback](cefpython.md#setglobalclientcallback) + * [SetGlobalClientHandler](cefpython.md#setglobalclienthandler) * [SetOsModalLoop](cefpython.md#setosmodalloop) * [Shutdown](cefpython.md#shutdown) * [Command line switches](CommandLineSwitches.md#command-line-switches) diff --git a/api/AccessibilityHandler.md b/api/AccessibilityHandler.md new file mode 100644 index 00000000..faea2605 --- /dev/null +++ b/api/AccessibilityHandler.md @@ -0,0 +1,50 @@ +[API categories](API-categories.md) | [API index](API-index.md) + + +# AccessibilityHandler (interface) + +This handler is for use only with off-screen rendering enabled. +See [RenderHandler](RenderHandler.md) for details. + +Implement this interface to receive accessibility notification when +accessibility events have been registered. The methods of this class will +be called on the UI thread. + +Callbacks in this interface are not associated with any specific browser, +thus you must call cefpython.[SetGlobalClientHandler] or +SetGlobalClientCallback() to use them. The callbacks names were prefixed +with "`_`" to distinguish this special behavior. + +For an example of how to implement handler see [cefpython](cefpython.md).CreateBrowser(). For a list of all handler interfaces see [API > Client handlers](API#Client_handlers). + + +Table of contents: +* [Callbacks](#callbacks) + * [_OnAccessibilityTreeChange](#_onaccessibilitytreechange) + * [_OnAccessibilityLocationChange](#_onaccessibilitylocationchange) + + +## Callbacks + + +### _OnAccessibilityTreeChange + +| Parameter | Type | +| --- | --- | +| value | list | +| __Return__ | void | + +Called after renderer process sends accessibility tree changes to the +browser process. + + +### _OnAccessibilityLocationChange + +| Parameter | Type | +| --- | --- | +| value | list | +| __Return__ | void | + +Called after renderer process sends accessibility location changes to the +browser process. + diff --git a/api/Browser.md b/api/Browser.md index 768adeca..cf2ab61d 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -76,6 +76,7 @@ Table of contents: * [SendMouseWheelEvent](#sendmousewheelevent) * [SendFocusEvent](#sendfocusevent) * [SendCaptureLostEvent](#sendcapturelostevent) + * [SetAccessibilityState](#setaccessibilitystate) * [SetClientCallback](#setclientcallback) * [SetClientHandler](#setclienthandler) * [SetFocus](#setfocus) @@ -844,6 +845,44 @@ Send a focus event to the browser. Send a capture lost event to the browser. +### SetAccessibilityState + +| | | +| --- | --- | +| state | cef_state_t | +| __Return__ | void | + +cef_state_t enum values defined in cefpython module: +- STATE_DEFAULT +- STATE_ENABLED +- STATE_DISABLED + +Description from upstream CEF: +> Set accessibility state for all frames. |accessibility_state| may be +> default, enabled or disabled. If |accessibility_state| is STATE_DEFAULT +> then accessibility will be disabled by default and the state may be further +> controlled with the "force-renderer-accessibility" and +> "disable-renderer-accessibility" command-line switches. If +> |accessibility_state| is STATE_ENABLED then accessibility will be enabled. +> If |accessibility_state| is STATE_DISABLED then accessibility will be +> completely disabled. +> +> For windowed browsers accessibility will be enabled in Complete mode (which +> corresponds to kAccessibilityModeComplete in Chromium). In this mode all +> platform accessibility objects will be created and managed by Chromium's +> internal implementation. The client needs only to detect the screen reader +> and call this method appropriately. For example, on macOS the client can +> handle the @"AXEnhancedUserInterface" accessibility attribute to detect +> VoiceOver state changes and on Windows the client can handle WM_GETOBJECT +> with OBJID_CLIENT to detect accessibility readers. +> +> For windowless browsers accessibility will be enabled in TreeOnly mode +> (which corresponds to kAccessibilityModeWebContentsOnly in Chromium). In +> this mode renderer accessibility is enabled, the full tree is computed, and +> events are passed to CefAccessibiltyHandler, but platform accessibility +> objects are not created. The client may implement platform accessibility +> objects using CefAccessibiltyHandler callbacks if desired. + ### SetClientCallback | Parameter | Type | diff --git a/api/cefpython.md b/api/cefpython.md index e68d0231..20cb8b9f 100644 --- a/api/cefpython.md +++ b/api/cefpython.md @@ -26,6 +26,7 @@ Table of contents: * [PostDelayedTask](#postdelayedtask) * [QuitMessageLoop](#quitmessageloop) * [SetGlobalClientCallback](#setglobalclientcallback) + * [SetGlobalClientHandler](#setglobalclienthandler) * [SetOsModalLoop](#setosmodalloop) * [Shutdown](#shutdown) @@ -133,7 +134,8 @@ Returns the [CommandLineSwitches](CommandLineSwitches.md) switch that was passed | name | string | | __Return__ | object | -Returns a global client callback that was set using SetGlobalClientCallback(). Returns None if callback was not set. +Returns a global client callback that was set using SetGlobalClientCallback() +or SetGlobalClientHandler. Returns None if callback was not set. ### GetModuleDirectory @@ -290,6 +292,23 @@ Some client callbacks are not associated with any browser. In such case use this Example of using SetGlobalClientCallback() is provided in the wxpython.py example. +### SetGlobalClientHandler + +| Parameter | Type | +| --- | --- | +| handler | object | +| __Return__ | void | + +Set client handler object (class instance). Its members will be inspected. +Private methods that are not meant to be callbacks should have their names +prepended with two underscores. Methods with single underscore or no +underscore are treated the same as client callbacks. + +You can call this method multiple times to set many handlers. For +example you can create in your code several objects named AccessibilityHandler, +RequestHandler etc. + + ### SetOsModalLoop | Parameter | Type | diff --git a/src/browser.pyx b/src/browser.pyx index 60194e54..5f26e7d7 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -5,6 +5,7 @@ include "cefpython.pyx" cimport cef_types +from cef_types cimport cef_state_t IF UNAME_SYSNAME == "Linux": cimport x11 @@ -513,6 +514,9 @@ cdef class PyBrowser: ELSE: NonCriticalError("SetBounds() not implemented on this platform") + cpdef py_void SetAccessibilityState(self, cef_state_t state): + self.GetCefBrowserHost().get().SetAccessibilityState(state) + cpdef py_void SetFocus(self, enable): self.GetCefBrowserHost().get().SetFocus(bool(enable)) diff --git a/src/cef_types.pyx b/src/cef_types.pyx new file mode 100644 index 00000000..6d9ad5fe --- /dev/null +++ b/src/cef_types.pyx @@ -0,0 +1,9 @@ +# Copyright (c) 2014 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +cimport cef_types + +STATE_DEFAULT = cef_types.STATE_DEFAULT +STATE_ENABLED = cef_types.STATE_ENABLED +STATE_DISABLED = cef_types.STATE_DISABLED diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 38a071e4..7a6fc5fb 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -70,6 +70,8 @@ MISC NEW FEATURES ------------ ++ unittests/osr_test.py - new test for off-screen rendering mode + internal/cef_types.h + cef_log_severity_t: new key LOGSEVERITY_DEBUG (no need to expose, same as LOGSEVERITY_VERBOSE, see code comments in setting.pyx @@ -81,14 +83,15 @@ internal/cef_types.h + cef_referrer_policy_t changes (not exposed, info only) cef_accessibility_handler.h -- CefAccessibilityHandler -- CefRenderHandler::GetAccessibilityHandler ++ CefAccessibilityHandler ++ CefRenderHandler::GetAccessibilityHandler ++ cefpython.SetGlobalClientHandler cef_render_handler.h - OnTextSelectionChanged cef_browser.h -- SetAccessibilityState ++ SetAccessibilityState - SetAutoResizeEnabled - GetExtension - IsBackgroundHost @@ -101,9 +104,9 @@ cef_display_handler.h - OnLoadingProgressChange cef_drag_data.h -- GetImage (cross-platform) -- GetImageHotspot (cross-platform) -- HasImage (cross-platform) ++ GetImage (cross-platform) ++ GetImageHotspot (cross-platform) ++ HasImage (cross-platform) cef_extension.h - CefExtension diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 2ed752c5..2f3d3ef1 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -329,6 +329,7 @@ cdef dict g_globalClientCallbacks = {} # ----------------------------------------------------------------------------- +include "cef_types.pyx" include "utils.pyx" include "string_utils.pyx" IF UNAME_SYSNAME == "Windows": @@ -371,6 +372,7 @@ include "helpers.pyx" include "image.pyx" # Handlers +include "handlers/accessibility_handler.pyx" include "handlers/browser_process_handler.pyx" include "handlers/display_handler.pyx" include "handlers/focus_handler.pyx" @@ -953,12 +955,32 @@ def SetOsModalLoop(py_bool modalLoop): cpdef py_void SetGlobalClientCallback(py_string name, object callback): global g_globalClientCallbacks - if name in ["OnCertificateError", "OnBeforePluginLoad", "OnAfterCreated"]: + # Global callbacks are prefixed with "_" in documentation. + # Accept both with and without a prefix. + if name.startswith("_"): + name = name[1:] + if name in ["OnCertificateError", "OnBeforePluginLoad", "OnAfterCreated", + "OnAccessibilityTreeChange", "OnAccessibilityLocationChange"]: g_globalClientCallbacks[name] = callback else: raise Exception("SetGlobalClientCallback() failed: "\ "invalid callback name = %s" % name) +cpdef py_void SetGlobalClientHandler(object clientHandler): + if not hasattr(clientHandler, "__class__"): + raise Exception("SetGlobalClientHandler() failed: __class__ " + "attribute missing") + cdef dict methods = {} + cdef py_string key + cdef object method + cdef tuple value + for value in inspect.getmembers(clientHandler, + predicate=inspect.ismethod): + key = value[0] + method = value[1] + if key and key[0:2] != '__': + SetGlobalClientCallback(key, method) + cpdef object GetGlobalClientCallback(py_string name): global g_globalClientCallbacks if name in g_globalClientCallbacks: diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile index bdb2ee72..d217660e 100644 --- a/src/client_handler/Makefile +++ b/src/client_handler/Makefile @@ -23,7 +23,7 @@ SRC = client_handler.cpp cookie_visitor.cpp resource_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 dialog_handler.cpp \ - cef_log.cpp \ + cef_log.cpp accessibility_handler.cpp \ $(SRC_MORE) OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o)) diff --git a/src/client_handler/accessibility_handler.cpp b/src/client_handler/accessibility_handler.cpp new file mode 100644 index 00000000..a75ab2f6 --- /dev/null +++ b/src/client_handler/accessibility_handler.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2018 CEF Python, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/cefpython + +#include "accessibility_handler.h" +#include "common/cefpython_public_api.h" + + +void AccessibilityHandler::OnAccessibilityTreeChange( + CefRefPtr value) { + REQUIRE_UI_THREAD(); + AccessibilityHandler_OnAccessibilityTreeChange(value); +} + +void AccessibilityHandler::OnAccessibilityLocationChange( + CefRefPtr value) { + REQUIRE_UI_THREAD(); + AccessibilityHandler_OnAccessibilityLocationChange(value); +} diff --git a/src/client_handler/accessibility_handler.h b/src/client_handler/accessibility_handler.h new file mode 100644 index 00000000..ba18171c --- /dev/null +++ b/src/client_handler/accessibility_handler.h @@ -0,0 +1,20 @@ +// Copyright (c) 2018 CEF Python, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/cefpython + +#include "common/cefpython_public_api.h" +#include "include/cef_accessibility_handler.h" + + +class AccessibilityHandler : public CefAccessibilityHandler +{ +public: + AccessibilityHandler(){} + virtual ~AccessibilityHandler(){} + + virtual void OnAccessibilityTreeChange(CefRefPtr value) override; + virtual void OnAccessibilityLocationChange(CefRefPtr value) override; + +private: + IMPLEMENT_REFCOUNTING(AccessibilityHandler); +}; diff --git a/src/client_handler/render_handler.h b/src/client_handler/render_handler.h index 5baae320..b9eb59fa 100644 --- a/src/client_handler/render_handler.h +++ b/src/client_handler/render_handler.h @@ -4,14 +4,21 @@ #include "common/cefpython_public_api.h" #include "include/cef_render_handler.h" +#include "accessibility_handler.h" -class RenderHandler : public CefRenderHandler +class RenderHandler : public CefRenderHandler, + public AccessibilityHandler { public: RenderHandler(){} virtual ~RenderHandler(){} + CefRefPtr GetAccessibilityHandler() + override { + return this; + } + bool GetRootScreenRect(CefRefPtr browser, CefRect& rect) override; diff --git a/src/extern/cef/cef_browser.pxd b/src/extern/cef/cef_browser.pxd index d0b16483..6c5e599a 100644 --- a/src/extern/cef/cef_browser.pxd +++ b/src/extern/cef/cef_browser.pxd @@ -11,7 +11,7 @@ from libcpp cimport bool as cpp_bool from libcpp.vector cimport vector as cpp_vector from cef_frame cimport CefFrame cimport cef_types -from cef_types cimport int64 +from cef_types cimport int64, cef_state_t from cef_types cimport CefBrowserSettings, CefPoint from cef_drag_data cimport CefDragData from cef_types cimport CefMouseEvent @@ -86,6 +86,8 @@ cdef extern from "include/cef_browser.h": void ReplaceMisspelling(const CefString& word) void AddWordToDictionary(const CefString& word) + void SetAccessibilityState(cef_state_t accessibility_state) + cdef cppclass CefBrowser: diff --git a/src/extern/cef/cef_values.pxd b/src/extern/cef/cef_values.pxd index 201bb82b..ee0bf506 100644 --- a/src/extern/cef/cef_values.pxd +++ b/src/extern/cef/cef_values.pxd @@ -12,6 +12,19 @@ cdef extern from "include/cef_values.h": cdef CefRefPtr[CefBinaryValue] CefBinaryValue_Create \ "CefBinaryValue::Create"(const void* data, size_t data_size) + cdef cppclass CefValue: + cpp_bool IsValid() + cpp_bool IsOwned() + cpp_bool IsReadOnly() + cef_value_type_t GetType() + cpp_bool GetBool() + int GetInt() + double GetDouble() + CefString GetString() + CefRefPtr[CefBinaryValue] GetBinary() + CefRefPtr[CefDictionaryValue] GetDictionary() + CefRefPtr[CefListValue] GetList() + cdef cppclass CefBinaryValue: cpp_bool IsValid() cpp_bool IsOwned() diff --git a/src/handlers/accessibility_handler.pyx b/src/handlers/accessibility_handler.pyx new file mode 100644 index 00000000..055fb61c --- /dev/null +++ b/src/handlers/accessibility_handler.pyx @@ -0,0 +1,34 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +include "../cefpython.pyx" +include "../browser.pyx" +include "../frame.pyx" +include "../process_message_utils.pyx" + +cdef public void AccessibilityHandler_OnAccessibilityTreeChange( + CefRefPtr[CefValue] cefValue + ) except * with gil: + cdef object value = CefValueToPyValue(cefValue) + cdef object callback + try: + callback = GetGlobalClientCallback("OnAccessibilityTreeChange") + if callback: + callback(value=value) + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) + +cdef public void AccessibilityHandler_OnAccessibilityLocationChange( + CefRefPtr[CefValue] cefValue + ) except * with gil: + cdef object value = CefValueToPyValue(cefValue) + cdef object callback + try: + callback = GetGlobalClientCallback("OnAccessibilityLocationChange") + if callback: + callback(value=value) + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) diff --git a/src/process_message_utils.pyx b/src/process_message_utils.pyx index a9c10f22..464c8f6b 100644 --- a/src/process_message_utils.pyx +++ b/src/process_message_utils.pyx @@ -10,6 +10,7 @@ # enumerate(), to an int. include "cefpython.pyx" +include "utils.pyx" # ----------------------------------------------------------------------------- # CEF values to Python values @@ -36,6 +37,53 @@ cdef object CheckForCefPythonMessageHash(CefRefPtr[CefBrowser] cefBrowser, return jsCallback return pyString +cdef object CefValueToPyValue(CefRefPtr[CefValue] cefValue): + assert cefValue.get().IsValid(), "cefValue is invalid" + cdef cef_types.cef_value_type_t valueType = cefValue.get().GetType() + cdef CefRefPtr[CefBinaryValue] binaryValue + cdef uint32 uint32_value = 0 + cdef int64 int64_value = 0 + + if valueType == cef_types.VTYPE_NULL: + return None + elif valueType == cef_types.VTYPE_BOOL: + return bool(cefValue.get().GetBool()) + elif valueType == cef_types.VTYPE_INT: + return cefValue.get().GetInt() + elif valueType == cef_types.VTYPE_DOUBLE: + return cefValue.get().GetDouble() + elif valueType == cef_types.VTYPE_STRING: + return CefToPyString(cefValue.get().GetString()) + elif valueType == cef_types.VTYPE_DICTIONARY: + return CefDictionaryValueToPyDict( + NULL, + cefValue.get().GetDictionary(), + 1) + elif valueType == cef_types.VTYPE_LIST: + return CefListValueToPyList( + NULL, + cefValue.get().GetList(), + 1) + elif valueType == cef_types.VTYPE_BINARY: + binaryValue = cefValue.get().GetBinary() + if binaryValue.get().GetSize() == sizeof(uint32_value): + binaryValue.get().GetData( + &uint32_value, sizeof(uint32_value), 0) + return uint32_value + elif binaryValue.get().GetSize() == sizeof(int64_value): + binaryValue.get().GetData( + &int64_value, sizeof(int64_value), 0) + return int64_value + else: + NonCriticalError("Unknown binary value, size=%s" % \ + binaryValue.get().GetSize()) + return None + else: + raise Exception("Unknown CefValue type=%s" % valueType) + +# TODO: Use CefListValue.GetValue to get CefValue and use CefValueToPyValue +# for dictionary and lists? + cdef list CefListValueToPyList( CefRefPtr[CefBrowser] cefBrowser, CefRefPtr[CefListValue] cefListValue, @@ -49,8 +97,8 @@ cdef list CefListValueToPyList( cdef cef_types.cef_value_type_t valueType cdef list ret = [] cdef CefRefPtr[CefBinaryValue] binaryValue - cdef uint32 uint32_value - cdef int64 int64_value + cdef uint32 uint32_value = 0 + cdef int64 int64_value = 0 cdef object originallyString for index in range(0, size): valueType = cefListValue.get().GetType(index) @@ -65,8 +113,9 @@ cdef list CefListValueToPyList( elif valueType == cef_types.VTYPE_STRING: originallyString = CefToPyString( cefListValue.get().GetString(index)) - originallyString = CheckForCefPythonMessageHash(cefBrowser, - originallyString) + if cefBrowser.get(): + originallyString = CheckForCefPythonMessageHash(cefBrowser, + originallyString) ret.append(originallyString) elif valueType == cef_types.VTYPE_DICTIONARY: ret.append(CefDictionaryValueToPyDict( @@ -89,10 +138,11 @@ cdef list CefListValueToPyList( &int64_value, sizeof(int64_value), 0) ret.append(int64_value) else: - raise Exception("Unknown binary value, size=%s" % \ - binaryValue.get().GetSize()) + NonCriticalError("Unknown binary value, size=%s" % \ + binaryValue.get().GetSize()) + ret.append(None) else: - raise Exception("Unknown value type=%s" % valueType) + raise Exception("Unknown CefValue type=%s" % valueType) return ret cdef dict CefDictionaryValueToPyDict( @@ -107,16 +157,19 @@ cdef dict CefDictionaryValueToPyDict( cefDictionaryValue.get().GetKeys(keyList) cdef cef_types.cef_value_type_t valueType cdef dict ret = {} + # noinspection PyUnresolvedReferences cdef cpp_vector[CefString].iterator iterator = keyList.begin() cdef CefString cefKey cdef py_string pyKey cdef CefRefPtr[CefBinaryValue] binaryValue - cdef uint32 uint32_value - cdef int64 int64_value + cdef uint32 uint32_value = 0 + cdef int64 int64_value = 0 cdef object originallyString while iterator != keyList.end(): + # noinspection PyUnresolvedReferences cefKey = deref(iterator) pyKey = CefToPyString(cefKey) + # noinspection PyUnresolvedReferences preinc(iterator) valueType = cefDictionaryValue.get().GetType(cefKey) if valueType == cef_types.VTYPE_NULL: @@ -130,8 +183,9 @@ cdef dict CefDictionaryValueToPyDict( elif valueType == cef_types.VTYPE_STRING: originallyString = CefToPyString( cefDictionaryValue.get().GetString(cefKey)) - originallyString = CheckForCefPythonMessageHash(cefBrowser, - originallyString) + if cefBrowser.get(): + originallyString = CheckForCefPythonMessageHash(cefBrowser, + originallyString) ret[pyKey] = originallyString elif valueType == cef_types.VTYPE_DICTIONARY: ret[pyKey] = CefDictionaryValueToPyDict( @@ -154,10 +208,11 @@ cdef dict CefDictionaryValueToPyDict( &int64_value, sizeof(int64_value), 0) ret[pyKey] = int64_value else: - raise Exception("Unknown binary value, size=%s" % \ - binaryValue.get().GetSize()) + NonCriticalError("Unknown binary value, size=%s" % \ + binaryValue.get().GetSize()) + ret[pyKey] = None else: - raise Exception("Unknown value type = %s" % valueType) + raise Exception("Unknown CefValue type = %s" % valueType) return ret # ----------------------------------------------------------------------------- diff --git a/tools/apidocs.py b/tools/apidocs.py index 8bd8da31..27fc311f 100644 --- a/tools/apidocs.py +++ b/tools/apidocs.py @@ -17,10 +17,25 @@ import glob import os import re +import subprocess def main(): """Main entry point.""" + # Call toc.py for docs/ and api/ directories + print("Running toc.py in {}/ dir".format(os.path.basename(API_DIR))) + retcode = subprocess.call([sys.executable, + os.path.join(TOOLS_DIR, "toc.py"), + API_DIR]) + assert retcode == 0, "Executing toc.py for API_DIR failed" + + print("Running toc.py in {}/ dir".format(os.path.basename(DOCS_DIR))) + retcode = subprocess.call([sys.executable, + os.path.join(TOOLS_DIR, "toc.py"), + DOCS_DIR]) + assert retcode == 0, "Executing toc.py for DOCS_DIR failed" + + # Generate API reference in api/ dir and in root/README.md api_links = generate_api_links() update_api_index_file(api_links) update_readme_file(api_links) @@ -69,8 +84,8 @@ def update_readme_file(api_links): assert re.search(re_find, readme_contents), ("API categories not found" " in README") contents = re.sub(re_find, - ("### API categories\r\n\r\n{categories_contents}" - "\r\n### API index" + (u"### API categories\r\n\r\n{categories_contents}" + u"\r\n### API index" .format(categories_contents=categories_contents)), contents) @@ -80,7 +95,7 @@ def update_readme_file(api_links): assert re.search(re_find, readme_contents), ("API index not found" " in README") contents = re.sub(re_find, - ("### API index\r\n\r\n{api_links}" + (u"### API index\r\n\r\n{api_links}" .format(api_links=api_links)), contents) @@ -103,7 +118,7 @@ def generate_api_links(): continue with open(file_, "rb") as fo: md_contents = fo.read().decode("utf-8") - md_contents = re.sub(r"```[\s\S]+?```", "", md_contents) + md_contents = re.sub(u"```[\\s\\S]+?```", u"", md_contents) matches = re.findall(r"^(#|###)\s+(.*)", md_contents, re.MULTILINE) for match in matches: diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 3d0a8105..65bfd5ee 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -634,7 +634,7 @@ def test_wheel_packages(pythons): command = ("\"{python}\" {unittests}" .format(python=python["executable"], unittests=os.path.join(UNITTESTS_DIR, - "main_test.py"))) + "_test_runner.py"))) pcode = subprocess.call(command, cwd=DISTRIB_DIR, shell=True) if pcode != 0: print("[build_distrib.py] ERROR: Unit tests failed for" diff --git a/tools/common.py b/tools/common.py index da24e8f6..06ab942e 100644 --- a/tools/common.py +++ b/tools/common.py @@ -125,8 +125,9 @@ assert __file__ ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -# API reference +# API reference and docs API_DIR = os.path.join(ROOT_DIR, "api") +DOCS_DIR = os.path.join(ROOT_DIR, "docs") # Build directories BUILD_DIR = os.path.join(ROOT_DIR, "build") diff --git a/unittests/_common.py b/unittests/_common.py new file mode 100644 index 00000000..2c2e52a9 --- /dev/null +++ b/unittests/_common.py @@ -0,0 +1,173 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +from cefpython3 import cefpython as cef + +import base64 +import os +import sys +import time + +# To show the window for an extended period of time increase this number. +MESSAGE_LOOP_RANGE = 100 # each iteration is 0.01 sec + +g_subtests_ran = 0 + + +def subtest_message(message): + global g_subtests_ran + g_subtests_ran += 1 + print(str(g_subtests_ran) + ". " + message) + sys.stdout.flush() + + +def show_test_summary(pyfile): + print("\nRan " + str(g_subtests_ran) + " sub-tests in " + + os.path.basename(pyfile)) + + +def html_to_data_uri(html): + html = html.encode("utf-8", "replace") + b64 = base64.b64encode(html).decode("utf-8", "replace") + ret = "data:text/html;base64,{data}".format(data=b64) + return ret + + +def run_message_loop(): + # Run message loop for some time. + # noinspection PyTypeChecker + for i in range(MESSAGE_LOOP_RANGE): + cef.MessageLoopWork() + time.sleep(0.01) + subtest_message("cef.MessageLoopWork() ok") + + +def do_message_loop_work(work_loops): + # noinspection PyTypeChecker + for i in range(work_loops): + cef.MessageLoopWork() + time.sleep(0.01) + + +def check_auto_asserts(test_case, objects): + # Automatic check of asserts in handlers and in external + for obj in objects: + test_for_True = False # Test whether asserts are working correctly + for key, value in obj.__dict__.items(): + if key == "test_for_True": + test_for_True = True + continue + if "_True" in key: + test_case.assertTrue(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_True", "") + + " ok") + elif "_False" in key: + test_case.assertFalse(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_False", "") + + " ok") + test_case.assertTrue(test_for_True) + + +class DisplayHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.javascript_errors_False = False + self.OnConsoleMessage_True = False + + def OnConsoleMessage(self, message, **_): + if "error" in message.lower() or "uncaught" in message.lower(): + self.javascript_errors_False = True + raise Exception("Javascript error: " + message) + else: + # Check whether messages from javascript are coming + self.OnConsoleMessage_True = True + subtest_message(message) + + +class GlobalHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnAfterCreated_True = False + + def _OnAfterCreated(self, browser, **_): + # For asserts that are checked automatically before shutdown its + # values should be set first, so that when other asserts fail + # (the ones called through the test_case member) they are reported + # correctly. + self.test_case.assertFalse(self.OnAfterCreated_True) + self.OnAfterCreated_True = True + self.test_case.assertEqual(browser.GetIdentifier(), 1) + + +class LoadHandler(object): + def __init__(self, test_case, datauri): + self.test_case = test_case + self.datauri = datauri + self.frame_source_visitor = None + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnLoadStart_True = False + self.OnLoadEnd_True = False + self.FrameSourceVisitor_True = False + # self.OnLoadingStateChange_Start_True = False # FAILS + self.OnLoadingStateChange_End_True = False + + def OnLoadStart(self, browser, frame, **_): + self.test_case.assertFalse(self.OnLoadStart_True) + self.OnLoadStart_True = True + self.test_case.assertEqual(browser.GetUrl(), frame.GetUrl()) + self.test_case.assertEqual(browser.GetUrl(), self.datauri) + + def OnLoadEnd(self, browser, frame, http_code, **_): + # OnLoadEnd should be called only once + self.test_case.assertFalse(self.OnLoadEnd_True) + self.OnLoadEnd_True = True + self.test_case.assertEqual(http_code, 200) + self.frame_source_visitor = FrameSourceVisitor(self, self.test_case) + frame.GetSource(self.frame_source_visitor) + browser.ExecuteJavascript("print('LoadHandler.OnLoadEnd() ok')") + + def OnLoadingStateChange(self, browser, is_loading, can_go_back, + can_go_forward, **_): + if is_loading: + # TODO: this test fails, looks like OnLoadingStaetChange with + # is_loading=False is being called very fast, before + # OnLoadStart and before client handler is set by calling + # browser.SetClientHandler(). + # SOLUTION: allow to set OnLoadingStateChange through + # SetGlobalClientCallback similarly to _OnAfterCreated(). + # self.test_case.assertFalse(self.OnLoadingStateChange_Start_True) + # self.OnLoadingStateChange_Start_True = True + pass + else: + self.test_case.assertFalse(self.OnLoadingStateChange_End_True) + self.OnLoadingStateChange_End_True = True + self.test_case.assertEqual(browser.CanGoBack(), can_go_back) + self.test_case.assertEqual(browser.CanGoForward(), can_go_forward) + + +class FrameSourceVisitor(object): + """Visitor for Frame.GetSource().""" + + def __init__(self, load_handler, test_case): + self.load_handler = load_handler + self.test_case = test_case + + def Visit(self, value): + self.test_case.assertFalse(self.load_handler.FrameSourceVisitor_True) + self.load_handler.FrameSourceVisitor_True = True + self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee", + value) + diff --git a/unittests/main_test.py b/unittests/main_test.py index b0d16f40..717daf6d 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -7,16 +7,13 @@ import unittest # noinspection PyUnresolvedReferences import _test_runner -from os.path import basename +from _common import * + from cefpython3 import cefpython as cef -import time -import base64 + import os import sys -# To show the window for an extended period of time increase this number. -MESSAGE_LOOP_RANGE = 200 # each iteration is 0.01 sec - g_datauri_data = """ @@ -91,25 +88,14 @@ """ -g_datauri = "data:text/html;base64,"+base64.b64encode(g_datauri_data.encode( - "utf-8", "replace")).decode("utf-8", "replace") - -g_subtests_ran = 0 - - -def subtest_message(message): - global g_subtests_ran - g_subtests_ran += 1 - print(str(g_subtests_ran) + ". " + message) - sys.stdout.flush() +g_datauri = html_to_data_uri(g_datauri_data) class MainTest_IsolatedTest(unittest.TestCase): - def test_main(self): - """Main entry point.""" - # All this code must run inside one single test, otherwise strange - # things happen. + """Main entry point. All the code must run inside one + single test, otherwise strange things happen.""" + print("") print("CEF Python {ver}".format(ver=cef.__version__)) print("Python {ver}".format(ver=sys.version[:6])) @@ -126,24 +112,24 @@ def test_main(self): cef.Initialize(settings) subtest_message("cef.Initialize() ok") - # Test global handler + # Global handler global_handler = GlobalHandler(self) cef.SetGlobalClientCallback("OnAfterCreated", global_handler._OnAfterCreated) subtest_message("cef.SetGlobalClientCallback() ok") - # Test creation of browser + # Create browser browser = cef.CreateBrowserSync(url=g_datauri) self.assertIsNotNone(browser, "Browser object") subtest_message("cef.CreateBrowserSync() ok") - # Test other handlers: LoadHandler, DisplayHandler etc. - client_handlers = [LoadHandler(self), DisplayHandler(self)] + # Client handlers + client_handlers = [LoadHandler(self, g_datauri), DisplayHandler(self)] for handler in client_handlers: browser.SetClientHandler(handler) subtest_message("browser.SetClientHandler() ok") - # Test javascript bindings + # Javascript bindings external = External(self) bindings = cef.JavascriptBindings( bindToFrames=False, bindToPopups=False) @@ -183,153 +169,32 @@ def test_main(self): self.assertEqual(req_data, req.GetPostData()) subtest_message("cef.Request.SetPostData(dict) ok") - # Run message loop for some time. - # noinspection PyTypeChecker - for i in range(MESSAGE_LOOP_RANGE): - cef.MessageLoopWork() - time.sleep(0.01) - subtest_message("cef.MessageLoopWork() ok") + # Run message loop + run_message_loop() - # Test browser closing. Remember to clean reference. + # Close browser and clean reference browser.CloseBrowser(True) del browser subtest_message("browser.CloseBrowser() ok") - # Give it some time to close before calling shutdown. - # noinspection PyTypeChecker - for i in range(25): - cef.MessageLoopWork() - time.sleep(0.01) + # Give it some time to close before checking asserts + # and calling shutdown. + do_message_loop_work(25) - # Automatic check of asserts in handlers and in external # noinspection PyTypeChecker - for obj in [] + client_handlers + [global_handler, external]: - test_for_True = False # Test whether asserts are working correctly - for key, value in obj.__dict__.items(): - if key == "test_for_True": - test_for_True = True - continue - if "_True" in key: - self.assertTrue(value, "Check assert: " + - obj.__class__.__name__ + "." + key) - subtest_message(obj.__class__.__name__ + "." + - key.replace("_True", "") + - " ok") - elif "_False" in key: - self.assertFalse(value, "Check assert: " + - obj.__class__.__name__ + "." + key) - subtest_message(obj.__class__.__name__ + "." + - key.replace("_False", "") + - " ok") - self.assertTrue(test_for_True) + check_auto_asserts(self, [] + client_handlers + + [global_handler, + external]) # Test shutdown of CEF cef.Shutdown() subtest_message("cef.Shutdown() ok") - # Display real number of tests there were run - print("\nRan " + str(g_subtests_ran) + " sub-tests in test_main") + # Display summary + show_test_summary(__file__) sys.stdout.flush() -class GlobalHandler(object): - def __init__(self, test_case): - self.test_case = test_case - - # Asserts for True/False will be checked just before shutdown - self.test_for_True = True # Test whether asserts are working correctly - self.OnAfterCreated_True = False - - def _OnAfterCreated(self, browser, **_): - # For asserts that are checked automatically before shutdown its - # values should be set first, so that when other asserts fail - # (the ones called through the test_case member) they are reported - # correctly. - self.test_case.assertFalse(self.OnAfterCreated_True) - self.OnAfterCreated_True = True - self.test_case.assertEqual(browser.GetIdentifier(), 1) - - -class LoadHandler(object): - def __init__(self, test_case): - self.test_case = test_case - self.frame_source_visitor = None - - # Asserts for True/False will be checked just before shutdown - self.test_for_True = True # Test whether asserts are working correctly - self.OnLoadStart_True = False - self.OnLoadEnd_True = False - self.FrameSourceVisitor_True = False - # self.OnLoadingStateChange_Start_True = False # FAILS - self.OnLoadingStateChange_End_True = False - - def OnLoadStart(self, browser, frame, **_): - self.test_case.assertFalse(self.OnLoadStart_True) - self.OnLoadStart_True = True - self.test_case.assertEqual(browser.GetUrl(), frame.GetUrl()) - self.test_case.assertEqual(browser.GetUrl(), g_datauri) - - def OnLoadEnd(self, browser, frame, http_code, **_): - # OnLoadEnd should be called only once - self.test_case.assertFalse(self.OnLoadEnd_True) - self.OnLoadEnd_True = True - self.test_case.assertEqual(http_code, 200) - self.frame_source_visitor = FrameSourceVisitor(self, self.test_case) - frame.GetSource(self.frame_source_visitor) - browser.ExecuteJavascript("print('LoadHandler.OnLoadEnd() ok')") - - def OnLoadingStateChange(self, browser, is_loading, can_go_back, - can_go_forward, **_): - if is_loading: - # TODO: this test fails, looks like OnLoadingStaetChange with - # is_loading=False is being called very fast, before - # OnLoadStart and before client handler is set by calling - # browser.SetClientHandler(). - # SOLUTION: allow to set OnLoadingStateChange through - # SetGlobalClientCallback similarly to _OnAfterCreated(). - # self.test_case.assertFalse(self.OnLoadingStateChange_Start_True) - # self.OnLoadingStateChange_Start_True = True - pass - else: - self.test_case.assertFalse(self.OnLoadingStateChange_End_True) - self.OnLoadingStateChange_End_True = True - self.test_case.assertEqual(browser.CanGoBack(), can_go_back) - self.test_case.assertEqual(browser.CanGoForward(), can_go_forward) - - -class DisplayHandler(object): - def __init__(self, test_case): - self.test_case = test_case - - # Asserts for True/False will be checked just before shutdown - self.test_for_True = True # Test whether asserts are working correctly - self.javascript_errors_False = False - self.OnConsoleMessage_True = False - - def OnConsoleMessage(self, message, **_): - if "error" in message.lower() or "uncaught" in message.lower(): - self.javascript_errors_False = True - raise Exception(message) - else: - # Check whether messages from javascript are coming - self.OnConsoleMessage_True = True - subtest_message(message) - - -class FrameSourceVisitor(object): - """Visitor for Frame.GetSource().""" - - def __init__(self, load_handler, test_case): - self.load_handler = load_handler - self.test_case = test_case - - def Visit(self, **_): - self.test_case.assertFalse(self.load_handler.FrameSourceVisitor_True) - self.load_handler.FrameSourceVisitor_True = True - self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee", - g_datauri_data) - - class External(object): """Javascript 'window.external' object.""" @@ -367,4 +232,4 @@ def py_callback(msg_from_js): if __name__ == "__main__": - _test_runner.main(basename(__file__)) + _test_runner.main(os.path.basename(__file__)) diff --git a/unittests/osr_test.py b/unittests/osr_test.py new file mode 100644 index 00000000..e1689329 --- /dev/null +++ b/unittests/osr_test.py @@ -0,0 +1,210 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +"""Off-screen rendering tests.""" + +import unittest +# noinspection PyUnresolvedReferences +import _test_runner +from _common import * + +from cefpython3 import cefpython as cef + +import sys + +g_datauri_data = """ + + + + + + + + + +

Off-screen rendering test

+
+ + +""" +g_datauri = html_to_data_uri(g_datauri_data) + + +class OsrTest_IsolatedTest(unittest.TestCase): + def test_osr(self): + """Main entry point. All the code must run inside one + single test, otherwise strange things happen.""" + + print("") + print("CEF Python {ver}".format(ver=cef.__version__)) + print("Python {ver}".format(ver=sys.version[:6])) + + # Application settings + settings = { + "debug": False, + "log_severity": cef.LOGSEVERITY_ERROR, + "log_file": "", + "windowless_rendering_enabled": True + } + if "--debug" in sys.argv: + settings["debug"] = True + settings["log_severity"] = cef.LOGSEVERITY_INFO + + # Initialize + cef.Initialize(settings) + subtest_message("cef.Initialize() ok") + + # Accessibility handler + accessibility_handler = AccessibilityHandler(self) + cef.SetGlobalClientHandler(accessibility_handler) + subtest_message("cef.SetGlobalClientHandler() ok") + + # Global handler + global_handler = GlobalHandler(self) + cef.SetGlobalClientCallback("OnAfterCreated", + global_handler._OnAfterCreated) + subtest_message("cef.SetGlobalClientCallback() ok") + + # Create browser + window_info = cef.WindowInfo() + window_info.SetAsOffscreen(0) + browser = cef.CreateBrowserSync(window_info=window_info, + url=g_datauri) + + # Javascript bindings + bindings = cef.JavascriptBindings( + bindToFrames=False, bindToPopups=False) + bindings.SetProperty("cefpython_version", cef.GetVersion()) + browser.SetJavascriptBindings(bindings) + subtest_message("browser.SetJavascriptBindings() ok") + + # Enable accessibility + browser.SetAccessibilityState(cef.STATE_ENABLED) + subtest_message("cef.SetAccessibilityState(STATE_ENABLED) ok") + + # Client handlers + client_handlers = [LoadHandler(self, g_datauri), + DisplayHandler(self), + RenderHandler(self)] + for handler in client_handlers: + browser.SetClientHandler(handler) + + # Initiate OSR rendering + browser.SendFocusEvent(True) + browser.WasResized() + + # Message loop + run_message_loop() + + # Close browser and clean reference + browser.CloseBrowser(True) + del browser + subtest_message("browser.CloseBrowser() ok") + + # Give it some time to close before checking asserts + # and calling shutdown. + do_message_loop_work(25) + + # Automatic check of asserts in handlers + # noinspection PyTypeChecker + check_auto_asserts(self, [] + client_handlers + + [global_handler, + accessibility_handler]) + + # Test shutdown of CEF + cef.Shutdown() + subtest_message("cef.Shutdown() ok") + + # Display summary + show_test_summary(__file__) + sys.stdout.flush() + + +class AccessibilityHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown. + # Test whether asserts are working correctly. + self.test_for_True = True + + self.javascript_errors_False = False + self._OnAccessibilityTreeChange_True = False + self._OnAccessibilityLocationChange_True = False + self.loadComplete_True = False + self.layoutComplete_True = False + + def _OnAccessibilityTreeChange(self, value): + self._OnAccessibilityTreeChange_True = True + for event in value: + if "event_type" in event: + if event["event_type"] == "loadComplete": + # LoadHandler.OnLoadEnd is called after this event + self.test_case.assertFalse(self.loadComplete_True) + self.loadComplete_True = True + elif event["event_type"] == "layoutComplete": + # layoutComplete event occurs twice, one when a blank + # page is loaded and second time when loading datauri. + if self.loadComplete_True: + self.test_case.assertFalse(self.layoutComplete_True) + self.layoutComplete_True = True + + def _OnAccessibilityLocationChange(self, **_): + self._OnAccessibilityLocationChange_True = True + + +class RenderHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown. + # Test whether asserts are working correctly. + self.test_for_True = True + + self.GetViewRect_True = False + self.OnPaint_True = False + + def GetViewRect(self, rect_out, **_): + """Called to retrieve the view rectangle which is relative + to screen coordinates. Return True if the rectangle was + provided.""" + # rect_out --> [x, y, width, height] + self.GetViewRect_True = True + rect_out.extend([0, 0, 800, 600]) + return True + + def OnPaint(self, element_type, paint_buffer, **_): + """Called when an element should be painted.""" + if element_type == cef.PET_VIEW: + self.test_case.assertEqual(paint_buffer.width, 800) + self.test_case.assertEqual(paint_buffer.height, 600) + if not self.OnPaint_True: + self.OnPaint_True = True + subtest_message("RenderHandler.OnPaint: viewport ok") + else: + raise Exception("Unsupported element_type in OnPaint") + + +if __name__ == "__main__": + _test_runner.main(os.path.basename(__file__)) From a737d26cad764c7ccfaf9c8151515dcbc2dce09d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 18 Aug 2018 15:37:47 +0200 Subject: [PATCH 055/177] Enhance unit tests reliability of js bindings (#59) --- unittests/_common.py | 16 +++++++++++++++- unittests/main_test.py | 23 +++++++++++++++++++---- unittests/osr_test.py | 22 ++++++++++++++++++---- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/unittests/_common.py b/unittests/_common.py index 2c2e52a9..5011bda4 100644 --- a/unittests/_common.py +++ b/unittests/_common.py @@ -13,6 +13,7 @@ MESSAGE_LOOP_RANGE = 100 # each iteration is 0.01 sec g_subtests_ran = 0 +g_js_code_completed = False def subtest_message(message): @@ -50,7 +51,21 @@ def do_message_loop_work(work_loops): time.sleep(0.01) +def js_code_completed(): + """Sometimes window.onload can execute before javascript bindings + are ready if the document loads very fast. When setting javascript + bindings it can take some time, because these bindings are sent + via IPC messaging to the Renderer process.""" + global g_js_code_completed + assert not g_js_code_completed + g_js_code_completed = True + subtest_message("js_code_completed() ok") + + def check_auto_asserts(test_case, objects): + # Check if js code completed + test_case.assertTrue(g_js_code_completed) + # Automatic check of asserts in handlers and in external for obj in objects: test_for_True = False # Test whether asserts are working correctly @@ -170,4 +185,3 @@ def Visit(self, value): self.load_handler.FrameSourceVisitor_True = True self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee", value) - diff --git a/unittests/main_test.py b/unittests/main_test.py index 717daf6d..58fec488 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -31,9 +31,17 @@ msg = msg.replace("error", "error"); document.getElementById("console").innerHTML += msg+"
"; } - window.onload = function(){ - print("window.onload() ok"); - + function onload_helper() { + if (!window.hasOwnProperty("cefpython_version")) { + // Sometimes page could finish loading before javascript + // bindings are available. Javascript bindings are sent + // from the browser process to the renderer process via + // IPC messaging and it can take some time (5-10ms). If + // the page loads very fast window.onload could execute + // before bindings are available. + setTimeout(onload_helper, 10); + return; + } version = cefpython_version print("CEF Python: "+version.version+""); print("Chrome: "+version.chrome_version+""); @@ -78,7 +86,12 @@ py_callback("String sent from Javascript"); print("py_callback() ok"); }); - }; + js_code_completed(); + } + window.onload = function() { + print("window.onload() ok"); + onload_helper(); + } @@ -133,6 +146,7 @@ def test_main(self): external = External(self) bindings = cef.JavascriptBindings( bindToFrames=False, bindToPopups=False) + bindings.SetFunction("js_code_completed", js_code_completed) bindings.SetFunction("test_function", external.test_function) bindings.SetProperty("test_property1", external.test_property1) bindings.SetProperty("test_property2", external.test_property2) @@ -181,6 +195,7 @@ def test_main(self): # and calling shutdown. do_message_loop_work(25) + # Asserts before shutdown # noinspection PyTypeChecker check_auto_asserts(self, [] + client_handlers + [global_handler, diff --git a/unittests/osr_test.py b/unittests/osr_test.py index e1689329..4c26cb8f 100644 --- a/unittests/osr_test.py +++ b/unittests/osr_test.py @@ -31,13 +31,26 @@ msg = msg.replace("error", "error"); document.getElementById("console").innerHTML += msg+"
"; } - window.onload = function(){ - print("window.onload() ok"); - + function onload_helper() { + if (!window.hasOwnProperty("cefpython_version")) { + // Sometimes page could finish loading before javascript + // bindings are available. Javascript bindings are sent + // from the browser process to the renderer process via + // IPC messaging and it can take some time (5-10ms). If + // the page loads very fast window.onload could execute + // before bindings are available. + setTimeout(onload_helper, 10); + return; + } version = cefpython_version print("CEF Python: "+version.version+""); print("Chrome: "+version.chrome_version+""); print("CEF: "+version.cef_version+""); + js_code_completed(); + } + window.onload = function() { + print("window.onload() ok"); + onload_helper(); } @@ -95,6 +108,7 @@ def test_osr(self): # Javascript bindings bindings = cef.JavascriptBindings( bindToFrames=False, bindToPopups=False) + bindings.SetFunction("js_code_completed", js_code_completed) bindings.SetProperty("cefpython_version", cef.GetVersion()) browser.SetJavascriptBindings(bindings) subtest_message("browser.SetJavascriptBindings() ok") @@ -126,7 +140,7 @@ def test_osr(self): # and calling shutdown. do_message_loop_work(25) - # Automatic check of asserts in handlers + # Asserts before shutdown # noinspection PyTypeChecker check_auto_asserts(self, [] + client_handlers + [global_handler, From 073f9d2abcb48091f7bd2288f20825bec44ad44f Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 18 Aug 2018 15:46:36 +0200 Subject: [PATCH 056/177] Fix run_examples.py on Mac with PyQt5 --- tools/run_examples.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/run_examples.py b/tools/run_examples.py index d78a1a47..87fe1296 100644 --- a/tools/run_examples.py +++ b/tools/run_examples.py @@ -188,8 +188,12 @@ def check_installed_packages(): } for package in packages: try: - importlib.import_module(package) - packages[package] = True + if package == "PyQt5": + # Strange issue on Mac, PyQt5 is an empty built-in module + from PyQt5 import QtGui + else: + importlib.import_module(package) + packages[package] = True except ImportError: packages[package] = False packages["gi"] = check_gi_installed() From 1671a40361b283c6b6036614b0615b2898d9872a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 18 Aug 2018 19:11:10 +0200 Subject: [PATCH 057/177] Add RenderHandler.OnTextSelectionChanged and Browser.Invalidate (#403). Add tests for OnTextSelectionChanged. Add on_load_end() helper func in unit tests. --- api/Browser.md | 17 +++++++++++++++ api/RenderHandler.md | 19 +++++++++++++++++ src/browser.pyx | 7 ++++++- src/cef_v59..v66_changes.txt | 5 +++-- src/client_handler/render_handler.cpp | 8 +++++++ src/client_handler/render_handler.h | 4 ++++ src/extern/cef/cef_browser.pxd | 3 ++- src/extern/cef/cef_types.pxd | 4 ++++ src/handlers/render_handler.pyx | 19 +++++++++++++++++ unittests/_common.py | 12 +++++++++++ unittests/osr_test.py | 30 ++++++++++++++++++++++++++- 11 files changed, 123 insertions(+), 5 deletions(-) diff --git a/api/Browser.md b/api/Browser.md index cf2ab61d..beda31e5 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -591,6 +591,23 @@ machinery. Returns true if a document has been loaded in the browser. +### Invalidate + +| | | +| --- | --- | +| element_type | PaintElementType | +| __Return__ | void | + +Description from upstream CEF: +> Invalidate the view. The browser will call CefRenderHandler::OnPaint +> asynchronously. This method is only used when window rendering is +> disabled. + +`PaintElementType` enum values defined in cefpython module: +* PET_VIEW +* PET_POPUP + + ### IsFullscreen | | | diff --git a/api/RenderHandler.md b/api/RenderHandler.md index bb25ac01..3b0d0f0d 100644 --- a/api/RenderHandler.md +++ b/api/RenderHandler.md @@ -177,6 +177,25 @@ Called when the browser's cursor has changed. If |type| is CT_CUSTOM then Called when the scroll offset has changed. +### OnTextSelectionChanged + +| Parameter | Type | +| --- | --- | +| browser | [Browser](Browser.md) | +| selected_text | str | +| selected_range | list[x, y] | +| __Return__ | void | + +Description from upstream CEF: +> Called when text selection has changed for the specified |browser|. +> |selected_text| is the currently selected text and |selected_range| is +> the character range. + +NOTE: this callback seems to be called only when selecting text +with a mouse. When selecting text programmatically using javascript +code it doesn't get called. + + ### StartDragging | Parameter | Type | diff --git a/src/browser.pyx b/src/browser.pyx index 5f26e7d7..335654d6 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -247,7 +247,8 @@ cdef class PyBrowser: "GetScreenRect", "OnPopupShow", "OnPopupSize", "OnPaint", "OnCursorChange", "OnScrollOffsetChanged", - "StartDragging", "UpdateDragCursor"] + "StartDragging", "UpdateDragCursor", + "OnTextSelectionChanged"] # JavascriptDialogHandler self.allowedClientCallbacks += ["OnJavascriptDialog", "OnBeforeUnloadJavascriptDialog", @@ -476,6 +477,10 @@ cdef class PyBrowser: cpdef py_bool HasDocument(self): return self.GetCefBrowser().get().HasDocument() + cpdef py_void Invalidate(self, + cef_types.cef_paint_element_type_t element_type): + return self.GetCefBrowserHost().get().Invalidate(element_type) + cpdef py_bool IsFullscreen(self): return bool(self.isFullscreen) diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 7a6fc5fb..2bb23ccf 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -71,6 +71,8 @@ NEW FEATURES ------------ + unittests/osr_test.py - new test for off-screen rendering mode ++ cefpython.SetGlobalClientHandler ++ Browser.Invalidate internal/cef_types.h + cef_log_severity_t: new key LOGSEVERITY_DEBUG (no need to expose, @@ -85,10 +87,9 @@ internal/cef_types.h cef_accessibility_handler.h + CefAccessibilityHandler + CefRenderHandler::GetAccessibilityHandler -+ cefpython.SetGlobalClientHandler cef_render_handler.h -- OnTextSelectionChanged ++ OnTextSelectionChanged cef_browser.h + SetAccessibilityState diff --git a/src/client_handler/render_handler.cpp b/src/client_handler/render_handler.cpp index f6b4d5be..cf45d15e 100644 --- a/src/client_handler/render_handler.cpp +++ b/src/client_handler/render_handler.cpp @@ -106,3 +106,11 @@ void RenderHandler::UpdateDragCursor(CefRefPtr browser, REQUIRE_UI_THREAD(); RenderHandler_UpdateDragCursor(browser, operation); } + +void RenderHandler::OnTextSelectionChanged(CefRefPtr browser, + const CefString& selected_text, + const CefRange& selected_range) { + REQUIRE_UI_THREAD(); + RenderHandler_OnTextSelectionChanged(browser, selected_text, + selected_range); +} diff --git a/src/client_handler/render_handler.h b/src/client_handler/render_handler.h index b9eb59fa..75eee86c 100644 --- a/src/client_handler/render_handler.h +++ b/src/client_handler/render_handler.h @@ -64,6 +64,10 @@ class RenderHandler : public CefRenderHandler, void UpdateDragCursor(CefRefPtr browser, cef_drag_operations_mask_t operation) override; + void OnTextSelectionChanged(CefRefPtr browser, + const CefString& selected_text, + const CefRange& selected_range) override; + private: IMPLEMENT_REFCOUNTING(RenderHandler); }; diff --git a/src/extern/cef/cef_browser.pxd b/src/extern/cef/cef_browser.pxd index 6c5e599a..31a4a29d 100644 --- a/src/extern/cef/cef_browser.pxd +++ b/src/extern/cef/cef_browser.pxd @@ -11,7 +11,7 @@ from libcpp cimport bool as cpp_bool from libcpp.vector cimport vector as cpp_vector from cef_frame cimport CefFrame cimport cef_types -from cef_types cimport int64, cef_state_t +from cef_types cimport int64, cef_state_t, PaintElementType from cef_types cimport CefBrowserSettings, CefPoint from cef_drag_data cimport CefDragData from cef_types cimport CefMouseEvent @@ -87,6 +87,7 @@ cdef extern from "include/cef_browser.h": void AddWordToDictionary(const CefString& word) void SetAccessibilityState(cef_state_t accessibility_state) + void Invalidate(cef_types.cef_paint_element_type_t type) cdef cppclass CefBrowser: diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index 70263780..6bd21ba4 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -377,3 +377,7 @@ cdef extern from "include/internal/cef_types.h": ctypedef enum cef_focus_source_t: FOCUS_SOURCE_NAVIGATION, FOCUS_SOURCE_SYSTEM, + + cdef cppclass CefRange: + int from_val "from" + int to_val "to" diff --git a/src/handlers/render_handler.pyx b/src/handlers/render_handler.pyx index 1594d6bd..a8ad4912 100644 --- a/src/handlers/render_handler.pyx +++ b/src/handlers/render_handler.pyx @@ -4,8 +4,10 @@ include "../cefpython.pyx" include "../browser.pyx" +include "../string_utils.pyx" cimport cef_types +from cef_types cimport CefRange # cef_paint_element_type_t, PaintElementType PET_VIEW = cef_types.PET_VIEW @@ -288,3 +290,20 @@ cdef public void RenderHandler_UpdateDragCursor( (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) +cdef public void RenderHandler_OnTextSelectionChanged( + CefRefPtr[CefBrowser] cef_browser, + const CefString& selected_text, + const CefRange& selected_range + ) except * with gil: + cdef PyBrowser browser + try: + browser = GetPyBrowser(cef_browser, "OnTextSelectionChanged") + callback = browser.GetClientCallback("OnTextSelectionChanged") + if callback: + callback(browser=browser, + selected_text=CefToPyString(selected_text), + selected_range=[selected_range.from_val, + selected_range.to_val]) + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) diff --git a/unittests/_common.py b/unittests/_common.py index 5011bda4..59052583 100644 --- a/unittests/_common.py +++ b/unittests/_common.py @@ -14,6 +14,7 @@ g_subtests_ran = 0 g_js_code_completed = False +g_on_load_end_callbacks = [] def subtest_message(message): @@ -51,6 +52,11 @@ def do_message_loop_work(work_loops): time.sleep(0.01) +def on_load_end(callback, *args): + global g_on_load_end_callbacks + g_on_load_end_callbacks.append([callback, args]) + + def js_code_completed(): """Sometimes window.onload can execute before javascript bindings are ready if the document loads very fast. When setting javascript @@ -154,6 +160,12 @@ def OnLoadEnd(self, browser, frame, http_code, **_): frame.GetSource(self.frame_source_visitor) browser.ExecuteJavascript("print('LoadHandler.OnLoadEnd() ok')") + subtest_message("Executing callbacks registered with on_load_end()") + global g_on_load_end_callbacks + for callback_data in g_on_load_end_callbacks: + callback_data[0](*callback_data[1]) + del g_on_load_end_callbacks + def OnLoadingStateChange(self, browser, is_loading, can_go_back, can_go_forward, **_): if is_loading: diff --git a/unittests/osr_test.py b/unittests/osr_test.py index 4c26cb8f..323f2de6 100644 --- a/unittests/osr_test.py +++ b/unittests/osr_test.py @@ -58,6 +58,7 @@

Off-screen rendering test

+
Test selection.
""" @@ -128,6 +129,9 @@ def test_osr(self): browser.SendFocusEvent(True) browser.WasResized() + # Test selection + on_load_end(select_h1_text, browser) + # Message loop run_message_loop() @@ -188,6 +192,17 @@ def _OnAccessibilityLocationChange(self, **_): self._OnAccessibilityLocationChange_True = True +def select_h1_text(browser): + browser.SendMouseClickEvent(0, 0, cef.MOUSEBUTTON_LEFT, + mouseUp=False, clickCount=1) + browser.SendMouseMoveEvent(400, 20, mouseLeave=False, + modifiers=cef.EVENTFLAG_LEFT_MOUSE_BUTTON) + browser.SendMouseClickEvent(400, 20, cef.MOUSEBUTTON_LEFT, + mouseUp=True, clickCount=1) + browser.Invalidate(cef.PET_VIEW) + subtest_message("select_h1_text() ok") + + class RenderHandler(object): def __init__(self, test_case): self.test_case = test_case @@ -198,6 +213,7 @@ def __init__(self, test_case): self.GetViewRect_True = False self.OnPaint_True = False + self.OnTextSelectionChanged_True = False def GetViewRect(self, rect_out, **_): """Called to retrieve the view rectangle which is relative @@ -208,7 +224,7 @@ def GetViewRect(self, rect_out, **_): rect_out.extend([0, 0, 800, 600]) return True - def OnPaint(self, element_type, paint_buffer, **_): + def OnPaint(self, browser, element_type, paint_buffer, **_): """Called when an element should be painted.""" if element_type == cef.PET_VIEW: self.test_case.assertEqual(paint_buffer.width, 800) @@ -219,6 +235,18 @@ def OnPaint(self, element_type, paint_buffer, **_): else: raise Exception("Unsupported element_type in OnPaint") + def OnTextSelectionChanged(self, selected_text, selected_range, **_): + if not self.OnTextSelectionChanged_True: + self.OnTextSelectionChanged_True = True + # First call + self.test_case.assertEqual(selected_text, "") + self.test_case.assertEqual(selected_range, [0, 0]) + else: + # Second call. + #

tag should be selected. + self.test_case.assertEqual(selected_text, + "Off-screen rendering test") + if __name__ == "__main__": _test_runner.main(os.path.basename(__file__)) From f8a460f4afdc0887aaf866da2b024f86646fbf56 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 18 Aug 2018 20:23:29 +0200 Subject: [PATCH 058/177] Add DisplayHandler.OnAutoResize and Browser.SetAutoResizeEnabled (#403) --- README.md | 114 +++++++++++++------------ api/API-index.md | 4 + api/Browser.md | 17 ++++ api/DisplayHandler.md | 17 ++++ api/RenderHandler.md | 1 + src/browser.pyx | 13 ++- src/cef_v59..v66_changes.txt | 4 +- src/client_handler/display_handler.cpp | 6 ++ src/client_handler/display_handler.h | 3 + src/extern/cef/cef_browser.pxd | 7 +- src/handlers/display_handler.pyx | 19 +++++ unittests/_common.py | 9 ++ unittests/main_test.py | 26 +++++- 13 files changed, 179 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 3b9f1a21..dafbf9ff 100644 --- a/README.md +++ b/README.md @@ -256,61 +256,61 @@ Additional information for v31.2 release: ### API categories -#### Modules - - * [cefpython](api/cefpython.md#cefpython) module - - -#### Settings - - * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary - * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary - * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary - - -#### Classes and objects - - * [Browser](api/Browser.md#browser-object) object - * [Callback](api/Callback.md#callback-object) object - * [Cookie](api/Cookie.md#cookie-class) class - * [CookieManager](api/CookieManager.md#cookiemanager-class) class - * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) - * [DragData](api/DragData.md#dragdata-object) object - * [Frame](api/Frame.md#frame-object) object - * [Image](api/Image.md#image-object) object - * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class - * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object - * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object - * [Request](api/Request.md#request-class) class - * [Response](api/Response.md#response-object) object - * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object - * [WebRequest](api/WebRequest.md#webrequest-class) class - * [WindowInfo](api/WindowInfo.md#windowinfo-class) class - * [WindowUtils](api/WindowUtils.md#windowutils-class) class - - -#### Client handlers (interfaces) - - * [AccessibilityHandler](api/AccessibilityHandler.md#accessibilityhandler-interface) - * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) - * [DownloadHandler](api/DownloadHandler.md#downloadhandler) - * [FocusHandler](api/FocusHandler.md#focushandler-interface) - * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) - * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) - * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) - * [LoadHandler](api/LoadHandler.md#loadhandler-interface) - * [RenderHandler](api/RenderHandler.md#renderhandler-interface) - * [RequestHandler](api/RequestHandler.md#requesthandler-interface) - * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) - * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) - - -#### Other interfaces - - * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface - * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface - * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface - +#### Modules + + * [cefpython](api/cefpython.md#cefpython) module + + +#### Settings + + * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary + * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary + * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary + + +#### Classes and objects + + * [Browser](api/Browser.md#browser-object) object + * [Callback](api/Callback.md#callback-object) object + * [Cookie](api/Cookie.md#cookie-class) class + * [CookieManager](api/CookieManager.md#cookiemanager-class) class + * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) + * [DragData](api/DragData.md#dragdata-object) object + * [Frame](api/Frame.md#frame-object) object + * [Image](api/Image.md#image-object) object + * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class + * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object + * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object + * [Request](api/Request.md#request-class) class + * [Response](api/Response.md#response-object) object + * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object + * [WebRequest](api/WebRequest.md#webrequest-class) class + * [WindowInfo](api/WindowInfo.md#windowinfo-class) class + * [WindowUtils](api/WindowUtils.md#windowutils-class) class + + +#### Client handlers (interfaces) + + * [AccessibilityHandler](api/AccessibilityHandler.md#accessibilityhandler-interface) + * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) + * [DownloadHandler](api/DownloadHandler.md#downloadhandler) + * [FocusHandler](api/FocusHandler.md#focushandler-interface) + * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) + * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) + * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) + * [LoadHandler](api/LoadHandler.md#loadhandler-interface) + * [RenderHandler](api/RenderHandler.md#renderhandler-interface) + * [RequestHandler](api/RequestHandler.md#requesthandler-interface) + * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) + * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) + + +#### Other interfaces + + * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface + * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface + * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface + ### API index @@ -391,6 +391,7 @@ Additional information for v31.2 release: * [HandleKeyEventAfterTextInputClient](api/Browser.md#handlekeyeventaftertextinputclient) * [HandleKeyEventBeforeTextInputClient](api/Browser.md#handlekeyeventbeforetextinputclient) * [HasDocument](api/Browser.md#hasdocument) + * [Invalidate](api/Browser.md#invalidate) * [IsFullscreen](api/Browser.md#isfullscreen) * [IsLoading](api/Browser.md#isloading) * [IsMouseCursorChangeDisabled](api/Browser.md#ismousecursorchangedisabled) @@ -405,6 +406,7 @@ Additional information for v31.2 release: * [Reload](api/Browser.md#reload) * [ReloadIgnoreCache](api/Browser.md#reloadignorecache) * [ReplaceMisspelling](api/Browser.md#replacemisspelling) + * [SetAutoResizeEnabled](api/Browser.md#setautoresizeenabled) * [SetBounds](api/Browser.md#setbounds) * [SendKeyEvent](api/Browser.md#sendkeyevent) * [SendMouseClickEvent](api/Browser.md#sendmouseclickevent) @@ -519,6 +521,7 @@ Additional information for v31.2 release: * [Visit](api/CookieVisitor.md#visit) * [DisplayHandler (interface)](api/DisplayHandler.md#displayhandler-interface) * [OnAddressChange](api/DisplayHandler.md#onaddresschange) + * [OnAutoResize](api/DisplayHandler.md#onautoresize) * [OnTitleChange](api/DisplayHandler.md#ontitlechange) * [OnTooltip](api/DisplayHandler.md#ontooltip) * [OnStatusMessage](api/DisplayHandler.md#onstatusmessage) @@ -670,6 +673,7 @@ Additional information for v31.2 release: * [OnPaint](api/RenderHandler.md#onpaint) * [OnCursorChange](api/RenderHandler.md#oncursorchange) * [OnScrollOffsetChanged](api/RenderHandler.md#onscrolloffsetchanged) + * [OnTextSelectionChanged](api/RenderHandler.md#ontextselectionchanged) * [StartDragging](api/RenderHandler.md#startdragging) * [UpdateDragCursor](api/RenderHandler.md#updatedragcursor) * [Request (class)](api/Request.md#request-class) diff --git a/api/API-index.md b/api/API-index.md index fb8c5efe..9c5374da 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -79,6 +79,7 @@ * [HandleKeyEventAfterTextInputClient](Browser.md#handlekeyeventaftertextinputclient) * [HandleKeyEventBeforeTextInputClient](Browser.md#handlekeyeventbeforetextinputclient) * [HasDocument](Browser.md#hasdocument) + * [Invalidate](Browser.md#invalidate) * [IsFullscreen](Browser.md#isfullscreen) * [IsLoading](Browser.md#isloading) * [IsMouseCursorChangeDisabled](Browser.md#ismousecursorchangedisabled) @@ -93,6 +94,7 @@ * [Reload](Browser.md#reload) * [ReloadIgnoreCache](Browser.md#reloadignorecache) * [ReplaceMisspelling](Browser.md#replacemisspelling) + * [SetAutoResizeEnabled](Browser.md#setautoresizeenabled) * [SetBounds](Browser.md#setbounds) * [SendKeyEvent](Browser.md#sendkeyevent) * [SendMouseClickEvent](Browser.md#sendmouseclickevent) @@ -207,6 +209,7 @@ * [Visit](CookieVisitor.md#visit) * [DisplayHandler (interface)](DisplayHandler.md#displayhandler-interface) * [OnAddressChange](DisplayHandler.md#onaddresschange) + * [OnAutoResize](DisplayHandler.md#onautoresize) * [OnTitleChange](DisplayHandler.md#ontitlechange) * [OnTooltip](DisplayHandler.md#ontooltip) * [OnStatusMessage](DisplayHandler.md#onstatusmessage) @@ -358,6 +361,7 @@ * [OnPaint](RenderHandler.md#onpaint) * [OnCursorChange](RenderHandler.md#oncursorchange) * [OnScrollOffsetChanged](RenderHandler.md#onscrolloffsetchanged) + * [OnTextSelectionChanged](RenderHandler.md#ontextselectionchanged) * [StartDragging](RenderHandler.md#startdragging) * [UpdateDragCursor](RenderHandler.md#updatedragcursor) * [Request (class)](Request.md#request-class) diff --git a/api/Browser.md b/api/Browser.md index beda31e5..977d9005 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -55,6 +55,7 @@ Table of contents: * [HandleKeyEventAfterTextInputClient](#handlekeyeventaftertextinputclient) * [HandleKeyEventBeforeTextInputClient](#handlekeyeventbeforetextinputclient) * [HasDocument](#hasdocument) + * [Invalidate](#invalidate) * [IsFullscreen](#isfullscreen) * [IsLoading](#isloading) * [IsMouseCursorChangeDisabled](#ismousecursorchangedisabled) @@ -69,6 +70,7 @@ Table of contents: * [Reload](#reload) * [ReloadIgnoreCache](#reloadignorecache) * [ReplaceMisspelling](#replacemisspelling) + * [SetAutoResizeEnabled](#setautoresizeenabled) * [SetBounds](#setbounds) * [SendKeyEvent](#sendkeyevent) * [SendMouseClickEvent](#sendmouseclickevent) @@ -753,6 +755,21 @@ If a misspelled word is currently selected in an editable node calling this method will replace it with the specified |word|. +### SetAutoResizeEnabled + +| Parameter | Type | +| --- | --- | +| enabled | bool | +| min_size | list[width, height] | +| max_size | list[width, heifght] | +| __Return__ | void | + +Description from upstream CEF: +> Enable notifications of auto resize via CefDisplayHandler::OnAutoResize. +> Notifications are disabled by default. |min_size| and |max_size| define the +> range of allowed sizes. + + ### SetBounds | Parameter | Type | diff --git a/api/DisplayHandler.md b/api/DisplayHandler.md index 4e45efaf..43f6342f 100644 --- a/api/DisplayHandler.md +++ b/api/DisplayHandler.md @@ -11,6 +11,7 @@ For an example of how to implement handler see [cefpython](cefpython.md).CreateB Table of contents: * [Callbacks](#callbacks) * [OnAddressChange](#onaddresschange) + * [OnAutoResize](#onautoresize) * [OnTitleChange](#ontitlechange) * [OnTooltip](#ontooltip) * [OnStatusMessage](#onstatusmessage) @@ -32,6 +33,22 @@ Table of contents: Called when a frame's address has changed. + +### OnAutoResize + +| Parameter | Type | +| --- | --- | +| browser | [Browser](Browser.md) | +| new_size | list[width, height] | +| __Return__ | bool | + +Description from upstream CEF: +> Called when auto-resize is enabled via CefBrowserHost::SetAutoResizeEnabled +> and the contents have auto-resized. |new_size| will be the desired size in +> view coordinates. Return true if the resize was handled or false for +> default handling. + + ### OnTitleChange | Parameter | Type | diff --git a/api/RenderHandler.md b/api/RenderHandler.md index 3b0d0f0d..a33a2154 100644 --- a/api/RenderHandler.md +++ b/api/RenderHandler.md @@ -22,6 +22,7 @@ Table of contents: * [OnPaint](#onpaint) * [OnCursorChange](#oncursorchange) * [OnScrollOffsetChanged](#onscrolloffsetchanged) + * [OnTextSelectionChanged](#ontextselectionchanged) * [StartDragging](#startdragging) * [UpdateDragCursor](#updatedragcursor) diff --git a/src/browser.pyx b/src/browser.pyx index 335654d6..44ec439d 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -218,7 +218,7 @@ cdef class PyBrowser: # DisplayHandler self.allowedClientCallbacks += [ "OnAddressChange", "OnTitleChange", "OnTooltip", - "OnStatusMessage", "OnConsoleMessage"] + "OnStatusMessage", "OnConsoleMessage", "OnAutoResize"] # KeyboardHandler self.allowedClientCallbacks += ["OnPreKeyEvent", "OnKeyEvent"] # RequestHandler @@ -513,6 +513,17 @@ cdef class PyBrowser: PyToCefString(word, cef_word) self.GetCefBrowserHost().get().ReplaceMisspelling(cef_word) + cpdef py_void SetAutoResizeEnabled(self, + py_bool enabled, + list min_size, + list max_size): + self.GetCefBrowserHost().get().SetAutoResizeEnabled( + bool(enabled), + CefSize(min_size[0], min_size[1]), + CefSize(max_size[0], max_size[1]) + ) + + cpdef py_void SetBounds(self, int x, int y, int width, int height): IF UNAME_SYSNAME == "Linux": x11.SetX11WindowBounds(self.GetCefBrowser(), x, y, width, height) diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 2bb23ccf..27260280 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -93,7 +93,7 @@ cef_render_handler.h cef_browser.h + SetAccessibilityState -- SetAutoResizeEnabled ++ SetAutoResizeEnabled - GetExtension - IsBackgroundHost @@ -101,7 +101,7 @@ cef_cookie.h - GetBlockingManager cef_display_handler.h -- OnAutoResize ++ OnAutoResize - OnLoadingProgressChange cef_drag_data.h diff --git a/src/client_handler/display_handler.cpp b/src/client_handler/display_handler.cpp index 8e770510..f5ad596e 100644 --- a/src/client_handler/display_handler.cpp +++ b/src/client_handler/display_handler.cpp @@ -48,3 +48,9 @@ bool DisplayHandler::OnConsoleMessage(CefRefPtr browser, return DisplayHandler_OnConsoleMessage(browser, level, message, source, line); } + +bool DisplayHandler::OnAutoResize(CefRefPtr browser, + const CefSize& new_size) { + REQUIRE_UI_THREAD(); + return DisplayHandler_OnAutoResize(browser, new_size); +} diff --git a/src/client_handler/display_handler.h b/src/client_handler/display_handler.h index f6f4c865..f058c475 100644 --- a/src/client_handler/display_handler.h +++ b/src/client_handler/display_handler.h @@ -33,6 +33,9 @@ class DisplayHandler : public CefDisplayHandler const CefString& source, int line) override; + bool OnAutoResize(CefRefPtr browser, + const CefSize& new_size) override; + private: IMPLEMENT_REFCOUNTING(DisplayHandler); }; diff --git a/src/extern/cef/cef_browser.pxd b/src/extern/cef/cef_browser.pxd index 31a4a29d..042d0194 100644 --- a/src/extern/cef/cef_browser.pxd +++ b/src/extern/cef/cef_browser.pxd @@ -11,7 +11,7 @@ from libcpp cimport bool as cpp_bool from libcpp.vector cimport vector as cpp_vector from cef_frame cimport CefFrame cimport cef_types -from cef_types cimport int64, cef_state_t, PaintElementType +from cef_types cimport int64, cef_state_t, CefSize from cef_types cimport CefBrowserSettings, CefPoint from cef_drag_data cimport CefDragData from cef_types cimport CefMouseEvent @@ -87,7 +87,10 @@ cdef extern from "include/cef_browser.h": void AddWordToDictionary(const CefString& word) void SetAccessibilityState(cef_state_t accessibility_state) - void Invalidate(cef_types.cef_paint_element_type_t type) + void Invalidate(cef_types.cef_paint_element_type_t element_type) + void SetAutoResizeEnabled(cpp_bool enabled, + const CefSize& min_size, + const CefSize& max_size) cdef cppclass CefBrowser: diff --git a/src/handlers/display_handler.pyx b/src/handlers/display_handler.pyx index 3ad7a7f6..c60e49bc 100644 --- a/src/handlers/display_handler.pyx +++ b/src/handlers/display_handler.pyx @@ -25,6 +25,25 @@ cdef public void DisplayHandler_OnAddressChange( (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) + +cdef public cpp_bool DisplayHandler_OnAutoResize( + CefRefPtr[CefBrowser] cef_browser, + const CefSize& new_size + ) except * with gil: + cdef PyBrowser browser + cdef object callback + try: + browser = GetPyBrowser(cef_browser, "OnAutoResize") + callback = browser.GetClientCallback("OnAutoResize") + if callback: + return bool(callback(browser=browser, new_size=[new_size.width, + new_size.height])) + return False + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) + + cdef public void DisplayHandler_OnTitleChange( CefRefPtr[CefBrowser] cefBrowser, const CefString& cefTitle diff --git a/unittests/_common.py b/unittests/_common.py index 59052583..b71c92f4 100644 --- a/unittests/_common.py +++ b/unittests/_common.py @@ -6,9 +6,18 @@ import base64 import os +import platform import sys import time +# Platforms +SYSTEM = platform.system().upper() +if SYSTEM == "DARWIN": + SYSTEM = "MAC" +WINDOWS = SYSTEM if SYSTEM == "WINDOWS" else False +LINUX = SYSTEM if SYSTEM == "LINUX" else False +MAC = SYSTEM if SYSTEM == "MAC" else False + # To show the window for an extended period of time increase this number. MESSAGE_LOOP_RANGE = 100 # each iteration is 0.01 sec diff --git a/unittests/main_test.py b/unittests/main_test.py index 58fec488..bfa1514c 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -14,6 +14,7 @@ import os import sys + g_datauri_data = """ @@ -134,10 +135,13 @@ def test_main(self): # Create browser browser = cef.CreateBrowserSync(url=g_datauri) self.assertIsNotNone(browser, "Browser object") + browser.SetFocus(True) subtest_message("cef.CreateBrowserSync() ok") # Client handlers - client_handlers = [LoadHandler(self, g_datauri), DisplayHandler(self)] + client_handlers = [LoadHandler(self, g_datauri), + DisplayHandler(self), + DisplayHandler2(self)] for handler in client_handlers: browser.SetClientHandler(handler) subtest_message("browser.SetClientHandler() ok") @@ -161,6 +165,12 @@ def test_main(self): browser.SetJavascriptBindings(bindings) subtest_message("browser.SetJavascriptBindings() ok") + # Set auto resize. Call it after js bindings were set. + browser.SetAutoResizeEnabled(enabled=True, + min_size=[800, 600], + max_size=[800, 600]) + subtest_message("browser.SetAutoResizeEnabled() ok") + # Test Request.SetPostData(list) # noinspection PyArgumentList req = cef.Request.CreateRequest() @@ -210,6 +220,20 @@ def test_main(self): sys.stdout.flush() +class DisplayHandler2(object): + def __init__(self, test_case): + self.test_case = test_case + # Asserts for True/False will be checked just before shutdown. + # Test whether asserts are working correctly. + self.test_for_True = True + self.OnAutoResize_True = False + + def OnAutoResize(self, new_size, **_): + assert not self.OnAutoResize_True + self.OnAutoResize_True = True + self.test_case.assertEqual(new_size, [800, 600]) + + class External(object): """Javascript 'window.external' object.""" From c559f44f70238ac695fbcb380991399cb8b561c4 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 18 Aug 2018 20:39:42 +0200 Subject: [PATCH 059/177] Fix AutoResize unit tests on Linux --- unittests/main_test.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/unittests/main_test.py b/unittests/main_test.py index bfa1514c..91f27cc8 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -23,6 +23,8 @@ body,html { font-family: Arial; font-size: 11pt; + width: 800px; + heiht: 600px; } + + +

Javascript Bindings

+
+ + +""" + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url=cef.GetDataUrl(g_htmlcode), + window_title="OnBeforeClose") + browser.SetClientHandler(LifespanHandler()) + bindings = cef.JavascriptBindings() + bindings.SetFunction("py_function", py_function) + bindings.SetFunction("py_callback", py_callback) + browser.SetJavascriptBindings(bindings) + cef.MessageLoop() + del browser + cef.Shutdown() + + +def py_function(value, js_callback): + print("Value sent from Javascript: "+value) + js_callback.Call("I am a Python string #2", py_callback) + + +def py_callback(value): + print("Value sent from Javascript: "+value) + + +class LifespanHandler(object): + def OnLoadEnd(self, browser, **_): + browser.ExecuteFunction("js_function", "I am a Python string #1") + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/mouse_clicks.py b/examples/snippets/mouse_clicks.py index fcdd5280..e8ace36a 100644 --- a/examples/snippets/mouse_clicks.py +++ b/examples/snippets/mouse_clicks.py @@ -43,7 +43,7 @@ class LifespanHandler(object): def OnLoadEnd(self, browser, **_): # Execute function with a delay of 1 second after page # has completed loading. - print("Page completed loading") + print("Page loading is complete") cef.PostDelayedTask(cef.TID_UI, 1000, click_after_1_second, browser) diff --git a/examples/snippets/network_cookies.py b/examples/snippets/network_cookies.py index 55a71ec0..c3631cff 100644 --- a/examples/snippets/network_cookies.py +++ b/examples/snippets/network_cookies.py @@ -30,7 +30,7 @@ def CanGetCookies(self, frame, request, **_): print("-- CanGetCookies #"+str(self.getcount)) print("url="+request.GetUrl()[0:80]) print("") - # Return True to allow reading cookies and False to block + # Return True to allow reading cookies or False to block return True def CanSetCookie(self, frame, request, cookie, **_): @@ -43,7 +43,7 @@ def CanSetCookie(self, frame, request, cookie, **_): print("Name="+cookie.GetName()) print("Value="+cookie.GetValue()) print("") - # Return True to allow setting cookie and False to block + # Return True to allow setting cookie or False to block return True diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index fdf96cb2..f78800d3 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -82,6 +82,8 @@ NEW FEATURES + onbeforeclose.py + network_cookies.py + mouse_clicks.py + + javascript_bindings.py ++ cef.GetDataUrl internal/cef_types.h + cef_log_severity_t: new key LOGSEVERITY_DEBUG (no need to expose, diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 5a526bcd..1631636f 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -134,6 +134,8 @@ import datetime import random # noinspection PyUnresolvedReferences import struct +# noinspection PyUnresolvedReferences +import base64 # Must use compile-time condition instead of checking sys.version_info.major # otherwise results in "ImportError: cannot import name urlencode" strange @@ -1023,3 +1025,9 @@ cpdef dict GetVersion(): cpdef LoadCrlSetsFile(py_string path): CefLoadCRLSetsFile(PyToCefStringValue(path)) + +cpdef GetDataUrl(data, mediatype="html"): + html = data.encode("utf-8", "replace") + b64 = base64.b64encode(html).decode("utf-8", "replace") + ret = "data:text/html;base64,{data}".format(data=b64) + return ret diff --git a/unittests/_common.py b/unittests/_common.py index a0dc1fd3..684a27b1 100644 --- a/unittests/_common.py +++ b/unittests/_common.py @@ -41,13 +41,6 @@ def show_test_summary(pyfile): + os.path.basename(pyfile)) -def html_to_data_uri(html): - html = html.encode("utf-8", "replace") - b64 = base64.b64encode(html).decode("utf-8", "replace") - ret = "data:text/html;base64,{data}".format(data=b64) - return ret - - def run_message_loop(): # Run message loop for some time. # noinspection PyTypeChecker diff --git a/unittests/main_test.py b/unittests/main_test.py index 25405ce0..b0c2200c 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -112,7 +112,7 @@ """ -g_datauri = html_to_data_uri(g_datauri_data) +g_datauri = cef.GetDataUrl(g_datauri_data) class MainTest_IsolatedTest(unittest.TestCase): diff --git a/unittests/osr_test.py b/unittests/osr_test.py index a5a3753b..0e4191be 100644 --- a/unittests/osr_test.py +++ b/unittests/osr_test.py @@ -62,7 +62,7 @@ """ -g_datauri = html_to_data_uri(g_datauri_data) +g_datauri = cef.GetDataUrl(g_datauri_data) class OsrTest_IsolatedTest(unittest.TestCase): From 6bb2d4ed7c3d498f999d6ac0c042336da1c11b18 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Aug 2018 12:13:43 +0200 Subject: [PATCH 078/177] Add javascript_erors.py and ondomready.py snippets (#403) --- docs/Tutorial.md | 3 +- examples/README-examples.md | 41 +++++++++------- examples/snippets/javascript_bindings.py | 3 +- examples/snippets/javascript_errors.py | 61 ++++++++++++++++++++++++ examples/snippets/ondomready.py | 50 +++++++++++++++++++ src/cef_v59..v66_changes.txt | 2 + 6 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 examples/snippets/javascript_errors.py create mode 100644 examples/snippets/ondomready.py diff --git a/docs/Tutorial.md b/docs/Tutorial.md index c6c0fcf7..ea32cb42 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -9,7 +9,8 @@ basics. This tutorial will discuss the three featured examples: [hello_world.py](../examples/hello_world.py), [tutorial.py](../examples/tutorial.py) and [screenshot.py](../examples/screenshot.py). There are many -more examples that you can find in the [README-examples.md](../examples/README-examples.md) +more examples that you can find in the +[README-examples.md](../examples/README-examples.md) file, but these examples are out of scope for this tutorial. diff --git a/examples/README-examples.md b/examples/README-examples.md index 0f0ef9a4..4ef36045 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -4,8 +4,8 @@ Table of contents: * [Hello World!](#hello-world) * [Supported examples](#supported-examples) * [Featured](#featured) - * [GUI frameworks](#gui-frameworks) * [Snippets](#snippets) + * [GUI frameworks](#gui-frameworks) * [Build executable with PyInstaller](#build-executable-with-pyinstaller) * [Unit tests](#unit-tests) * [Other examples](#other-examples) @@ -42,8 +42,31 @@ workarounds. discussed in great details in Tutorial in the [Off-screen rendering](../docs/Tutorial.md#off-screen-rendering) section. + +### Snippets + +See small code snippets that show various CEF features in the +[examples/snippets/](snippets/) directory: + +- [javascript_bindings.py](snippets/javascript_bindings.py) - Communicate + between Python and Javascript asynchronously using + inter-process messaging with the use of Javascript Bindings. +- [javascript_errors.py](snippets/javascript_errors.py) - Two ways for + intercepting Javascript errors. +- [mouse_clicks.py](snippets/mouse_clicks.py) - Perform mouse clicks + and mouse movements programmatically. +- [network_cookies.py](snippets/network_cookies.py) - Implement + interfaces to block or allow cookies over network requests. +- [onbeforeclose.py](snippets/onbeforeclose.py) - Implement interface + to execute custom code before browser window closes. +- [ondomready.py](snippets/ondomready.py) - Execute custom Python code + on a web page as soon as DOM is ready. + + ### GUI frameworks +Examples of embedding CEF browser using various GUI frameworks: + - [gtk2.py](gtk2.py): example for [PyGTK](http://www.pygtk.org/) library (GTK 2) - [gtk3.py](gtk3.py): example for [PyGObject / PyGI](https://wiki.gnome.org/Projects/PyGObject) @@ -64,22 +87,6 @@ workarounds. toolkit. This example implements High DPI support on Windows. -### Snippets - -See small code snippets that test various features in the -[examples/snippets/](snippets/) directory: - -- [javascript_bindings.py](snippets/javascript_bindings.py) - Communicate - between Python and Javascript asynchronously using - inter-process messaging with the use of Javascript Bindings. -- [mouse_clicks.py](snippets/mouse_clicks.py) - Perform mouse clicks - and mouse movements programmatically. -- [network_cookies.py](snippets/network_cookies.py) - Implement - interfaces to block or allow cookies over network requests. -- [onbeforeclose.py](snippets/onbeforeclose.py) - Implement interface - to execute custom code before browser window closes. - - ### Build executable with PyInstaller - [PyInstaller example](pyinstaller/README-pyinstaller.md): diff --git a/examples/snippets/javascript_bindings.py b/examples/snippets/javascript_bindings.py index 7c7dccc5..63135463 100644 --- a/examples/snippets/javascript_bindings.py +++ b/examples/snippets/javascript_bindings.py @@ -17,7 +17,6 @@ + + +

Javascript Errors

+
+ + +""" + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url=cef.GetDataUrl(g_htmlcode), + window_title="Javascript Errors") + browser.SetClientHandler(DisplayHandler()) + cef.MessageLoop() + cef.Shutdown() + + +class DisplayHandler(object): + def OnConsoleMessage(self, browser, message, line, **_): + if "error" in message.lower() or "uncaught" in message.lower(): + logmsg = "[Py:OnConsoleMessage] {message} (line {line})" \ + .format(message=message, line=line) + print(logmsg) + browser.ExecuteFunction("print", logmsg) + + +if __name__ == '__main__': + main() diff --git a/examples/snippets/ondomready.py b/examples/snippets/ondomready.py new file mode 100644 index 00000000..caeacc02 --- /dev/null +++ b/examples/snippets/ondomready.py @@ -0,0 +1,50 @@ +""" +Execute custom Python code on a web page as soon as DOM is ready. +Implements a custom "_OnDomReady" event in the LifespanHandler object. +""" + +from cefpython3 import cefpython as cef + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_title="_OnDomReady event") + lifespan_handler = LifespanHandler(browser) + browser.SetClientHandler(lifespan_handler) + bindings = cef.JavascriptBindings() + bindings.SetFunction("LifespanHandler_OnDomReady", + lifespan_handler["_OnDomReady"]) + browser.SetJavascriptBindings(bindings) + cef.MessageLoop() + del lifespan_handler + del browser + cef.Shutdown() + + +class LifespanHandler(object): + def __init__(self, browser): + self.browser = browser + + def __getitem__(self, key): + return getattr(self, key) + + def OnLoadStart(self, browser, **_): + browser.ExecuteJavascript(""" + if (document.readyState === "complete") { + LifespanHandler_OnDomReady(); + } else { + document.addEventListener("DOMContentLoaded", function() { + LifespanHandler_OnDomReady(); + }); + } + """) + + def _OnDomReady(self): + print("DOM is ready!") + self.browser.ExecuteFunction("alert", + "Message from Python: DOM is ready!") + + +if __name__ == '__main__': + main() diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index f78800d3..1024bdaf 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -83,6 +83,8 @@ NEW FEATURES + network_cookies.py + mouse_clicks.py + javascript_bindings.py + + javascript_errors.py + + ondomready.py + cef.GetDataUrl internal/cef_types.h From 7448372a26632092876e026a4a9f7711839bb83e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Aug 2018 12:46:43 +0200 Subject: [PATCH 079/177] Add onpagecomplete.py snippet (#403) and others. Add tools/run_snippets.py. --- examples/README-examples.md | 2 ++ examples/snippets/ondomready.py | 18 +++++----- examples/snippets/onpagecomplete.py | 35 ++++++++++++++++++ src/cef_v59..v66_changes.txt | 1 + tools/common.py | 1 + tools/run_examples.py | 3 +- tools/run_snippets.py | 55 +++++++++++++++++++++++++++++ 7 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 examples/snippets/onpagecomplete.py create mode 100644 tools/run_snippets.py diff --git a/examples/README-examples.md b/examples/README-examples.md index 4ef36045..b877607a 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -61,6 +61,8 @@ See small code snippets that show various CEF features in the to execute custom code before browser window closes. - [ondomready.py](snippets/ondomready.py) - Execute custom Python code on a web page as soon as DOM is ready. +- [onpagecomplete.py](snippets/onpagecomplete.py) - Execute custom + Python code on a web page when page loading is complete. ### GUI frameworks diff --git a/examples/snippets/ondomready.py b/examples/snippets/ondomready.py index caeacc02..8775129c 100644 --- a/examples/snippets/ondomready.py +++ b/examples/snippets/ondomready.py @@ -1,6 +1,6 @@ """ Execute custom Python code on a web page as soon as DOM is ready. -Implements a custom "_OnDomReady" event in the LifespanHandler object. +Implements a custom "_OnDomReady" event in the LoadHandler object. """ from cefpython3 import cefpython as cef @@ -10,19 +10,19 @@ def main(): cef.Initialize() browser = cef.CreateBrowserSync(url="https://www.google.com/", window_title="_OnDomReady event") - lifespan_handler = LifespanHandler(browser) - browser.SetClientHandler(lifespan_handler) + load_handler = LoadHandler(browser) + browser.SetClientHandler(load_handler) bindings = cef.JavascriptBindings() - bindings.SetFunction("LifespanHandler_OnDomReady", - lifespan_handler["_OnDomReady"]) + bindings.SetFunction("LoadHandler_OnDomReady", + load_handler["_OnDomReady"]) browser.SetJavascriptBindings(bindings) cef.MessageLoop() - del lifespan_handler + del load_handler del browser cef.Shutdown() -class LifespanHandler(object): +class LoadHandler(object): def __init__(self, browser): self.browser = browser @@ -32,10 +32,10 @@ def __getitem__(self, key): def OnLoadStart(self, browser, **_): browser.ExecuteJavascript(""" if (document.readyState === "complete") { - LifespanHandler_OnDomReady(); + LoadHandler_OnDomReady(); } else { document.addEventListener("DOMContentLoaded", function() { - LifespanHandler_OnDomReady(); + LoadHandler_OnDomReady(); }); } """) diff --git a/examples/snippets/onpagecomplete.py b/examples/snippets/onpagecomplete.py new file mode 100644 index 00000000..e118e8e7 --- /dev/null +++ b/examples/snippets/onpagecomplete.py @@ -0,0 +1,35 @@ +""" +Execute custom Python code on a web page when page loading is complete. +Implements a custom "_OnPageComplete" event in the LoadHandler object. +""" + +from cefpython3 import cefpython as cef + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_title="_OnPageComplete event") + browser.SetClientHandler(LoadHandler()) + cef.MessageLoop() + del browser + cef.Shutdown() + + +class LoadHandler(object): + def OnLoadingStateChange(self, browser, is_loading, **_): + """For detecting if page loading has ended it is recommended + to use OnLoadingStateChange which is most reliable. The OnLoadEnd + callback also available in LoadHandler can sometimes fail in + some cases e.g. when image loading hangs.""" + if not is_loading: + self._OnPageComplete(browser) + + def _OnPageComplete(self, browser): + print("Page loading is complete!") + browser.ExecuteFunction("alert", "Message from Python: Page loading" + " is complete!") + + +if __name__ == '__main__': + main() diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 1024bdaf..9060ad14 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -85,6 +85,7 @@ NEW FEATURES + javascript_bindings.py + javascript_errors.py + ondomready.py + + onpagecomplete + cef.GetDataUrl internal/cef_types.h diff --git a/tools/common.py b/tools/common.py index 06ab942e..ec92b0fe 100644 --- a/tools/common.py +++ b/tools/common.py @@ -158,6 +158,7 @@ # -- end build directories EXAMPLES_DIR = os.path.join(ROOT_DIR, "examples") +SNIPPETS_DIR = os.path.join(EXAMPLES_DIR, "snippets") SRC_DIR = os.path.join(ROOT_DIR, "src") # Subdirectories in src/ diff --git a/tools/run_examples.py b/tools/run_examples.py index 87fe1296..bf70f045 100644 --- a/tools/run_examples.py +++ b/tools/run_examples.py @@ -3,7 +3,8 @@ # Project website: https://github.com/cztomczak/cefpython """ -Run all examples that can be run on current configuration. +Run all examples that can be run on current configuration +and display a summary at the end. Note on GTK 2 / GTK 3 on Windows: Installing both PyGTK and PyGI on Windows will cause errors. diff --git a/tools/run_snippets.py b/tools/run_snippets.py new file mode 100644 index 00000000..193d9558 --- /dev/null +++ b/tools/run_snippets.py @@ -0,0 +1,55 @@ +# Copyright (c) 2018 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +""" +Run all snippets from the examples/snippets/ directory +and display a summary at the end. +""" + +from common import * + +import glob +import os +import subprocess +import sys + + +def main(): + # Iterate over all snippets + snippets_iter = glob.glob(os.path.join(SNIPPETS_DIR, "*.py")) + succeeded = [] + failed = [] + for snippet in snippets_iter: + print("[run_snippets.py] Running '{snippet}'..." + .format(snippet=os.path.basename(snippet))) + retcode = subprocess.call([sys.executable, snippet]) + if retcode == 0: + succeeded.append(os.path.basename(snippet)) + else: + print("[run_snippets.py] ERROR while running snippet: {snippet}" + .format(snippet=snippet)) + failed.append(os.path.basename(snippet)) + + # Print summary + summary = "" + for snippet in succeeded: + summary += " OK {snippet}{nl}"\ + .format(snippet=snippet, nl=os.linesep) + for snippet in failed: + summary += " ERROR {snippet}{nl}"\ + .format(snippet=snippet, nl=os.linesep) + summary = summary[:-(len(os.linesep))] + print("[run_snippets.py] SUMMARY:") + print(summary.format()) + if len(failed): + print("[run_snippets.py] ERRORS ({failed}) while running snippets" + .format(failed=len(failed))) + sys.exit(1) + else: + print("[run_snippets.py] OK ({succeeded})" + .format(succeeded=len(succeeded))) + + +if __name__ == "__main__": + main() From 9f3ba137d3d243711ee40e21b1b958907bf63abb Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Aug 2018 13:18:43 +0200 Subject: [PATCH 080/177] Update cefpython.md --- api/cefpython.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/cefpython.md b/api/cefpython.md index a876bd69..29720c48 100644 --- a/api/cefpython.md +++ b/api/cefpython.md @@ -66,7 +66,8 @@ any third party GUI framework for creation of top-level window. After the call to CreateBrowserSync() the page is not yet loaded, if you want your next lines of code to do some stuff on the -webpage you will have to implement LoadHandler.[OnLoadingStateChange]((LoadHandler.md#onloadingstatechange)) +webpage you will have to implement +LoadHandler.[OnLoadingStateChange](LoadHandler.md#onloadingstatechange) callback. From ef6febb7d3ca751ce86eed0061b11538f2379e64 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Aug 2018 14:30:14 +0200 Subject: [PATCH 081/177] Fix build_distrib.py tool on Windows (#403) --- tools/build_distrib.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 65bfd5ee..fc8c9db3 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -593,15 +593,18 @@ def check_cpp_extension_dependencies_issue359(setup_dir, all_pythons): for python in all_pythons: if python["version2"] in ((3, 5), (3, 6), (3, 7)): checked_any = True - if not os.path.exists(os.path.join(setup_dir, "msvcp140.dll")): + if not os.path.exists(os.path.join(setup_dir, "cefpython3", + "msvcp140.dll")): raise Exception("C++ ext dependency missing: msvcp140.dll") elif python["version2"] == (3, 4): checked_any = True - if not os.path.exists(os.path.join(setup_dir, "msvcp100.dll")): + if not os.path.exists(os.path.join(setup_dir, "cefpython3", + "msvcp100.dll")): raise Exception("C++ ext dependency missing: msvcp100.dll") elif python["version2"] == (2, 7): - if not os.path.exists(os.path.join(setup_dir, "msvcp90.dll")): - raise Exception("C++ ext dependency missing: msvcp100.dll") + if not os.path.exists(os.path.join(setup_dir, "cefpython3", + "msvcp90.dll")): + raise Exception("C++ ext dependency missing: msvcp90.dll") checked_any = True assert checked_any From 209fe5ad81b89529c3e3e0282ae0917da1815103 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Aug 2018 19:51:17 +0200 Subject: [PATCH 082/177] CEF Python v66.0 release --- README.md | 136 ++++++++++++++-------------- api/AccessibilityHandler.md | 8 +- docs/Tutorial.md | 2 +- examples/README-examples.md | 2 +- examples/pywin32.py | 2 +- src/cef_v59..v66_changes.txt | 5 +- tools/installer/cefpython3.setup.py | 2 +- 7 files changed, 81 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 9ecad822..1e6697e8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Table of contents: * [Introduction](#introduction) * [Sponsors](#sponsors) -* [Funding for the v66 release](#funding-for-the-v66-release) + * [Thanks to Lampix for the v66 release](#thanks-to-lampix-for-the-v66-release) * [Install](#install) * [Tutorial](#tutorial) * [Examples](#examples) @@ -92,15 +92,15 @@ See the [Support development](#support-development) section for a list of all the individuals and companies supporting CEF Python. -## Funding for the v66 release +### Thanks to Lampix for the v66 release -I am happy to announce that Lampix is sponsoring the v66 release of cefpython. +Many Thanks to Lampix for sponsoring the [v66 release](../../releases) of CEF Python. Lampix is the first hardware and software solution that turns any surface into a smart, augmented reality or interactive surface. Please visit its -website here: https://lampix.com/ . See the [v66 milestone](../../milestone/13) -to see progress on the new release. +website here: https://lampix.com/ . + +[![lampix360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/lampix360.png)](https://lampix.com/) -[![lampix600](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/lampix600.png)](https://lampix.com/) ## Install @@ -110,9 +110,10 @@ also download packages for offline installation available on the [GitHub Releases](../../releases) pages. Command to install with pip: ``` -pip install cefpython3==57.0 +pip install cefpython3==66.0 ``` + ## Tutorial See the [Tutorial.md](docs/Tutorial.md) file. @@ -170,6 +171,7 @@ so your company logo and a website link will be visible to many Python developer The statistics are from Mar 9, 2018. Complete statistics are available in this topic on the Forum: [[ANN] Call for sponsors](https://groups.google.com/d/topic/cefpython/CjkZF2P4rMk/discussion). + ### Thanks * [2018] Many thanks to [Lampix](https://lampix.com/) for sponsoring the v66 @@ -254,66 +256,66 @@ Additional information for v31.2 release: - [Tutorial](docs/Tutorial.md) -### API categories - -#### Modules - - * [cefpython](api/cefpython.md#cefpython) module - - -#### Settings - - * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary - * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary - * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary - - -#### Classes and objects - - * [Browser](api/Browser.md#browser-object) object - * [Callback](api/Callback.md#callback-object) object - * [Cookie](api/Cookie.md#cookie-class) class - * [CookieManager](api/CookieManager.md#cookiemanager-class) class - * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) - * [DragData](api/DragData.md#dragdata-object) object - * [Frame](api/Frame.md#frame-object) object - * [Image](api/Image.md#image-object) object - * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class - * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object - * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object - * [Request](api/Request.md#request-class) class - * [Response](api/Response.md#response-object) object - * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object - * [WebRequest](api/WebRequest.md#webrequest-class) class - * [WindowInfo](api/WindowInfo.md#windowinfo-class) class - * [WindowUtils](api/WindowUtils.md#windowutils-class) class - - -#### Client handlers (interfaces) - - * [AccessibilityHandler](api/AccessibilityHandler.md#accessibilityhandler-interface) - * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) - * [DownloadHandler](api/DownloadHandler.md#downloadhandler) - * [FocusHandler](api/FocusHandler.md#focushandler-interface) - * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) - * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) - * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) - * [LoadHandler](api/LoadHandler.md#loadhandler-interface) - * [RenderHandler](api/RenderHandler.md#renderhandler-interface) - * [RequestHandler](api/RequestHandler.md#requesthandler-interface) - * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) - * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) - - -#### Other interfaces - - * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface - * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface - * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface - - -### API index - +### API categories + +#### Modules + + * [cefpython](api/cefpython.md#cefpython) module + + +#### Settings + + * [ApplicationSettings](api/ApplicationSettings.md#application-settings) dictionary + * [BrowserSettings](api/BrowserSettings.md#browser-settings) dictionary + * [CommandLineSwitches](api/CommandLineSwitches.md#command-line-switches) dictionary + + +#### Classes and objects + + * [Browser](api/Browser.md#browser-object) object + * [Callback](api/Callback.md#callback-object) object + * [Cookie](api/Cookie.md#cookie-class) class + * [CookieManager](api/CookieManager.md#cookiemanager-class) class + * [DpiAware](api/DpiAware.md#dpiaware-class) class (Win) + * [DragData](api/DragData.md#dragdata-object) object + * [Frame](api/Frame.md#frame-object) object + * [Image](api/Image.md#image-object) object + * [JavascriptBindings](api/JavascriptBindings.md#javascriptbindings-class) class + * [JavascriptCallback](api/JavascriptCallback.md#javascriptcallback-object) object + * [PaintBuffer](api/PaintBuffer.md#paintbuffer-object) object + * [Request](api/Request.md#request-class) class + * [Response](api/Response.md#response-object) object + * [WebPluginInfo](api/WebPluginInfo.md#webplugininfo-object) object + * [WebRequest](api/WebRequest.md#webrequest-class) class + * [WindowInfo](api/WindowInfo.md#windowinfo-class) class + * [WindowUtils](api/WindowUtils.md#windowutils-class) class + + +#### Client handlers (interfaces) + + * [AccessibilityHandler](api/AccessibilityHandler.md#accessibilityhandler-interface) + * [DisplayHandler](api/DisplayHandler.md#displayhandler-interface) + * [DownloadHandler](api/DownloadHandler.md#downloadhandler) + * [FocusHandler](api/FocusHandler.md#focushandler-interface) + * [JavascriptDialogHandler](api/JavascriptDialogHandler.md#javascriptdialoghandler-interface) + * [KeyboardHandler](api/KeyboardHandler.md#keyboardhandler-interface) + * [LifespanHandler](api/LifespanHandler.md#lifespanhandler-interface) + * [LoadHandler](api/LoadHandler.md#loadhandler-interface) + * [RenderHandler](api/RenderHandler.md#renderhandler-interface) + * [RequestHandler](api/RequestHandler.md#requesthandler-interface) + * [ResourceHandler](api/ResourceHandler.md#resourcehandler-interface) + * [V8ContextHandler](api/V8ContextHandler.md#v8contexthandler-interface) + + +#### Other interfaces + + * [CookieVisitor](api/CookieVisitor.md#cookievisitor-interface) interface + * [StringVisitor](api/StringVisitor.md#stringvisitor-interface) interface + * [WebRequestClient](api/WebRequestClient.md#webrequestclient-interface) interface + + +### API index + * [AccessibilityHandler (interface)](api/AccessibilityHandler.md#accessibilityhandler-interface) * [_OnAccessibilityTreeChange](api/AccessibilityHandler.md#_onaccessibilitytreechange) * [_OnAccessibilityLocationChange](api/AccessibilityHandler.md#_onaccessibilitylocationchange) diff --git a/api/AccessibilityHandler.md b/api/AccessibilityHandler.md index faea2605..3433e003 100644 --- a/api/AccessibilityHandler.md +++ b/api/AccessibilityHandler.md @@ -11,12 +11,12 @@ accessibility events have been registered. The methods of this class will be called on the UI thread. Callbacks in this interface are not associated with any specific browser, -thus you must call cefpython.[SetGlobalClientHandler] or -SetGlobalClientCallback() to use them. The callbacks names were prefixed +thus you must call +`cefpython`.[SetGlobalClientHandler](cefpython.md#setglobalclienthandler) +or [SetGlobalClientCallback](cefpython.md#setglobalclientcallback) +to use them. The callbacks names were prefixed with "`_`" to distinguish this special behavior. -For an example of how to implement handler see [cefpython](cefpython.md).CreateBrowser(). For a list of all handler interfaces see [API > Client handlers](API#Client_handlers). - Table of contents: * [Callbacks](#callbacks) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index ea32cb42..d67b6204 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -40,7 +40,7 @@ Run the commands below to install the cefpython3 package, clone the repository and run the Hello World example: ```commandline -pip install cefpython3==57.0 +pip install cefpython3==66.0 git clone https://github.com/cztomczak/cefpython.git cd cefpython/examples/ python hello_world.py diff --git a/examples/README-examples.md b/examples/README-examples.md index b877607a..de756013 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -17,7 +17,7 @@ Instructions to install the cefpython3 package, clone the repository and run the hello_world.py example: ``` -pip install cefpython3==57.0 +pip install cefpython3==66.0 git clone https://github.com/cztomczak/cefpython.git cd cefpython/examples/ python hello_world.py diff --git a/examples/pywin32.py b/examples/pywin32.py index d18aa54e..bf8d2b9b 100644 --- a/examples/pywin32.py +++ b/examples/pywin32.py @@ -108,7 +108,7 @@ def check_versions(): pywin32_version = fp.read().strip() print("[pywin32.py] pywin32 {ver}".format(ver=pywin32_version)) - assert cef.__version__ >= "57.0", "CEF Python v57.0 required to run this" + assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this" def create_browser(window_info, settings, url): diff --git a/src/cef_v59..v66_changes.txt b/src/cef_v59..v66_changes.txt index 9060ad14..fab7e5e7 100644 --- a/src/cef_v59..v66_changes.txt +++ b/src/cef_v59..v66_changes.txt @@ -165,4 +165,7 @@ cef_v8.h - CreateArrayBuffer (does not own the buffer) - IsArrayBuffer - GetArrayBufferReleaseCallback - - NeuterArrayBuffer \ No newline at end of file + - NeuterArrayBuffer + +cef_urlrequest.h +- ResponseWasCached \ No newline at end of file diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index c8e52699..6814cbb7 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -129,7 +129,7 @@ def main(): "https://github.com/cztomczak/cefpython", license="BSD 3-clause", author="Czarek Tomczak", - author_email="czarek.tomczak2@@gmail.com", + author_email="czarek.tomczak@gmail.com", url="https://github.com/cztomczak/cefpython", download_url="https://github.com/cztomczak/cefpython/releases", platforms=["{{SYSCONFIG_PLATFORM}}"], From 2de75b7f884fe12266b701e1ed7cd531346754d3 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Aug 2018 11:30:08 +0200 Subject: [PATCH 083/177] Update screenshot.py. Note on viewport size limits in Chromium. --- examples/screenshot.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/screenshot.py b/examples/screenshot.py index c27e72d0..7392ed5b 100644 --- a/examples/screenshot.py +++ b/examples/screenshot.py @@ -24,6 +24,15 @@ Tested configurations: - CEF Python v57.0+ - Pillow 2.3.0 / 4.1.0 + +NOTE: There are limits in Chromium on viewport size. For some + websites with huge viewport size it won't work. In such + case it is required to reduce viewport size to an usual + size of a window and perform scrolling programmatically + using javascript while making a screenshot for each of + the scrolled region. Then at the end combine all the + screenshots into one. To force a paint event in OSR + mode call cef.Invalidate(). """ from cefpython3 import cefpython as cef From 774820848b37da3b01e0f2436354057b1c1faaad Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Wed, 22 Aug 2018 09:29:20 +0200 Subject: [PATCH 084/177] Update Tutorial.md ExceptHook update --- docs/Tutorial.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index d67b6204..b4533e34 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -69,10 +69,10 @@ Google website. Let's analyze the code from that example: 1. `from cefpython3 import cefpython as cef` - Import the cefpython module and make a short "cef" alias 2. `sys.excepthook = cef.ExceptHook` - Overwrite Python's default - exception handler so that all CEF sub-processes are terminated - when Python exception occurs. To understand this better read the - "Architecture" and "Handling Python exceptions" sections - further down in this Tutorial. + exception handler so that all CEF sub-processes are reliably + terminated when Python exception occurs. To understand this + better read the "Architecture" and "Handling Python exceptions" + sections further down in this Tutorial. 3. `cef.Initialize()` - Initialize CEF. This function must be called somewhere in the beginning of your code. It must be called before any application window is created. It must be called only once @@ -140,9 +140,9 @@ special handling. When Python exception occurs then main process is terminated. For CEF this means that the Browser process is terminated, however there may still be running CEF sub-processes like Renderer process, GPU process, etc. To terminate these -sub-processes cef.[Shutdown](../api/cefpython.md#shutdown) +sub-processes cleanly cef.[Shutdown](../api/cefpython.md#shutdown) must be called and if running CEF message loop then it must be -stopped first. In all CEF Python examples you can find such +stopped first. In most of CEF Python examples you can find such a line that overwrites the default exception handler in Python: ```python @@ -160,7 +160,8 @@ The cef.ExceptHook helper function does the following: which exits the process with status 1, without calling cleanup handlers, flushing stdio buffers, etc. -See CEF Python's ExceptHook source code in src/[helpers.pyx](../src/helpers.pyx). +If you would like to modify `ExceptHook` behavior, see its source code +in src/[helpers.pyx](../src/helpers.pyx) file. ## Settings From c160430867ab55a9dcee5e4855852da6f95d9a1a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Aug 2018 11:26:24 +0200 Subject: [PATCH 085/177] Auto cleanup in the examples/ directory, so that build scripts do not include trash directories. See Issue #432. --- tools/common.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/common.py b/tools/common.py index ec92b0fe..4bee285f 100644 --- a/tools/common.py +++ b/tools/common.py @@ -175,6 +175,25 @@ UNITTESTS_DIR = os.path.abspath(os.path.join(ROOT_DIR, "unittests")) # ---------------------------------------------------------------------------- + +# Auto cleanup in the examples/ directory, so that build scripts +# do not include trash directories. See Issue #432. + +shutil.rmtree(os.path.join(EXAMPLES_DIR, "blob_storage"), + ignore_errors=True) +shutil.rmtree(os.path.join(EXAMPLES_DIR, "webrtc_event_logs"), + ignore_errors=True) +shutil.rmtree(os.path.join(EXAMPLES_DIR, "webcache"), + ignore_errors=True) + +shutil.rmtree(os.path.join(SNIPPETS_DIR, "blob_storage"), + ignore_errors=True) +shutil.rmtree(os.path.join(SNIPPETS_DIR, "webrtc_event_logs"), + ignore_errors=True) +shutil.rmtree(os.path.join(SNIPPETS_DIR, "webcache"), + ignore_errors=True) + + # cefpython API header file and a fixed copy of it CEFPYTHON_API_HFILE = os.path.join(BUILD_CEFPYTHON, "cefpython_py{pyver}.h" From 3364c8fbcf4b48b0e41446939d23ebe1892d0f63 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Aug 2018 11:37:40 +0200 Subject: [PATCH 086/177] Add window_size.py snippet --- examples/snippets/window_size.py | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 examples/snippets/window_size.py diff --git a/examples/snippets/window_size.py b/examples/snippets/window_size.py new file mode 100644 index 00000000..6ae1ba54 --- /dev/null +++ b/examples/snippets/window_size.py @@ -0,0 +1,34 @@ +""" +Set initial size of window to 900px / 640px without using +any third party GUI framework. On Linux/Mac you can set +window size by calling WindowInfo.SetAsChild. On Windows +you can accomplish this by calling Windows native functions +using the ctypes module. +""" + +from cefpython3 import cefpython as cef +import platform + + +def main(): + cef.Initialize() + window_info = cef.WindowInfo() + window_info.SetAsChild(0, [0, 0, 900, 640]) + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_info=window_info, + window_title="Window size") + if platform.system() == "Windows": + pass + cef.MessageLoop() + del browser + cef.Shutdown() + + +class LifespanHandler(object): + def OnBeforeClose(self, browser): + print("Browser ID: {}".format(browser.GetIdentifier())) + print("Browser will close and app will exit") + + +if __name__ == '__main__': + main() From f6e7de20300bd33bc1dc2807f480c8efd5b9b689 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Aug 2018 11:54:44 +0200 Subject: [PATCH 087/177] Update window_size.py snippet. Works on Windows now. --- examples/README-examples.md | 2 ++ examples/snippets/window_size.py | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/README-examples.md b/examples/README-examples.md index de756013..c87eee07 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -63,6 +63,8 @@ See small code snippets that show various CEF features in the on a web page as soon as DOM is ready. - [onpagecomplete.py](snippets/onpagecomplete.py) - Execute custom Python code on a web page when page loading is complete. +- [window_size.py](snippets/window_size.py) - Set initial window size + without using any third party GUI framework. ### GUI frameworks diff --git a/examples/snippets/window_size.py b/examples/snippets/window_size.py index 6ae1ba54..48e99160 100644 --- a/examples/snippets/window_size.py +++ b/examples/snippets/window_size.py @@ -1,5 +1,5 @@ """ -Set initial size of window to 900px / 640px without using +Set initial window size to 900/600 pixels without using any third party GUI framework. On Linux/Mac you can set window size by calling WindowInfo.SetAsChild. On Windows you can accomplish this by calling Windows native functions @@ -7,18 +7,27 @@ """ from cefpython3 import cefpython as cef +import ctypes import platform def main(): cef.Initialize() window_info = cef.WindowInfo() - window_info.SetAsChild(0, [0, 0, 900, 640]) + parent_handle = 0 + # All rect coordinates are applied including X and Y parameters + window_info.SetAsChild(parent_handle, [0, 0, 900, 640]) browser = cef.CreateBrowserSync(url="https://www.google.com/", window_info=window_info, window_title="Window size") if platform.system() == "Windows": - pass + window_handle = browser.GetOuterWindowHandle() + # X and Y parameters are ignored by setting the SWP_NOMOVE flag + SWP_NOMOVE = 0x0002 + insert_after_handle = 0 + # noinspection PyUnresolvedReferences + ctypes.windll.user32.SetWindowPos(window_handle, insert_after_handle, + 0, 0, 900, 640, SWP_NOMOVE) cef.MessageLoop() del browser cef.Shutdown() From 39ca247b0c143b25326f9d1a4b173a22006a916e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Aug 2018 11:59:49 +0200 Subject: [PATCH 088/177] Update window_size.py snippet --- examples/README-examples.md | 2 +- examples/snippets/window_size.py | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/examples/README-examples.md b/examples/README-examples.md index c87eee07..c8943656 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -64,7 +64,7 @@ See small code snippets that show various CEF features in the - [onpagecomplete.py](snippets/onpagecomplete.py) - Execute custom Python code on a web page when page loading is complete. - [window_size.py](snippets/window_size.py) - Set initial window size - without using any third party GUI framework. + without use of any third party GUI framework. ### GUI frameworks diff --git a/examples/snippets/window_size.py b/examples/snippets/window_size.py index 48e99160..5a7b04d0 100644 --- a/examples/snippets/window_size.py +++ b/examples/snippets/window_size.py @@ -1,5 +1,5 @@ """ -Set initial window size to 900/600 pixels without using +Set initial window size to 900/640px without use of any third party GUI framework. On Linux/Mac you can set window size by calling WindowInfo.SetAsChild. On Windows you can accomplish this by calling Windows native functions @@ -15,16 +15,17 @@ def main(): cef.Initialize() window_info = cef.WindowInfo() parent_handle = 0 - # All rect coordinates are applied including X and Y parameters + # This call has effect only on Mac and Linux. + # All rect coordinates are applied including X and Y parameters. window_info.SetAsChild(parent_handle, [0, 0, 900, 640]) browser = cef.CreateBrowserSync(url="https://www.google.com/", window_info=window_info, window_title="Window size") if platform.system() == "Windows": window_handle = browser.GetOuterWindowHandle() + insert_after_handle = 0 # X and Y parameters are ignored by setting the SWP_NOMOVE flag SWP_NOMOVE = 0x0002 - insert_after_handle = 0 # noinspection PyUnresolvedReferences ctypes.windll.user32.SetWindowPos(window_handle, insert_after_handle, 0, 0, 900, 640, SWP_NOMOVE) @@ -33,11 +34,5 @@ def main(): cef.Shutdown() -class LifespanHandler(object): - def OnBeforeClose(self, browser): - print("Browser ID: {}".format(browser.GetIdentifier())) - print("Browser will close and app will exit") - - if __name__ == '__main__': main() From d61fd8b1114c440c298bf7a224f6f17bc19fc510 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Aug 2018 18:05:18 +0200 Subject: [PATCH 089/177] Add "setcookie.py" snippet --- examples/snippets/setcookie.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 examples/snippets/setcookie.py diff --git a/examples/snippets/setcookie.py b/examples/snippets/setcookie.py new file mode 100644 index 00000000..04a99508 --- /dev/null +++ b/examples/snippets/setcookie.py @@ -0,0 +1,36 @@ +""" +Shows how to set a cookie. +""" + +from cefpython3 import cefpython as cef +import datetime + + +def main(): + cef.Initialize() + cef.CreateBrowserSync( + url="http://www.html-kit.com/tools/cookietester/", + window_title="Set a cookie") + manager = cef.CookieManager.GetGlobalManager() + cookie = cef.Cookie() + cookie.Set({ + "name": "my_cookie", + "value": "my_value", + # Make sure domain is a valid value otherwise it crashes + # app (Issue #459) + "domain": "www.html-kit.com", + "path": "/", + "secure": False, + "httpOnly": False, + "creation": datetime.datetime(2018, 8, 22), + "lastAccess": datetime.datetime(2018, 8, 22), + "hasExpires": True, + "expires": datetime.datetime(2028, 12, 31, 23, 59, 59), + }) + manager.SetCookie("http://www.html-kit.com/", cookie) + cef.MessageLoop() + cef.Shutdown() + + +if __name__ == '__main__': + main() From a1ddc14ca1650f84d85535eced76d36cbe806ab0 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Aug 2018 18:08:08 +0200 Subject: [PATCH 090/177] Update README-examples.md --- examples/README-examples.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README-examples.md b/examples/README-examples.md index c8943656..ed104c39 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -63,6 +63,7 @@ See small code snippets that show various CEF features in the on a web page as soon as DOM is ready. - [onpagecomplete.py](snippets/onpagecomplete.py) - Execute custom Python code on a web page when page loading is complete. +- [setcookie.py](snippets/setcookie.py) - Shows how to set a cookie - [window_size.py](snippets/window_size.py) - Set initial window size without use of any third party GUI framework. From fa8737b3863f49a000656799559b66f69d4b2fae Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Thu, 23 Aug 2018 10:57:55 +0200 Subject: [PATCH 091/177] Update Knowledge-Base.md --- docs/Knowledge-Base.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/Knowledge-Base.md b/docs/Knowledge-Base.md index 91d5a516..fddb6a7d 100644 --- a/docs/Knowledge-Base.md +++ b/docs/Knowledge-Base.md @@ -34,15 +34,14 @@ To be notified on new commits subscribe to this [RSS/Atom feed](../../../commits CEF Python depends on CEF and API breaks are inevitable when updating to latest CEF. The [Migration Guide](Migration-guide.md) document -lists most notable breaking changes for each release. Other changes -that do not backwards compatibility can be seen on [GitHub Releases](../../../releases) -pages. +lists most notable breaking changes for each release. Full chanelogs +can be found on [GitHub Releases](../../../releases) pages. -Due to unavoidable changes in API it is recommended for your setup -scripts that use for example PIP to install the cefpython3 package, +Due to unavoidable changes in upstream API it is recommended for your setup +scripts, that for example use PIP to install the cefpython3 package, to hardcode the cefpython version string. If for example using PIP's -requirements.txt file then list the cefpython3 package in the -following format if using cefpython v57.0: `cefpython3 == 57.0`. +`requirements.txt` file then include the cefpython3 package in the +following format if using e.g. cefpython v57.0: `cefpython3 == 57.0`. ## Differences between Python 2 and Python 3 From 1ebc485ec499f15423802980936af5ac832b53cd Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 27 Aug 2018 20:05:36 +0200 Subject: [PATCH 092/177] Update README and Migration Guide dos - v49.0 release (#460, #462) --- README.md | 60 ++++++++- docs/Migration-guide.md | 291 ++++++++++++++++++++++++++-------------- 2 files changed, 241 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 1e6697e8..2c4e38ab 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Table of contents: * [Introduction](#introduction) * [Sponsors](#sponsors) * [Thanks to Lampix for the v66 release](#thanks-to-lampix-for-the-v66-release) + * [Thanks to Fivestars for the v49 release (WinXP/Vista)](#thanks-to-fivestars-for-the-v49-release-winxpvista) * [Install](#install) * [Tutorial](#tutorial) * [Examples](#examples) @@ -11,6 +12,10 @@ Table of contents: * [Support development](#support-development) * [Thanks](#thanks) * [Releases](#releases) + * [Next release](#next-release) + * [Latest release](#latest-release) + * [v49 release (WinXP/Vista)](#v49-release-winxpvista) + * [v31 release (old systems)](#v31-release-old-systems) * [Other READMEs](#other-readmes) * [Quick links](#quick-links) @@ -43,6 +48,14 @@ or other kind of internet bots. + + + - -
+ +
@@ -94,7 +107,7 @@ all the individuals and companies supporting CEF Python. ### Thanks to Lampix for the v66 release -Many Thanks to Lampix for sponsoring the [v66 release](../../releases) of CEF Python. +Many Thanks to Lampix for sponsoring the [v66 release](../../releases/tag/v66.0) of CEF Python. Lampix is the first hardware and software solution that turns any surface into a smart, augmented reality or interactive surface. Please visit its website here: https://lampix.com/ . @@ -102,6 +115,17 @@ website here: https://lampix.com/ . [![lampix360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/lampix360.png)](https://lampix.com/) +### Thanks to Fivestars for the v49 release (WinXP/Vista) + +Thanks to Fivestars Loyalty, Inc. for sponsoring the +[v49 release](../../releases/tag/v49.0) for legacy systems such +as Windows XP and Vista. Please visit its website: +https://www.fivestars.com/ . + +[![fivestars360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/fivestars360.png)](https://www.fivestars.com/) + + + ## Install You can install [pypi/cefpython3](https://pypi.python.org/pypi/cefpython3) @@ -201,7 +225,7 @@ Information on planned new and current releases, supported platforms, python versions, architectures and requirements. If you want to support old operating systems then choose the v31 release. -**Next release** +### Next release - To see planned new features or bugs to be fixed in the next release, see the @@ -212,7 +236,7 @@ support old operating systems then choose the v31 release. [next release 2](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22next+release+2%22) label in the tracker -**Latest release** +### Latest release OS | Py2 | Py3 | 32bit | 64bit | Requirements --- | --- | --- | --- | --- | --- @@ -224,7 +248,31 @@ These platforms are not supported yet: - ARM - see [Issue #267](../../issues/267) - Android - see [Issue #307](../../issues/307) -**v31 release** + +### v49 release (WinXP/Vista) + +OS | Py2 | Py3 | 32bit | 64bit | Requirements +--- | --- | --- | --- | --- | --- +Windows | 2.7 | 3.4 | Yes | Yes | Windows XP+ + +- Install with command: `pip --no-cache-dir install cefpython3==49.0`. + - Please note that if you were previously installing cefpython3 + package it is required to use the `--no-cache-dir` flag, + otherwise pip will end up with error message + `No matching distribution found for cefpython3==49.0`. + This happens because 49.0 release occured after 57.0 and 66.0 + releases. +- Downloads are available on GitHub Releases tagged + [v49.0](../../releases/tag/v49.0). +- See [Migration guide](docs/Migration-guide.md) document for changes + in this release +- Documentation is available in the [docs/](../../tree/cefpython49-winxp/docs) + directory in the `cefpython49-winxp` branch +- API reference is available in the [api/](../../tree/cefpython49-winxp/api) + directory in the `cefpython49-winxp` branch + + +### v31 release (old systems) OS | Py2 | Py3 | 32bit | 64bit | Requirements --- | --- | --- | --- | --- | --- diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index 3063ada1..7567f0a8 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -15,25 +15,29 @@ in your application. Table of contents: -* [v50+ Distribution packages](#v50-distribution-packages) +* [v49+ Distribution packages](#v49-distribution-packages) +* [v49+ cefbuilds.com is deprected, use Spotify Automated CEF Builds](#v49-cefbuildscom-is-deprected-use-spotify-automated-cef-builds) +* [v49+ Build instructions and build tools](#v49-build-instructions-and-build-tools) +* [v49: GPU acceleration should be disabled on Windows XP](#v49-gpu-acceleration-should-be-disabled-on-windows-xp) +* [v49 (Win) Handlers' callbacks and other interfaces](#v49-win-handlers-callbacks-and-other-interfaces) +* [v49+ High DPI support on Windows](#v49-high-dpi-support-on-windows) +* [v49 (Win) Do not call the 'WindowUtils.OnSize' function](#v49-win-do-not-call-the-windowutilsonsize-function) +* [v49+ Notify CEF on move or resize events](#v49-notify-cef-on-move-or-resize-events) +* [v49+ Flash support](#v49-flash-support) +* [v49+ Off-screen-rendering: new option "windowless_rendering_enabled"](#v49-off-screen-rendering-new-option-windowless_rendering_enabled) +* [v49+ BrowserSettings options removed](#v49-browsersettings-options-removed) +* [v49+ cef.Request.Flags changed](#v49-cefrequestflags-changed) +* [v49+ Request.GetHeaderMap and SetHeaderMap change](#v49-requestgetheadermap-and-setheadermap-change) +* [v49+ (Win) HTTPS cache problems on pages with certificate errors](#v49-win-https-cache-problems-on-pages-with-certificate-errors) * [v50+ Importing the cefpython3 package on Linux](#v50-importing-the-cefpython3-package-on-linux) * [v50+ Install X11 error handlers on Linux](#v50-install-x11-error-handlers-on-linux) * [v50+ Set window bounds on Linux](#v50-set-window-bounds-on-linux) -* [v50+ Notify CEF on move or resize events](#v50-notify-cef-on-move-or-resize-events) * [v50+ Keyboard focus issues on Linux](#v50-keyboard-focus-issues-on-linux) * [v50+ Windows XP and Vista are no more supported](#v50-windows-xp-and-vista-are-no-more-supported) * [v50+ Mac 32-bit is no more supported](#v50-mac-32-bit-is-no-more-supported) -* [v50+ cefbuilds.com is deprected, use Spotify Automated CEF Builds](#v50-cefbuildscom-is-deprected-use-spotify-automated-cef-builds) -* [v50+ Build instructions and build tools](#v50-build-instructions-and-build-tools) -* [v51+ Off-screen-rendering: new option "windowless_rendering_enabled"](#v51-off-screen-rendering-new-option-windowless_rendering_enabled) * [v51+ Remove LifespanHandler.RunModal](#v51-remove-lifespanhandlerrunmodal) -* [v51+ BrowserSettings options removed](#v51-browsersettings-options-removed) -* [v51+ cef.Request.Flags changed](#v51-cefrequestflags-changed) -* [v51+ Request.GetHeaderMap and SetHeaderMap change](#v51-requestgetheadermap-and-setheadermap-change) -* [v54+ GTK 3 example doesn't work anymore on Linux](#v54-gtk-3-example-doesnt-work-anymore-on-linux) * [v54+ libcef.so library is stripped from symbols on Linux](#v54-libcefso-library-is-stripped-from-symbols-on-linux) -* [v55+ HTTPS cache problems on pages with certificate errors](#v55-https-cache-problems-on-pages-with-certificate-errors) -* [v55.3+ Handlers' callbacks and other interfaces are now called using keyword args](#v553-handlers-callbacks-and-other-interfaces-are-now-called-using-keyword-args) +* [v55.3+ Handlers' callbacks and other interfaces](#v553-handlers-callbacks-and-other-interfaces) * [v56+ MacOS 10.9+ required to run](#v56-macos-109-required-to-run) * [v57.1+ High DPI support on Windows](#v571-high-dpi-support-on-windows) * [v66+ Linux patch that fixed HTTPS cache problems on pages with certificate errors was disabled](#v66-linux-patch-that-fixed-https-cache-problems-on-pages-with-certificate-errors-was-disabled) @@ -46,10 +50,10 @@ Table of contents: * [v66+ cef.Request.Flags changed](#v66-cefrequestflags-changed) * [v66+ RequestHandler.GetCookieManager not getting called in some cases](#v66-requesthandlergetcookiemanager-not-getting-called-in-some-cases) * [v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loop-qt-wxpython) +* [v67+ Do not call the 'WindowUtils.OnSize' function](#v67-do-not-call-the-windowutilsonsize-function) - -## v50+ Distribution packages +## v49+ Distribution packages In latest CEF Python there is only one distribution package available: a wheel package. Wheel packages are distributed on @@ -77,120 +81,133 @@ in an automated manner, it might be reconsidered in the future to provide debian packages again. -## v50+ Importing the cefpython3 package on Linux +## v49+ cefbuilds.com is deprected, use Spotify Automated CEF Builds -In the past on Linux it was required for the cefpython3 package -to be imported before any other packages due to tcmalloc global -hook being loaded. This is not required anymore, tcmalloc is -disabled by default. +The cefbuilds.com site with CEF prebuilt binaries is now deprecated. +From now on download prebuilt CEF binaries from the Spotify Automated +CEF Builds: +http://opensource.spotify.com/cefbuilds/index.html -## v50+ Install X11 error handlers on Linux -It is required to install X11 error handlers on Linux, otherwise -you will see 'BadWindow' errors happening - sometimes randomly - -which will cause application to terminate. Since v56+ x11 error -handlers are installed automatically by default during the call -to cef.Initialize(). However sometimes that is not enough like -for example in the wxpython.py example which requires the x11 -error handlers to be installed manually after wx was initialized, -and that is because wx initialization had reset x11 error handlers -that were installed earlier during cef initialization (Issue [#334](../../../issues/334)). +## v49+ Build instructions and build tools -You can install X11 error handlers by calling: -``` -WindowUtils = cef.WindowUtils() -WindowUtils.InstallX11ErrorHandlers() -``` +There were many changes in regards to building CEF and CEF Python. +There are now new tools in the tools/ root directory that fully +automate building CEF and CEF Python. CEF Python now provides +upstream CEF prebuilt binaries and libraries on GitHub Releases +tagged eg. "v49-upstream". With these binaries you can build +CEF Python from sources in less than 10 minutes. See the new +[Build instructions](Build-instructions.md) document. -API ref: WindowUtils.[InstallX11ErrorHandlers()](../api/WindowUtils.md#installx11errorhandlers-linux) +## v49: GPU acceleration should be disabled on Windows XP -## v50+ Set window bounds on Linux +On XP you should disable GPU acceleration by setting the `--disable-gpu` +and `--disable-gpu-compositing` switches. These switches can +be passed programmatically to `cef.Initialize`, see +[api/Command Line Switches](../api/CommandLineSwitches.md). -It is now required to set window bounds during window "resize", -"move" and "configure" events on Linux. You can do so by calling: -``` -browser.SetBounds(x, y, width, height) -``` +## v49 (Win) Handlers' callbacks and other interfaces -API ref: Browser.[SetBounds()](../api/Browser.md#setbounds) +In v49.0 release for Windows all handlers' +callbacks and other interfaces such as +CookieVisitor, StringVisitor and WebRequestClient, are now called +using keyword arguments (Issue [#291](../../../issues/291)). +This will cause many of existing code to break. This is how you +should declare callbacks using the new style: + +``` +def OnLoadStart(self, browser, **_): + pass +def OnLoadStart(self, **kwargs): + browser = kwargs["browser"] +``` -## v50+ Notify CEF on move or resize events +In the first declaration you see that only one argument is +declared, the browser, the others unused will be in the "_" +variable (the name of the variable is so that PyCharm doesn't +warn about unused variable). -It is required to notify the browser on move or resize events -so that popup widgets (e.g. \) are displayed in the correct -location and dismissed when the window moves. Also so that -drag & drop areas are updated accordingly. +Even if you specify and use all arguments, always add the +unused kwargs (`**_`) at the end: ``` -browser.NotifyMoveOrResizeStarted() +def OnLoadStart(self, browser, frame, **_): + pass ``` -API ref: Browser.[NotifyMoveOrResizeStarted()](../api/Browser.md#notifymoveorresizestarted) - +This will be handy in the future, in a case when upstream CEF +adds a new argument to the API, your code won't break. When +an argument is removed in upstream CEF API, if it's possible +CEF Python will try to keep backward compatibility by +emulating behavior of the old argument. -## v50+ Keyboard focus issues on Linux +In case of OnLoadStart, when you've used "browser" and "frame" +names for the arguments, your code won't break. However in +many other callbacks, where you've used argument names that +differed from how they were named in API docs, your code will +break. Also argument names were changed from camelCase +to underscores. For example the OnLoadEnd callback has renamed +the `httpStatusCode` argument to `http_code`. So in this case +your code will definitely break, unless you've also used +"http_code" for argument name. -There several keyboard focus issues on Linux since CEF library -replaced GTK library with X11 library. Most of these issues are -fixed in examples by calling SetFocus in LoadHandler.OnLoadStart -during initial app loading and/or by calling SetFocus in -FocusHandler.OnGotFocus. This keyboard focus issues need to be -fixed in usptream CEF. For more details see Issue [#284](../../../issues/284). +## v49+ High DPI support on Windows -## v50+ Windows XP and Vista are no more supported +It is recommended to embed a DPI awareness manifest in both the main +process and the subprocesses (the subprocess.exe executable) instead +of calling `DpiAware`.[SetProcessDpiAware](../api/DpiAware.md#setprocessdpiaware) +which sets DPI awareness only for the main process. -CEF Python v31.2 was the last version to support Windows XP. -This is due to Chromium/CEF dropping XP support, last version -that supported XP was CEF v49. +The `ApplicationSettings`.[auto_zooming](../api/ApplicationSettings.md#auto_zooming) +option has a default value of an empty string now. Previously the +default was "system_dpi". When enabling High DPI support you should +set it to "system_dpi" explicitilly. +Note that `DpiAware`.[CalculateWindowSize](../api/DpiAware.md#calculatewindowsize) +does not handle all DPI settings (e.g. 132% on Windows 10). +In newer CEF Python there is available `DpiAware`[Scale](../api/DpiAware.md#scale) +which is more reliable and can handle all DPI resolutions. You can copy see +its implementation in `src/dpi_aware_win.pyx`. -## v50+ Mac 32-bit is no more supported -CEF Python v31.2 was the last version to support Mac 32-bit. -This is due to CEF/Chromium dropping 32-bit support, last version -that supported 32-bit was CEF v38. +## v49 (Win) Do not call the 'WindowUtils.OnSize' function +This function can sometimes cause app hanging during window resize. +Call instead the new `WindowUtils`.[UpdateBrowserSize](../api/WindowUtils.md#updatebrowsersize) +function. See [Issue #464](../../../issues/464) for more details. -## v50+ cefbuilds.com is deprected, use Spotify Automated CEF Builds -The cefbuilds.com site with CEF prebuilt binaries is now deprecated. -From now on download prebuilt CEF binaries from the Spotify Automated -CEF Builds: +## v49+ Notify CEF on move or resize events -http://opensource.spotify.com/cefbuilds/index.html +It is required to notify the browser on move or resize events +so that popup widgets (e.g. \) are displayed in the correct +location and dismissed when the window moves. Also so that +drag & drop areas are updated accordingly. Call +Browser.[NotifyMoveOrResizeStarted()](../api/Browser.md#notifymoveorresizestarted) +during a move or resize event in your app window. -## v50+ Build instructions and build tools +## v49+ Flash support -There were many changes in regards to building CEF and CEF Python. -There are now new tools in the tools/ root directory that fully -automate building CEF and CEF Python. CEF Python now provides -upstream CEF prebuilt binaries and libraries on GitHub Releases -tagged eg. "v56-upstream". With these binaries you can build -CEF Python from sources in less than 10 minutes. See the new -[Build instructions](Build-instructions.md) document. +See [Issue #235](../../../issues/235) ("Flash support in CEF v49+") +for instructions on how to enable Flash. -## v51+ Off-screen-rendering: new option "windowless_rendering_enabled" +## v49+ Off-screen-rendering: new option "windowless_rendering_enabled" When using off-screen-rendering you must set the ApplicationSettings "windowless_rendering_enabled" option to True. This applies to -examples such as: Kivy, Panda3D and screenshot example. +examples such as: Kivy, Panda3D, PySDL2 and screenshot example. API ref: ApplicationSettings.[windowless_rendering_enabled](../api/ApplicationSettings.md#windowless_rendering_enabled) -## v51+ Remove LifespanHandler.RunModal - -LifespanHandler.RunModal callback is no more available. - - -## v51+ BrowserSettings options removed +## v49+ BrowserSettings options removed The following options were removed from BrowserSettings: - user_style_sheet_location @@ -199,7 +216,7 @@ The following options were removed from BrowserSettings: - author_and_user_styles_disabled -## v51+ cef.Request.Flags changed +## v49+ cef.Request.Flags changed The following flags were removed from cef.Request.Flags: - AllowCookies @@ -209,7 +226,7 @@ The following flags were removed from cef.Request.Flags: API ref: Request.[GetFlags](../api/Request.md#getflags) -## v51+ Request.GetHeaderMap and SetHeaderMap change +## v49+ Request.GetHeaderMap and SetHeaderMap change GetHeaderMap() will not include the Referer value if any and SetHeaderMap() will ignore the Referer value. @@ -217,35 +234,93 @@ and SetHeaderMap() will ignore the Referer value. API ref: Request.[GetHeaderMap](../api/Request.md#getheadermap) -## v54+ GTK 3 example doesn't work anymore on Linux +## v49+ (Win) HTTPS cache problems on pages with certificate errors -Update: GTK 3 example is back working in v57+. +The fix for HTTPS cache problems on pages with certificate errors +(and that includes self-signed certificates) is no more applied +on Windows. -Currently GTK 3 example is broken on Linux. You can either -downgrade to an old cefpython v53 (available on GitHub release -page) or use GTK 2 example. For more details on the problem see -Issue [#261](../../../issues/261). +See Issue [#125](../../../issues/125) for more details. -## v54+ libcef.so library is stripped from symbols on Linux +## v50+ Importing the cefpython3 package on Linux -Symbols useful for debugging are no more available in libcef.so -shipped with distribution packages on Linux. This is explained -in details in Issue [#262](../../../issues/262). +In the past on Linux it was required for the cefpython3 package +to be imported before any other packages due to tcmalloc global +hook being loaded. This is not required anymore, tcmalloc is +disabled by default. -## v55+ HTTPS cache problems on pages with certificate errors +## v50+ Install X11 error handlers on Linux -The fix for HTTPS cache problems on pages with certificate errors -is no more applied on Windows. +It is required to install X11 error handlers on Linux, otherwise +you will see 'BadWindow' errors happening - sometimes randomly - +which will cause application to terminate. Since v56+ x11 error +handlers are installed automatically by default during the call +to cef.Initialize(). However sometimes that is not enough like +for example in the wxpython.py example which requires the x11 +error handlers to be installed manually after wx was initialized, +and that is because wx initialization had reset x11 error handlers +that were installed earlier during cef initialization (Issue [#334](../../../issues/334)). -Soon this will fix also won't be applied on Linux anymore when -cefpython starts using CEF prebuilt binaries from Spotify. +You can install X11 error handlers by calling: +``` +WindowUtils = cef.WindowUtils() +WindowUtils.InstallX11ErrorHandlers() +``` -See Issue [#125](../../../issues/125) for more details. +API ref: WindowUtils.[InstallX11ErrorHandlers()](../api/WindowUtils.md#installx11errorhandlers-linux) + + +## v50+ Set window bounds on Linux + +It is now required to set window bounds during window "resize", +"move" and "configure" events on Linux. You can do so by calling: + +``` +browser.SetBounds(x, y, width, height) +``` + +API ref: Browser.[SetBounds()](../api/Browser.md#setbounds) + + +## v50+ Keyboard focus issues on Linux + +There several keyboard focus issues on Linux since CEF library +replaced GTK library with X11 library. Most of these issues are +fixed in examples by calling SetFocus in LoadHandler.OnLoadStart +during initial app loading and/or by calling SetFocus in +FocusHandler.OnGotFocus. This keyboard focus issues need to be +fixed in usptream CEF. For more details see Issue [#284](../../../issues/284). -## v55.3+ Handlers' callbacks and other interfaces are now called using keyword args +## v50+ Windows XP and Vista are no more supported + +CEF Python v49.0 was the last version to support Windows XP. +This is due to Chromium/CEF dropping XP support, last version +that supported XP was CEF v49. + + +## v50+ Mac 32-bit is no more supported + +CEF Python v31.2 was the last version to support Mac 32-bit. +This is due to CEF/Chromium dropping 32-bit support, last version +that supported 32-bit was CEF v38. + + +## v51+ Remove LifespanHandler.RunModal + +LifespanHandler.RunModal callback is no more available. + + +## v54+ libcef.so library is stripped from symbols on Linux + +Symbols useful for debugging are no more available in libcef.so +shipped with distribution packages on Linux. This is explained +in details in Issue [#262](../../../issues/262). + + +## v55.3+ Handlers' callbacks and other interfaces Since v55.3 all handlers' callbacks and other interfaces such as CookieVisitor, StringVisitor and WebRequestClient, are now called @@ -407,3 +482,11 @@ with message loop freezing. Only basic testing was performed. It was not tested of how this change affects performance. See Issue [#442](../../../issues/442) for more details on the issues. + + +## v67+ Do not call the 'WindowUtils.OnSize' function + +This function can sometimes cause app hanging during window resize. +Call instead the new `WindowUtils`.[UpdateBrowserSize](../api/WindowUtils.md#updatebrowsersize) +function. See [Issue #464](../../../issues/464) for more details. + From 6cf7455179e7dd79f8a8e9c7fbbbe686c3682258 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 27 Aug 2018 20:07:56 +0200 Subject: [PATCH 093/177] Update README - style changes --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2c4e38ab..497d810f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ Table of contents: * [Introduction](#introduction) * [Sponsors](#sponsors) - * [Thanks to Lampix for the v66 release](#thanks-to-lampix-for-the-v66-release) - * [Thanks to Fivestars for the v49 release (WinXP/Vista)](#thanks-to-fivestars-for-the-v49-release-winxpvista) * [Install](#install) * [Tutorial](#tutorial) * [Examples](#examples) @@ -105,7 +103,7 @@ See the [Support development](#support-development) section for a list of all the individuals and companies supporting CEF Python. -### Thanks to Lampix for the v66 release +**Thanks to Lampix for the v66 release** Many Thanks to Lampix for sponsoring the [v66 release](../../releases/tag/v66.0) of CEF Python. Lampix is the first hardware and software solution that turns any surface @@ -115,7 +113,7 @@ website here: https://lampix.com/ . [![lampix360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/lampix360.png)](https://lampix.com/) -### Thanks to Fivestars for the v49 release (WinXP/Vista) +**Thanks to Fivestars for the v49 release (WinXP/Vista)** Thanks to Fivestars Loyalty, Inc. for sponsoring the [v49 release](../../releases/tag/v49.0) for legacy systems such From 263024423e22dfdcf720259d5c47aa2f48e949b5 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 27 Aug 2018 20:10:59 +0200 Subject: [PATCH 094/177] Update README, style changes again --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 497d810f..b6cc9eb4 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,6 @@ Lampix is the first hardware and software solution that turns any surface into a smart, augmented reality or interactive surface. Please visit its website here: https://lampix.com/ . -[![lampix360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/lampix360.png)](https://lampix.com/) - **Thanks to Fivestars for the v49 release (WinXP/Vista)** @@ -120,9 +118,6 @@ Thanks to Fivestars Loyalty, Inc. for sponsoring the as Windows XP and Vista. Please visit its website: https://www.fivestars.com/ . -[![fivestars360](https://raw.githubusercontent.com/wiki/cztomczak/cefpython/images/fivestars360.png)](https://www.fivestars.com/) - - ## Install From 3e6f421a2bdba024522092c91e9e2031a8dded10 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 27 Aug 2018 20:17:30 +0200 Subject: [PATCH 095/177] Disable GPU acceleration and add perf. enhancing switches in OSR (#463) --- examples/screenshot.py | 28 +++++++++++++++++++++++----- unittests/osr_test.py | 22 +++++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/examples/screenshot.py b/examples/screenshot.py index 7392ed5b..d6dc661c 100644 --- a/examples/screenshot.py +++ b/examples/screenshot.py @@ -43,7 +43,7 @@ try: from PIL import Image, PILLOW_VERSION -except: +except ImportError: print("[screenshot.py] Error: PIL module not available. To install" " type: pip install Pillow") sys.exit(1) @@ -64,9 +64,26 @@ def main(): os.remove(SCREENSHOT_PATH) command_line_arguments() # Off-screen-rendering requires setting "windowless_rendering_enabled" - # option, so that RenderHandler callbacks are called. - cef.Initialize(settings={"windowless_rendering_enabled": True}) - create_browser() + # option. + settings = { + "windowless_rendering_enabled": True, + } + switches = { + # GPU acceleration is not supported in OSR mode, so must disable + # it using these Chromium switches (Issue #240 and #463) + "disable-gpu": "", + "disable-gpu-compositing": "", + # Tweaking OSR performance by setting the same Chromium flags + # as in upstream cefclient (Issue #240). + "enable-begin-frame-scheduling": "", + "disable-surfaces": "", # This is required for PDF ext to work + } + browser_settings = { + # Tweaking OSR performance (Issue #240) + "windowless_frame_rate": 30, # Default frame rate in CEF is 30 + } + cef.Initialize(settings=settings, switches=switches) + create_browser(browser_settings) cef.MessageLoop() cef.Shutdown() print("[screenshot.py] Opening screenshot with default application") @@ -108,7 +125,7 @@ def command_line_arguments(): sys.exit(1) -def create_browser(): +def create_browser(settings): # Create browser in off-screen-rendering mode (windowless mode) # by calling SetAsOffscreen method. In such mode parent window # handle can be NULL (0). @@ -120,6 +137,7 @@ def create_browser(): print("[screenshot.py] Loading url: {url}" .format(url=URL)) browser = cef.CreateBrowserSync(window_info=window_info, + settings=settings, url=URL) browser.SetClientHandler(LoadHandler()) browser.SetClientHandler(RenderHandler()) diff --git a/unittests/osr_test.py b/unittests/osr_test.py index 0e4191be..672e98fe 100644 --- a/unittests/osr_test.py +++ b/unittests/osr_test.py @@ -23,7 +23,7 @@ font-size: 11pt; } - + + + +""" + + +class RequestHandler: + def GetResourceHandler(self, browser, frame, request): + print(request.GetPostData()) + return None + +def main(): + sys.excepthook = cef.ExceptHook + cef.Initialize() + browser = cef.CreateBrowserSync(url=cef.GetDataUrl(html)) + browser.SetClientHandler(RequestHandler()) + cef.MessageLoop() + del browser + cef.Shutdown() + + +if __name__ == '__main__': + main() diff --git a/unittests/main_test.py b/unittests/main_test.py index b0c2200c..f3602c21 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -235,7 +235,7 @@ def test_main(self): req_file = os.path.dirname(os.path.abspath(__file__)) req_file = os.path.join(req_file, "main_test.py") if sys.version_info.major > 2: - req_file = req_file.encode() + req_file = req_file.encode("utf-8") req_data = [b"--key=value", b"@"+req_file] req.SetMethod("POST") req.SetPostData(req_data) From b7f25aa7eecf59569dbb0bd7513ff84cee669c00 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sun, 12 Jan 2020 11:00:47 +0100 Subject: [PATCH 132/177] Fix tkinter focus issues on Windows (#535). --- examples/tkinter_.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/tkinter_.py b/examples/tkinter_.py index 58dc402e..f1e2c818 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -13,6 +13,8 @@ # Known issue on Linux: When typing url, mouse must be over url # entry widget otherwise keyboard focus is lost (Issue #255 # and Issue #284). +# Other focus issues discussed in Issue #535. + from cefpython3 import cefpython as cef import ctypes @@ -153,6 +155,7 @@ def __init__(self, master, navigation_bar=None): self.bind("", self.on_focus_in) self.bind("", self.on_focus_out) self.bind("", self.on_configure) + """For focus problems see Issue #255 and Issue #535. """ self.focus_set() def embed_browser(self): @@ -216,8 +219,6 @@ def on_focus_in(self, _): def on_focus_out(self, _): logger.debug("BrowserFrame.on_focus_out") - if self.browser: - self.browser.SetFocus(False) def on_root_close(self): if self.browser: @@ -242,6 +243,7 @@ def OnLoadStart(self, browser, **_): class FocusHandler(object): + """For focus problems see Issue #255 and Issue #535. """ def __init__(self, browser_frame): self.browser_frame = browser_frame @@ -253,13 +255,10 @@ def OnTakeFocus(self, next_component, **_): def OnSetFocus(self, source, **_): logger.debug("FocusHandler.OnSetFocus, source={source}" .format(source=source)) - return False + return True def OnGotFocus(self, **_): - """Fix CEF focus issues (#255). Call browser frame's focus_set - to get rid of type cursor in url entry widget.""" logger.debug("FocusHandler.OnGotFocus") - self.browser_frame.focus_set() class NavigationBar(tk.Frame): @@ -340,7 +339,7 @@ def on_load_url(self, _): self.master.get_browser().LoadUrl(self.url_entry.get()) def on_button1(self, _): - """Fix CEF focus issues (#255). See also FocusHandler.OnGotFocus.""" + """For focus problems see Issue #255 and Issue #535. """ logger.debug("NavigationBar.on_button1") self.master.master.focus_force() From 076b6a661246c5855233730c766ab1dfa58264a9 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sun, 12 Jan 2020 11:12:15 +0100 Subject: [PATCH 133/177] Fix tkinter focus problems on Linux (#535). --- examples/tkinter_.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/tkinter_.py b/examples/tkinter_.py index f1e2c818..c823f3dc 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -219,6 +219,9 @@ def on_focus_in(self, _): def on_focus_out(self, _): logger.debug("BrowserFrame.on_focus_out") + """For focus problems see Issue #255 and Issue #535. """ + if LINUX and self.browser: + self.browser.SetFocus(False) def on_root_close(self): if self.browser: @@ -255,10 +258,15 @@ def OnTakeFocus(self, next_component, **_): def OnSetFocus(self, source, **_): logger.debug("FocusHandler.OnSetFocus, source={source}" .format(source=source)) - return True + if LINUX: + return False + else: + return True def OnGotFocus(self, **_): logger.debug("FocusHandler.OnGotFocus") + if LINUX: + self.browser_frame.focus_set() class NavigationBar(tk.Frame): From bddb859d52a6c4bc65d7a3400d1fed71d40e3d7c Mon Sep 17 00:00:00 2001 From: GiovanniL <13402461+GiovaLomba@users.noreply.github.com> Date: Sun, 12 Jan 2020 17:08:27 +0100 Subject: [PATCH 134/177] Fixed typo (#556) Original: The actual key code genenerated by the platform New: The actual key code generated by the platform --- api/KeyboardHandler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/KeyboardHandler.md b/api/KeyboardHandler.md index f8082805..4735a88a 100644 --- a/api/KeyboardHandler.md +++ b/api/KeyboardHandler.md @@ -38,7 +38,7 @@ KeyEvent is a dictionary with the following keys: | type | KeyEventType | The type of keyboard event | | modifiers | KeyEventFlags | Bit flags describing any pressed modifier keys | | windows_key_code | int | The Windows key code for the key event. This value is used by the DOM specification. Sometimes it comes directly from the event (i.e. on Windows) and sometimes it's determined using a mapping function. See "chromium/KeyboardCodes.h" for a list of values. | -| native_key_code | int | The actual key code genenerated by the platform | +| native_key_code | int | The actual key code generated by the platform | | is_system_key | bool | Indicates whether the event is considered a "system key" event. For Windows see [WM_SYSKEYDOWN](http://msdn.microsoft.com/en-us/library/ms646286.aspx). This value will always be false on non-Windows platforms. | | character | wchar_t or unsigned short | The character generated by the keystroke | | unmodified_character | wchar_t or unsigned short | Same as 'character' but unmodified by any concurrently-held modifiers (except shift). This is useful for working out shortcut keys. | From d9587bc6e8198de84aaedf9dbbbc42b569384730 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Mon, 13 Jan 2020 11:36:30 +0100 Subject: [PATCH 135/177] Issue 554 (#557) * Do not link against libpython on Linux (#554). * Part 2. Do not link against libpython library on Linux (#554). --- tools/build.py | 2 +- tools/cython_setup.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/build.py b/tools/build.py index 66d1244e..5e578b94 100644 --- a/tools/build.py +++ b/tools/build.py @@ -238,7 +238,7 @@ def check_directories(): prebuilt_name = get_cef_binaries_libraries_basename(OS_POSTFIX2) print("[build.py] ERROR: Couldn't find CEF prebuilt binaries and" " libraries: 'build/{prebuilt_dir}/'. Download it" - " from GitHub released tagged eg. 'v50-upstream` or download" + " from GitHub released tagged eg. 'vXX-upstream` or download" " CEF binaries from Spotify Automated Builds and then run" "`automate.py --prebuilt-cef`." .format(prebuilt_dir=prebuilt_name)) diff --git a/tools/cython_setup.py b/tools/cython_setup.py index e033cf84..4f0cafc6 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -25,6 +25,7 @@ import sys import platform import Cython +import copy import os # Must monkey patch Cython's ModuleNode to inject custom C++ code @@ -65,6 +66,27 @@ def generate_extern_c_macro_definition(self, code): generate_extern_c_macro_definition) +# Issue #554: Shared libraries in manylinux1 wheel should not +# be linked against libpythonX.Y.so.1.0. +if LINUX: + get_libraries_old = (build_ext.get_libraries) + def get_libraries_new(self, ext): + libraries = get_libraries_old(self, ext) + libpython = ('python' + str(sys.version_info.major) + '.' + + str(sys.version_info.minor)) + for lib in copy.copy(libraries): + # Library name for Python versions before 3.8 may have + # an 'm' at the end. + if lib.startswith(libpython): + print("[cython_setup.py] Do not link against -l%s (Issue #554)" + % lib) + libraries.remove(lib) + return libraries + build_ext.get_libraries = ( + get_libraries_new + ) + + # Command line args FAST_FLAG = False ENABLE_PROFILING = False From fb2d94ca42c8ca54fe383a0ea55c9d3904fd5298 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 14 Jan 2020 16:02:46 +0100 Subject: [PATCH 136/177] Fix CefListValue "int index" params (#461). --- src/extern/cef/cef_values.pxd | 34 +++++++++++++++++----------------- src/process_message_utils.pyx | 8 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/extern/cef/cef_values.pxd b/src/extern/cef/cef_values.pxd index ee0bf506..3210432b 100644 --- a/src/extern/cef/cef_values.pxd +++ b/src/extern/cef/cef_values.pxd @@ -72,20 +72,20 @@ cdef extern from "include/cef_values.h": cpp_bool SetSize(size_t size) size_t GetSize() cpp_bool Clear() - cpp_bool Remove(int index) - cef_value_type_t GetType(int index) - cpp_bool GetBool(int index) - int GetInt(int index) - double GetDouble(int index) - CefString GetString(int index) - CefRefPtr[CefBinaryValue] GetBinary(int index) - CefRefPtr[CefDictionaryValue] GetDictionary(int index) - CefRefPtr[CefListValue] GetList(int index) - cpp_bool SetNull(int index) - cpp_bool SetBool(int index, cpp_bool value) - cpp_bool SetInt(int index, int value) - cpp_bool SetDouble(int index, double value) - cpp_bool SetString(int index, const CefString& value) - cpp_bool SetBinary(int index, CefRefPtr[CefBinaryValue] value) - cpp_bool SetDictionary(int index, CefRefPtr[CefDictionaryValue] value) - cpp_bool SetList(int index, CefRefPtr[CefListValue] value) + cpp_bool Remove(size_t index) + cef_value_type_t GetType(size_t index) + cpp_bool GetBool(size_t index) + int GetInt(size_t index) + double GetDouble(size_t index) + CefString GetString(size_t index) + CefRefPtr[CefBinaryValue] GetBinary(size_t index) + CefRefPtr[CefDictionaryValue] GetDictionary(size_t index) + CefRefPtr[CefListValue] GetList(size_t index) + cpp_bool SetNull(size_t index) + cpp_bool SetBool(size_t index, cpp_bool value) + cpp_bool SetInt(size_t index, int value) + cpp_bool SetDouble(size_t index, double value) + cpp_bool SetString(size_t index, const CefString& value) + cpp_bool SetBinary(size_t index, CefRefPtr[CefBinaryValue] value) + cpp_bool SetDictionary(size_t index, CefRefPtr[CefDictionaryValue] value) + cpp_bool SetList(size_t index, CefRefPtr[CefListValue] value) diff --git a/src/process_message_utils.pyx b/src/process_message_utils.pyx index 464c8f6b..8ced3f6c 100644 --- a/src/process_message_utils.pyx +++ b/src/process_message_utils.pyx @@ -92,8 +92,8 @@ cdef list CefListValueToPyList( if nestingLevel > 8: raise Exception("CefListValueToPyList(): max nesting level (8)" " exceeded") - cdef int index - cdef int size = int(cefListValue.get().GetSize()) + cdef size_t index + cdef size_t size = cefListValue.get().GetSize() cdef cef_types.cef_value_type_t valueType cdef list ret = [] cdef CefRefPtr[CefBinaryValue] binaryValue @@ -230,7 +230,7 @@ cdef CefRefPtr[CefListValue] PyListToCefListValue( cdef type valueType cdef CefRefPtr[CefListValue] ret = CefListValue_Create() cdef CefRefPtr[CefBinaryValue] binaryValue - cdef int index + cdef size_t index for index_size_t, value in enumerate(pyList): index = int(index_size_t) valueType = type(value) @@ -289,7 +289,7 @@ cdef void PyListToExistingCefListValue( " exceeded") cdef type valueType cdef CefRefPtr[CefListValue] newCefListValue - cdef int index + cdef size_t index for index_size_t, value in enumerate(pyList): index = int(index_size_t) valueType = type(value) From 2c3681f633cd86024e0a4031a4efcea54570fb53 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 15 Jan 2020 14:32:49 +0100 Subject: [PATCH 137/177] Fix crash when setting a cookie with invalid domain (#459). --- src/cookie.pyx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cookie.pyx b/src/cookie.pyx index ce078c10..6d90144b 100644 --- a/src/cookie.pyx +++ b/src/cookie.pyx @@ -119,6 +119,19 @@ cdef class Cookie: return CefToPyString(cefString) cpdef py_void SetDomain(self, py_string domain): + pattern = re.compile(r"^(?:[a-z0-9](?:[a-z0-9-_]{0,61}[a-z0-9])?\.)" + r"+[a-z0-9][a-z0-9-_]{0,61}[a-z]$") + if PY_MAJOR_VERSION == 2: + assert isinstance(domain, bytes), "domain type is not bytes" + domain = domain.decode(g_applicationSettings["string_encoding"], + errors=BYTES_DECODE_ERRORS) + try: + if not pattern.match(domain.encode("idna").decode("ascii")): + raise Exception("Cookie.SetDomain() failed, invalid domain: {0}" + .format(domain)) + except UnicodeError: + raise Exception("Cookie.SetDomain() failed, invalid domain: {0}" + .format(domain)) cdef CefString cefString cefString.Attach(&self.cefCookie.domain, False) PyToCefString(domain, cefString) From a5d97f03e1762f79ac0d69ab446ade33b597ff5d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Jan 2020 11:56:45 +0100 Subject: [PATCH 138/177] Add GIL section to Contributing-code.md document (#102). --- docs/Contributing-code.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/Contributing-code.md b/docs/Contributing-code.md index bbcefc26..fb56e115 100644 --- a/docs/Contributing-code.md +++ b/docs/Contributing-code.md @@ -12,6 +12,7 @@ Table of contents: * [API docs](#api-docs) * [Unit tests](#unit-tests) * [Platforms](#platforms) +* [GIL](#gil) * [Authors](#authors) * [Updating CEF version](#updating-cef-version) @@ -123,6 +124,24 @@ In most cases new code should run fine on all platforms, but in some cases it might be required to test on all platforms before PR is merged. +## GIL + +In the pxd file, functions should be defined as "nogil" to avoid +deadlocks when calling CEF functions. In the pyx file the call must +use the "with nogil" statement. + +From [Cython's documentation](http://docs.cython.org/src/userguide/external_C_code.html#acquiring-and-releasing-the-gil): + +> Note that acquiring the GIL is a blocking thread-synchronising operation, +> and therefore potentially costly. It might not be worth releasing the GIL +> for minor calculations. Usually, I/O operations and substantial computations +> in parallel code will benefit from it. + +Revision [ec1ce78](https://github.com/cztomczak/cefpython/commit/ec1ce788373bb9e0fd2cedd71e900c3877e9185a) removes the GIL lock from the +following calls: Initialize(), Shutdown(), CreateBrowserSync(), +SetOsModalLoop(), QuitMessageLoop(), CefExecuteProcess(). There still +might be some more functions from which the GIL lock should be removed. + ## Authors In your pull request modify also the [Authors](../Authors) file From 11017dc68358b6adb4a3d822f53fa658ff4fa4f5 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Jan 2020 12:31:08 +0100 Subject: [PATCH 139/177] Update LifespanHandler.md document. Info on OnBeforePopup and scripting between popup and parent window. (#171) --- api/LifespanHandler.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/api/LifespanHandler.md b/api/LifespanHandler.md index 2b929512..1794d62d 100644 --- a/api/LifespanHandler.md +++ b/api/LifespanHandler.md @@ -107,10 +107,6 @@ Description from upstream CEF: > browser is destroyed before the popup browser creation completes (indicated > by a call to OnAfterCreated for the popup browser). -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. - `WindowOpenDisposition` constants in the cefpython module: * WOD_UNKNOWN, * WOD_CURRENT_TAB, @@ -122,3 +118,11 @@ There will be no "window.opener" property available in the popup window. * WOD_SAVE_TO_DISK, * WOD_OFF_THE_RECORD, * WOD_IGNORE_ACTION + +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. +To avoid this issue create a hidden window when your application starts. +Parent the new popup browser to the hidden window in OnBeforePopup. After +the browser exists (OnAfterCreated) create the desired target window +and re-parent the browser to that target window. From 42924f5ee00ea4a5b97a099b0afb2ed9b877a71a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Jan 2020 15:52:10 +0100 Subject: [PATCH 140/177] Update README links for the old v31.2 release. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 24370549..0d0344ba 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,6 @@ See the [README-examples.md](examples/README-examples.md) and in the issue. See [Most popular issues](../../issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) sorted by reactions. -- Wiki pages are deprecated and for v31 only ## Releases @@ -196,10 +195,11 @@ Mac | 2.7 | No | Yes | Yes | MacOS 10.7+ Additional information for v31.2 release: - On Windows/Mac you can install with command: `pip install cefpython3==31.2` -- Downloads are available on [wiki pages](../../wiki#downloads) - and on GitHub Releases tagged [v31.2](../../releases/tag/v31.2). -- Documentation is on [wiki pages](../../wiki) +- Downloads are available on the GitHub Releases page tagged + [v31.2](../../releases/tag/v31.2). - API reference is available in revision [169a1b2](../../tree/169a1b20d3cd09879070d41aab28cfa195d2a7d5/docs/api) +- Other documentation can be downloaded by cloning the + [cefpython.wiki.git](git@github.com:cztomczak/cefpython.wiki.git) repository. ## Support development From e9116faa2094de85ccebd403aee09c61e2c8b8f9 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 16 Jan 2020 15:54:30 +0100 Subject: [PATCH 141/177] Update README with cefpython.wiki git clone command. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d0344ba..9cef2431 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ Additional information for v31.2 release: [v31.2](../../releases/tag/v31.2). - API reference is available in revision [169a1b2](../../tree/169a1b20d3cd09879070d41aab28cfa195d2a7d5/docs/api) - Other documentation can be downloaded by cloning the - [cefpython.wiki.git](git@github.com:cztomczak/cefpython.wiki.git) repository. + cefpython.wiki repository: `git clone git@github.com:cztomczak/cefpython.wiki.git` ## Support development From b03b78b64d0162eb186d05fb22452f934cd31152 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 17 Jan 2020 17:19:08 +0100 Subject: [PATCH 142/177] Fix V8ContextHandler.OnContextCreated and OnContextReleased (#484). These callbacks were never called previously. Rename --no-run-examples flag to --unittests in build scripts. --- api/V8ContextHandler.md | 4 +++- src/browser.pyx | 3 +++ tools/build.py | 23 +++++++++++++---------- tools/build_distrib.py | 23 ++++++++++++----------- unittests/_common.py | 5 +++-- unittests/main_test.py | 33 ++++++++++++++++++++++++++++++++- 6 files changed, 66 insertions(+), 25 deletions(-) diff --git a/api/V8ContextHandler.md b/api/V8ContextHandler.md index 9c6f6fbc..b88a8641 100644 --- a/api/V8ContextHandler.md +++ b/api/V8ContextHandler.md @@ -3,7 +3,9 @@ # V8ContextHandler (interface) -Implement this interface to handle javascript exceptions globally. +Implement this interface to handle render process callbacks. +Through inter-process messaging you are notified about these events +in the browser process. Table of contents: diff --git a/src/browser.pyx b/src/browser.pyx index d378ca22..8dfb7aed 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -267,6 +267,9 @@ cdef class PyBrowser: "OnScrollOffsetChanged", "StartDragging", "UpdateDragCursor", "OnTextSelectionChanged"] + # V8ContextHandler + self.allowedClientCallbacks += ["OnContextCreated", + "OnContextReleased"] # JavascriptDialogHandler self.allowedClientCallbacks += ["OnJavascriptDialog", "OnBeforeUnloadJavascriptDialog", diff --git a/tools/build.py b/tools/build.py index 5e578b94..522a9b08 100644 --- a/tools/build.py +++ b/tools/build.py @@ -18,12 +18,15 @@ from sources or use ready binaries from Spotify Automated Builds. Usage: - build.py VERSION [--rebuild-cpp] [--fast] [--clean] [--kivy] - [--hello-world] + build.py VERSION [--rebuild-cpp] [--unittests] [--fast] [--clean] [--kivy] + [--hello-world] [--enable-profiling] + [--enable-line-tracing] Options: VERSION Version number eg. 50.0 - --no-run-examples Do not run examples after build, only unit tests + --unittests Run only unit tests. Do not run examples while + building cefpython modules. Examples require + interaction such as closing window before proceeding. --fast Fast mode --clean Clean C++ projects build files on Linux/Mac --kivy Run only Kivy example @@ -79,7 +82,7 @@ # Command line args variables SYS_ARGV_ORIGINAL = None VERSION = "" -NO_RUN_EXAMPLES = False +UNITTESTS = False DEBUG_FLAG = False FAST_FLAG = False CLEAN_FLAG = False @@ -125,7 +128,7 @@ def main(): def command_line_args(): global DEBUG_FLAG, FAST_FLAG, CLEAN_FLAG, KIVY_FLAG, HELLO_WORLD_FLAG, \ - REBUILD_CPP, VERSION, NO_RUN_EXAMPLES + REBUILD_CPP, VERSION, UNITTESTS VERSION = get_version_from_command_line_args(__file__) # Other scripts called by this script expect that version number @@ -140,10 +143,10 @@ def command_line_args(): global SYS_ARGV_ORIGINAL SYS_ARGV_ORIGINAL = copy.copy(sys.argv) - if "--no-run-examples" in sys.argv: - NO_RUN_EXAMPLES = True - print("[build.py] Running examples disabled (--no-run-examples)") - sys.argv.remove("--no-run-examples") + if "--unittests" in sys.argv: + UNITTESTS = True + print("[build.py] Running examples disabled (--unittests)") + sys.argv.remove("--unittests") if "--debug" in sys.argv: DEBUG_FLAG = True @@ -913,7 +916,7 @@ def install_and_run(): sys.exit(1) # Run examples - if not NO_RUN_EXAMPLES: + if not UNITTESTS: print("[build.py] Run examples") os.chdir(EXAMPLES_DIR) flags = "" diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 8ff5a6b8..e0a5bde1 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -7,13 +7,14 @@ python versions. Usage: - build_distrib.py VERSION [--no-run-examples] [--no-rebuild] + build_distrib.py VERSION [--unittests] [--no-rebuild] [--no-automate] + [--allow-partial] Options: VERSION Version number eg. 50.0 - --no-run-examples Do not run examples while building cefpython modules. - Examples require interaction, closing window before - proceeding. Only unit tests will be run in such case. + --unittests Run only unit tests. Do not run examples while building + cefpython modules. Examples require interaction such as + closing window before proceeding. --no-rebuild Do not rebuild cefpython modules. For internal use so that changes to packaging can be quickly tested. --no-automate Do not run automate.py --prebuilt-cef. This flag @@ -73,7 +74,7 @@ # Command line args VERSION = "" -NO_RUN_EXAMPLES = False +UNITTESTS = False NO_REBUILD = False NO_AUTOMATE = False ALLOW_PARTIAL = False @@ -152,15 +153,15 @@ def main(): def command_line_args(): - global VERSION, NO_RUN_EXAMPLES, NO_REBUILD, NO_AUTOMATE, ALLOW_PARTIAL + global VERSION, UNITTESTS, NO_REBUILD, NO_AUTOMATE, ALLOW_PARTIAL version = get_version_from_command_line_args(__file__) if not version or "--help" in sys.argv: print(__doc__) sys.exit(1) VERSION = version - if "--no-run-examples" in sys.argv: - NO_RUN_EXAMPLES = True - sys.argv.remove("--no-run-examples") + if "--unittests" in sys.argv: + UNITTESTS = True + sys.argv.remove("--unittests") if "--no-rebuild" in sys.argv: NO_REBUILD = True sys.argv.remove("--no-rebuild") @@ -488,8 +489,8 @@ def build_cefpython_modules(pythons, arch): print("[build_distrib.py] Build cefpython module for {python_name}" .format(python_name=python["name"])) flags = "" - if NO_RUN_EXAMPLES: - flags += " --no-run-examples" + if UNITTESTS: + flags += " --unittests" # On Linux/Mac Makefiles are used and must pass --clean flag command = ("\"{python}\" {build_py} {version} --clean {flags}" .format(python=python["executable"], diff --git a/unittests/_common.py b/unittests/_common.py index 684a27b1..fcd89917 100644 --- a/unittests/_common.py +++ b/unittests/_common.py @@ -82,7 +82,7 @@ def check_auto_asserts(test_case, objects): test_for_True = False # Test whether asserts are working correctly for key, value in obj.__dict__.items(): if key == "test_for_True": - test_for_True = True + test_for_True = value continue if "_True" in key: test_case.assertTrue(value, "Check assert: " + @@ -96,7 +96,8 @@ def check_auto_asserts(test_case, objects): subtest_message(obj.__class__.__name__ + "." + key.replace("_False", "") + " ok") - test_case.assertTrue(test_for_True) + if "test_for_True" in obj.__dict__.keys(): + test_case.assertTrue(test_for_True) class DisplayHandler(object): diff --git a/unittests/main_test.py b/unittests/main_test.py index f3602c21..99397da8 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -197,9 +197,11 @@ def test_main(self): # Client handlers display_handler2 = DisplayHandler2(self) + v8context_handler = V8ContextHandler(self) client_handlers = [LoadHandler(self, g_datauri), DisplayHandler(self), - display_handler2] + display_handler2, + v8context_handler] for handler in client_handlers: browser.SetClientHandler(handler) subtest_message("browser.SetClientHandler() ok") @@ -336,6 +338,35 @@ def OnLoadingProgressChange(self, progress, **_): self.OnLoadingProgressChange_Progress = progress +class V8ContextHandler(object): + def __init__(self, test_case): + self.test_case = test_case + self.OnContextCreatedFirstCall_True = False + self.OnContextCreatedSecondCall_True = False + self.OnContextReleased_True = False + + def OnContextCreated(self, browser, frame): + """CEF creates one context when creating browser and this one is + released immediately. Then when it loads url another context is + created.""" + if not self.OnContextCreatedFirstCall_True: + self.OnContextCreatedFirstCall_True = True + else: + self.test_case.assertFalse(self.OnContextCreatedSecondCall_True) + self.OnContextCreatedSecondCall_True = True + self.test_case.assertEqual(browser.GetIdentifier(), MAIN_BROWSER_ID) + self.test_case.assertEqual(frame.GetIdentifier(), 2) + + def OnContextReleased(self, browser, frame): + """This gets called only for the initial empty context, see comment + in OnContextCreated. This should never get called for the main frame + of the main browser, because it happens during app exit and there + isn't enough time for the IPC messages to go through.""" + self.test_case.assertFalse(self.OnContextReleased_True) + self.OnContextReleased_True = True + self.test_case.assertEqual(browser.GetIdentifier(), MAIN_BROWSER_ID) + self.test_case.assertEqual(frame.GetIdentifier(), 2) + class External(object): """Javascript 'window.external' object.""" From 6cc7f9c8058475ab9aadc04ff614b9904ce063b9 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Jan 2020 14:35:29 +0100 Subject: [PATCH 143/177] Fix issues with urls containing special characters (#384). BC BREAK! Updated Migration-Guide.md document and other documentation. --- api/Browser.md | 9 +++++++- api/Frame.md | 9 +++++++- api/cefpython.md | 9 +++++++- docs/Migration-guide.md | 14 +++++++++++-- src/cefpython.pyx | 6 ------ src/frame.pyx | 1 - src/utils.pyx | 46 ----------------------------------------- 7 files changed, 36 insertions(+), 58 deletions(-) diff --git a/api/Browser.md b/api/Browser.md index 66272c0a..b34b90e2 100644 --- a/api/Browser.md +++ b/api/Browser.md @@ -376,7 +376,7 @@ Returns the focused [Frame](Frame.md) for the browser window. | name | string | | __Return__ | Frame | -Returns the [Frame](Frame.md) with the specified name, or NULL if not found. +Returns the [Frame](Frame.md) with the specified name, or NULL if not found. ### GetFrameByIdentifier @@ -697,6 +697,13 @@ Returns true if window rendering is disabled. Load url in the main frame. +If the url is a local path it needs to start with the `file://` prefix. +If the url contains special characters it may need proper handling. +Starting with v66.1+ it is required for the app code to encode the url +properly. You can use the `pathlib.PurePath.as_uri` in Python 3 +or `urllib.pathname2url` in Python 2 (`urllib.request.pathname2url` +in Python 3) depending on your case. + ### Navigate diff --git a/api/Frame.md b/api/Frame.md index 87dbc89c..05712912 100644 --- a/api/Frame.md +++ b/api/Frame.md @@ -228,9 +228,16 @@ Take also a look at a [custom resource handler](ResourceHandler.md). Load the contents of |value| with the specified dummy |url|. |url| should have a standard scheme (for example, http scheme) or behaviors like -link clicks and web security restrictions may not behave as expected. +link clicks and web security restrictions may not behave as expected. LoadString() can be called only after the Renderer process has been created. +If the url is a local path it needs to start with the `file://` prefix. +If the url contains special characters it may need proper handling. +Starting with v66.1+ it is required for the app code to encode the url +properly. You can use the `pathlib.PurePath.as_uri` in Python 3 +or `urllib.pathname2url` in Python 2 (`urllib.request.pathname2url` +in Python 3) depending on your case. + ### LoadUrl diff --git a/api/cefpython.md b/api/cefpython.md index 29720c48..ae7d255c 100644 --- a/api/cefpython.md +++ b/api/cefpython.md @@ -59,6 +59,13 @@ All parameters are optional. This function can only be called on the UI thread. +If the url is a local path it needs to start with the `file://` prefix. +If the url contains special characters it may need proper handling. +Starting with v66.1+ it is required for the app code to encode the url +properly. You can use the `pathlib.PurePath.as_uri` in Python 3 +or `urllib.pathname2url` in Python 2 (`urllib.request.pathname2url` +in Python 3) depending on your case. + The "window_title" parameter will be used only when parent window provided in window_info was set to 0. This is for use with hello_world.py and tutorial.py examples which don't use @@ -240,7 +247,7 @@ Description from upstream CEF: | __Return__ | void | Run the CEF message loop. Use this function instead of an application- -provided message loop to get the best balance between performance and +provided message loop to get the best balance between performance and CPU usage. This function should only be called on the main application thread (UI thread) and only if cefpython.Initialize() is called with a [ApplicationSettings](ApplicationSettings.md).multi_threaded_message_loop diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index 2359562c..5ad3d438 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -50,6 +50,7 @@ Table of contents: * [v66+ cef.Request.Flags changed](#v66-cefrequestflags-changed) * [v66+ RequestHandler.GetCookieManager not getting called in some cases](#v66-requesthandlergetcookiemanager-not-getting-called-in-some-cases) * [v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loop-qt-wxpython) +* [v66.1+ Navigation urls passed to CreateBrowserSync or LoadUrl methods need to be encoded by app code](#v661-navigation-urls-passed-to-createbrowsersync-or-loadurl-methods-need-to-be-encoded-by-app-code) * [v67+ Do not call the 'WindowUtils.OnSize' function](#v67-do-not-call-the-windowutilsonsize-function) @@ -487,6 +488,16 @@ tested of how this change affects performance. See Issue [#442](../../../issues/442) for more details on the issues. +## v66.1+ Navigation urls passed to CreateBrowserSync or LoadUrl methods need to be encoded by app code + +[Issue #384](../../../issues/384) fixes problems with browser failing to load +urls containing certain characters by not encoding the url anymore. From now +on it is required for the app code to encode the url properly. You can use +the `pathlib.PurePath.as_uri` in Python 3 or `urllib.pathname2url` in +Python 2 (`urllib.request.pathname2url` in Python 3) depending on your case. + +The `cef.GetNavigateUrl` function was removed from the cefpython3 module. + ## v67+ Do not call the 'WindowUtils.OnSize' function @@ -494,5 +505,4 @@ This function can sometimes cause app hanging during window resize. Call instead the new `WindowUtils`.[UpdateBrowserSize](../api/WindowUtils.md#updatebrowsersize) function. Except when you use the `pywin32.py` example, in such case `WindowUtils.OnSize` must be called. -See [Issue #464](../../../issues/464) for more details. - +See [Issue #464](../../../issues/464) for more details. \ No newline at end of file diff --git a/src/cefpython.pyx b/src/cefpython.pyx index aff190a0..c3b1e1a1 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -144,8 +144,6 @@ IF PY_MAJOR_VERSION == 2: # noinspection PyUnresolvedReferences import urlparse # noinspection PyUnresolvedReferences - from urllib import pathname2url as urllib_pathname2url - # noinspection PyUnresolvedReferences from urllib import urlencode as urllib_urlencode from urllib import quote as urlparse_quote ELSE: @@ -153,8 +151,6 @@ ELSE: from urllib import parse as urlparse from urllib.parse import quote as urlparse_quote # noinspection PyUnresolvedReferences - from urllib.request import pathname2url as urllib_pathname2url - # noinspection PyUnresolvedReferences from urllib.parse import urlencode as urllib_urlencode # noinspection PyUnresolvedReferences @@ -739,8 +735,6 @@ def CreateBrowserSync(windowInfo=None, cdef CefWindowInfo cefWindowInfo SetCefWindowInfo(cefWindowInfo, windowInfo) - navigateUrl = GetNavigateUrl(navigateUrl) - Debug("navigateUrl: %s" % navigateUrl) cdef CefString cefNavigateUrl PyToCefString(navigateUrl, cefNavigateUrl) diff --git a/src/frame.pyx b/src/frame.pyx index ec3ef8bc..812e4b3c 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -211,7 +211,6 @@ cdef class PyFrame: self.GetCefFrame().get().LoadString(cefValue, cefUrl) cpdef py_void LoadUrl(self, py_string url): - url = GetNavigateUrl(url) cdef CefString cefUrl PyToCefString(url, cefUrl) self.GetCefFrame().get().LoadURL(cefUrl) diff --git a/src/utils.pyx b/src/utils.pyx index 0e499554..3d1acd89 100644 --- a/src/utils.pyx +++ b/src/utils.pyx @@ -72,52 +72,6 @@ cpdef str GetSystemError(): ELSE: return "" -cpdef str GetNavigateUrl(py_string url): - # Encode local file paths so that CEF can load them correctly: - # | some.html, some/some.html, D:\, /var, file:// - if re.search(r"^file:", url, re.I) or \ - re.search(r"^[a-zA-Z]:", url) or \ - not re.search(r"^[\w-]+:", url): - - # Function pathname2url will complain if url starts with "file://". - # CEF may also change local urls to "file:///C:/" - three slashes. - is_file_protocol = False - file_prefix = "" - file_prefixes = ["file:///", "file://"] - for file_prefix in file_prefixes: - if url.startswith(file_prefix): - is_file_protocol = True - # Remove the file:// prefix - url = url[len(file_prefix):] - break - - # Need to encode chinese characters in local file paths, - # otherwise CEF will try to encode them by itself. But it - # will fail in doing so. CEF will return the following string: - # >> %EF%BF%97%EF%BF%80%EF%BF%83%EF%BF%A6 - # But it should be: - # >> %E6%A1%8C%E9%9D%A2 - url = urllib_pathname2url(url) - - if is_file_protocol: - url = "%s%s" % (file_prefix, url) - - # If it is C:\ then colon was encoded. Decode it back. - url = re.sub(r"^([a-zA-Z])%3A", r"\1:", url) - - # Allow hash when loading urls. The pathname2url function - # replaced hashes with "%23" (Issue #114). - url = url.replace("%23", "#") - - # Allow more special characters when loading urls. The pathname2url - # function encoded them and need to decode them back here - # Characters: ? & = (Issue #273). - url = url.replace("%3F", "?") - url = url.replace("%26", "&") - url = url.replace("%3D", "=") - - return str(url) - cpdef py_bool IsFunctionOrMethod(object valueType): if (valueType == types.FunctionType or valueType == types.MethodType From d97ba3d19e36c754792f004c4d0a01b2baad9a09 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Jan 2020 12:37:50 +0100 Subject: [PATCH 144/177] Update Sponsors in README. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9cef2431..b2c06aad 100644 --- a/README.md +++ b/README.md @@ -246,10 +246,10 @@ priority.
@@ -278,7 +278,7 @@ priority. the v49 release for legacy systems (WinXP/Vista) * [2018] Many thanks to [Lampix](https://lampix.com/) for sponsoring the v66 release for all platforms -* [2017] Many thanks to [ClearChat Inc.](https://clearchat.com/) for sponsoring +* [2017] Many thanks to [HighSide Inc.](https://highside.io/) for sponsoring the v55/v56 releases for all platforms * [2016-2018] Thanks to JetBrains for providing an Open Source license for [PyCharm](https://www.jetbrains.com/pycharm/) From 99fb58a6badf905afde66903d5c87e39a5f32b38 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Jan 2020 12:57:22 +0100 Subject: [PATCH 145/177] Restructure Sponsors section in README. --- README.md | 120 +++++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index b2c06aad..747381bf 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ Table of contents: * [Introduction](#introduction) -* [Sponsors](#sponsors) +* [Latest releases sponsored by](#latest-releases-sponsored-by) + * [Thanks to all sponsors](#thanks-to-all-sponsors) * [Install](#install) * [Tutorial](#tutorial) * [Examples](#examples) @@ -13,7 +14,6 @@ Table of contents: * [v49 release (WinXP/Vista)](#v49-release-winxpvista) * [v31 release (old systems)](#v31-release-old-systems) * [Support development](#support-development) - * [Thanks to sponsors](#thanks-to-sponsors) * [Thanks to all](#thanks-to-all) * [Seeking new sponsors](#seeking-new-sponsors) * [Other READMEs](#other-readmes) @@ -49,7 +49,7 @@ frameworks such as PyQt, wxPython, PyGTK, PyGObject, Tkinter, Kivy, Panda3D, PyGame, PyOpenGL, PyWin32, PySide and PySDL2. -## Sponsors +## Latest releases sponsored by
@@ -82,6 +82,63 @@ businesses with cutting edge marketing technology. Please visit their website:
+### Thanks to all sponsors + + + + + + + + + + + + +
+ + + + + +
+ + + + +
+ ## Install @@ -214,63 +271,6 @@ priority.

-### Thanks to sponsors - - - - - - - - - - - - -
- - - - - -
- - - - -
- ### Thanks to all From a314559af3270f2a0e48d085e23dc8cc76af7966 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Jan 2020 11:54:44 +0100 Subject: [PATCH 146/177] Fix focus issues in qt.py example (#404). --- examples/qt.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/qt.py b/examples/qt.py index a272b80d..9b3e96b9 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -337,7 +337,11 @@ def __init__(self, cef_widget): self.cef_widget = cef_widget def OnSetFocus(self, **_): - pass + print("[qt.py] FocusHandler.OnSetFocus") + if LINUX: + return False + else: + return True def OnGotFocus(self, browser, **_): # Temporary fix no. 1 for focus issues on Linux (Issue #284) From 181588ce611ef4675b4948b71f9ed933a9519a98 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Jan 2020 12:21:14 +0100 Subject: [PATCH 147/177] Fix QLineEdit focus issues in qt.py example on Windows (#404). Revert previous commit. --- examples/qt.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/examples/qt.py b/examples/qt.py index 9b3e96b9..efb1b381 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -212,6 +212,8 @@ def __init__(self, parent=None): def focusInEvent(self, event): # This event seems to never get called on Linux, as CEF is # stealing all focus due to Issue #284. + if cef.GetAppSetting("debug"): + print("[qt.py] CefWidget.focusInEvent") if self.browser: if WINDOWS: WindowUtils.OnSetFocus(self.getHandle(), 0, 0, 0) @@ -220,6 +222,8 @@ def focusInEvent(self, event): def focusOutEvent(self, event): # This event seems to never get called on Linux, as CEF is # stealing all focus due to Issue #284. + if cef.GetAppSetting("debug"): + print("[qt.py] CefWidget.focusOutEvent") if self.browser: self.browser.SetFocus(False) @@ -336,18 +340,20 @@ class FocusHandler(object): def __init__(self, cef_widget): self.cef_widget = cef_widget + def OnTakeFocus(self, **_): + if cef.GetAppSetting("debug"): + print("[qt.py] FocusHandler.OnTakeFocus") + def OnSetFocus(self, **_): - print("[qt.py] FocusHandler.OnSetFocus") - if LINUX: - return False - else: - return True + if cef.GetAppSetting("debug"): + print("[qt.py] FocusHandler.OnSetFocus") def OnGotFocus(self, browser, **_): + if cef.GetAppSetting("debug"): + print("[qt.py] FocusHandler.OnGotFocus") + self.cef_widget.setFocus() # Temporary fix no. 1 for focus issues on Linux (Issue #284) if LINUX: - print("[qt.py] FocusHandler.OnGotFocus:" - " keyboard focus fix no. 1 (Issue #284)") browser.SetFocus(True) From 30439b659bf1974420c3ec7954f76f2fc93902e2 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Thu, 23 Jan 2020 16:32:52 +0100 Subject: [PATCH 148/177] Update utils.pyx. --- src/utils.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils.pyx b/src/utils.pyx index 3d1acd89..7d855361 100644 --- a/src/utils.pyx +++ b/src/utils.pyx @@ -76,6 +76,7 @@ cpdef py_bool IsFunctionOrMethod(object valueType): if (valueType == types.FunctionType or valueType == types.MethodType or valueType == types.BuiltinFunctionType - or valueType == types.BuiltinMethodType): + or valueType == types.BuiltinMethodType + or valueType.__name__ == "cython_function_or_method"): return True return False From 042579dc0d7e0010dca4d23f0ccbbbaf2e72700e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 25 Jan 2020 14:32:29 +0100 Subject: [PATCH 149/177] Allow for automatic conversion of string like objects to CEF strings. For example a QString passed to LoadUrl() method was causing error and now it's handled fine. --- src/string_utils.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/string_utils.pyx b/src/string_utils.pyx index c5980b94..7b1c9a06 100644 --- a/src/string_utils.pyx +++ b/src/string_utils.pyx @@ -103,11 +103,17 @@ cdef void PyToCefString( CefString& cefString ) except *: if PY_MAJOR_VERSION < 3: + # Handle objects that may be converted to string e.g. QString + if not isinstance(pyString, str) and not isinstance(pyString, unicode): + pyString = str(pyString) if type(pyString) == unicode: pyString = (pyString.encode( g_applicationSettings["string_encoding"], errors=UNICODE_ENCODE_ERRORS)) else: + # Handle objects that may be converted to string e.g. QString + if not isinstance(pyString, str) and not isinstance(pyString, bytes): + pyString = str(pyString) # The unicode type is not defined in Python 3. if type(pyString) == str: pyString = (pyString.encode( From 28afa80a2f23fa2e6160926d0352b861466bf5a8 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 25 Jan 2020 14:33:42 +0100 Subject: [PATCH 150/177] Pass proper parent window handle for DevTools window (#381). This still doesn't fix keyboard issues in wxpython.py example. --- src/browser.pyx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/browser.pyx b/src/browser.pyx index 8dfb7aed..c8cba03a 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -576,15 +576,8 @@ cdef class PyBrowser: cpdef py_void ShowDevTools(self): cdef CefWindowInfo window_info IF UNAME_SYSNAME == "Windows": - # On Windows with empty window_info structure the devtools - # window doesn't appear. - window_info.SetAsPopup( - # TODO: - # According to docs this returns NULL for non-popup - # windows, so looks like we shouldn't use that and - # either pass NULL or GetWindowHandle(). - self.GetOpenerWindowHandle(), - PyToCefStringValue("DevTools")) + window_info.SetAsPopup(self.GetWindowHandle(), + PyToCefStringValue("DevTools")) cdef CefBrowserSettings settings cdef CefPoint inspect_element_at self.GetCefBrowserHost().get().ShowDevTools( From 0da13937e50f389cba25533959c2b760403fb80f Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Mon, 20 Jul 2020 19:19:03 +0200 Subject: [PATCH 151/177] Fix Mac issues in Tkinter example (#309, #441) Still some issues on startup, see: https://github.com/cztomczak/cefpython/issues/583 --- examples/tkinter_.py | 73 +++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/examples/tkinter_.py b/examples/tkinter_.py index c823f3dc..327f171f 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -44,7 +44,7 @@ def main(): - logger.setLevel(_logging.INFO) + logger.setLevel(_logging.DEBUG) stream_handler = _logging.StreamHandler() formatter = _logging.Formatter("[%(filename)s] %(message)s") stream_handler.setFormatter(formatter) @@ -55,19 +55,23 @@ def main(): logger.info("Tk {ver}".format(ver=tk.Tcl().eval('info patchlevel'))) assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error + # Tk must be initialized before CEF otherwise fatal error (Issue #306) root = tk.Tk() app = MainFrame(root) - # Tk must be initialized before CEF otherwise fatal error (Issue #306) - cef.Initialize() + settings = {} + if MAC: + settings["external_message_pump"] = True + cef.Initialize(settings=settings) app.mainloop() + logger.debug("Main loop exited") cef.Shutdown() - class MainFrame(tk.Frame): def __init__(self, root): self.browser_frame = None self.navigation_bar = None + self.root = root # Root root.geometry("900x640") @@ -124,7 +128,9 @@ def on_focus_out(self, _): def on_close(self): if self.browser_frame: self.browser_frame.on_root_close() - self.master.destroy() + self.browser_frame = None + else: + self.master.destroy() def get_browser(self): if self.browser_frame: @@ -147,11 +153,12 @@ def setup_icon(self): class BrowserFrame(tk.Frame): - def __init__(self, master, navigation_bar=None): + def __init__(self, mainframe, navigation_bar=None): self.navigation_bar = navigation_bar self.closing = False self.browser = None - tk.Frame.__init__(self, master) + tk.Frame.__init__(self, mainframe) + self.mainframe = mainframe self.bind("", self.on_focus_in) self.bind("", self.on_focus_out) self.bind("", self.on_configure) @@ -165,27 +172,42 @@ def embed_browser(self): self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") assert self.browser + self.browser.SetClientHandler(LifespanHandler(self)) self.browser.SetClientHandler(LoadHandler(self)) self.browser.SetClientHandler(FocusHandler(self)) self.message_loop_work() def get_window_handle(self): - if self.winfo_id() > 0: - return self.winfo_id() - elif MAC: - # On Mac window id is an invalid negative value (Issue #308). - # This is kind of a dirty hack to get window handle using - # PyObjC package. If you change structure of windows then you + if MAC: + # Do not use self.winfo_id() on Mac, because of these issues: + # 1. Window id sometimes has an invalid negative value (Issue #308). + # 2. Even with valid window id it crashes during the call to NSView.setAutoresizingMask: + # https://github.com/cztomczak/cefpython/issues/309#issuecomment-661094466 + # + # To fix it using PyObjC package to obtain window handle. If you change structure of windows then you # need to do modifications here as well. + # + # There is still one issue with this solution. Sometimes there is more than one window, for example when application + # didn't close cleanly last time Python displays an NSAlert window asking whether to Reopen that window. In such + # case app will crash and you will see in console: + # > Fatal Python error: PyEval_RestoreThread: NULL tstate + # > zsh: abort python tkinter_.py + # Error messages related to this: https://github.com/cztomczak/cefpython/issues/441 + # + # There is yet another issue that might be related as well: + # https://github.com/cztomczak/cefpython/issues/583 + # noinspection PyUnresolvedReferences from AppKit import NSApp # noinspection PyUnresolvedReferences import objc - # Sometimes there is more than one window, when application - # didn't close cleanly last time Python displays an NSAlert - # window asking whether to Reopen that window. + logger.info("winfo_id={}".format(self.winfo_id())) # noinspection PyUnresolvedReferences - return objc.pyobjc_id(NSApp.windows()[-1].contentView()) + content_view = objc.pyobjc_id(NSApp.windows()[-1].contentView()) + logger.info("content_view={}".format(content_view)) + return content_view + elif self.winfo_id() > 0: + return self.winfo_id() else: raise Exception("Couldn't obtain window handle") @@ -224,10 +246,15 @@ def on_focus_out(self, _): self.browser.SetFocus(False) def on_root_close(self): + logger.info("BrowserFrame.on_root_close") if self.browser: + logger.debug("CloseBrowser") self.browser.CloseBrowser(True) self.clear_browser_references() - self.destroy() + else: + logger.debug("tk.Frame.destroy") + self.destroy() + def clear_browser_references(self): # Clear browser references that you keep anywhere in your @@ -235,6 +262,16 @@ def clear_browser_references(self): self.browser = None +class LifespanHandler(object): + + def __init__(self, tkFrame): + self.tkFrame = tkFrame + + def OnBeforeClose(self, browser, **_): + logger.debug("LifespanHandler.OnBeforeClose") + self.tkFrame.quit() + + class LoadHandler(object): def __init__(self, browser_frame): From 3cc7606101f2b1b8191b4db3648c264349ca4628 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 19 Aug 2020 11:14:04 +0200 Subject: [PATCH 152/177] Add keyboard handler snippet. --- examples/snippets/keyboard_handler.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 examples/snippets/keyboard_handler.py diff --git a/examples/snippets/keyboard_handler.py b/examples/snippets/keyboard_handler.py new file mode 100644 index 00000000..9b8e40c8 --- /dev/null +++ b/examples/snippets/keyboard_handler.py @@ -0,0 +1,19 @@ +from cefpython3 import cefpython as cef + + +def main(): + cef.Initialize() + browser = cef.CreateBrowserSync(url="https://www.google.com/", + window_title="Keyboard Handler") + browser.SetClientHandler(KeyboardHandler()) + cef.MessageLoop() + del browser + cef.Shutdown() + + +class KeyboardHandler(object): + def OnKeyEvent(self, browser, event, event_handle, **_): + print("OnKeyEvent: "+str(event)) + +if __name__ == '__main__': + main() From 2fcf395f4e67e83e5c1dc06b3bfd6818d4e42602 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Fri, 9 Oct 2020 13:53:46 +0200 Subject: [PATCH 153/177] Update README.md Fix links. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 747381bf..546cd001 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Table of contents: ## Introduction CEF Python is an open source project founded by -[Czarek Tomczak](https://drive.google.com/file/d/17xmoT5Z_zTHkVclqPzrs2aAV64Uiu7fh/view) +[Czarek Tomczak](https://www.linkedin.com/in/czarektomczak/) in 2012 to provide Python bindings for the [Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef) (CEF). The Chromium project focuses mainly on Google Chrome application @@ -317,7 +317,7 @@ notable are: If your company would like to sponsor CEF Python development efforts then please contact -[Czarek](https://drive.google.com/file/d/17xmoT5Z_zTHkVclqPzrs2aAV64Uiu7fh/view). +[Czarek](https://www.linkedin.com/in/czarektomczak/). Long term sponsorships are welcome and Czarek is open to ideas about the project. He would love to spend more time on developing this project, but he can't afford doing so in his free time. Currently there is no company From 019033d2919a4fab84cb832ec5b76aad12e39c40 Mon Sep 17 00:00:00 2001 From: Xianguang Zhou Date: Sun, 14 Feb 2021 22:44:22 +0800 Subject: [PATCH 154/177] Fix the bug of loading shared libraries. (#561) * Prepend the package directory path to the "LD_LIBRARY_PATH" environment variable. * Fix the bug of getting "LD_LIBRARY_PATH" environment variable. * Fix loading shared libraries on Linux. Co-authored-by: Czarek Tomczak --- tools/installer/cefpython3.__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index fc00000d..ef784f2c 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -26,9 +26,13 @@ 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 +# This loads the libcef.so library for the subprocess executable on Linux. +# TODO: Use -Wl,-rpath=\$$ORIGIN in Makefile. +ld_library_path = os.environ.get("LD_LIBRARY_PATH") +if ld_library_path and ld_library_path.strip(): + os.environ["LD_LIBRARY_PATH"] = package_dir + os.pathsep + ld_library_path +else: + os.environ["LD_LIBRARY_PATH"] = package_dir # This env variable will be returned by cefpython.GetModuleDirectory(). os.environ["CEFPYTHON3_PATH"] = package_dir From 96f3b5ec1ef951a1959d378a5d4d87bf247b2c36 Mon Sep 17 00:00:00 2001 From: Martin Cejp Date: Sun, 14 Feb 2021 16:20:20 +0100 Subject: [PATCH 155/177] Fix pango library wrong include path (hb.h: No such file or directory) (#589) * Fix Fedora 32 build error (hb.h: No such file or directory) * Update client_handler/Makefile * Update client_handler/Makefile * Update cpp_utils/Makefile * Update subprocess/Makefile * Update subprocess/Makefile-libcefpythonapp * Update tools/cython_setup.py Co-authored-by: Czarek Tomczak --- src/client_handler/Makefile | 1 + src/cpp_utils/Makefile | 2 +- src/subprocess/Makefile | 1 + src/subprocess/Makefile-libcefpythonapp | 1 + tools/cython_setup.py | 2 ++ 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile index d217660e..25e645ff 100644 --- a/src/client_handler/Makefile +++ b/src/client_handler/Makefile @@ -37,6 +37,7 @@ INC = -I./../ -I./../common/ -I$(PYTHON_INCLUDE) \ -I/usr/include/glib-2.0 \ -I/usr/include/cairo \ -I/usr/include/pango-1.0 \ + -I/usr/include/harfbuzz \ -I/usr/include/gdk-pixbuf-2.0 \ -I/usr/include/atk-1.0 \ -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \ diff --git a/src/cpp_utils/Makefile b/src/cpp_utils/Makefile index 338fd960..e6a6a2fe 100644 --- a/src/cpp_utils/Makefile +++ b/src/cpp_utils/Makefile @@ -7,7 +7,7 @@ OUT = libcpp_utils.a INC = -I./../ -I/usr/include/gtk-2.0 \ -I/usr/include/glib-2.0 -I/usr/lib/i386-linux-gnu/gtk-2.0/include \ -I/usr/lib/i386-linux-gnu/glib-2.0/include -I/usr/include/cairo \ - -I/usr/include/pango-1.0 -I/usr/include/gdk-pixbuf-2.0 \ + -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/gdk-pixbuf-2.0 \ -I/usr/include/atk-1.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include \ -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \ -I/usr/lib64/glib-2.0/include -I/usr/lib64/gtk-2.0/include \ diff --git a/src/subprocess/Makefile b/src/subprocess/Makefile index f52b72df..adc34fca 100644 --- a/src/subprocess/Makefile +++ b/src/subprocess/Makefile @@ -11,6 +11,7 @@ INC = -I./../ -I./../common/ -I$(PYTHON_INCLUDE) \ -I/usr/include/glib-2.0 \ -I/usr/include/cairo \ -I/usr/include/pango-1.0 \ + -I/usr/include/harfbuzz \ -I/usr/include/gdk-pixbuf-2.0 \ -I/usr/include/atk-1.0 \ -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \ diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index 23fbde39..e8a6b852 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -36,6 +36,7 @@ INC = -I./../ -I./../common/ -I$(PYTHON_INCLUDE) \ -I/usr/include/glib-2.0 \ -I/usr/include/cairo \ -I/usr/include/pango-1.0 \ + -I/usr/include/harfbuzz \ -I/usr/include/gdk-pixbuf-2.0 \ -I/usr/include/atk-1.0 \ -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \ diff --git a/tools/cython_setup.py b/tools/cython_setup.py index 4f0cafc6..52482cba 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -318,6 +318,7 @@ def get_include_dirs(): '/usr/include/gtk-unix-print-2.0', '/usr/include/cairo', '/usr/include/pango-1.0', + '/usr/include/harfbuzz', '/usr/include/gdk-pixbuf-2.0', '/usr/include/atk-1.0', # Fedora @@ -337,6 +338,7 @@ def get_include_dirs(): '/usr/include/gtk-unix-print-2.0', '/usr/include/cairo', '/usr/include/pango-1.0', + '/usr/include/harfbuzz', '/usr/include/gdk-pixbuf-2.0', '/usr/include/atk-1.0', # Ubuntu From 054ff3ca52106099d078f37b3f8986e5252c7733 Mon Sep 17 00:00:00 2001 From: Bryan Koroleski <44987192+bryan-koroleski-fivestars@users.noreply.github.com> Date: Sun, 14 Feb 2021 10:09:52 -0600 Subject: [PATCH 156/177] Update int range detection for Python 3 (#603) * Update int range detection for Python 3 Python 3 unified ints and longs into a single int type. The long type is still available in Cython as an alias to int. * Update javascript_bindings.pyx * Update process_message_utils.pyx Co-authored-by: Czarek Tomczak --- Authors | 1 + src/javascript_bindings.pyx | 2 ++ src/process_message_utils.pyx | 33 +++++++++------------------------ tools/cython_setup.py | 3 +++ 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/Authors b/Authors index b4c55d20..b44b24e6 100644 --- a/Authors +++ b/Authors @@ -19,3 +19,4 @@ Contributors: Dónal McMullan nobodyguy Elliot Woods + Bryan Koroleski diff --git a/src/javascript_bindings.pyx b/src/javascript_bindings.pyx index 9043639e..31620a6d 100644 --- a/src/javascript_bindings.pyx +++ b/src/javascript_bindings.pyx @@ -140,6 +140,8 @@ cdef class JavascriptBindings: return True elif valueType == int: return True + elif valueType == long: + return True elif valueType == type(None): return True elif IsFunctionOrMethod(valueType): diff --git a/src/process_message_utils.pyx b/src/process_message_utils.pyx index 8ced3f6c..e2d55dab 100644 --- a/src/process_message_utils.pyx +++ b/src/process_message_utils.pyx @@ -238,14 +238,9 @@ cdef CefRefPtr[CefListValue] PyListToCefListValue( ret.get().SetNull(index) elif valueType == bool: ret.get().SetBool(index, bool(value)) - elif valueType == int: - ret.get().SetInt(index, int(value)) - elif valueType == long: - # Int32 range is -2147483648..2147483647, we've increased the - # minimum size by one as Cython was throwing a warning: - # "unary minus operator applied to unsigned type, result still - # unsigned". - if -2147483647 <= value <= 2147483647: + elif valueType == int or valueType == long: # In Py3 int and long types are the same type. + # Int32 range is -2147483648..2147483647 + if INT_MIN <= value <= INT_MAX: ret.get().SetInt(index, int(value)) else: # Long values become strings. @@ -297,14 +292,9 @@ cdef void PyListToExistingCefListValue( cefListValue.get().SetNull(index) elif valueType == bool: cefListValue.get().SetBool(index, bool(value)) - elif valueType == int: - cefListValue.get().SetInt(index, int(value)) - elif valueType == long: - # Int32 range is -2147483648..2147483647, we've increased the - # minimum size by one as Cython was throwing a warning: - # "unary minus operator applied to unsigned type, result still - # unsigned". - if -2147483647 <= value <= 2147483647: + elif valueType == int or valueType == long: # In Py3 int and long types are the same type. + # Int32 range is -2147483648..2147483647 + if INT_MIN <= value <= INT_MAX: cefListValue.get().SetInt(index, int(value)) else: # Long values become strings. @@ -357,14 +347,9 @@ cdef CefRefPtr[CefDictionaryValue] PyDictToCefDictionaryValue( ret.get().SetNull(cefKey) elif valueType == bool: ret.get().SetBool(cefKey, bool(value)) - elif valueType == int: - ret.get().SetInt(cefKey, int(value)) - elif valueType == long: - # Int32 range is -2147483648..2147483647, we've increased the - # minimum size by one as Cython was throwing a warning: - # "unary minus operator applied to unsigned type, result still - # unsigned". - if -2147483647 <= value <= 2147483647: + elif valueType == int or valueType == long: # In Py3 int and long types are the same type. + # Int32 range is -2147483648..2147483647 + if INT_MIN <= value <= INT_MAX: ret.get().SetInt(cefKey, int(value)) else: # Long values become strings. diff --git a/tools/cython_setup.py b/tools/cython_setup.py index 52482cba..5b3049db 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -470,6 +470,9 @@ def compile_time_constants(): # A way around Python 3.2 bug: UNAME_SYSNAME is not set contents += 'DEF UNAME_SYSNAME = "%s"\n' % platform.uname()[0] contents += 'DEF PY_MAJOR_VERSION = %s\n' % sys.version_info.major + contents += 'cdef extern from "limits.h":\n' + contents += ' cdef int INT_MIN\n' + contents += ' cdef int INT_MAX\n' fo.write(contents.encode("utf-8")) From 686d528b3eebbe5b84a770f5d266aea8dd185721 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sun, 14 Feb 2021 17:21:13 +0100 Subject: [PATCH 157/177] Fix pyinstaller hook for pyinstaller >= 4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Brénainn Woodsend (https://github.com/bwoodsend) for the fix. See also https://github.com/cztomczak/cefpython/pull/600 . --- examples/pyinstaller/hook-cefpython3.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/pyinstaller/hook-cefpython3.py b/examples/pyinstaller/hook-cefpython3.py index 7522b7be..9160f67f 100644 --- a/examples/pyinstaller/hook-cefpython3.py +++ b/examples/pyinstaller/hook-cefpython3.py @@ -14,8 +14,13 @@ import sys import PyInstaller from PyInstaller.utils.hooks import is_module_satisfies, get_package_paths -from PyInstaller.compat import is_win, is_darwin, is_linux, is_py2 +from PyInstaller.compat import is_win, is_darwin, is_linux from PyInstaller import log as logging +try: + # PyInstaller >= 4.0 doesn't support Python 2.7 + from PyInstaller.compat import is_py2 +except ImportError: + is_py2 = None # Constants CEFPYTHON_MIN_VERSION = "57.0" From 8e36e82a21a95574fbc2cc329be4f2d5e82f6d00 Mon Sep 17 00:00:00 2001 From: Holger Badorreck <6099617+hoba87@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:30:11 +0100 Subject: [PATCH 158/177] update kivy example (#573) * update old print syntax * remove pygtk, gtk dependencies * restore kivy pygtk dependency for linux, add platform dependent conditional statements * kivy example macos keyboard working * external pump message only for macos * Update kivy_.py Co-authored-by: Czarek Tomczak --- src/linux/binaries_64bit/kivy_.py | 46 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/linux/binaries_64bit/kivy_.py b/src/linux/binaries_64bit/kivy_.py index bdfb6d2c..49543bf9 100644 --- a/src/linux/binaries_64bit/kivy_.py +++ b/src/linux/binaries_64bit/kivy_.py @@ -8,11 +8,20 @@ from cefpython3 import cefpython as cef -import pygtk -import gtk import sys import os import time +if sys.platform == 'linux': + import pygtk + import gtk + pygtk.require('2.0') +elif sys.platform == 'darwin': + import gi + gi.require_version("Gtk", "3.0") + from gi.repository import Gtk +elif sys.platform == 'win32': + # no gtk needed on Windows + pass from kivy.app import App from kivy.uix.button import Button @@ -28,9 +37,6 @@ # Global variables g_switches = None -# PyGTK required -pygtk.require('2.0') - class BrowserLayout(BoxLayout): @@ -150,9 +156,6 @@ def start_cef(self): # Configure CEF settings = { - # This directories must be set on Linux - "locales_dir_path": cef.GetModuleDirectory()+"/locales", - "resources_dir_path": cef.GetModuleDirectory(), "browser_subprocess_path": "%s/%s" % ( cef.GetModuleDirectory(), "subprocess"), "windowless_rendering_enabled": True, @@ -161,7 +164,15 @@ def start_cef(self): "enabled": False, }, "external_message_pump": False, # See Issue #246 + "multi_threaded_message_loop": False, } + if sys.platform == 'linux': + # This directories must be set on Linux + settings["locales_dir_path"] = cef.GetModuleDirectory() + "/locales" + settings["resources_dir_path"] = cef.GetModuleDirectory() + if sys.platform == 'darwin': + settings["external_message_pump"] = True # Temporary fix for Issue #246 + switches = { # Tweaking OSR performance by setting the same Chromium flags # as in upstream cefclient (# Issue #240). @@ -190,18 +201,21 @@ def start_cef(self): # Start idle - CEF message loop work. Clock.schedule_once(self._message_loop_work, 0) + windowInfo = cef.WindowInfo() + # TODO: For printing to work in off-screen-rendering mode # it is enough to call gtk_init(). It is not required # to provide window handle when calling SetAsOffscreen(). # However it still needs to be tested whether providing # window handle is required for mouse context menu and # popup widgets to work. - gtkwin = gtk.Window() - gtkwin.realize() - # WindowInfo offscreen flag - windowInfo = cef.WindowInfo() - windowInfo.SetAsOffscreen(gtkwin.window.xid) + if sys.platform == 'linux': + gtkwin = gtk.Window() + gtkwin.realize() + windowInfo.SetAsOffscreen(gtkwin.window.xid) + elif sys.platform == 'darwin' or sys.platform == 'win32': + windowInfo.SetAsOffscreen(0) # Create Broswer and naviagte to empty page <= OnPaint won't get # called yet @@ -519,12 +533,12 @@ def get_windows_key_code(self, kivycode): def go_forward(self, *_): """Going to forward in browser history.""" - print "go forward" + print("go forward") self.browser.GoForward() def go_back(self, *_): """Going back in browser history.""" - print "go back" + print("go back") self.browser.GoBack() def reload(self, *_): @@ -864,7 +878,7 @@ def OnLoadingStateChange(self, is_loading, **_): def OnPaint(self, element_type, paint_buffer, **_): # print "OnPaint()" if element_type != cef.PET_VIEW: - print "Popups aren't implemented yet" + print("Popups aren't implemented yet") return # FPS meter ("fps" arg) From 55e63607afdce5d122f3d78884200a5e3f84938d Mon Sep 17 00:00:00 2001 From: gary Date: Sun, 14 Feb 2021 12:03:24 -0800 Subject: [PATCH 159/177] Add support for Python 3.8 and Upgrade Cython (#594) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/cztomczak/cefpython/issues/546 Had to include harfbuzz manually as newer Pango change this. See: https://github.com/eiskaltdcpp/eiskaltdcpp/issues/413 https://gitlab.gnome.org/GNOME/pango/-/issues/387 Also had to add `-Wno-deprecated-declarations` to get this to compile because of the following errors that didn't seem to be coming from this code directly: warning: ‘GTimeVal’ is deprecated: Use 'GDateTime' instead warning: ‘GTypeDebugFlags’ is deprecated --- README.md | 6 +++--- src/common/cefpython_public_api.h | 2 ++ tools/build.py | 2 +- tools/build_distrib.py | 2 +- tools/common.py | 2 ++ tools/installer/cefpython3.__init__.py | 3 +++ tools/installer/cefpython3.setup.py | 1 + tools/requirements.txt | 2 +- 8 files changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 546cd001..d4f4c493 100644 --- a/README.md +++ b/README.md @@ -210,9 +210,9 @@ support old operating systems then choose the v31 release. OS | Py2 | Py3 | 32bit | 64bit | Requirements --- | --- | --- | --- | --- | --- -Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Windows 7+ -Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 8+, Ubuntu 14.04+,
Fedora 24+, openSUSE 13.3+ -Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ +Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 | Yes | Yes | Windows 7+ +Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 3.8 | Yes | Yes | Debian 8+, Ubuntu 14.04+,
Fedora 24+, openSUSE 13.3+ +Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 | No | Yes | MacOS 10.9+ These platforms are not supported yet: - ARM - see [Issue #267](../../issues/267) diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h index 53ad62c6..8e4ff60b 100644 --- a/src/common/cefpython_public_api.h +++ b/src/common/cefpython_public_api.h @@ -46,6 +46,8 @@ #include "../../build/build_cefpython/cefpython_py36_fixed.h" #elif PY_MINOR_VERSION == 7 #include "../../build/build_cefpython/cefpython_py37_fixed.h" +#elif PY_MINOR_VERSION == 8 +#include "../../build/build_cefpython/cefpython_py38_fixed.h" #endif // PY_MINOR_VERSION #endif // PY_MAJOR_VERSION diff --git a/tools/build.py b/tools/build.py index 522a9b08..b3bc9920 100644 --- a/tools/build.py +++ b/tools/build.py @@ -293,7 +293,7 @@ def setup_environ(): print("[build.py] PYTHON_INCLUDE: {python_include}" .format(python_include=os.environ["PYTHON_INCLUDE"])) - os.environ["CEF_CCFLAGS"] = "-std=gnu++11 -DNDEBUG -Wall -Werror" + os.environ["CEF_CCFLAGS"] = "-std=gnu++11 -DNDEBUG -Wall -Werror -Wno-deprecated-declarations" if FAST_FLAG: os.environ["CEF_CCFLAGS"] += " -O0" else: diff --git a/tools/build_distrib.py b/tools/build_distrib.py index e0a5bde1..f452bc0d 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -80,7 +80,7 @@ ALLOW_PARTIAL = False # Python versions -SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6), (3, 7)] +SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8)] # Python search paths. It will use first Python found for specific version. # Supports replacement of one environment variable in path eg.: %ENV_KEY%. diff --git a/tools/common.py b/tools/common.py index 113bcd9e..226fd391 100644 --- a/tools/common.py +++ b/tools/common.py @@ -473,6 +473,8 @@ def get_msvs_for_python(vs_prefix=False): return "VS2015" if vs_prefix else "2015" elif sys.version_info[:2] == (3, 7): return "VS2015" if vs_prefix else "2015" + elif sys.version_info[:2] == (3, 8): + return "VS2015" if vs_prefix else "2015" else: print("ERROR: This version of Python is not supported") sys.exit(1) diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index ef784f2c..5f779a54 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -64,5 +64,8 @@ elif sys.version_info[:2] == (3, 7): # noinspection PyUnresolvedReferences from . import cefpython_py37 as cefpython +elif sys.version_info[:2] == (3, 8): + # noinspection PyUnresolvedReferences + from . import cefpython_py38 as cefpython else: raise Exception("Python version not supported: " + sys.version) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 6814cbb7..2a926913 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -148,6 +148,7 @@ def main(): "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Topic :: Desktop Environment", "Topic :: Internet", "Topic :: Internet :: WWW/HTTP", diff --git a/tools/requirements.txt b/tools/requirements.txt index 81ebc0c5..4b083579 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,4 +1,4 @@ -Cython == 0.28.4 +Cython == 0.29.21 docopt >= 0.6.2 setuptools wheel From d6deaf8e2b87266d7b5fc4052fc05b5622056b17 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sun, 14 Feb 2021 23:44:38 +0100 Subject: [PATCH 160/177] Support Python 3.8 and Python 3.9 (Issues #546 and #593). --- README.md | 6 +++--- docs/Build-instructions.md | 8 +++++--- examples/screenshot.py | 2 +- src/common/cefpython_public_api.h | 2 ++ src/compile_time_constants.pxi | 3 +++ src/subprocess/main_message_loop/util_win.cpp | 4 ++-- src/subprocess/main_message_loop/util_win.h | 2 +- tools/build.py | 2 +- tools/build_distrib.py | 5 +++-- tools/common.py | 6 ++++++ tools/cython_setup.py | 7 +++---- tools/installer/cefpython3.__init__.py | 3 +++ tools/installer/cefpython3.setup.py | 1 + tools/make_installer.py | 8 +++++--- 14 files changed, 39 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d4f4c493..48a9af4a 100644 --- a/README.md +++ b/README.md @@ -210,9 +210,9 @@ support old operating systems then choose the v31 release. OS | Py2 | Py3 | 32bit | 64bit | Requirements --- | --- | --- | --- | --- | --- -Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 | Yes | Yes | Windows 7+ -Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 3.8 | Yes | Yes | Debian 8+, Ubuntu 14.04+,
Fedora 24+, openSUSE 13.3+ -Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 | No | Yes | MacOS 10.9+ +Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 / 3.9 | Yes | Yes | Windows 7+ (Note that Python 3.9 supports Windows 8.1+) +Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 8+, Ubuntu 14.04+,
Fedora 24+, openSUSE 13.3+ +Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ These platforms are not supported yet: - ARM - see [Issue #267](../../issues/267) diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 5df63bec..9ca4d6e9 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -66,8 +66,9 @@ are named "cefpythonXX" where XX is Chromium version number. 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) + you have to install "Visual C++ 2008 Redistributable Package" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=29) + and [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) 6) Clone cefpython, checkout for example "cefpython57" branch that includes Chromium v57, then create a build/ directory and enter it: @@ -164,7 +165,8 @@ requirements common for all platforms. * 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 + "Microsoft Visual C++ 2008 Redistributable Package" from + [here](https://www.microsoft.com/en-us/download/details.aspx?id=29) and [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) * For Python 2.7 copy "cefpython/src/windows/py27/stdint.h" to "%LocalAppData%\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\include\" diff --git a/examples/screenshot.py b/examples/screenshot.py index d6dc661c..976ba538 100644 --- a/examples/screenshot.py +++ b/examples/screenshot.py @@ -42,7 +42,7 @@ import sys try: - from PIL import Image, PILLOW_VERSION + from PIL import Image, __version__ as PILLOW_VERSION except ImportError: print("[screenshot.py] Error: PIL module not available. To install" " type: pip install Pillow") diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h index 8e4ff60b..c796388e 100644 --- a/src/common/cefpython_public_api.h +++ b/src/common/cefpython_public_api.h @@ -48,6 +48,8 @@ #include "../../build/build_cefpython/cefpython_py37_fixed.h" #elif PY_MINOR_VERSION == 8 #include "../../build/build_cefpython/cefpython_py38_fixed.h" +#elif PY_MINOR_VERSION == 9 +#include "../../build/build_cefpython/cefpython_py39_fixed.h" #endif // PY_MINOR_VERSION #endif // PY_MAJOR_VERSION diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 609470c1..bf130d6e 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -5,3 +5,6 @@ DEF UNAME_SYSNAME = "Windows" DEF PY_MAJOR_VERSION = 3 +cdef extern from "limits.h": + cdef int INT_MIN + cdef int INT_MAX diff --git a/src/subprocess/main_message_loop/util_win.cpp b/src/subprocess/main_message_loop/util_win.cpp index bc1f0965..834850e3 100644 --- a/src/subprocess/main_message_loop/util_win.cpp +++ b/src/subprocess/main_message_loop/util_win.cpp @@ -136,8 +136,8 @@ int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam) { return modifiers; } -bool IsKeyDown(WPARAM wparam) { - return (GetKeyState(wparam) & 0x8000) != 0; +bool IsKeyDown(int keycode) { + return (GetKeyState(keycode) & 0x8000) != 0; } float GetDeviceScaleFactor() { diff --git a/src/subprocess/main_message_loop/util_win.h b/src/subprocess/main_message_loop/util_win.h index 39870204..a716d938 100644 --- a/src/subprocess/main_message_loop/util_win.h +++ b/src/subprocess/main_message_loop/util_win.h @@ -31,7 +31,7 @@ WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc); int GetCefMouseModifiers(WPARAM wparam); int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam); -bool IsKeyDown(WPARAM wparam); +bool IsKeyDown(int keycode); // Returns the device scale factor. For example, 200% display scaling will // return 2.0. diff --git a/tools/build.py b/tools/build.py index b3bc9920..f3960283 100644 --- a/tools/build.py +++ b/tools/build.py @@ -809,7 +809,7 @@ def build_cefpython_module(): args = list() args.append("\"{python}\"".format(python=sys.executable)) args.append(os.path.join(TOOLS_DIR, os.path.basename(__file__))) - assert __file__ in sys.argv[0] + assert os.path.basename(__file__) in sys.argv[0] args.extend(SYS_ARGV_ORIGINAL[1:]) command = " ".join(args) print("[build.py] Running command: %s" % command) diff --git a/tools/build_distrib.py b/tools/build_distrib.py index f452bc0d..01ae66da 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -80,13 +80,14 @@ ALLOW_PARTIAL = False # Python versions -SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8)] +SUPPORTED_PYTHON_VERSIONS = [(2, 7), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9)] # Python search paths. It will use first Python found for specific version. # Supports replacement of one environment variable in path eg.: %ENV_KEY%. PYTHON_SEARCH_PATHS = dict( WINDOWS=[ "C:\\Python*\\", + "C:\\Pythons\\Python*\\", "%LOCALAPPDATA%\\Programs\\Python\\Python*\\", "C:\\Program Files\\Python*\\", "C:\\Program Files (x86)\\Python*\\", @@ -614,7 +615,7 @@ def check_cpp_extension_dependencies_issue359(setup_dir, all_pythons): return checked_any = False for python in all_pythons: - if python["version2"] in ((3, 5), (3, 6), (3, 7)): + if python["version2"] in ((3, 5), (3, 6), (3, 7), (3, 8), (3, 9)): checked_any = True if not os.path.exists(os.path.join(setup_dir, "cefpython3", "msvcp140.dll")): diff --git a/tools/common.py b/tools/common.py index 226fd391..303ccb85 100644 --- a/tools/common.py +++ b/tools/common.py @@ -219,15 +219,19 @@ VS_PLATFORM_ARG = "x86" if ARCH32 else "amd64" +# Python 3.5 / 3.6 / 3.7 / 3.8 / 3.9 VS2015_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0" "\\VC\\vcvarsall.bat") +# Required for building old CEF branches < 2704 VS2013_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 12.0" "\\VC\\vcvarsall.bat") +# Python 3.4 VS2010_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0" "\\VC\\vcvarsall.bat") +# Python 2.7 VS2008_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 9.0" "\\VC\\vcvarsall.bat") @@ -475,6 +479,8 @@ def get_msvs_for_python(vs_prefix=False): return "VS2015" if vs_prefix else "2015" elif sys.version_info[:2] == (3, 8): return "VS2015" if vs_prefix else "2015" + elif sys.version_info[:2] == (3, 9): + return "VS2015" if vs_prefix else "2015" else: print("ERROR: This version of Python is not supported") sys.exit(1) diff --git a/tools/cython_setup.py b/tools/cython_setup.py index 5b3049db..436bcb2c 100644 --- a/tools/cython_setup.py +++ b/tools/cython_setup.py @@ -147,17 +147,15 @@ def get_winsdk_lib(): if WINDOWS: if ARCH32: winsdk_libs = [ + # Windows 7 SDKs. r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Lib", r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0\\Lib", - # Visual Studio 2008 installation - r"C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib", ] elif ARCH64: winsdk_libs = [ + # Windows 7 SDKs. r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Lib\\x64", r"C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0\\Lib\\x64", - # Visual Studio 2008 installation - r"C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib\\x64", ] else: raise Exception("Unknown architecture") @@ -433,6 +431,7 @@ def get_ext_modules(options): # > Unknown Extension options: 'cython_directives' warnings.warn(msg) cython_directives={ # Any conversion to unicode must be explicit using .decode(). + "language_level": 2, # Yes, Py2 for all python versions. "c_string_type": "bytes", "c_string_encoding": "utf-8", "profile": ENABLE_PROFILING, diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index 5f779a54..624a26e7 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -67,5 +67,8 @@ elif sys.version_info[:2] == (3, 8): # noinspection PyUnresolvedReferences from . import cefpython_py38 as cefpython +elif sys.version_info[:2] == (3, 9): + # noinspection PyUnresolvedReferences + from . import cefpython_py39 as cefpython else: raise Exception("Python version not supported: " + sys.version) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 2a926913..d14ee2ce 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -149,6 +149,7 @@ def main(): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Desktop Environment", "Topic :: Internet", "Topic :: Internet :: WWW/HTTP", diff --git a/tools/make_installer.py b/tools/make_installer.py index bc3b5cef..012e6657 100644 --- a/tools/make_installer.py +++ b/tools/make_installer.py @@ -338,7 +338,7 @@ def create_empty_log_file(log_file): def copy_cpp_extension_dependencies_issue359(pkg_dir): """CEF Python module is written in Cython and is a Python C++ - extension and depends on msvcpXX.dll. For Python 3.5 / 3.6 / 3.7 + extension and depends on msvcpXX.dll. For Python 3.5 / 3.6 / 3.7 / 3.8 / 3.9 msvcp140.dll is required. See Issue #359. For Python 2.7 msvcp90.dll is required. Etc. These dependencies are not included with Python binaries from Python.org.""" @@ -365,10 +365,12 @@ def copy_cpp_extension_dependencies_issue359(pkg_dir): # in the package. Thus if included, msvcpxx.dll dependency is # required as well. - # Python 3.5 / 3.6 / 3.7 + # Python 3.5 / 3.6 / 3.7 / 3.8 / 3.9 if os.path.exists(os.path.join(pkg_dir, "cefpython_py35.pyd")) \ or os.path.exists(os.path.join(pkg_dir, "cefpython_py36.pyd")) \ - or os.path.exists(os.path.join(pkg_dir, "cefpython_py37.pyd")): + or os.path.exists(os.path.join(pkg_dir, "cefpython_py37.pyd")) \ + or os.path.exists(os.path.join(pkg_dir, "cefpython_py38.pyd")) \ + or os.path.exists(os.path.join(pkg_dir, "cefpython_py39.pyd")): search_paths = [ # This is where Microsoft Visual C++ 2015 Update 3 installs # (14.00.24212). From 92aa6236268620105be0b3d5ce60412a8b5dbf5b Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 15 Feb 2021 19:01:48 +0100 Subject: [PATCH 161/177] Fix Python 3 error in build_distrib.py script (#546). --- tools/build_distrib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 01ae66da..61560743 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -379,7 +379,7 @@ def uninstall_cefpython3_packages(pythons): .format(python=python["executable"])) try: output = subprocess.check_output(command, shell=True) - except subprocess.CalledProcessError, exc: + except subprocess.CalledProcessError as exc: # pip show returns error code when package is not installed output = exc.output if not len(output.strip()): From 3beaaf5dc39c80eea54a9bba6c4c35965764d628 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 15 Feb 2021 19:31:02 +0100 Subject: [PATCH 162/177] Fix build_distrib.py on Python 3 (Issue #546). --- tools/build_distrib.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 61560743..7adb535b 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -86,7 +86,7 @@ # Supports replacement of one environment variable in path eg.: %ENV_KEY%. PYTHON_SEARCH_PATHS = dict( WINDOWS=[ - "C:\\Python*\\", + "C:\\Python??*\\", "C:\\Pythons\\Python*\\", "%LOCALAPPDATA%\\Programs\\Python\\Python*\\", "C:\\Program Files\\Python*\\", @@ -277,6 +277,8 @@ def search_for_pythons(search_arch): version_str = subprocess.check_output([python, "-c", version_code]) version_str = version_str.strip() + if sys.version_info >= (3, 0): + version_str = version_str.decode("utf-8") match = re.search("^\((\d+), (\d+), (\d+)\)$", version_str) assert match, version_str major = match.group(1) @@ -288,6 +290,8 @@ def search_for_pythons(search_arch): "print(str(platform.architecture()[0]));") arch = subprocess.check_output([python, "-c", arch_code]) arch = arch.strip() + if sys.version_info >= (3, 0): + arch = arch.decode("utf-8") if version_tuple2 in SUPPORTED_PYTHON_VERSIONS \ and arch == search_arch: name = ("Python {major}.{minor}.{micro} {arch}" From 06d3b8c8e8164ea1a51caf00591b7eaedcbfc5ac Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 15 Feb 2021 23:26:45 +0100 Subject: [PATCH 163/177] Fix build_distrib.py script for Python 2.7 and Python 3.4 (latest pip versions are broken with these old Pythons). Fix screenshot.py example sometimes failing. When compiling for Python 3.4 you can encounter the the missing "ammintrin.h" header file error. Just copy it from the VS 2019 include/ directory. For Pythons 3.5 / 3.6 / 3.7 / 3.8 / 3.9 you can use the same VC++ 14.2 (VS 2019) compiler which is binary compatible with VC++ 14.1 (VS 2017) and VC++ 14.0 (VS 2015). Currently the subprocess.exe executable built by Python 3.4 (VC++ 10.0 compiler) gives the least amount of false-positives by AVs on virustotal.com . For comparison: Py34 (1/69), Py27 (2/69), Py39 (5/69). Results differ for 32bit and 64bit binaries. --- examples/screenshot.py | 9 ++++++--- tools/build_distrib.py | 28 ++++++++++++++++------------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/examples/screenshot.py b/examples/screenshot.py index 976ba538..5ca8d491 100644 --- a/examples/screenshot.py +++ b/examples/screenshot.py @@ -158,6 +158,8 @@ def save_screenshot(browser): "raw", "RGBA", 0, 1) image.save(SCREENSHOT_PATH, "PNG") print("[screenshot.py] Saved image: {path}".format(path=SCREENSHOT_PATH)) + # See comments in exit_app() why PostTask must be used + cef.PostTask(cef.TID_UI, exit_app, browser) def open_with_default_application(path): @@ -188,9 +190,10 @@ def OnLoadingStateChange(self, browser, is_loading, **_): # Loading is complete sys.stdout.write(os.linesep) print("[screenshot.py] Web page loading is complete") - save_screenshot(browser) - # See comments in exit_app() why PostTask must be used - cef.PostTask(cef.TID_UI, exit_app, browser) + print("[screenshot.py] Will save screenshot in 2 seconds") + # Give up to 2 seconds for the OnPaint call. Most of the time + # it is already called, but sometimes it may be called later. + cef.PostDelayedTask(cef.TID_UI, 2000, save_screenshot, browser) def OnLoadError(self, browser, frame, error_code, failed_url, **_): """Called when the resource load for a navigation fails diff --git a/tools/build_distrib.py b/tools/build_distrib.py index 7adb535b..051914f5 100644 --- a/tools/build_distrib.py +++ b/tools/build_distrib.py @@ -353,8 +353,14 @@ def install_upgrade_requirements(pythons): " for: {name}".format(name=python["name"])) # Upgrade pip - command = ("\"{python}\" -m pip install --upgrade pip" - .format(python=python["executable"])) + pip_version = "pip" + # Old Python versions require specific versions of pip, latest versions are broken with these. + if python["version2"] == (2, 7): + pip_version = "pip==20.3.4" + elif python["version2"] == (3, 4): + pip_version = "pip==19.1.1" + command = ("\"{python}\" -m pip install --upgrade {pip_version}" + .format(python=python["executable"], pip_version=pip_version)) command = sudo_command(command, python=python["executable"]) pcode = subprocess.call(command, shell=True) if pcode != 0: @@ -522,39 +528,37 @@ def build_cefpython_modules(pythons, arch): def backup_subprocess_executable_issue342(python): - """Use subprocess executable build by Python 2.7 to avoid - false-positives by AVs when building subprocess with Python 3. - Windows-only issue.""" + """Use subprocess executable built by Python 3.4 to have the least amount of + false-positives by AVs. Windows-only issue.""" if not WINDOWS: return if python["version2"] == (2, 7): print("[build_distrib.py] Backup subprocess executable built" - " with Python 2.7 (Issue #342)") + " with Python 3.4 (Issue #342)") cefpython_binary_basename = get_cefpython_binary_basename( get_os_postfix2_for_arch(python["arch"])) cefpython_binary = os.path.join(BUILD_DIR, cefpython_binary_basename) assert os.path.isdir(cefpython_binary) src = os.path.join(cefpython_binary, "subprocess.exe") dst = os.path.join(BUILD_CEFPYTHON, - "subprocess_py27_{arch}_issue342.exe" + "subprocess_py34_{arch}_issue342.exe" .format(arch=python["arch"])) shutil.copy(src, dst) def restore_subprocess_executable_issue342(arch): - """Use subprocess executable build by Python 2.7 to avoid - false-positives by AVs when building subprocess with Python 3. - Windows-only issue.""" + """Use subprocess executable built by Python 3.4 to have the least amount of + false-positives by AVs. Windows-only issue.""" if not WINDOWS: return print("[build_distrib.py] Restore subprocess executable built" - " with Python 2.7 (Issue #342)") + " with Python 3.4 (Issue #342)") cefpython_binary_basename = get_cefpython_binary_basename( get_os_postfix2_for_arch(arch)) cefpython_binary = os.path.join(BUILD_DIR, cefpython_binary_basename) assert os.path.isdir(cefpython_binary) src = os.path.join(BUILD_CEFPYTHON, - "subprocess_py27_{arch}_issue342.exe" + "subprocess_py34_{arch}_issue342.exe" .format(arch=arch)) assert os.path.isfile(src) dst = os.path.join(cefpython_binary, "subprocess.exe") From 0e2503c4675d683d2b853267ee0183e4e7fb5726 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 16 Feb 2021 19:18:02 +0100 Subject: [PATCH 164/177] Add JS bindings long integer test. Add comments on DPI awareness. --- examples/hello_world.py | 7 +++++++ unittests/main_test.py | 26 ++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/examples/hello_world.py b/examples/hello_world.py index 478c7060..789f4666 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -1,5 +1,12 @@ # Hello world example. Doesn't depend on any third party GUI framework. # Tested with CEF Python v57.0+. +# +# ==== High DPI support on Windows ==== +# To enable DPI awareness on Windows you have to either embed DPI aware manifest +# in your executable created with pyinstaller or change python.exe properties manually: +# Compatibility > High DPI scaling override > Application. +# Setting DPI awareness programmatically via a call to cef.DpiAware.EnableHighDpiSupport +# is problematic in Python, may not work and can cause display glitches. from cefpython3 import cefpython as cef import platform diff --git a/unittests/main_test.py b/unittests/main_test.py index 99397da8..ce71af20 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -58,17 +58,18 @@ print("test_function() ok"); // Test binding property: test_property1 - if (test_property1 == "Test binding property to the 'window' object") { + if (test_property1 === "Test binding property to the 'window' object") { print("test_property_1 ok"); } else { throw new Error("test_property1 contains invalid string"); } // Test binding property: test_property2 - if (JSON.stringify(test_property2) == '{"key1":"Test binding property'+ - ' to the \\'window\\' object","key2":["Inside list",1,2]}') { + if (JSON.stringify(test_property2) === '{"key1":"Test binding property'+ + ' to the \\'window\\' object","key2":["Inside list",2147483647,"2147483648"]}') { print("test_property2 ok"); } else { + print("test_property2 invalid value: " + JSON.stringify(test_property2)); throw new Error("test_property2 contains invalid value"); } @@ -81,7 +82,7 @@ print("[TIMER] Call Python function and then js callback that was"+ " passed (Issue #277 test)"); external.test_callbacks(function(msg_from_python, py_callback){ - if (msg_from_python == "String sent from Python") { + if (msg_from_python === "String sent from Python") { print("test_callbacks() ok"); var execution_time = new Date().getTime() - start_time; print("[TIMER]: Elapsed = "+String(execution_time)+" ms"); @@ -163,15 +164,23 @@ def test_main(self): cef.LoadCrlSetsFile(crlset) subtest_message("cef.LoadCrlSetsFile ok") - # High DPI on Windows + # High DPI on Windows. + # Setting DPI awareness from Python is usually too late and should be done + # via manifest file. Alternatively change python.exe properties > Compatibility + # > High DPI scaling override > Application. + # Using cef.DpiAware.EnableHighDpiSupport is problematic, it can cause + # display glitches. if WINDOWS: self.assertIsInstance(cef.DpiAware.GetSystemDpi(), tuple) window_size = cef.DpiAware.CalculateWindowSize(800, 600) self.assertIsInstance(window_size, tuple) self.assertGreater(window_size[0], 0) self.assertGreater(cef.DpiAware.Scale((800, 600))[0], 0) - cef.DpiAware.EnableHighDpiSupport() - self.assertTrue(cef.DpiAware.IsProcessDpiAware()) + + # OFF - see comments above. + # cef.DpiAware.EnableHighDpiSupport() + # self.assertTrue(cef.DpiAware.IsProcessDpiAware()) + # Make some calls again after DPI Aware was set self.assertIsInstance(cef.DpiAware.GetSystemDpi(), tuple) self.assertGreater(cef.DpiAware.Scale([800, 600])[0], 0) @@ -374,9 +383,10 @@ def __init__(self, test_case): self.test_case = test_case # Test binding properties to the 'window' object. + # 2147483648 is out of INT_MAX limit and will be sent to JS as string value. self.test_property1 = "Test binding property to the 'window' object" self.test_property2 = {"key1": self.test_property1, - "key2": ["Inside list", 1, 2]} + "key2": ["Inside list", 2147483647, 2147483648]} # Asserts for True/False will be checked just before shutdown self.test_for_True = True # Test whether asserts are working correctly From d2c72528addad8ef41a2a460946f32fe425ed183 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 16 Feb 2021 19:31:32 +0100 Subject: [PATCH 165/177] Update the sponsors sections in README. --- README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 48a9af4a..e9175971 100644 --- a/README.md +++ b/README.md @@ -55,30 +55,29 @@ PyGame, PyOpenGL, PyWin32, PySide and PySDL2.

- - + +

-Many Thanks to Lampix for sponsoring the -[v66 release](../../releases/tag/v66.0). Lampix is the first hardware -and software solution that turns any surface into a smart, augmented reality -or interactive surface. Please visit their website: -Lampix.com +Thank you to Fivestars for sponsoring the [v66.1 release](../../releases/tag/v66.1) +with Python 3.8 / 3.9 support. Fivestars helps local communities thrive by empowering +small businesses with cutting edge marketing technology. Please visit their website: +Fivestars.com

- - + +

-Many thanks to Fivestars for sponsoring the -[v49 legacy release](../../releases/tag/v49.0). Fivestars helps local -communities thrive by empowering small -businesses with cutting edge marketing technology. Please visit their website: -Fivestars.com +Thank you to Lampix for sponsoring the +[v66 release](../../releases/tag/v66.0). Lampix is the first hardware +and software solution that turns any surface into a smart, augmented reality +or interactive surface. Please visit their website: +Lampix.com
@@ -274,6 +273,8 @@ priority. ### Thanks to all +* [2021] Thank you to [Fivestars](https://www.fivestars.com/) for sponsoring + the v66.1 release with Python 3.8 / 3.9 support * [2018] Thanks to [Fivestars](https://www.fivestars.com/) for sponsoring the v49 release for legacy systems (WinXP/Vista) * [2018] Many thanks to [Lampix](https://lampix.com/) for sponsoring the v66 From 431b9956db660d5552c31fb4a3a891f619ed84a8 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Tue, 13 Apr 2021 10:42:46 +0200 Subject: [PATCH 166/177] Fix Kivy example on Windows (#613). --- src/linux/binaries_64bit/kivy_.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/linux/binaries_64bit/kivy_.py b/src/linux/binaries_64bit/kivy_.py index 49543bf9..a9a1590b 100644 --- a/src/linux/binaries_64bit/kivy_.py +++ b/src/linux/binaries_64bit/kivy_.py @@ -1,7 +1,5 @@ -# 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, only on Linux. +# An example of embedding CEF browser with the Kivy framework +# by using off-screen rendering mode. # In this example kivy-lang is used to declare the layout which # contains two buttons (back, forward) and the browser view. @@ -44,7 +42,7 @@ def __init__(self, **kwargs): super(BrowserLayout, self).__init__(**kwargs) self.orientation = "vertical" - self.browser_widget = CefBrowser(id="browser") + self.browser_widget = CefBrowser() layout = BoxLayout() layout.size_hint_y = None From 80e6f3c9828fce40b103a4f51b8bc29d64c54b9c Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Wed, 21 Apr 2021 13:50:04 +0200 Subject: [PATCH 167/177] Fix link --- examples/README-examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README-examples.md b/examples/README-examples.md index ea5871c8..ec2f9bcd 100644 --- a/examples/README-examples.md +++ b/examples/README-examples.md @@ -107,7 +107,7 @@ yet ported to latest CEF. Some of them are externally maintained. for reading/modifying web requests: see the [wxpython-response.py](https://github.com/cztomczak/cefpython/blob/cefpython31/cefpython/cef3/linux/binaries_64bit/wxpython-response.py) example in the cefpython31 branch. - Example of using Python network library (urllib3/openssl) instead of Chromium's - network library - see [gist by Massimiliano Dal Cero](https://gist.github.com/yattamax/0252a3c5dc54a2f81650d5c0eafabf99) + network library - see [gist by Massimiliano Dal Cero](https://gist.github.com/cztomczak/83b77cbdda03ccef81e22e8bd36a51f6) - Example of passing exceptions from Python to Javascript and using await syntax to receive values from python return values - see [Managed python calls example by Elliot Woods](https://github.com/elliotwoods/cefpython-tests/tree/0180b22eac10a1bde08820ca192fdc30eb93f00d/6.%20Managed%20python%20calls) ## More examples to come From 5679f28cec18a57a56e298da2927aac8d8f83ad6 Mon Sep 17 00:00:00 2001 From: relyx <12107287+Reskayoi@users.noreply.github.com> Date: Fri, 14 May 2021 17:18:03 +0800 Subject: [PATCH 168/177] fix OnDownloadData decoding bug (#621) --- api/WebRequestClient.md | 2 +- src/web_request.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/WebRequestClient.md b/api/WebRequestClient.md index d8431cec..0c75a668 100644 --- a/api/WebRequestClient.md +++ b/api/WebRequestClient.md @@ -65,7 +65,7 @@ response (or -1 if not determined). | Parameter | Type | | --- | --- | | web_request | [WebRequest](WebRequest.md) | -| data | string | +| data | bytes | | __Return__ | void | Called when some part of the response is read. |data| contains the current diff --git a/src/web_request.pyx b/src/web_request.pyx index 9891dade..8e51fca1 100644 --- a/src/web_request.pyx +++ b/src/web_request.pyx @@ -176,7 +176,7 @@ cdef public void WebRequestClient_OnDownloadData( if userCallback: userCallback( web_request=webRequest, - data=VoidPtrToString(data, dataLength)) + data=VoidPtrToBytes(data, dataLength)) except: (exc_type, exc_value, exc_trace) = sys.exc_info() sys.excepthook(exc_type, exc_value, exc_trace) From b070673ba96572f49354b5507822528430525099 Mon Sep 17 00:00:00 2001 From: jonastieppo Date: Thu, 19 Dec 2024 14:41:39 -0300 Subject: [PATCH 169/177] Update README.md (#675) Just updating to the latest version (really minor change). --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9175971..f9a76a57 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ also download packages for offline installation available on the [GitHub Releases](../../releases) pages. Command to install with pip: ``` -pip install cefpython3==66.0 +pip install cefpython3==66.1 ``` From 05586d5bd92b9b98f0d96f9d069f8c5b10223e9b Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:06:33 +0100 Subject: [PATCH 170/177] Update README.md --- README.md | 311 +++++++++++++----------------------------------------- 1 file changed, 71 insertions(+), 240 deletions(-) diff --git a/README.md b/README.md index f9a76a57..80bda463 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,12 @@ Table of contents: * [Introduction](#introduction) -* [Latest releases sponsored by](#latest-releases-sponsored-by) - * [Thanks to all sponsors](#thanks-to-all-sponsors) * [Install](#install) -* [Tutorial](#tutorial) * [Examples](#examples) * [Support](#support) -* [Releases](#releases) - * [Next release](#next-release) - * [Latest release](#latest-release) - * [v49 release (WinXP/Vista)](#v49-release-winxpvista) - * [v31 release (old systems)](#v31-release-old-systems) * [Support development](#support-development) - * [Thanks to all](#thanks-to-all) -* [Seeking new sponsors](#seeking-new-sponsors) -* [Other READMEs](#other-readmes) -* [Quick links](#quick-links) +* [Seeking sponsors](#seeking-sponsors) +* [API](#api) ## Introduction @@ -49,39 +39,82 @@ frameworks such as PyQt, wxPython, PyGTK, PyGObject, Tkinter, Kivy, Panda3D, PyGame, PyOpenGL, PyWin32, PySide and PySDL2. -## Latest releases sponsored by +## Install - -
+You can install [pypi/cefpython3](https://pypi.python.org/pypi/cefpython3) +package using pip tool. On Linux pip 8.1+ is required. You can +also download packages for offline installation available on the +[GitHub Releases](../../releases) pages. Command to install with pip: -

- - - -

+``` +pip install cefpython3==66.1 +``` -Thank you to Fivestars for sponsoring the [v66.1 release](../../releases/tag/v66.1) -with Python 3.8 / 3.9 support. Fivestars helps local communities thrive by empowering -small businesses with cutting edge marketing technology. Please visit their website: -Fivestars.com +Below is a table with supported platforms, python versions and architectures. -
+OS | Py2 | Py3 | 32bit | 64bit | Requirements +--- | --- | --- | --- | --- | --- +Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 / 3.9 | Yes | Yes | Windows 7+ (Note that Python 3.9 supports Windows 8.1+) +Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 8+, Ubuntu 14.04+,
Fedora 24+, openSUSE 13.3+ +Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ -

- - - -

-Thank you to Lampix for sponsoring the -[v66 release](../../releases/tag/v66.0). Lampix is the first hardware -and software solution that turns any surface into a smart, augmented reality -or interactive surface. Please visit their website: -Lampix.com +## Examples + +- [Tutorial](docs/Tutorial.md) +- [All examples](examples/README-examples.md). +- [Snippets](examples/snippets/README-snippets.md). +- [PyInstaller packager](examples/pyinstaller/README-pyinstaller.md). -
-### Thanks to all sponsors +## Support + +- Ask questions and report problems on the + [Forum](https://groups.google.com/group/cefpython) +- Supported examples are listed in the + [README-examples.md](examples/README-examples.md) file +- Documentation is in the [docs/](docs) directory: + - [Build instructions](docs/Build-instructions.md) + - [Contributing code](docs/Contributing-code.md) + - [Knowledge Base](docs/Knowledge-Base.md) + - [Migration guide](docs/Migration-guide.md) + - [Tutorial](docs/Tutorial.md) +- API reference is in the [api/](api) directory: + - [API categories](api/API-categories.md#api-categories) + - [API index](api/API-index.md#api-index) +- Additional documentation is available in + [Issues labelled Knowledge Base](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22Knowledge+Base%22) +- To search documentation use GitHub "This repository" search + at the top. To narrow results to documentation only select + "Markdown" in the right pane. +- You can vote on issues in the tracker to let us know which issues are + important to you. To do that add a +1 thumb up reaction to the first post + in the issue. See + [Most popular issues](../../issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) + sorted by reactions. + + +## Support development + +If you would like to support general CEF Python development efforts +by making a donation then please click the Paypal Donate button below. + + + +

+ + +## Seeking sponsors + +CEF Python is seeking companies to sponsor development of this project. Most important +thing would be to have continuous monthly releases with updates to latest Chromium. There is +also lots of cool features and new settings that would be nice to implement. We have not yet +exposed all of upstream CEF APIs. If your company would like to sponsor CEF Python development efforts +then please contact [Czarek](https://www.linkedin.com/in/czarektomczak/). There are no active sponsors +at this moment. + + +### Previous sponsors @@ -139,209 +172,7 @@ or interactive surface. Please visit their website:
-## Install - -You can install [pypi/cefpython3](https://pypi.python.org/pypi/cefpython3) -package using pip tool. On Linux pip 8.1+ is required. You can -also download packages for offline installation available on the -[GitHub Releases](../../releases) pages. Command to install with pip: - -``` -pip install cefpython3==66.1 -``` - - -## Tutorial - -See the [Tutorial.md](docs/Tutorial.md) document. - - -## Examples - -See the [README-examples.md](examples/README-examples.md) and -[README-snippets.md](examples/snippets/README-snippets.md) documents. - - -## Support - -- Ask questions and report problems on the - [Forum](https://groups.google.com/group/cefpython) -- Supported examples are listed in the - [README-examples.md](examples/README-examples.md) file -- Documentation is in the [docs/](docs) directory: - - [Build instructions](docs/Build-instructions.md) - - [Contributing code](docs/Contributing-code.md) - - [Knowledge Base](docs/Knowledge-Base.md) - - [Migration guide](docs/Migration-guide.md) - - [Tutorial](docs/Tutorial.md) -- API reference is in the [api/](api) directory: - - [API categories](api/API-categories.md#api-categories) - - [API index](api/API-index.md#api-index) -- Additional documentation is available in - [Issues labelled Knowledge Base](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22Knowledge+Base%22) -- To search documentation use GitHub "This repository" search - at the top. To narrow results to documentation only select - "Markdown" in the right pane. -- You can vote on issues in the tracker to let us know which issues are - important to you. To do that add a +1 thumb up reaction to the first post - in the issue. See - [Most popular issues](../../issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) - sorted by reactions. - - -## Releases - -Information on planned new and current releases, supported platforms, -python versions, architectures and requirements. If you want to -support old operating systems then choose the v31 release. - -### Next release - -- To see planned new features or bugs to be fixed in the near future in one of - next releases, see the - [next release](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22next+release%22) - label in the tracker -- To see planned new features or bugs to be fixed in further future, see the - [next release 2](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22next+release+2%22) - label in the tracker - -### Latest release - -OS | Py2 | Py3 | 32bit | 64bit | Requirements ---- | --- | --- | --- | --- | --- -Windows | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 / 3.8 / 3.9 | Yes | Yes | Windows 7+ (Note that Python 3.9 supports Windows 8.1+) -Linux | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | Yes | Yes | Debian 8+, Ubuntu 14.04+,
Fedora 24+, openSUSE 13.3+ -Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ - -These platforms are not supported yet: -- ARM - see [Issue #267](../../issues/267) -- Android - see [Issue #307](../../issues/307) - - -### v49 release (WinXP/Vista) - -OS | Py2 | Py3 | 32bit | 64bit | Requirements ---- | --- | --- | --- | --- | --- -Windows | 2.7 | 3.4 | Yes | Yes | Windows XP+ - -- Install with command: `pip --no-cache-dir install cefpython3==49.0`. - - Please note that if you were previously installing cefpython3 - package it is required to use the `--no-cache-dir` flag, - otherwise pip will end up with error message - `No matching distribution found for cefpython3==49.0`. - This happens because 49.0 release occured after 57.0 and 66.0 - releases. -- Downloads are available on GitHub Releases tagged - [v49.0](../../releases/tag/v49.0). -- See [Migration guide](docs/Migration-guide.md) document for changes - in this release -- Documentation is available in the [docs/](../../tree/cefpython49-winxp/docs) - directory in the `cefpython49-winxp` branch -- API reference is available in the [api/](../../tree/cefpython49-winxp/api) - directory in the `cefpython49-winxp` branch - - -### v31 release (old systems) - -OS | Py2 | Py3 | 32bit | 64bit | Requirements ---- | --- | --- | --- | --- | --- -Windows | 2.7 | No | Yes | Yes | Windows XP+ -Linux | 2.7 | No | Yes | Yes | Debian 7+ / Ubuntu 12.04+ -Mac | 2.7 | No | Yes | Yes | MacOS 10.7+ - -Additional information for v31.2 release: -- On Windows/Mac you can install with command: `pip install cefpython3==31.2` -- Downloads are available on the GitHub Releases page tagged - [v31.2](../../releases/tag/v31.2). -- API reference is available in revision [169a1b2](../../tree/169a1b20d3cd09879070d41aab28cfa195d2a7d5/docs/api) -- Other documentation can be downloaded by cloning the - cefpython.wiki repository: `git clone git@github.com:cztomczak/cefpython.wiki.git` - - -## Support development - -If you would like to support general CEF Python development efforts -by making a donation then please click the Paypal Donate button below. -If you would like to see a specific feature implemented then you can make -a comment about that when making a donation and that will give it a higher -priority. - - - -

- - -### Thanks to all - -* [2021] Thank you to [Fivestars](https://www.fivestars.com/) for sponsoring - the v66.1 release with Python 3.8 / 3.9 support -* [2018] Thanks to [Fivestars](https://www.fivestars.com/) for sponsoring - the v49 release for legacy systems (WinXP/Vista) -* [2018] Many thanks to [Lampix](https://lampix.com/) for sponsoring the v66 - release for all platforms -* [2017] Many thanks to [HighSide Inc.](https://highside.io/) for sponsoring - the v55/v56 releases for all platforms -* [2016-2018] Thanks to JetBrains for providing an Open Source license for - [PyCharm](https://www.jetbrains.com/pycharm/) -* [2014] Thanks to Adam Duston for donating a Macbook to aid the development - of Mac port -* [2013-2015] Lots of thanks goes to [Cyan Inc.](http://www.blueplanet.com/) - for sponsoring this project for a long time, making CEF Python 3 mature -* [2013] Thanks to [Rentouch GmbH](http://www.rentouch.ch/) for sponsoring the - development of the off-screen rendering support -* [2013] Thanks to Thomas Wusatiuk for sponsoring the development of the web - response reading features -* [2012-2018] Thanks to those who have made a Paypal donation: - [Rentouch GmbH](http://www.rentouch.ch/), Walter Purvis, Rokas Stupuras, - Alex Rattray, Greg Kacy, Paul Korzhyk, Tomasz Tomanek. -* [2012-2017] Thanks to those who have donated their time through code - contributions, they are listed in the [Authors](Authors) file - - -## Seeking new sponsors - -CEF Python is seeking companies to sponsor further development of the project. -There are many proposals for new features submitted in the issue tracker. Most -notable are: - -* Monthly releases with latest Chromium -* An automated build system similar to upstream CEF Spotify Automated Builds -* ARM and Android support -* Multi-threaded support for increased performance -* Proprietary codecs support in build tools: H264, H265,AC3, EAC3, MPEG-4 -* More CEF API exposed, only about 50% is exposed so far -* Hundreds of new settings and Chromium preferences not yet exposed -* Easier integration with popular GUI toolkits in just a few lines of code - and support for more third party GUI frameworks -* More examples of implementing various advanced features and more snippets - as well - -If your company would like to sponsor CEF Python development efforts then -please contact -[Czarek](https://www.linkedin.com/in/czarektomczak/). -Long term sponsorships are welcome and Czarek is open to ideas about -the project. He would love to spend more time on developing this project, -but he can't afford doing so in his free time. Currently there is no company -supporting this project actively on a daily basis. - - -## Other READMEs - -- [PyInstaller packager](examples/pyinstaller/README-pyinstaller.md) - - - -## Quick links - -### Docs - -- [Build instructions](docs/Build-instructions.md) -- [Knowledge Base](docs/Knowledge-Base.md) -- [Migration guide](docs/Migration-guide.md) -- [Tutorial](docs/Tutorial.md) - - -### API categories +## API #### Modules From 811fa74c8ef413d5b82371d6d74166e7d5eab47b Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:07:19 +0100 Subject: [PATCH 171/177] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 80bda463..35127ab7 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,9 @@ Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ ## Examples - [Tutorial](docs/Tutorial.md) -- [All examples](examples/README-examples.md). -- [Snippets](examples/snippets/README-snippets.md). -- [PyInstaller packager](examples/pyinstaller/README-pyinstaller.md). +- [All examples](examples/README-examples.md) +- [Snippets](examples/snippets/README-snippets.md) +- [PyInstaller packager](examples/pyinstaller/README-pyinstaller.md) ## Support From 6c72dc3d093d3eebbee3ff0b79dc58b762f10846 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:11:02 +0100 Subject: [PATCH 172/177] Update README.md --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 35127ab7..2e127e63 100644 --- a/README.md +++ b/README.md @@ -86,13 +86,7 @@ Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ [Issues labelled Knowledge Base](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22Knowledge+Base%22) - To search documentation use GitHub "This repository" search at the top. To narrow results to documentation only select - "Markdown" in the right pane. -- You can vote on issues in the tracker to let us know which issues are - important to you. To do that add a +1 thumb up reaction to the first post - in the issue. See - [Most popular issues](../../issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) - sorted by reactions. - + "Markdown" in the side pane. ## Support development From 0d3295e05212333f14155f416cc483bd2b2f3e3f Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:12:07 +0100 Subject: [PATCH 173/177] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e127e63..06d3a4e3 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ by making a donation then please click the Paypal Donate button below. -

+
## Seeking sponsors From a6f5def34270b8d64a733597146b98633a7e7dd3 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:17:13 +0100 Subject: [PATCH 174/177] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 06d3a4e3..b6fe4539 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,7 @@ Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ ## Support development -If you would like to support general CEF Python development efforts -by making a donation then please click the Paypal Donate button below. +To support general CEF Python development efforts please make a donation using PayPal button below: From de22be237b20b504eb6bdc385eae436eb12d225b Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:18:30 +0100 Subject: [PATCH 175/177] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6fe4539..755d6299 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ ## Support development -To support general CEF Python development efforts please make a donation using PayPal button below: +To support general CEF Python development efforts you can make a donation using PayPal button below: From 5217c04feaa6b8a4abbcb18bba34ad586e3cecd1 Mon Sep 17 00:00:00 2001 From: Czarek Tomczak Date: Sat, 8 Feb 2025 01:23:50 +0100 Subject: [PATCH 176/177] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 755d6299..4ff688b0 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,16 @@ PyGame, PyOpenGL, PyWin32, PySide and PySDL2. ## Install -You can install [pypi/cefpython3](https://pypi.python.org/pypi/cefpython3) -package using pip tool. On Linux pip 8.1+ is required. You can -also download packages for offline installation available on the -[GitHub Releases](../../releases) pages. Command to install with pip: +Command to install with pip: ``` pip install cefpython3==66.1 ``` +Hosted at [pypi/cefpython3](https://pypi.python.org/pypi/cefpython3). On Linux pip 8.1+ is required. + +You can also download packages for offline installation available on the [GitHub Releases](../../releases) pages. + Below is a table with supported platforms, python versions and architectures. OS | Py2 | Py3 | 32bit | 64bit | Requirements From 3d6f6c19992cb112d6de8ff856bce443bdf3bbea Mon Sep 17 00:00:00 2001 From: Mandru-JashwanthKumar Date: Wed, 26 Nov 2025 10:17:23 +0530 Subject: [PATCH 177/177] Fix formatting of README.md documentation section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ff688b0..b85f34c6 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Mac | 2.7 | 3.4 / 3.5 / 3.6 / 3.7 | No | Yes | MacOS 10.9+ [Issues labelled Knowledge Base](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22Knowledge+Base%22) - To search documentation use GitHub "This repository" search at the top. To narrow results to documentation only select - "Markdown" in the side pane. + "Markdown" in the side pane ## Support development