Add CEF 147, Python 3.10–3.14, Linux/macOS ARM64/Windows support with modern build system - #691
Add CEF 147, Python 3.10–3.14, Linux/macOS ARM64/Windows support with modern build system#691linesight wants to merge 179 commits into
Conversation
- Add tools/download_cef.py to auto-download CEF from Spotify CDN
with SHA1 verification and progress output
- Detect vcvarsall.bat dynamically via vswhere.exe instead of
hardcoding VS2022 Community path; fall back through known editions
- Remove dead code for VS2008/VS2010/VS2013 and simplify
prepare_build_command()
- Make VERSION argument optional in build.py, defaulting to
{CHROME_VERSION_MAJOR}.0 from the version header
- Add venv support notes and document download_cef.py workflow
in Build-instructions.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass explicit plat_name to distutils compiler.initialize() in build_cpp_projects.py, and add --plat-name to the cython_setup.py build_ext invocation in build.py. Without these, distutils defaults to win32 even on 64-bit Python, causing C1905 (front/back end incompatible) when linking against x64 CEF libraries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er creation Two root causes fixed to make windowed browsers work on CEF 123+: 1. Set CEF_RUNTIME_STYLE_ALLOY explicitly after SetAsChild/SetAsPopup in window_info.pyx. CEF 123+ defaults to Chrome runtime (no native Win32 title bar, blank content). Also declared cef_runtime_style_t enum and runtime_style field in cef_win.pxd. 2. Defer CreateBrowserSync() until OnContextInitialized fires. Calling before context is ready causes blink.mojom.WidgetHost rejection and a blank window. g_pending_browsers list + CefPostTask posts creation at the outer message-loop level after the callback returns. Also fix PyQt6 high-DPI sizing in qt.py: call WindowUtils.OnSize after CreateBrowserSync() so the browser fills the HWND client rect in device pixels rather than the smaller logical-pixel rect Qt reports. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sync all CEF public headers, capi headers, and internal headers from CEF 146 binary distribution. Includes new headers for task manager, component updater, OSR types, color types, runtime style, and API versioning. Update client handlers (dialog, download, lifespan, request) for CEF 146 API changes. Update cef_types.pxd, network_error.pyx, settings.pyx, and version header for CEF 146. Update build tools (cython_setup.py, build_cpp_projects.py) for the new toolchain. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
osr_test.py: - OnTextSelectionChanged: Chrome 130+ requires the Selection API to run inside a real user-gesture event handler to propagate the callback. Added selectText() onclick handler to h1 and trigger it via a real mouse click posted after the first OnPaint (guarantees layout is complete and hit-testing works). Simplified the callback handler to not assume a specific empty-then-selected call sequence. - AccessibilityHandler: removed layoutComplete_True assertion — the layoutComplete AX event was removed from Chrome's event system in Chrome 130+. main_test.py: - V8ContextHandler: CEF 146 no longer creates an initial empty-document V8 context on browser creation. Removed OnContextCreatedSecondCall_True and OnContextReleased_True assertions which could never be satisfied. - Added disable-popup-blocking switch to cef.Initialize — Chrome 130+ blocks window.open() calls that lack a user gesture at the renderer level, preventing the popup test from running. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sion _detect_cefpython_binary_dir() runs at import time (bottom of common.py), before build.py's command_line_args() can inject the default version into sys.argv. Fall back to reading the version directly from the CEF header file so CEFPYTHON_BINARY is resolved correctly even when no version argument is passed on the command line. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cefpython.pyx: - Pump the message loop (up to 200×10ms = 2s) after CefInitialize() until OnContextInitialized fires. This guarantees CreateBrowserSync() can be called immediately after Initialize() and always gets a valid browser, eliminating the race condition where deferred creation could return None when CreateBrowserSync() was called before the context was ready. main_test.py: - Remove OnAutoResize_True assertion: CEF 146 no longer triggers OnAutoResize for windowed (non-OSR) browsers. Consistent with the other Chrome 130+ behavioral changes removed in the previous commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ANGLE D3D11 backend in CEF 146 / Chrome 130+ triggers a CHECK failure
(STATUS_BREAKPOINT, exit_code=-2147483645) in libcef.dll during GPU
process initialization. CEF auto-recovers 3 times then falls back to
software rendering, silently degrading performance. The D3D9 backend
avoids the crash but only implements ES 2.0, causing eglCreateContext
ES 3.0 failures. Defaulting to the OpenGL ANGLE backend eliminates both
issues — it supports ES 3.0 and initializes without crashes. Users can
override by passing {"use-angle": "d3d11"} in the switches dict. Fixes cztomczak#686.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Framework fixes: - Remove BindedFunctionExists renderer-side pre-check in V8FunctionHandler::Execute that raced against global registration and threw spurious "function does not exist" errors; browser process handles missing functions gracefully via NonCriticalError - Fix DoJavascriptBindingsForBrowser: guard null/invalid V8 context to prevent crash/freeze in CEF 146, register globals synchronously (not via PostTask) when outside V8 execution so they are in place before queued ExecuteJavascript IPCs run - Add Rebind() before user OnContextCreated callback in V8ContextHandler so that DoJavascriptBindings IPC is ordered ahead of any ExecuteJavascript the user sends from their callback on the same IPC channel Example updates: - ondomready.py: inject JS from OnContextCreated instead of OnLoadStart; OnLoadStart fires before the page V8 context is committed so ExecuteJavascript ran in about:blank where globals are not registered; wrap callback in setTimeout(0) to allow paint first - onpagecomplete.py: replace OnLoadingStateChange with window.load + requestAnimationFrame via JS bindings to fire only after all resources are loaded and a paint frame is committed; fixes double-alert on sites with JS-initiated redirects (e.g. google.com) and ensures page content is visible before the alert dialog blocks the renderer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- crossdomain_bindings.py: demonstrates JS binding behaviour across a cross-domain auth flow (app -> auth domain -> back to app); uses a URL-based state machine to avoid misfire from multiple OnLoadEnd fires, and readyState fallback so the callback is not missed if window.load already fired before JS is injected - README-snippets.md: add entry for crossdomain_bindings.py and update onpagecomplete.py description to reflect window.load + requestAnimationFrame Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cookie.pyx: change CookieVisitor_Visit thread assertion from TID_IO to TID_UI — CEF 146 changed CookieVisitor::Visit to always fire on the UI thread (previously IO thread), causing AssertionError on every callback - cookies.py: fix bug where OnLoadingStateChange checked "if is_loading" (fires on load start) instead of "if not is_loading" (fires on complete); update dead html-kit.com URL to https://www.google.com/ - network_cookies.py: update dead html-kit.com URL to https://www.google.com/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wire GetResourceRequestHandler in RequestHandler to return a ResourceRequestHandler (new file) that exposes GetCookieAccessFilter, restoring CanSendCookie/CanSaveCookie callbacks broken since CEF 146 moved them out of CefRequestHandler into CefCookieAccessFilter - Add #pragma once to cookie_access_filter.h to prevent C2011 redefinition when included from both request_handler.h and resource_request_handler.h - Fix Cookie.SetDomain() to accept leading-dot domains (.example.com) by stripping the dot before IDNA validation while preserving the original value stored in the cookie (RFC 2109 subdomain-matching convention) - Update setcookie.py: replace dead html-kit.com URL with google.com, set cookie in OnLoadEnd and verify with VisitUrlCookies Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cefpython_public_api.h: add PY_MINOR_VERSION cases for 3.12, 3.13, 3.14 - build.py: fix invalid escape sequences in regex strings (SyntaxWarning on 3.12+) - cefpython3.__init__.py: add import branches for 3.12, 3.13, 3.14 - cefpython3.setup.py: add PyPI classifiers for 3.12, 3.13, 3.14 - make_installer.py: add .pyd checks and update comments for 3.12, 3.13, 3.14 - build_distrib.py: add (3,12)/(3,13)/(3,14) to version lists Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cython 0.29.36 generates code using CPython internals removed in Python 3.13 (_PyDict_SetItem_KnownHash, _PyInterpreterState_GetConfig, _PyLong_AsByteArray), causing CI build failures on Python 3.13+. - requirements.txt: Cython == 0.29.36 -> == 3.2.4 - cython_setup.py: language_level 2 -> "3str"; remove c_string_type / c_string_encoding directives - All .pyx files: py_string type annotations -> object (Cython 3.x rejects string literals assigned to ctypedef object aliases) - All .pyx files: iteritems() -> items(), basestring -> (str, bytes), long -> int (Python 2 builtins removed under language_level=3str) - cefpython_app.cpp: remove extern "C" forward declarations that conflicted with Cython 3.x extern "C++" default in _fixed.h - build.py: fix_cefpython_api_header_file to write #pragma only to _fixed.h so Cython 3.x can overwrite its own output on rebuilds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace IF/ELIF/ELSE compile-time conditionals with runtime sys.platform checks and C macro helpers in cefpython.pyx, window_info.pyx, window_utils_win.pyx, utils.pyx, and browser.pyx - Replace IF-based cimport blocks in cef_platform.pxd, cef_app.pxd, cef_browser.pxd, cef_browser_static.pxd with generated platform_cimports.pxi files; add generation of src/platform_cimports.pxi and src/extern/cef/platform_cimports.pxi to cython_setup.py - Fix platform-conditional char16_t typedef in cef_types.pxd using cdef extern from * C macro instead of IF block - Fix nogil keyword placement (must follow except+) in wstring.pxd and multimap.pxd; fix size_t npos assignment in wstring.pxd - Change 44 cdef public callback functions from except * with gil to noexcept with gil across handlers and core pyx files; these functions already catch all exceptions internally so the exception check GIL acquisition was redundant Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows manually triggering a clean build from GitHub Actions UI by skipping CEF binary cache, useful for verifying the build script works end-to-end without relying on cached artifacts.
Cython must transpile the .pyx file before C++ projects can be compiled, because the C++ code includes cefpython_pyXX_fixed.h which is derived from the Cython-generated header. The previous two-pass re-run hack let the first C++ compile fail noisily, then re-ran the whole script. Now build.py detects a missing API header at startup and calls cython_setup.py --cython-only (new flag) to transpile the .pyx and produce cefpython_pyXX.h before fix_cefpython_api_header_file() and the C++ compilation run. The FIRST_RUN re-run machinery is removed entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the old setup.py / build_cefpython.py toolchain with a modern scikit-build-core backend driven by CMake, enabling clean incremental dev builds, a structured CI pipeline (compile → test → wheel), and optional Cython profiling/line-tracing flags. Key changes: - CMakeLists.txt (root + per-subdir): full CMake build for the extension module, subprocess executable, client_handler and cpp_utils static libs; auto-detects CEF root; installs CEF runtime files via cmake --install - pyproject.toml: scikit-build-core build backend, Cython >=3.2 dependency - tools/cmake_generate_pxi.py: generates platform .pxi files at configure time - tools/cmake_prepare_pyx.py: stages .pyx files and injects version constants - tools/cmake_fix_header.py: patches Cython-generated header for MSVC compat - tools/build.py: unified dev/CI entry point; --clean for full rebuild, --wheel for pip wheel, --enable-profiling / --enable-line-tracing for Cython instrumentation - .github/workflows/ci-windows.yml: split into compile / test / wheel jobs with artifact hand-off; supports Python 3.10–3.14 - .gitignore: add _cmake_test/, _skbuild/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
__init__.py is source; .pyd/.dll/.exe/.pak/.dat/.bin/.json/locales/ are build outputs or CEF runtime files and should not be committed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
automate.py --prebuilt-cef requires docopt; add Install build tools step to the test job same as compile and wheel jobs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
automate.py requires docopt; requirements.py must run first. Fixed in compile, test, and wheel jobs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_test_runner.py os.chdir()s to unittests/, so the repo root (where cefpython3/ lives) falls off sys.path. Set PYTHONPATH to the workspace root so the package is findable without installing it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Include CEF runtime files in the compile artifact so the test job needs nothing except: checkout, setup-python, download artifact, copy, test. No requirements.py, no CEF cache restore, no automate.py. Also add pip cache to the wheel job's setup-python to speed up repeated requirements.py installs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old script searched for multiple Python installations and drove a full build + setup.py bdist_wheel — all of that is now the CI matrix's job. The new build_distrib.py does the one thing that still belongs here: package the pre-built cefpython3/ directory into a .whl using stdlib only (zipfile/hashlib), with correct dist-info/RECORD hashes. The CI wheel job now mirrors the test job (checkout, setup-python, download artifact, copy, build_distrib.py). Timeout drops 60 -> 15 min. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
UNAME_SYSNAME and PY_MAJOR_VERSION are Cython built-in compile-time constants — no DEF needed. INT_MIN/INT_MAX now come from libc.limits via a direct cimport. The two platform_cimports.pxi files use Cython's own IF/ELIF/ELSE on UNAME_SYSNAME instead of being generated per-OS. Removes cmake_generate_pxi.py and its CMakeLists.txt custom command entirely — no generation step, no gitignore entries, no cmake target. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add .github/workflows/ci-linux.yml targeting ubuntu-24.04 - Update src/version/cef_version_linux.h from CEF 66 to CEF 146 - Make tools/build.py cross-platform (cmake flags, artifact paths, CEF runtime detection) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GCC 13 added -Wself-move which fires on intentional self-move test code in CEF's ref_counted_unittest.cc. Pass -Wno-self-move when configuring the CEF binary's build_cefclient on Linux. Safe on older GCC where the flag is silently ignored. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d GTK3 - cef_types_linux.h: Sync with real CEF 146 — adds size_t size to _cef_window_info_t, cef_runtime_style_t runtime_style field, the full _cef_accelerated_paint_info_t + plane struct for Linux DMA-BUF, and missing includes (cef_types_color.h, cef_types_osr.h, cef_types_runtime.h). The old vendored header was from CEF ~66. - cef_linux.h: Sync with real CEF 146 — CefWindowInfoTraits::init now sets s->size, set() copies runtime_style, SetAsWindowless sets CEF_RUNTIME_STYLE_ALLOY. CefWindowInfo uses idiomatic using-inheritance. - client_handler/CMakeLists.txt: Add gtk+-3.0 pkg-config includes on Linux so dialog_handler_gtk.h (<gtk/gtk.h>) can be found. - subprocess/CMakeLists.txt: Add gtk+-3.0 pkg-config includes for the cefpython_app library on Linux (cefpython_app.cpp includes <gtk/gtk.h> when BROWSER_PROCESS is defined). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… OSR test Reviewer feedback on cztomczak#691: the unit tests must run with the default cefpython configuration on a normal development machine, not with Chromium switches added only to make CI pass. _common.py: revert to base. Drop the Linux Initialize monkey-patch, init_gtk(), the dead _linux_needs_no_sandbox() helper, the off-screen OnBeforePopup hack and CloseBrowser(False). The library already applies the needed Linux defaults (ozone-platform=x11, windowless_rendering_enabled, no-sandbox, GDK display) in window_utils_linux.pyx, and the windowed popup-close no longer crashes, so these test-side hacks are unnecessary. main_test.py: remove the per-platform CI switch block. Keep only disable-popup-blocking, a functional requirement of the popup sub-test (Chrome blocks gesture-less window.open) rather than a CI workaround. osr_test.py: remove the Linux/macOS CI switch blocks (keep the OSR switches from Issue cztomczak#240/cztomczak#463). Fix the intermittent OnTextSelectionChanged failure: post the selection click after OnLoadEnd instead of from the first paint, let the body fill the viewport so a fixed center click reliably hits it, and drive the test with cef.MessageLoop()/QuitMessageLoop() plus a watchdog task instead of looping for a fixed duration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… OSR test Reviewer feedback on cztomczak#691: the unit tests must run with the default cefpython configuration on a normal development machine, not with Chromium switches added only to make CI pass. _common.py: revert to base. Drop the Linux Initialize monkey-patch, init_gtk(), the dead _linux_needs_no_sandbox() helper, the off-screen OnBeforePopup hack and CloseBrowser(False). The library already applies the needed Linux defaults (ozone-platform=x11, windowless_rendering_enabled, no-sandbox, GDK display) in window_utils_linux.pyx, and the windowed popup-close no longer crashes, so these test-side hacks are unnecessary. main_test.py: remove the per-platform CI switch block. Keep only disable-popup-blocking, a functional requirement of the popup sub-test (Chrome blocks gesture-less window.open) rather than a CI workaround. osr_test.py: remove the Linux/macOS CI switch blocks (keep the OSR switches from Issue cztomczak#240/cztomczak#463). Fix the intermittent OnTextSelectionChanged failure: post the selection click after OnLoadEnd instead of from the first paint, let the body fill the viewport so a fixed center click reliably hits it, and drive the test with cef.MessageLoop()/QuitMessageLoop() plus a watchdog task instead of looping for a fixed duration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the --single-process / CFProcessPath workaround with the canonical CEF macOS multi-process design so the unit tests run under the default cefpython configuration. - Build five process-specific "cefpython Helper*.app" bundles (generic, Alerts, GPU, Plugin, Renderer), each with a unique CFBundleIdentifier, from a shared helper-Info.plist.in template; drop the old flat subprocess Info.plist. - Wire the runtime paths on macOS: browser_subprocess_path -> generic helper executable, main_bundle_path -> real NSBundle (else the packaged helper app) so every process shares a valid CEF BaseBundleID for Mach port rendezvous. Add main_bundle_path to CefSettings bindings and MacGetMainBundlePath(). - Sign staged code inside-out (framework --deep, modules, each helper --deep) and verify strictly; package the signed tree in build_distrib.py, validating the complete helper set. Stage/copy helpers in build.py and CMake install. - Update the installer, PyInstaller hook/spec (framework + helper binaries, PyInstaller 6), screenshot example, chromectrl wrapper, docs and .gitignore for the helper-bundle layout; GetModuleDirectory() honors PyInstaller _MEIPASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub is deprecating the Node.js 20 runtime. Bump the flagged actions to their first Node 24 major: checkout v4->v5, setup-python v5->v6, cache/cache-restore v4->v5. upload-artifact is already on v7 (Node 24). GitHub-hosted runners meet the required runner version (>= 2.327.1). Also set the pull_request trigger to branches: ["master"] so the three workflow files are byte-identical to the cefpython147 branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI intermittently failed test_main at check_auto_asserts -> assertTrue(g_js_code_completed): the fixed run_message_loop() (200 x 10ms = 2s) could end before the JavaScript bindings round-trip finished on a slow runner. This is timing-dependent and can occur on any platform. Replace the fixed-iteration loop with run_message_loop_until(condition), which pumps MessageLoopWork() until the whole asynchronous flow has actually completed (JS done, popup created and destroyed, DevTools shown then closed, load progress 1.0, and every handler callback fired) or a generous timeout elapses, turning a race into a clear assertion. Also: - close_devtools() retries via PostDelayedTask until ShowDevTools() has taken effect instead of assuming 800ms is always enough. - After CloseBrowser(), wait for the browser registry entry to disappear before checking asserts and shutting down. - Register cef.Shutdown() via addCleanup so an assertion/timeout failure still releases CEF and its child processes (idempotent with the explicit shutdown). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
above test harness concern is fixed |
|
I see lots of Chromium flags removed from unit tests e.g. |
|
I guess at some point you started using xvfb - so all these switches aren't required anymore? |
|
It's best to run unit tests using default cefpython configuration, however I didn't state that this is a must for CI (although prefered to be as close to default configuration if possible). To be clear,my comment on unit tests was that the unit test scripts should use cefpython configuration as default, however I am aware that CI has limitations. But by just running "python main_test.py" it shouldn't use CI configuration as default - that was my point. |
The main_test hardening added a pre-Shutdown wait for GetBrowserByIdentifier(MAIN_BROWSER_ID) to return None. All five macOS jobs timed out there even though the JavaScript, popup, DevTools and handler callbacks had completed. CloseBrowser(True) starts an asynchronous native close and does not guarantee that a synchronously created macOS browser reaches OnBeforeClose before Shutdown. This matches upstream CEF issues chromiumembedded/cef#3469 and chromiumembedded/cef#3810. cefpython removes the browser from its registry in OnBeforeClose, so registry disappearance is not a portable pre-Shutdown condition. Remove only that close wait and restore the existing 25-iteration MessageLoopWork grace period. Keep the condition-driven pre-close completion gate that fixes the original Windows Python 3.14 timing failure, and let cef.Shutdown() perform its existing final browser cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Linux CI was already using Xvfb before this cleanup. It currently runs: Xvfb only supplies the X11 display. The unit tests themselves do not detect CI or inject CI-specific Chromium switches. Flags such as |
|
Why is |
Reviewer feedback: main_test_async_completed() is specific to main_test and does not belong in the shared _common module. Move the predicate into main_test.py. Add a generic is_js_code_completed() accessor beside the shared state in _common.py so the existing wildcard import can read the live rebound boolean without adding a duplicate module import. The shared callback and assertion state remain available to both main_test and osr_test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fixed |
|
Is there any remaining code concern I've missed? The only knowingly-deferred code item is the |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I believe this PR is now ready to merge — please take another look when you have a moment and let me know if anything else is needed. |
cztomczak
left a comment
There was a problem hiding this comment.
I checked some of the code. Please see my comments.
| ::Window xwindow = browser->GetHost()->GetWindowHandle(); | ||
| ::Display* xdisplay = cef_get_xdisplay(); | ||
| // NULL xdisplay (Ozone Wayland) / 0 xwindow (OSR); see SetX11WindowBounds. | ||
| if (!xdisplay || !xwindow) return; |
There was a problem hiding this comment.
Should we log a message here? The function fails silently.
There was a problem hiding this comment.
Extra logging is added
linesight@24b01a1
| .format(domain)) | ||
| except UnicodeError: | ||
| raise Exception("Cookie.SetDomain() failed, invalid domain: {0}" | ||
| .format(domain)) |
There was a problem hiding this comment.
The Python-side regex is now redundant, and removing it is intended. CEF + Chromium already validate the domain and reject invalid ones gracefully — the crash from #459 can no longer happen.
The path a cookie takes through CookieManager.SetCookie():
Chromium — net::CanonicalCookie::CreateSanitizedCookie() (net/cookies/canonical_cookie.cc) validates the domain (character set, size, and resolvability via GetCookieDomainWithString), records any problem as an EXCLUDE_INVALID_DOMAIN exclusion reason, then fails closed:
if (!status->IsInclude())
return nullptr;
It never builds a cookie from a bad domain — validation runs before GetCookieDomainWithString specifically "so it doesn't fail DCHECKs."
CEF — CefCookieManagerImpl::SetCookieInternal()
(libcef/browser/net_service/cookie_manager_impl.cc) null-checks that result:
auto canonical_cookie = net::CanonicalCookie::CreateSanitizedCookie(...);
if (!canonical_cookie) {
// adds EXCLUDE_UNKNOWN_ERROR, runs the callback, returns cleanly
return true;
}
So an invalid domain becomes: return nullptr -> null-check -> clean return with a LOG(WARNING) << "SetCookie failed with reason: ...". No dereference, no crash. The #459 segfault was in old pre-net_service CEF that lacked this guarding.
The old regex was also over-strict — it required at least one dot, so it rejected legitimate cookie domains like localhost and single-label intranet hosts, and duplicated validation Chromium already does more correctly.
| # top-level CMakeLists. Only the Linux-specific extra is listed here. | ||
| target_link_libraries(cpp_utils PRIVATE cefpython_common_flags) | ||
| if(CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||
| target_compile_options(cpp_utils PRIVATE -Wno-stringop-overflow) |
There was a problem hiding this comment.
Why is -Wno-stringop-overflow needed? Which warning does it suppress? Could we fix the underlying code instead of disabling the warning?
I'm not accepting any workarounds unless there is a corresponding issue in the CEF Python issue tracker and it has been discussed first.
There was a problem hiding this comment.
Opened #696 to track and discuss this properly.
The -Wno-stringop-overflow was suppressing a GCC false positive on the CefRefPtr<T> x = new T(id) idiom under -O3 -flto.
I've dropped the suppression from this PR in linesight/cefpython@9e3ba1b so no workaround remains here — the actual fix can land separately once we've agreed on the approach in #696.
| CefBrowserSettings&, | ||
| CefRefPtr[CefDictionaryValue], | ||
| CefRefPtr[CefRequestContext]) nogil | ||
|
|
There was a problem hiding this comment.
Why is this exposed? For testing? I don't think we actually use it.
There was a problem hiding this comment.
Thanks for catching, it was other experimental code I fogot to clean up. Now I have removed at linesight@53f4c90
| # noinspection PyUnresolvedReferences | ||
| ctypedef wchar_t char16_t | ||
| ELSE: | ||
| ctypedef unsigned short char16_t |
There was a problem hiding this comment.
I don't understand why this change is needed. We still use IF UNAME_SYSNAME in many places, so why replace it only here?
There was a problem hiding this comment.
It was one of early attempt to mute IF UNAME_SYSNAME == "Windows": warning. CEF 147 defaults to CEF_STRING_TYPE_UTF16 and uses the built-in char16_t uniformly on all platforms, so the generated char16_t already matches CEF's ABI with no per-platform typedef. Simplified in linesight/cefpython@01343d0
| return True | ||
| except: | ||
| (exc_type, exc_value, exc_trace) = sys.exc_info() | ||
| sys.excepthook(exc_type, exc_value, exc_trace) |
There was a problem hiding this comment.
GetScreenInfo used to be unimplemented. This adds a fair amount of new logic. Is all of this required for CEF 147? Which problem does it solve?
There was a problem hiding this comment.
it isn't required for cef 147. What it solves is HiDPI OSR rendering, I did spend quite a bit of time to crack HiDPI issue: in OSR there's no native window to carry display density — device_scale_factor here is the only way to tell Chromium the pixel ratio, the pysdl2 example is the concrete consumer: it detects the host ratio and feeds it through here so that buffer matches the physical surface 1:1
| if not cefFrame.get() or not cefFrame.get().GetBrowser().get(): | ||
| Debug("OnBeforeResourceLoad: no resolvable browser for the request" | ||
| " (IO-thread / service-worker request); continuing by default") | ||
| return False |
There was a problem hiding this comment.
These callbacks are being dropped because there is no associated PyBrowser to dispatch them on. Since this is a limitation of the current cefpython architecture rather than a CEF bug, it should be documented. Otherwise users may assume these callbacks are always delivered, as in the CEF API.
| cdef JavascriptBindings jsBindings | ||
| try: | ||
| if not cefFrame.get().GetBrowser().get(): | ||
| return |
There was a problem hiding this comment.
This is another silent early return. At least add a comment explaining why GetBrowser() can legitimately be NULL here and why skipping the callback is the correct behavior. A debug log would also make unexpected cases much easier to diagnose.
| pyFrame = GetPyFrame(cefFrame) | ||
| # Re-send bindings before user callback so that any ExecuteJavascript | ||
| # the user sends from OnContextCreated has globals already registered | ||
| # when it arrives at the renderer (both travel the same IPC channel). |
There was a problem hiding this comment.
The comment explains why Rebind() happens before the callback, but not why Rebind() is needed in the first place. It would be helpful to mention that this handles new V8 contexts (e.g. after cross-origin navigation / site isolation), otherwise the reason for the rebind isn't obvious from the code.
| if pyFrame.IsMain(): | ||
| jsBindings = pyBrowser.GetJavascriptBindings() | ||
| if jsBindings: | ||
| jsBindings.Rebind() |
There was a problem hiding this comment.
Does this mean Rebind() is now called for every OnContextCreated, including cases where the renderer already has the bindings? If so, is Rebind() expected to be cheap/idempotent, or can we avoid the unnecessary resend?
There was a problem hiding this comment.
Yes — it runs on every OnContextCreated (main frame only). But the "cases where the renderer already has the bindings" part is the bit worth unpacking: that situation doesn't really arise here.
Bindings live per V8 context, and OnContextCreated fires because a new context was just created (page load, navigation, cross-origin nav under site isolation). That context starts empty — globals from the previous one don't carry over — so each call is the first send for that context, not a resend to one that already has them. That's the race this fixes: without it a new context can end up with no bindings.
The genuinely "nothing to do" case is already short-circuited: if an app never calls SetJavascriptBindings, GetJavascriptBindings() is None and the if jsBindings: check skips Rebind().
On cost: Rebind() builds the payload and sends one DoJavascriptBindings message — idempotent, and only once per main-frame navigation, so not a hot path.
The one avoidable resend left is an app that attaches an empty JavascriptBindings() object — that'd still send an empty message. I can add a quick early-out for that if you think it's worth it, though it's a fairly degenerate case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Return early for windowless (OSR) browsers before touching X11, and LOG(INFO) when cef_get_xdisplay() is NULL on a non-X11 Ozone backend instead of returning silently. Correct the comment: the display is NULL only when no X server is reachable (pure Wayland, no XWayland), not merely when the app selects the Wayland Ozone backend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add why-comments and Debug() logging to the CefFrame::GetBrowser()==NULL guards across the V8 context, load, lifespan and cookie-access handlers so these skipped callbacks are diagnosable rather than silently dropped. - v8context: explain the async-IPC frame-detach case; document why bindings are re-sent on each new V8 context. - cookie filter: drop the incorrect issue cztomczak#676 attribution (that was the callbacks-not-called bug, fixed separately); document in RequestHandler.md that requests without a browser/frame cannot be filtered. - load handler: comment + log OnLoadStart/OnLoadEnd/OnLoadError. - lifespan: fix the OnBeforePopup comment (return false ALLOWS the popup, it does not cancel) and defer to CEF's default, matching the other guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dead since fb56b04 reverted the async-create workaround. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CEF 147 uses the C++11 built-in char16_t uniformly; declare it for Cython without the per-platform inline C. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The C++ external message pump includes <glib.h> directly; these Cython externs were unused. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Overview
This PR modernizes cefpython from CEF 66 (2018) to CEF 147, adds full support
for Linux and macOS Apple Silicon, and replaces the legacy build toolchain with
a pip-installable wheel workflow. It also incorporates all work from the
cefpython123branch (PR #679) which was never merged to master.What's new
CEF & Python versions
Platform support
Build system
setup.pywith scikit-build-core + CMakebuild_distrib.pyproduces installable wheels per platform/Python versionCI (GitHub Actions)
from cache rather than downloading independently
Linux
macOS
Qt
API changes
OnPluginCrashed;SendFocusEventkeptas no-op stub for compatibility
CanSendCookie/CanSaveCookiehandler signatures revisedOpen issues addressed
Definitely fixed
Likely fixed