diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml
new file mode 100644
index 000000000..360a3382c
--- /dev/null
+++ b/.github/workflows/ci-linux.yml
@@ -0,0 +1,143 @@
+name: CI Linux
+
+on:
+ push:
+ branches: ["master", "cefpython147"]
+ pull_request:
+ branches: ["master"]
+ workflow_dispatch:
+ inputs:
+ bypass_cache:
+ description: "Bypass all caches for a clean run"
+ type: boolean
+ default: false
+
+jobs:
+ download-cef:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 15
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+
+ - name: Read CEF version
+ id: cef-version
+ run: |
+ ver=$(grep -oP '(?<=#define CEF_VERSION ")[^"]+' src/version/cef_version_linux.h)
+ echo "value=$ver" >> $GITHUB_OUTPUT
+
+ - name: Cache CEF binaries
+ uses: actions/cache@v4
+ if: ${{ inputs.bypass_cache != true }}
+ with:
+ path: |
+ build/cef_binary_*
+ build/cef*_linux64
+ key: cef-linux64-${{ steps.cef-version.outputs.value }}
+
+ - name: Install build tools
+ run: python tools/requirements.py
+
+ - name: Download CEF binaries
+ run: python tools/download_cef.py
+
+ wheel:
+ needs: download-cef
+ runs-on: ubuntu-24.04
+ timeout-minutes: 75
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Read CEF version
+ id: cef-version
+ run: |
+ ver=$(grep -oP '(?<=#define CEF_VERSION ")[^"]+' src/version/cef_version_linux.h)
+ echo "value=$ver" >> $GITHUB_OUTPUT
+
+ - name: Restore CEF cache
+ uses: actions/cache/restore@v4
+ if: ${{ inputs.bypass_cache != true }}
+ with:
+ path: |
+ build/cef_binary_*
+ build/cef*_linux64
+ key: cef-linux64-${{ steps.cef-version.outputs.value }}
+
+ - name: Install system dependencies
+ run: |
+ sudo apt-get update -q
+ sudo apt-get install -y --no-install-recommends \
+ cmake ninja-build pkg-config \
+ libgtk2.0-dev libgtk-3-dev \
+ libglib2.0-dev libx11-dev \
+ libnss3-dev libatk1.0-dev \
+ libxcomposite-dev libxdamage-dev libxext-dev \
+ libxfixes-dev libxrandr-dev libxrender-dev
+
+ - name: Install build tools
+ run: python tools/requirements.py
+
+ - name: Prepare prebuilt CEF
+ run: python tools/automate.py --prebuilt-cef
+
+ - name: Configure CMake
+ run: cmake -S . -B build/_cmake_build -G Ninja -DCMAKE_BUILD_TYPE=Release
+
+ - name: Build
+ run: cmake --build build/_cmake_build --parallel
+
+ - name: Stage build outputs
+ run: |
+ mkdir -p build/artifacts
+ cp build/_cmake_build/cefpython_py*.so build/artifacts/
+ cp build/_cmake_build/subprocess_build/subprocess build/artifacts/
+ cef_dir=$(ls -d build/cef*_linux64 2>/dev/null | head -1)
+ find "$cef_dir/bin" -maxdepth 1 -mindepth 1 \
+ ! -name 'cefclient*' ! -name 'cefsimple*' ! -name 'ceftests*' ! -name 'chrome-sandbox' \
+ -exec cp -r {} build/artifacts/ \;
+
+ - name: Install runtime dependencies
+ run: |
+ sudo apt-get install -y --no-install-recommends \
+ libnss3 libatk1.0-0 libatk-bridge2.0-0 \
+ libx11-6 libxcomposite1 libxdamage1 libxext6 \
+ libxfixes3 libxrandr2 libxrender1 \
+ libgtk2.0-0 libglib2.0-0 xvfb
+
+ - name: Set up cefpython3 package
+ run: |
+ cp -r build/artifacts/. cefpython3/
+ chmod +x cefpython3/subprocess
+
+ - name: Resize /dev/shm for Chromium shared memory
+ run: sudo mount -o remount,size=512m /dev/shm
+
+ - name: Run unit tests
+ run: xvfb-run python unittests/_test_runner.py
+ env:
+ PYTHONPATH: ${{ github.workspace }}
+
+ - name: Build wheel
+ run: python tools/build_distrib.py
+
+ - name: Upload wheel artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: cefpython3-py${{ matrix.python-version }}-linux64
+ path: build/dist/*.whl
diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml
new file mode 100644
index 000000000..7bb7d060e
--- /dev/null
+++ b/.github/workflows/ci-macos.yml
@@ -0,0 +1,237 @@
+name: CI macOS ARM
+
+on:
+ push:
+ branches: ["master", "cefpython147"]
+ pull_request:
+ branches: ["master"]
+ workflow_dispatch:
+ inputs:
+ bypass_cache:
+ description: "Bypass all caches for a clean run"
+ type: boolean
+ default: false
+
+jobs:
+ download-cef:
+ runs-on: macos-14
+ timeout-minutes: 15
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+
+ - name: Read CEF version
+ id: cef-version
+ run: |
+ ver=$(python3 -c "
+ import re, sys
+ h = open('src/version/cef_version_macarm64.h').read()
+ m = re.search(r'#define CEF_VERSION \"([^\"]+)\"', h)
+ print(m.group(1))
+ ")
+ echo "value=$ver" >> $GITHUB_OUTPUT
+
+ - name: Cache CEF binaries
+ uses: actions/cache@v4
+ if: ${{ inputs.bypass_cache != true }}
+ with:
+ path: |
+ build/cef_binary_*
+ build/cef*_macarm64
+ key: cef-macosarm64-v3-${{ steps.cef-version.outputs.value }}
+
+ - name: Install build tools
+ run: python tools/requirements.py
+
+ - name: Download CEF binaries
+ run: python tools/download_cef.py
+
+ compile:
+ needs: download-cef
+ runs-on: macos-14
+ timeout-minutes: 90
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Read CEF version
+ id: cef-version
+ run: |
+ ver=$(python3 -c "
+ import re, sys
+ h = open('src/version/cef_version_macarm64.h').read()
+ m = re.search(r'#define CEF_VERSION \"([^\"]+)\"', h)
+ print(m.group(1))
+ ")
+ echo "value=$ver" >> $GITHUB_OUTPUT
+
+ - name: Restore CEF cache
+ uses: actions/cache/restore@v4
+ if: ${{ inputs.bypass_cache != true }}
+ with:
+ path: |
+ build/cef_binary_*
+ build/cef*_macarm64
+ key: cef-macosarm64-v3-${{ steps.cef-version.outputs.value }}
+
+ - name: Install build tools
+ run: python tools/requirements.py
+
+ - name: Prepare prebuilt CEF
+ run: python tools/automate.py --prebuilt-cef
+
+ - name: Verify CEF architecture
+ run: |
+ wrapper=$(ls build/cef*_macarm64/lib/libcef_dll_wrapper.a 2>/dev/null | head -1)
+ if [ -z "$wrapper" ]; then echo "libcef_dll_wrapper.a not found"; exit 1; fi
+ archs=$(lipo -info "$wrapper" 2>&1)
+ echo "$archs"
+ echo "$archs" | grep -q arm64 || { echo "ERROR: libcef_dll_wrapper.a is not arm64"; exit 1; }
+
+ - name: Configure CMake
+ run: cmake -S . -B build/_cmake_build -G Ninja -DCMAKE_BUILD_TYPE=Release
+
+ - name: Build
+ run: cmake --build build/_cmake_build --parallel
+
+ - name: Stage build outputs
+ run: |
+ mkdir -p build/artifacts
+ cp build/_cmake_build/cefpython_py*.so build/artifacts/
+ cp build/_cmake_build/subprocess_build/subprocess build/artifacts/
+ cef_dir=$(ls -d build/cef*_macarm64 2>/dev/null | head -1)
+ find "$cef_dir/bin" -maxdepth 1 -mindepth 1 \
+ ! -name 'cefclient*' ! -name 'cefsimple*' ! -name 'ceftests*' \
+ -exec cp -r {} build/artifacts/ \;
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-py${{ matrix.python-version }}-macosarm64
+ path: build/artifacts/
+ retention-days: 1
+
+ test:
+ needs: compile
+ runs-on: macos-14
+ timeout-minutes: 30
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Download build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: build-py${{ matrix.python-version }}-macosarm64
+ path: build/artifacts/
+
+ - name: Set up cefpython3 package for testing
+ run: |
+ cp -r build/artifacts/. cefpython3/
+ chmod +x cefpython3/subprocess
+ # Ad-hoc sign our compiled binaries so macOS allows them to run.
+ # (Our freshly built binaries have no signature; unsigned binaries
+ # are blocked on macOS 14+.)
+ codesign --force --sign - cefpython3/subprocess
+ for f in cefpython3/cefpython_py*.so; do codesign --force --sign - "$f"; done
+
+ - name: Run unit tests
+ run: |
+ # Create a minimal app bundle so Python has a CFBundleIdentifier.
+ # CEF 130+ forms the MachPortRendezvousServer bootstrap service name
+ # as BaseBundleID()+".MachPortRendezvousServer."+pid. Without a
+ # bundle ID the name starts with "." which bootstrap_register rejects,
+ # causing renderer subprocesses to crash. Setting CFProcessPath
+ # before Python starts makes CFBundleGetMainBundle() return this
+ # bundle, giving BaseBundleID() a valid value before CefInitialize().
+ BUNDLE_DIR="${{ github.workspace }}/CEFPython.app"
+ mkdir -p "$BUNDLE_DIR/Contents/MacOS"
+ ln -sf "$(which python)" "$BUNDLE_DIR/Contents/MacOS/python"
+ cat > "$BUNDLE_DIR/Contents/Info.plist" << 'PLIST'
+
+
+
+
+ CFBundleIdentifier
+ org.cefpython
+ CFBundleName
+ CEFPython
+ CFBundleExecutable
+ python
+
+
+ PLIST
+ PYTHONPATH="${{ github.workspace }}" \
+ CFProcessPath="$BUNDLE_DIR/Contents/MacOS/python" \
+ python unittests/_test_runner.py
+
+ wheel:
+ needs: test
+ runs-on: macos-14
+ timeout-minutes: 15
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Download build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: build-py${{ matrix.python-version }}-macosarm64
+ path: build/artifacts/
+
+ - name: Set up cefpython3 package
+ run: cp -r build/artifacts/. cefpython3/
+
+ - name: Codesign binaries for distribution
+ run: |
+ # Ad-hoc sign the subprocess binary so macOS 14+ Gatekeeper allows
+ # it to execute when users install the wheel. Without signing,
+ # macOS blocks the unsigned binary and the GPU process (which uses
+ # the same binary with --type=gpu-process) exits with code 1003
+ # (GPU_DEAD_ON_ARRIVAL), causing a fatal crash.
+ codesign --force --sign - cefpython3/subprocess
+ for f in cefpython3/cefpython_py*.so; do codesign --force --sign - "$f"; done
+
+ - name: Build wheel
+ run: python tools/build_distrib.py
+
+ - name: Upload wheel artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: cefpython3-py${{ matrix.python-version }}-macosarm64
+ path: build/dist/*.whl
diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml
new file mode 100644
index 000000000..399ad0f98
--- /dev/null
+++ b/.github/workflows/ci-windows.yml
@@ -0,0 +1,123 @@
+name: CI Windows
+
+on:
+ push:
+ branches: [ "master", "cefpython147" ]
+ pull_request:
+ branches: [ "master" ]
+ workflow_dispatch:
+ inputs:
+ bypass_cache:
+ description: "Bypass all caches for a clean run"
+ type: boolean
+ default: false
+
+jobs:
+ download-cef:
+ runs-on: windows-latest
+ timeout-minutes: 15
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+
+ - name: Read CEF version
+ id: cef-version
+ run: |
+ $ver = (Select-String -Path src/version/cef_version_win.h `
+ -Pattern '#define CEF_VERSION "([^"]+)"').Matches[0].Groups[1].Value
+ echo "value=$ver" >> $env:GITHUB_OUTPUT
+
+ - name: Cache CEF binaries
+ uses: actions/cache@v4
+ if: ${{ inputs.bypass_cache != true }}
+ with:
+ path: |
+ build/cef_binary_*
+ build/cef*_win64
+ key: cef-windows64-${{ steps.cef-version.outputs.value }}
+
+ - name: Install build tools
+ run: python tools/requirements.py
+
+ - name: Download CEF binaries
+ run: python tools/download_cef.py
+
+ wheel:
+ needs: download-cef
+ runs-on: windows-latest
+ timeout-minutes: 75
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Read CEF version
+ id: cef-version
+ run: |
+ $ver = (Select-String -Path src/version/cef_version_win.h `
+ -Pattern '#define CEF_VERSION "([^"]+)"').Matches[0].Groups[1].Value
+ echo "value=$ver" >> $env:GITHUB_OUTPUT
+
+ - name: Restore CEF cache
+ uses: actions/cache/restore@v4
+ if: ${{ inputs.bypass_cache != true }}
+ with:
+ path: |
+ build/cef_binary_*
+ build/cef*_win64
+ key: cef-windows64-${{ steps.cef-version.outputs.value }}
+
+ - name: Install build tools
+ run: python tools/requirements.py
+
+ - name: Prepare prebuilt CEF
+ run: python tools/automate.py --prebuilt-cef
+
+ - name: Configure CMake
+ run: cmake -S . -B build/_cmake_build -A x64
+
+ - name: Build
+ run: cmake --build build/_cmake_build --config Release --parallel
+
+ - name: Stage build outputs
+ run: |
+ New-Item -ItemType Directory -Force build/artifacts
+ Copy-Item build/_cmake_build/Release/cefpython_py*.pyd build/artifacts/
+ Copy-Item build/_cmake_build/subprocess_build/Release/subprocess.exe build/artifacts/
+ $cefBin = (Get-ChildItem build/cef*_win64 -Directory | Select-Object -First 1).FullName + "/bin"
+ Get-ChildItem $cefBin | Where-Object { $_.Name -notmatch '^(cefclient|cefsimple|ceftests)' } |
+ ForEach-Object { Copy-Item $_.FullName build/artifacts/ -Recurse -Force }
+
+ - name: Set up cefpython3 package
+ run: |
+ Get-ChildItem build/artifacts/ | ForEach-Object {
+ Copy-Item $_.FullName cefpython3/ -Recurse -Force
+ }
+
+ - name: Run unit tests
+ run: python unittests/_test_runner.py
+ env:
+ PYTHONPATH: ${{ github.workspace }}
+
+ - name: Build wheel
+ run: python tools/build_distrib.py
+
+ - name: Upload wheel artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: cefpython3-py${{ matrix.python-version }}-win64
+ path: build/dist/*.whl
diff --git a/.gitignore b/.gitignore
index b786188bc..e377f35ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,22 @@
.idea/
build/
dist/
+cefpython3/*.pyd
+cefpython3/*.dll
+cefpython3/*.exe
+cefpython3/*.so
+cefpython3/*.so.*
+cefpython3/subprocess
+cefpython3/chrome_crashpad_handler
+cefpython3/Chromium Embedded Framework.framework/
+cefpython3/*.pak
+cefpython3/*.dat
+cefpython3/*.bin
+cefpython3/*.json
+cefpython3/locales/
+_cmake_test/
+_skbuild/
+venv/
*.log
__pycache__/
*.pyc
@@ -16,3 +32,6 @@ unittests/GPUCache/
unittests/blob_storage/
unittests/webrtc_event_logs/
.DS_Store
+MediaDeviceSalts
+MediaDeviceSalts-journal
+.claude/
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000..777e19134
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,285 @@
+cmake_minimum_required(VERSION 3.21)
+
+# Read CEF major version from the Windows header (used on all platforms at
+# configure time; the per-platform header values are identical for this field).
+file(STRINGS "src/version/cef_version_win.h" _ver_line
+ REGEX "^#define CHROME_VERSION_MAJOR ")
+string(REGEX REPLACE "^#define CHROME_VERSION_MAJOR ([0-9]+).*" "\\1" _chrome_major "${_ver_line}")
+
+project(cefpython3 VERSION "${_chrome_major}.0" LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+# ---- Python -----------------------------------------------------------------
+find_package(Python REQUIRED COMPONENTS Interpreter Development.Module)
+
+# "310" for Python 3.10, "314" for Python 3.14, etc.
+string(CONCAT PYVERSION "${Python_VERSION_MAJOR}" "${Python_VERSION_MINOR}")
+set(MODULE_NAME "cefpython_py${PYVERSION}")
+message(STATUS "Python ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} -> module: ${MODULE_NAME}")
+
+# ---- Cython -----------------------------------------------------------------
+find_program(CYTHON_EXECUTABLE NAMES cython cython3 REQUIRED)
+message(STATUS "Cython: ${CYTHON_EXECUTABLE}")
+
+option(ENABLE_PROFILING "Cython: enable cProfile instrumentation (profile=True)" OFF)
+option(ENABLE_LINE_TRACING "Cython: enable line-level tracing/coverage (linetrace=True)" OFF)
+
+set(_cython_directive_args "")
+if(ENABLE_PROFILING)
+ list(APPEND _cython_directive_args --directive profile=True)
+endif()
+if(ENABLE_LINE_TRACING)
+ list(APPEND _cython_directive_args --directive linetrace=True)
+endif()
+
+# ---- CEF location -----------------------------------------------------------
+if(NOT CEF_ROOT)
+ # Auto-detect: build/cef*_win64 (versioned dir created by automate.py)
+ if(WIN32)
+ set(_cef_glob_pat "${CMAKE_SOURCE_DIR}/build/cef*_win64")
+ elseif(APPLE)
+ set(_cef_glob_pat "${CMAKE_SOURCE_DIR}/build/cef*_macarm64")
+ else()
+ set(_cef_glob_pat "${CMAKE_SOURCE_DIR}/build/cef*_linux64")
+ endif()
+ file(GLOB _cef_candidates LIST_DIRECTORIES true "${_cef_glob_pat}")
+ # Prefer the versioned directory over cef_ fallback
+ foreach(_d IN LISTS _cef_candidates)
+ if(IS_DIRECTORY "${_d}")
+ set(CEF_ROOT "${_d}")
+ break()
+ endif()
+ endforeach()
+ if(NOT CEF_ROOT)
+ message(FATAL_ERROR
+ "CEF_ROOT not set and cannot be auto-detected. "
+ "Download and prepare CEF first (python tools/download_cef.py && "
+ "python tools/automate.py --prebuilt-cef), or pass -DCEF_ROOT=.")
+ endif()
+endif()
+message(STATUS "CEF_ROOT: ${CEF_ROOT}")
+
+set(SRC_DIR "${CMAKE_SOURCE_DIR}/src")
+set(PYX_STAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pyx_stage")
+
+# ---- Prepare .pyx files -----------------------------------------------------
+# Copies src/*.pyx + src/handlers/*.pyx to PYX_STAGE_DIR, flattening the
+# include paths and injecting version variables into the main file.
+if(WIN32)
+ set(_cef_ver_header "${SRC_DIR}/version/cef_version_win.h")
+elseif(APPLE)
+ set(_cef_ver_header "${SRC_DIR}/version/cef_version_macarm64.h")
+else()
+ set(_cef_ver_header "${SRC_DIR}/version/cef_version_linux.h")
+endif()
+
+file(GLOB _pyx_src_files
+ "${SRC_DIR}/*.pyx"
+ "${SRC_DIR}/handlers/*.pyx"
+)
+set(MAIN_PYX "${PYX_STAGE_DIR}/${MODULE_NAME}.pyx")
+
+add_custom_command(
+ OUTPUT "${MAIN_PYX}"
+ COMMAND "${Python_EXECUTABLE}"
+ "${CMAKE_SOURCE_DIR}/tools/cmake_prepare_pyx.py"
+ --src "${SRC_DIR}"
+ --out "${PYX_STAGE_DIR}"
+ --pyversion "${PYVERSION}"
+ --cef-version-header "${_cef_ver_header}"
+ DEPENDS
+ ${_pyx_src_files}
+ "${CMAKE_SOURCE_DIR}/tools/cmake_prepare_pyx.py"
+ COMMENT "Preparing .pyx files -> ${PYX_STAGE_DIR}"
+ VERBATIM
+)
+add_custom_target(cefpython_pyx DEPENDS "${MAIN_PYX}")
+
+# ---- Transpile pyx -> cpp ----------------------------------------------------
+set(CEFPYTHON_CPP "${PYX_STAGE_DIR}/${MODULE_NAME}.cpp")
+set(CEFPYTHON_H "${PYX_STAGE_DIR}/${MODULE_NAME}.h")
+
+set(_cython_inc_args
+ -I "${SRC_DIR}"
+ -I "${SRC_DIR}/common"
+ -I "${SRC_DIR}/extern"
+ -I "${SRC_DIR}/extern/cef"
+)
+
+add_custom_command(
+ OUTPUT "${CEFPYTHON_CPP}" "${CEFPYTHON_H}"
+ COMMAND "${CYTHON_EXECUTABLE}"
+ --cplus --3str
+ ${_cython_inc_args}
+ ${_cython_directive_args}
+ -o "${CEFPYTHON_CPP}"
+ "${MAIN_PYX}"
+ DEPENDS "${MAIN_PYX}"
+ COMMENT "Cython: ${MODULE_NAME}.pyx -> C++"
+ VERBATIM
+)
+
+# ---- Fix Cython API header --------------------------------------------------
+# Generates:
+# cefpython_pyXX_fixed.h – original .h with #pragma warning(disable:4190)
+# cefpython_api_fixed.h – stable single-name wrapper for cefpython_public_api.h
+set(CEFPYTHON_H_FIXED "${PYX_STAGE_DIR}/${MODULE_NAME}_fixed.h")
+set(CEF_API_FIXED_H "${PYX_STAGE_DIR}/cefpython_api_fixed.h")
+
+add_custom_command(
+ OUTPUT "${CEFPYTHON_H_FIXED}" "${CEF_API_FIXED_H}"
+ COMMAND "${Python_EXECUTABLE}"
+ "${CMAKE_SOURCE_DIR}/tools/cmake_fix_header.py"
+ "${CEFPYTHON_H}"
+ "${CEFPYTHON_H_FIXED}"
+ "${CEF_API_FIXED_H}"
+ "${MODULE_NAME}"
+ DEPENDS "${CEFPYTHON_H}" "${CMAKE_SOURCE_DIR}/tools/cmake_fix_header.py"
+ COMMENT "Generating ${MODULE_NAME}_fixed.h + cefpython_api_fixed.h"
+ VERBATIM
+)
+add_custom_target(cefpython_headers
+ DEPENDS "${CEFPYTHON_H_FIXED}" "${CEF_API_FIXED_H}")
+
+# ---- Static C++ libraries ---------------------------------------------------
+# All three libs share these settings so pass them via cache variables that
+# the subdirectory CMakeLists files can read.
+set(CEFPYTHON_PYX_STAGE_DIR "${PYX_STAGE_DIR}" CACHE INTERNAL "")
+set(CEFPYTHON_CEF_ROOT "${CEF_ROOT}" CACHE INTERNAL "")
+set(CEFPYTHON_SRC_DIR "${SRC_DIR}" CACHE INTERNAL "")
+
+add_subdirectory("${SRC_DIR}/client_handler"
+ "${CMAKE_CURRENT_BINARY_DIR}/client_handler")
+add_subdirectory("${SRC_DIR}/subprocess"
+ "${CMAKE_CURRENT_BINARY_DIR}/subprocess_build")
+add_subdirectory("${SRC_DIR}/cpp_utils"
+ "${CMAKE_CURRENT_BINARY_DIR}/cpp_utils")
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
+endif()
+
+# ---- Python extension -------------------------------------------------------
+Python_add_library(${MODULE_NAME} MODULE "${CEFPYTHON_CPP}")
+add_dependencies(${MODULE_NAME} cefpython_headers)
+
+target_include_directories(${MODULE_NAME} PRIVATE
+ "${SRC_DIR}"
+ "${SRC_DIR}/common"
+ "${SRC_DIR}/extern"
+ "${SRC_DIR}/extern/cef"
+ "${PYX_STAGE_DIR}"
+)
+
+if(ENABLE_LINE_TRACING)
+ target_compile_definitions(${MODULE_NAME} PRIVATE CYTHON_TRACE=1)
+endif()
+
+if(WIN32)
+ target_include_directories(${MODULE_NAME} PRIVATE "${SRC_DIR}/windows")
+ target_compile_options(${MODULE_NAME} PRIVATE /EHsc /wd4305 /wd4190)
+ target_compile_definitions(${MODULE_NAME} PRIVATE
+ WIN32 _WIN32 _WINDOWS
+ NTDDI_VERSION=0x06010000 WINVER=0x0601 _WIN32_WINNT=0x0601
+ NDEBUG _NDEBUG _CRT_SECURE_NO_WARNINGS NOMINMAX
+ BROWSER_PROCESS
+ CEFPYTHON_API_H_FILE="cefpython_api_fixed.h"
+ )
+ target_link_libraries(${MODULE_NAME} PRIVATE
+ cefpython_app
+ client_handler
+ cpp_utils
+ "${CEF_ROOT}/lib/libcef.lib"
+ "${CEF_ROOT}/lib/VS2015/libcef_dll_wrapper_MD.lib"
+ User32
+ )
+ target_link_options(${MODULE_NAME} PRIVATE /ignore:4217)
+elseif(APPLE)
+ target_compile_options(${MODULE_NAME} PRIVATE
+ -DNDEBUG -O3
+ -Wno-return-type-c-linkage -Wno-constant-logical-operand
+ -stdlib=libc++ -fno-strict-aliasing -fno-rtti
+ -fno-threadsafe-statics -fobjc-call-cxx-cdtors
+ -fvisibility=hidden -fvisibility-inlines-hidden
+ )
+ target_compile_definitions(${MODULE_NAME} PRIVATE
+ BROWSER_PROCESS
+ CEFPYTHON_API_H_FILE="cefpython_api_fixed.h"
+ )
+ target_link_options(${MODULE_NAME} PRIVATE
+ -mmacosx-version-min=11.0
+ -Wl,-search_paths_first -Wl,-dead_strip
+ "-F${CEF_ROOT}/bin" "-framework" "Chromium Embedded Framework"
+ "-Wl,-rpath,@loader_path/"
+ )
+ target_link_libraries(${MODULE_NAME} PRIVATE
+ c++ c++abi
+ cefpython_app client_handler cpp_utils
+ "${CEF_ROOT}/lib/libcef_dll_wrapper.a"
+ )
+else()
+ target_compile_options(${MODULE_NAME} PRIVATE
+ -DNDEBUG -O3
+ -flto -fdata-sections -ffunction-sections
+ -Wno-stringop-overflow
+ )
+ target_compile_definitions(${MODULE_NAME} PRIVATE
+ BROWSER_PROCESS
+ CEFPYTHON_API_H_FILE="cefpython_api_fixed.h"
+ )
+ target_include_directories(${MODULE_NAME} PRIVATE
+ ${GTK3_INCLUDE_DIRS}
+ )
+ target_link_options(${MODULE_NAME} PRIVATE
+ -flto -Wl,--gc-sections
+ -Wno-stringop-overflow
+ )
+ target_link_libraries(${MODULE_NAME} PRIVATE
+ X11 ${GTK3_LIBRARIES}
+ cefpython_app client_handler cpp_utils
+ "${CEF_ROOT}/lib/libcef_dll_wrapper.a"
+ )
+endif()
+
+# ---- Install ----------------------------------------------------------------
+# The extension module itself
+install(TARGETS ${MODULE_NAME}
+ LIBRARY DESTINATION "cefpython3"
+ RUNTIME DESTINATION "cefpython3"
+)
+
+# subprocess executable (OUTPUT_NAME "subprocess" is set in src/subprocess/CMakeLists.txt)
+install(TARGETS cefpython_subprocess
+ RUNTIME DESTINATION "cefpython3"
+)
+
+# CEF runtime files (DLLs, .pak, .dat, locales/, etc.)
+# Sample apps (cefclient, cefsimple, ceftests) are excluded.
+if(WIN32)
+ install(DIRECTORY "${CEF_ROOT}/bin/"
+ DESTINATION "cefpython3"
+ PATTERN "cefclient*" EXCLUDE
+ PATTERN "cefsimple*" EXCLUDE
+ PATTERN "ceftests*" EXCLUDE
+ PATTERN "chrome-sandbox" EXCLUDE
+ )
+elseif(APPLE)
+ install(DIRECTORY "${CEF_ROOT}/bin/"
+ DESTINATION "cefpython3"
+ PATTERN "cefclient*" EXCLUDE
+ PATTERN "cefsimple*" EXCLUDE
+ PATTERN "ceftests*" EXCLUDE
+ )
+else()
+ install(DIRECTORY "${CEF_ROOT}/bin/"
+ DESTINATION "cefpython3"
+ PATTERN "cefclient*" EXCLUDE
+ PATTERN "cefsimple*" EXCLUDE
+ PATTERN "ceftests*" EXCLUDE
+ PATTERN "chrome-sandbox" EXCLUDE
+ )
+endif()
diff --git a/README.md b/README.md
index 4ff688b09..4fa42ac40 100644
--- a/README.md
+++ b/README.md
@@ -53,11 +53,11 @@ You can also download packages for offline installation available on the [GitHub
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+
+OS | Python | 32bit | 64bit | Requirements
+--- | --- | --- | --- | ---
+Windows | 3.10 / 3.11 / 3.12 / 3.13 / 3.14 | No | Yes | Windows 10+
+Linux | 3.10 / 3.11 / 3.12 / 3.13 / 3.14 | No | Yes | Ubuntu 20.04+, Debian 11+
+Mac | 3.10 / 3.11 / 3.12 / 3.13 / 3.14 | No | Yes | macOS 10.15+
## Examples
@@ -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
@@ -195,7 +195,6 @@ at this moment.
* [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
@@ -230,7 +229,6 @@ at this moment.
* [_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)
* [auto_zooming](api/ApplicationSettings.md#auto_zooming)
* [background_color](api/ApplicationSettings.md#background_color)
@@ -241,7 +239,6 @@ at this moment.
* [downloads_enabled](api/ApplicationSettings.md#downloads_enabled)
* [external_message_pump](api/ApplicationSettings.md#external_message_pump)
* [framework_dir_path](api/ApplicationSettings.md#framework_dir_path)
- * [ignore_certificate_errors](api/ApplicationSettings.md#ignore_certificate_errors)
* [javascript_flags](api/ApplicationSettings.md#javascript_flags)
* [locale](api/ApplicationSettings.md#locale)
* [locales_dir_path](api/ApplicationSettings.md#locales_dir_path)
@@ -261,7 +258,6 @@ at this moment.
* [uncaught_exception_stack_size](api/ApplicationSettings.md#uncaught_exception_stack_size)
* [unique_request_context_per_browser](api/ApplicationSettings.md#unique_request_context_per_browser)
* [user_agent](api/ApplicationSettings.md#user_agent)
- * [user_data_path](api/ApplicationSettings.md#user_data_path)
* [windowless_rendering_enabled](api/ApplicationSettings.md#windowless_rendering_enabled)
* [Browser (object)](api/Browser.md#browser-object)
* [AddWordToDictionary](api/Browser.md#addwordtodictionary)
@@ -308,7 +304,6 @@ at this moment.
* [Invalidate](api/Browser.md#invalidate)
* [IsFullscreen](api/Browser.md#isfullscreen)
* [IsLoading](api/Browser.md#isloading)
- * [IsMouseCursorChangeDisabled](api/Browser.md#ismousecursorchangedisabled)
* [IsPopup](api/Browser.md#ispopup)
* [IsWindowRenderingDisabled](api/Browser.md#iswindowrenderingdisabled)
* [LoadUrl](api/Browser.md#loadurl)
@@ -326,13 +321,11 @@ at this moment.
* [SendMouseClickEvent](api/Browser.md#sendmouseclickevent)
* [SendMouseMoveEvent](api/Browser.md#sendmousemoveevent)
* [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)
- * [SetMouseCursorChangeDisabled](api/Browser.md#setmousecursorchangedisabled)
* [SetJavascriptBindings](api/Browser.md#setjavascriptbindings)
* [SetUserData](api/Browser.md#setuserdata)
* [SetZoomLevel](api/Browser.md#setzoomlevel)
@@ -346,27 +339,22 @@ at this moment.
* [WasHidden](api/Browser.md#washidden)
* [Browser settings](api/BrowserSettings.md#browser-settings)
* [Font settings](api/BrowserSettings.md#font-settings)
- * [accept_language_list](api/BrowserSettings.md#accept_language_list)
* [application_cache_disabled](api/BrowserSettings.md#application_cache_disabled)
* [background_color](api/BrowserSettings.md#background_color)
* [databases_disabled](api/BrowserSettings.md#databases_disabled)
* [default_encoding](api/BrowserSettings.md#default_encoding)
* [dom_paste_disabled](api/BrowserSettings.md#dom_paste_disabled)
- * [file_access_from_file_urls_allowed](api/BrowserSettings.md#file_access_from_file_urls_allowed)
* [inherit_client_handlers_for_popups](api/BrowserSettings.md#inherit_client_handlers_for_popups)
* [image_load_disabled](api/BrowserSettings.md#image_load_disabled)
* [javascript_disabled](api/BrowserSettings.md#javascript_disabled)
* [javascript_close_windows_disallowed](api/BrowserSettings.md#javascript_close_windows_disallowed)
* [javascript_access_clipboard_disallowed](api/BrowserSettings.md#javascript_access_clipboard_disallowed)
* [local_storage_disabled](api/BrowserSettings.md#local_storage_disabled)
- * [plugins_disabled](api/BrowserSettings.md#plugins_disabled)
* [remote_fonts](api/BrowserSettings.md#remote_fonts)
* [shrink_standalone_images_to_fit](api/BrowserSettings.md#shrink_standalone_images_to_fit)
* [tab_to_links_disabled](api/BrowserSettings.md#tab_to_links_disabled)
* [text_area_resize_disabled](api/BrowserSettings.md#text_area_resize_disabled)
- * [universal_access_from_file_urls_allowed](api/BrowserSettings.md#universal_access_from_file_urls_allowed)
* [user_style_sheet_location](api/BrowserSettings.md#user_style_sheet_location)
- * [web_security_disabled](api/BrowserSettings.md#web_security_disabled)
* [webgl_disabled](api/BrowserSettings.md#webgl_disabled)
* [windowless_frame_rate](api/BrowserSettings.md#windowless_frame_rate)
* [Callback (object)](api/Callback.md#callback-object)
@@ -427,9 +415,6 @@ at this moment.
* [GetExpires](api/Cookie.md#getexpires)
* [CookieManager (class)](api/CookieManager.md#cookiemanager-class)
* [GetGlobalManager](api/CookieManager.md#getglobalmanager)
- * [GetBlockingManager](api/CookieManager.md#getblockingmanager)
- * [CreateManager](api/CookieManager.md#createmanager)
- * [SetSupportedSchemes](api/CookieManager.md#setsupportedschemes)
* [VisitAllCookies](api/CookieManager.md#visitallcookies)
* [VisitUrlCookies](api/CookieManager.md#visiturlcookies)
* [SetCookie](api/CookieManager.md#setcookie)
@@ -449,7 +434,6 @@ at this moment.
* [DownloadHandler](api/DownloadHandler.md#downloadhandler)
* [DpiAware (class)](api/DpiAware.md#dpiaware-class)
* [CalculateWindowSize](api/DpiAware.md#calculatewindowsize)
- * [EnableHighDpiSupport](api/DpiAware.md#enablehighdpisupport)
* [GetSystemDpi](api/DpiAware.md#getsystemdpi)
* [IsProcessDpiAware](api/DpiAware.md#isprocessdpiaware)
* [SetProcessDpiAware](api/DpiAware.md#setprocessdpiaware)
@@ -616,10 +600,7 @@ at this moment.
* [GetResourceType](api/Request.md#getresourcetype)
* [GetTransitionType](api/Request.md#gettransitiontype)
* [RequestHandler (interface)](api/RequestHandler.md#requesthandler-interface)
- * [CanGetCookies](api/RequestHandler.md#cangetcookies)
- * [CanSetCookie](api/RequestHandler.md#cansetcookie)
* [GetAuthCredentials](api/RequestHandler.md#getauthcredentials)
- * [GetCookieManager](api/RequestHandler.md#getcookiemanager)
* [GetResourceHandler](api/RequestHandler.md#getresourcehandler)
* [OnBeforeBrowse](api/RequestHandler.md#onbeforebrowse)
* [_OnBeforePluginLoad](api/RequestHandler.md#_onbeforepluginload)
@@ -657,11 +638,6 @@ at this moment.
* [OnContextCreated](api/V8ContextHandler.md#oncontextcreated)
* [OnContextReleased](api/V8ContextHandler.md#oncontextreleased)
* [Virtual Key codes](api/VirtualKey.md#virtual-key-codes)
-* [WebPluginInfo (object)](api/WebPluginInfo.md#webplugininfo-object)
- * [GetName](api/WebPluginInfo.md#getname)
- * [GetPath](api/WebPluginInfo.md#getpath)
- * [GetVersion](api/WebPluginInfo.md#getversion)
- * [GetDescription](api/WebPluginInfo.md#getdescription)
* [WebRequest (class)](api/WebRequest.md#webrequest-class)
* [Create](api/WebRequest.md#create)
* [GetRequest](api/WebRequest.md#getrequest)
diff --git a/api/API-categories.md b/api/API-categories.md
index d1d7320e8..024694cd6 100644
--- a/api/API-categories.md
+++ b/api/API-categories.md
@@ -30,7 +30,6 @@
* [PaintBuffer](PaintBuffer.md#paintbuffer-object) object
* [Request](Request.md#request-class) class
* [Response](Response.md#response-object) object
- * [WebPluginInfo](WebPluginInfo.md#webplugininfo-object) object
* [WebRequest](WebRequest.md#webrequest-class) class
* [WindowInfo](WindowInfo.md#windowinfo-class) class
* [WindowUtils](WindowUtils.md#windowutils-class) class
diff --git a/api/API-index.md b/api/API-index.md
index 9535cb5b7..f69893db4 100644
--- a/api/API-index.md
+++ b/api/API-index.md
@@ -6,7 +6,6 @@
* [_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)
* [auto_zooming](ApplicationSettings.md#auto_zooming)
* [background_color](ApplicationSettings.md#background_color)
@@ -17,7 +16,6 @@
* [downloads_enabled](ApplicationSettings.md#downloads_enabled)
* [external_message_pump](ApplicationSettings.md#external_message_pump)
* [framework_dir_path](ApplicationSettings.md#framework_dir_path)
- * [ignore_certificate_errors](ApplicationSettings.md#ignore_certificate_errors)
* [javascript_flags](ApplicationSettings.md#javascript_flags)
* [locale](ApplicationSettings.md#locale)
* [locales_dir_path](ApplicationSettings.md#locales_dir_path)
@@ -37,7 +35,6 @@
* [uncaught_exception_stack_size](ApplicationSettings.md#uncaught_exception_stack_size)
* [unique_request_context_per_browser](ApplicationSettings.md#unique_request_context_per_browser)
* [user_agent](ApplicationSettings.md#user_agent)
- * [user_data_path](ApplicationSettings.md#user_data_path)
* [windowless_rendering_enabled](ApplicationSettings.md#windowless_rendering_enabled)
* [Browser (object)](Browser.md#browser-object)
* [AddWordToDictionary](Browser.md#addwordtodictionary)
@@ -84,7 +81,6 @@
* [Invalidate](Browser.md#invalidate)
* [IsFullscreen](Browser.md#isfullscreen)
* [IsLoading](Browser.md#isloading)
- * [IsMouseCursorChangeDisabled](Browser.md#ismousecursorchangedisabled)
* [IsPopup](Browser.md#ispopup)
* [IsWindowRenderingDisabled](Browser.md#iswindowrenderingdisabled)
* [LoadUrl](Browser.md#loadurl)
@@ -102,13 +98,11 @@
* [SendMouseClickEvent](Browser.md#sendmouseclickevent)
* [SendMouseMoveEvent](Browser.md#sendmousemoveevent)
* [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)
- * [SetMouseCursorChangeDisabled](Browser.md#setmousecursorchangedisabled)
* [SetJavascriptBindings](Browser.md#setjavascriptbindings)
* [SetUserData](Browser.md#setuserdata)
* [SetZoomLevel](Browser.md#setzoomlevel)
@@ -122,27 +116,22 @@
* [WasHidden](Browser.md#washidden)
* [Browser settings](BrowserSettings.md#browser-settings)
* [Font settings](BrowserSettings.md#font-settings)
- * [accept_language_list](BrowserSettings.md#accept_language_list)
* [application_cache_disabled](BrowserSettings.md#application_cache_disabled)
* [background_color](BrowserSettings.md#background_color)
* [databases_disabled](BrowserSettings.md#databases_disabled)
* [default_encoding](BrowserSettings.md#default_encoding)
* [dom_paste_disabled](BrowserSettings.md#dom_paste_disabled)
- * [file_access_from_file_urls_allowed](BrowserSettings.md#file_access_from_file_urls_allowed)
* [inherit_client_handlers_for_popups](BrowserSettings.md#inherit_client_handlers_for_popups)
* [image_load_disabled](BrowserSettings.md#image_load_disabled)
* [javascript_disabled](BrowserSettings.md#javascript_disabled)
* [javascript_close_windows_disallowed](BrowserSettings.md#javascript_close_windows_disallowed)
* [javascript_access_clipboard_disallowed](BrowserSettings.md#javascript_access_clipboard_disallowed)
* [local_storage_disabled](BrowserSettings.md#local_storage_disabled)
- * [plugins_disabled](BrowserSettings.md#plugins_disabled)
* [remote_fonts](BrowserSettings.md#remote_fonts)
* [shrink_standalone_images_to_fit](BrowserSettings.md#shrink_standalone_images_to_fit)
* [tab_to_links_disabled](BrowserSettings.md#tab_to_links_disabled)
* [text_area_resize_disabled](BrowserSettings.md#text_area_resize_disabled)
- * [universal_access_from_file_urls_allowed](BrowserSettings.md#universal_access_from_file_urls_allowed)
* [user_style_sheet_location](BrowserSettings.md#user_style_sheet_location)
- * [web_security_disabled](BrowserSettings.md#web_security_disabled)
* [webgl_disabled](BrowserSettings.md#webgl_disabled)
* [windowless_frame_rate](BrowserSettings.md#windowless_frame_rate)
* [Callback (object)](Callback.md#callback-object)
@@ -203,9 +192,6 @@
* [GetExpires](Cookie.md#getexpires)
* [CookieManager (class)](CookieManager.md#cookiemanager-class)
* [GetGlobalManager](CookieManager.md#getglobalmanager)
- * [GetBlockingManager](CookieManager.md#getblockingmanager)
- * [CreateManager](CookieManager.md#createmanager)
- * [SetSupportedSchemes](CookieManager.md#setsupportedschemes)
* [VisitAllCookies](CookieManager.md#visitallcookies)
* [VisitUrlCookies](CookieManager.md#visiturlcookies)
* [SetCookie](CookieManager.md#setcookie)
@@ -225,7 +211,6 @@
* [DownloadHandler](DownloadHandler.md#downloadhandler)
* [DpiAware (class)](DpiAware.md#dpiaware-class)
* [CalculateWindowSize](DpiAware.md#calculatewindowsize)
- * [EnableHighDpiSupport](DpiAware.md#enablehighdpisupport)
* [GetSystemDpi](DpiAware.md#getsystemdpi)
* [IsProcessDpiAware](DpiAware.md#isprocessdpiaware)
* [SetProcessDpiAware](DpiAware.md#setprocessdpiaware)
@@ -392,10 +377,7 @@
* [GetResourceType](Request.md#getresourcetype)
* [GetTransitionType](Request.md#gettransitiontype)
* [RequestHandler (interface)](RequestHandler.md#requesthandler-interface)
- * [CanGetCookies](RequestHandler.md#cangetcookies)
- * [CanSetCookie](RequestHandler.md#cansetcookie)
* [GetAuthCredentials](RequestHandler.md#getauthcredentials)
- * [GetCookieManager](RequestHandler.md#getcookiemanager)
* [GetResourceHandler](RequestHandler.md#getresourcehandler)
* [OnBeforeBrowse](RequestHandler.md#onbeforebrowse)
* [_OnBeforePluginLoad](RequestHandler.md#_onbeforepluginload)
@@ -433,11 +415,6 @@
* [OnContextCreated](V8ContextHandler.md#oncontextcreated)
* [OnContextReleased](V8ContextHandler.md#oncontextreleased)
* [Virtual Key codes](VirtualKey.md#virtual-key-codes)
-* [WebPluginInfo (object)](WebPluginInfo.md#webplugininfo-object)
- * [GetName](WebPluginInfo.md#getname)
- * [GetPath](WebPluginInfo.md#getpath)
- * [GetVersion](WebPluginInfo.md#getversion)
- * [GetDescription](WebPluginInfo.md#getdescription)
* [WebRequest (class)](WebRequest.md#webrequest-class)
* [Create](WebRequest.md#create)
* [GetRequest](WebRequest.md#getrequest)
diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md
index a9ad20f1b..d07327765 100644
--- a/api/ApplicationSettings.md
+++ b/api/ApplicationSettings.md
@@ -7,7 +7,6 @@
Table of contents:
* [Introduction](#introduction)
* [Settings](#settings)
- * [accept_language_list](#accept_language_list)
* [app_user_model_id](#app_user_model_id)
* [auto_zooming](#auto_zooming)
* [background_color](#background_color)
@@ -38,7 +37,6 @@ Table of contents:
* [uncaught_exception_stack_size](#uncaught_exception_stack_size)
* [unique_request_context_per_browser](#unique_request_context_per_browser)
* [user_agent](#user_agent)
- * [user_data_path](#user_data_path)
* [windowless_rendering_enabled](#windowless_rendering_enabled)
@@ -57,18 +55,6 @@ to Chromium Preferences.
## Settings
-### accept_language_list
-
-(string)
-Comma delimited ordered list of language codes without any
-whitespace that
-will be used in the "Accept-Language" HTTP header. May be overridden on a
-per-browser basis using the CefBrowserSettings.accept_language_list value.
-If both values are empty then "en-US,en" will be used. Can be overridden
-for individual CefRequestContext instances via the
-CefRequestContextSettings.accept_language_list value.
-
-
### app_user_model_id
This is setting is applied only on Windows.
@@ -231,40 +217,6 @@ the "framework-dir-path" command-line switch.
See also [Issue #304](../../../issues/304).
-### ignore_certificate_errors
-
-(bool)
-Set to true (1) to ignore errors related to invalid SSL certificates.
-Enabling this setting can lead to potential security vulnerabilities like
-"man in the middle" attacks. Applications that load content from the
-internet should not enable this setting. Also configurable using the
-"ignore-certificate-errors" [command-line switch](CommandLineSwitches.md).
-Can be overridden for individual CefRequestContext instances via the
-CefRequestContextSettings.ignore_certificate_errors value.
-
-__IMPORTANT__: This option not only ignores all certificate errors,
-but it also enables caching of content due to custom patch being
-applied (read more in "NOTE ON CACHING" further down). If you don't
-want this caching feature of insecure content then alternatively you
-can ignore certificate errors using the
-RequestHandler.[_OnCertificateError()](#_oncertificateerror)
-callback. Note that disk caching is enabled only when the "cache_path"
-option is set.
-
-__NOTE ON CACHING__: Chromium by default disallows caching of
-content when there is certificate error. There is a issue125.patch
-in the patches/ directory that can be enabled when doing a custom
-CEF build. This patch changes the caching behavior on sites with SSL
-certificate errors when used with this setting. This patch can be
-applied Chromium sources to allow for caching even when there is
-certificate error, but only when the "ignore_certificate_errors"
-option is set to True.
-When it's set to False then the Chromium's caching behavior does not
-change. Enabling caching with certificate errors is useful on local
-private networks that use self-signed SSL certificates. See the
-referenced CEF topic in [Issue #125](../../../issues/125) for more details.
-
-
### javascript_flags
(string)
@@ -485,17 +437,6 @@ default User-Agent string will be used. Also configurable using the
"user-agent" [command-line switch](CommandLineSwitches.md).
-### user_data_path
-
-(string)
-The location where user data such as spell checking dictionary files will
-be stored on disk. If empty then the default platform-specific user data
-directory will be used (`"~/.cef_user_data"` directory on Linux,
-`"~/Library/Application Support/CEF/User Data"` directory on Mac OS X,
-`"Local Settings\Application Data\CEF\User Data"` directory under the user
-profile directory on Windows).
-
-
### windowless_rendering_enabled
(bool)
diff --git a/api/Browser.md b/api/Browser.md
index b34b90e2f..c97b2d04f 100644
--- a/api/Browser.md
+++ b/api/Browser.md
@@ -36,16 +36,13 @@ Table of contents:
* [GetClientCallback](#getclientcallback)
* [GetClientCallbacksDict](#getclientcallbacksdict)
* [GetFocusedFrame](#getfocusedframe)
- * [GetFrame](#getframe)
* [GetFrameByIdentifier](#getframebyidentifier)
- * [GetFrames](#getframes)
- * [GetFrameCount](#getframecount)
- * [GetFrameIdentifiers](#getframeidentifiers)
+ * [GetFrameByName](#getframebyname)
* [GetFrameNames](#getframenames)
+ * [GetFrames](#getframes)
* [GetImage](#getimage)
* [GetJavascriptBindings](#getjavascriptbindings)
* [GetMainFrame](#getmainframe)
- * [GetNSTextInputContext](#getnstextinputcontext)
* [GetOpenerWindowHandle](#getopenerwindowhandle)
* [GetOuterWindowHandle](#getouterwindowhandle)
* [GetSetting](#getsetting)
@@ -56,14 +53,10 @@ Table of contents:
* [GetZoomLevel](#getzoomlevel)
* [GoBack](#goback)
* [GoForward](#goforward)
- * [HandleKeyEventAfterTextInputClient](#handlekeyeventaftertextinputclient)
- * [HandleKeyEventBeforeTextInputClient](#handlekeyeventbeforetextinputclient)
* [HasDevTools](#hasdevtools)
* [HasDocument](#hasdocument)
* [Invalidate](#invalidate)
* [IsFullscreen](#isfullscreen)
- * [IsLoading](#isloading)
- * [IsMouseCursorChangeDisabled](#ismousecursorchangedisabled)
* [IsPopup](#ispopup)
* [IsWindowRenderingDisabled](#iswindowrenderingdisabled)
* [LoadUrl](#loadurl)
@@ -81,13 +74,11 @@ Table of contents:
* [SendMouseClickEvent](#sendmouseclickevent)
* [SendMouseMoveEvent](#sendmousemoveevent)
* [SendMouseWheelEvent](#sendmousewheelevent)
- * [SendFocusEvent](#sendfocusevent)
* [SendCaptureLostEvent](#sendcapturelostevent)
* [SetAccessibilityState](#setaccessibilitystate)
* [SetClientCallback](#setclientcallback)
* [SetClientHandler](#setclienthandler)
* [SetFocus](#setfocus)
- * [SetMouseCursorChangeDisabled](#setmousecursorchangedisabled)
* [SetJavascriptBindings](#setjavascriptbindings)
* [SetUserData](#setuserdata)
* [SetZoomLevel](#setzoomlevel)
@@ -110,6 +101,12 @@ Methods available in upstream CEF which were not yet exposed in CEF Python
* ImeCommitText
* ImeFinishComposingText
* ImeCancelComposition
+* GetFrameCount
+* GetFrameIdentifiers
+* GetNSTextInputContext (Mac, OSR)
+* HandleKeyEventAfterTextInputClient (Mac, OSR)
+* HandleKeyEventBeforeTextInputClient (Mac, OSR)
+* IsLoading
There are some edge cases when after the OnBeforeClose event browser objects
are no more globally referenced thus a new instance is created that
@@ -172,7 +169,7 @@ information.
| | |
| --- | --- |
-| __Return__ | bool |
+| __Return__ | void |
Explicitly close the associated DevTools browser, if any.
@@ -322,24 +319,17 @@ Calling javascript from native code synchronously is not possible in CEF 3. It i
| Parameter | Type |
| --- | --- |
-| searchId | int |
| searchText | string |
| forward | bool |
| matchCase | bool |
| findNext | bool |
| __Return__ | void |
-Description from upstream CEF:
-
-> Search for |searchText|. |identifier| must be a unique ID and these IDs
-> must strictly increase so that newer requests always have greater IDs than
-> older requests. If |identifier| is zero or less than the previous ID value
-> then it will be automatically assigned a new valid ID. |forward| indicates
-> whether to search forward or backward within the page. |matchCase|
-> indicates whether the search should be case-sensitive. |findNext| indicates
-> whether this is the first request or a follow-up. The CefFindHandler
-> instance, if any, returned via CefClient::GetFindHandler will be called to
-> report find results.
+Search for |searchText|. |forward| indicates whether to search forward or
+backward within the page. |matchCase| indicates whether the search should be
+case-sensitive. |findNext| indicates whether this is the first request or a
+follow-up. The CefFindHandler instance, if any, returned via
+CefClient::GetFindHandler will be called to report find results.
### GetClientCallback
@@ -369,64 +359,46 @@ Get client callbacks as a dictionary.
Returns the focused [Frame](Frame.md) for the browser window.
-### GetFrame
+### GetFrameByIdentifier
| Parameter | Type |
| --- | --- |
-| name | string |
+| identifier | 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 identifier, or None if not found.
-### GetFrameByIdentifier
+### GetFrameByName
| Parameter | Type |
| --- | --- |
-| identifier | long |
+| name | string |
| __Return__ | Frame |
-Available only in CEF 3. Returns the [Frame](Frame.md) with the specified identifier, or None if not found.
-
-
-### GetFrames
-
-| | |
-| --- | --- |
-| __Return__ | list |
-
-Get all frames. This is an internal CEF Python implementation that uses GetFrameNames() and GetFrame() methods to list through all frames. The main frame is not included in that list.
-
-
-### GetFrameCount
-
-| | |
-| --- | --- |
-| __Return__ | int |
-
-Available only in CEF 3. Not yet implemented.
-
-Returns the number of frames that currently exist.
+Returns the [Frame](Frame.md) with the specified name, or None if not found.
+This method should only be called on the UI thread.
-### GetFrameIdentifiers
+### GetFrameNames
| | |
| --- | --- |
-| __Return__ | void |
-
-Available only in CEF 3. Not yet implemented.
+| __Return__ | string[] |
-Returns the identifiers of all existing frames.
+Returns the names of all existing frames. This list does not include the main frame.
+This method should only be called on the UI thread.
-### GetFrameNames
+### GetFrames
| | |
| --- | --- |
-| __Return__ | string[] |
+| __Return__ | list |
-Returns the names of all existing frames. This list does not include the main frame.
+Get all frames. This is a CEF Python helper that calls GetFrameNames() and
+GetFrameByName() to return a list of [Frame](Frame.md) objects. The main frame
+is not included in that list.
### GetImage
@@ -470,18 +442,6 @@ Returns the [JavascriptBindings](JavascriptBindings.md) object that was passed t
Returns the main (top-level) [Frame](Frame.md) for the browser window.
-### GetNSTextInputContext
-
-| | |
-| --- | --- |
-| __Return__ | TextInputContext |
-
-Not yet ported. Available only in CEF 3.
-
-Get the NSTextInputContext implementation for enabling IME on Mac when
-window rendering is disabled.
-
-
### GetOpenerWindowHandle
| | |
@@ -576,30 +536,6 @@ Navigate backwards.
Navigate forwards.
-### HandleKeyEventAfterTextInputClient
-
-| Parameter | Type |
-| --- | --- |
-| keyEvent | eventHandle |
-| __Return__ | void |
-
-Available only in CEF 3. Not yet implemented.
-
-Performs any additional actions after NSTextInputClient handles the event.
-
-
-### HandleKeyEventBeforeTextInputClient
-
-| | |
-| --- | --- |
-| __Return__ | void |
-
-Available only in CEF 3. Not yet implemented.
-
-Handles a keyDown event prior to passing it through the NSTextInputClient
-machinery.
-
-
### HasDevTools
| | |
@@ -639,35 +575,11 @@ Description from upstream CEF:
### IsFullscreen
-| | |
-| --- | --- |
-| __Return__ | void |
-
-Whether in fullscreen mode, see ToggleFullscreen().
-
-This function is Windows-only.
-
-
-### IsLoading
-
| | |
| --- | --- |
| __Return__ | bool |
-Available only in CEF 3. Not yet implemented.
-
-Returns true if the browser is currently loading.
-
-
-### IsMouseCursorChangeDisabled
-
-| | |
-| --- | --- |
-| __Return__ | bool |
-
-Available only in CEF 3.
-
-Returns true if mouse cursor change is disabled.
+Whether in fullscreen mode, see ToggleFullscreen(). Windows-only.
### IsPopup
@@ -700,9 +612,8 @@ 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.
+properly. You can use `pathlib.PurePath.as_uri` or
+`urllib.request.pathname2url` depending on your case.
### Navigate
@@ -795,7 +706,7 @@ this method will replace it with the specified |word|.
| --- | --- |
| enabled | bool |
| min_size | list[width, height] |
-| max_size | list[width, heifght] |
+| max_size | list[width, height] |
| __Return__ | void |
Description from upstream CEF:
@@ -894,16 +805,6 @@ flags see SendMouseClickEvent().
Send a mouse wheel event to the browser. The |x| and |y| coordinates are relative to the upper-left corner of the view. The |deltaX| and |deltaY| values represent the movement delta in the X and Y directions respectively. In order to scroll inside select popups with window rendering disabled [RenderHandler](RenderHandler.md).GetScreenPoint() should be implemented properly. For a list of modifiers flags see SendMouseClickEvent().
-### SendFocusEvent
-
-| Parameter | Type |
-| --- | --- |
-| setFocus | bool |
-| __Return__ | void |
-
-Send a focus event to the browser.
-
-
### SendCaptureLostEvent
| | |
@@ -988,16 +889,6 @@ LifespanHandler etc.
Set whether the browser is focused.
-### SetMouseCursorChangeDisabled
-
-| Parameter | Type |
-| --- | --- |
-| disabled | bool |
-| __Return__ | void |
-
-Set whether mouse cursor change is disabled.
-
-
### SetJavascriptBindings
| Parameter | Type |
diff --git a/api/BrowserSettings.md b/api/BrowserSettings.md
index ed2b2e50d..15ad4bd74 100644
--- a/api/BrowserSettings.md
+++ b/api/BrowserSettings.md
@@ -8,27 +8,23 @@ Table of contents:
* [Introduction](#introduction)
* [Settings](#settings)
* [Font settings](#font-settings)
- * [accept_language_list](#accept_language_list)
* [application_cache_disabled](#application_cache_disabled)
* [background_color](#background_color)
* [databases_disabled](#databases_disabled)
* [default_encoding](#default_encoding)
* [dom_paste_disabled](#dom_paste_disabled)
- * [file_access_from_file_urls_allowed](#file_access_from_file_urls_allowed)
* [inherit_client_handlers_for_popups](#inherit_client_handlers_for_popups)
* [image_load_disabled](#image_load_disabled)
* [javascript_disabled](#javascript_disabled)
* [javascript_close_windows_disallowed](#javascript_close_windows_disallowed)
* [javascript_access_clipboard_disallowed](#javascript_access_clipboard_disallowed)
* [local_storage_disabled](#local_storage_disabled)
- * [plugins_disabled](#plugins_disabled)
* [remote_fonts](#remote_fonts)
* [shrink_standalone_images_to_fit](#shrink_standalone_images_to_fit)
* [tab_to_links_disabled](#tab_to_links_disabled)
* [text_area_resize_disabled](#text_area_resize_disabled)
* [universal_access_from_file_urls_allowed](#universal_access_from_file_urls_allowed)
* [user_style_sheet_location](#user_style_sheet_location)
- * [web_security_disabled](#web_security_disabled)
* [webgl_disabled](#webgl_disabled)
* [windowless_frame_rate](#windowless_frame_rate)
@@ -60,15 +56,6 @@ In some cases, the default values of settings that are suggested by its name may
* minimum_logical_font_size (int)
-### accept_language_list
-
-(string)
-Comma delimited ordered list of language codes without any whitespace that
-will be used in the "Accept-Language" HTTP header. May be set globally
-using the CefBrowserSettings.accept_language_list value. If both values are
-empty then "en-US,en" will be used.
-
-
### application_cache_disabled
(bool) Controls whether the application cache can be used. Also configurable using the --disable-application-cache switch.
@@ -106,11 +93,6 @@ in a known order. Equivalent to the `SkColor` type in Chromium.
(bool) Controls whether DOM pasting is supported in the editor via `execCommand("paste")`. The |javascript_access_clipboard_disallowed| setting must also be set (to true or false). Also configurable using the --disable-javascript-dom-paste switch.
-### file_access_from_file_urls_allowed
-
-(bool) Controls whether file URLs will have access to other file URLs. Also configurable using the --allow-access-from-files switch. Other similar switches are: --allow-file-access and --allow-file-access-from-files.
-
-
### inherit_client_handlers_for_popups
@@ -155,11 +137,6 @@ switch.
(bool) Controls whether local storage can be used. Also configurable using the --disable-local-storage switch.
-### plugins_disabled
-
-(bool) Controls whether any plugins will be loaded. Also configurable using the --disable-plugins switch.
-
-
### remote_fonts
(bool) Controls the loading of fonts from remote sources. Also configurable using the --disable-remote-fonts switch.
@@ -180,11 +157,6 @@ switch.
(bool) Controls whether text areas can be resized. Also configurable using the --disable-text-area-resize switch.
-### universal_access_from_file_urls_allowed
-
-(bool) Controls whether file URLs will have access to all URLs. Also configurable using the --allow-universal-access-from-files switch. Other similar switches are --allow-file-access and --allow-file-access-from-files.
-
-
### user_style_sheet_location
(string) Location of the user style sheet that will be used for all pages. This must be a data URL of the form `data:text/css;charset=utf-8;base64,content` where "content" is the base64 encoded contents of the CSS file. Also configurable using the "user-style-sheet-location" command-line switch.
@@ -192,11 +164,6 @@ switch.
This setting was removed in Chrome 33. Soon it will be removed from cefpython as well.
-### web_security_disabled
-
-(bool) Controls whether web security restrictions (same-origin policy) will be enforced. Disabling this setting is not recommend as it will allow risky security behavior such as cross-site scripting (XSS). Also configurable using the --disable-web-security switch.
-
-
### webgl_disabled
(bool) Controls whether WebGL can be used. Note that WebGL requires hardware support and may not work on all systems even when enabled. Also configurable using the --disable-webgl switch.
diff --git a/api/Cookie.md b/api/Cookie.md
index 0a46e99b0..ffa399fe2 100644
--- a/api/Cookie.md
+++ b/api/Cookie.md
@@ -31,6 +31,10 @@ Table of contents:
* [GetHasExpires](#gethasexpires)
* [SetExpires](#setexpires)
* [GetExpires](#getexpires)
+ * [SetSameSite](#setsamesite)
+ * [GetSameSite](#getsamesite)
+ * [SetPriority](#setpriority)
+ * [GetPriority](#getpriority)
## Methods
@@ -56,6 +60,8 @@ The cookie may have the following keys:
- lastAccess (datetime.datetime)
- hasExpires (bool)
- expires (datetime.datetime)
+- sameSite (int)
+- priority (int)
### Get
@@ -261,3 +267,48 @@ Set the cookie expiration date. You should also call SetHasExpires().
| __Return__ | datetime.datetime |
Get the expires property.
+
+
+### SetSameSite
+
+| Parameter | Type |
+| --- | --- |
+| sameSite | int |
+| __Return__ | void |
+
+Set the cookie SameSite attribute. Use the `CEF_COOKIE_SAME_SITE_*` constants:
+- `CEF_COOKIE_SAME_SITE_UNSPECIFIED` (0)
+- `CEF_COOKIE_SAME_SITE_NO_RESTRICTION` (1)
+- `CEF_COOKIE_SAME_SITE_LAX_MODE` (2)
+- `CEF_COOKIE_SAME_SITE_STRICT_MODE` (3)
+
+
+### GetSameSite
+
+| | |
+| --- | --- |
+| __Return__ | int |
+
+Get the cookie SameSite attribute. See SetSameSite() for possible values.
+
+
+### SetPriority
+
+| Parameter | Type |
+| --- | --- |
+| priority | int |
+| __Return__ | void |
+
+Set the cookie priority. Use the `CEF_COOKIE_PRIORITY_*` constants:
+- `CEF_COOKIE_PRIORITY_LOW` (-1)
+- `CEF_COOKIE_PRIORITY_MEDIUM` (0)
+- `CEF_COOKIE_PRIORITY_HIGH` (1)
+
+
+### GetPriority
+
+| | |
+| --- | --- |
+| __Return__ | int |
+
+Get the cookie priority. See SetPriority() for possible values.
diff --git a/api/CookieManager.md b/api/CookieManager.md
index b0d06a911..c947a3f47 100644
--- a/api/CookieManager.md
+++ b/api/CookieManager.md
@@ -18,9 +18,6 @@ also have an OnComplete callback.
Table of contents:
* [Methods](#methods)
* [GetGlobalManager](#getglobalmanager)
- * [GetBlockingManager](#getblockingmanager)
- * [CreateManager](#createmanager)
- * [SetSupportedSchemes](#setsupportedschemes)
* [VisitAllCookies](#visitallcookies)
* [VisitUrlCookies](#visiturlcookies)
* [SetCookie](#setcookie)
@@ -49,52 +46,6 @@ Description from upstream CEF:
> calling CefRequestContext::GetGlobalContext()->GetDefaultCookieManager()
-### GetBlockingManager
-
-| | |
-| --- | --- |
-| __Return__ | [CookieManager](CookieManager.md) |
-
-Description from upstream CEF:
-> Returns a cookie manager that neither stores nor retrieves cookies. All
-> usage of cookies will be blocked including cookies accessed via the network
-> (request/response headers), via JavaScript (document.cookie), and via
-> CefCookieManager methods. No cookies will be displayed in DevTools. If you
-> wish to only block cookies sent via the network use the CefRequestHandler
-> CanGetCookies and CanSetCookie methods instead.
-
-
-### CreateManager
-
-| Parameter | Type |
-| --- | --- |
-| path | string |
-| persistSessionCookies=False | bool |
-| __Return__ | [CookieManager](CookieManager.md) |
-
-Creates a new cookie manager. Otherwise, data will be stored at the
-specified |path|. To persist session cookies (cookies without an expiry
-date or validity interval) set |persistSessionCookies|
-to true. If using global manager then see the [ApplicationSettings](ApplicationSettings.md).`persist_session_cookies`
-option. Session cookies are generally intended to be transient and most
-Web browsers do not persist them. Returns None if creation fails.
-
-You can have a separate cookie manager for each browser,
-see [RequestHandler](RequestHandler.md).GetCookieManager().
-
-
-### SetSupportedSchemes
-
-| Parameter | Type |
-| --- | --- |
-| schemes | list |
-| __Return__ | void |
-
-Set the schemes supported by this manager. The default schemes ("http",
-"https", "ws" and "wss") will always be supported. Must be called before
-any cookies are accessed.
-
-
### VisitAllCookies
| Parameter | Type |
diff --git a/api/DpiAware.md b/api/DpiAware.md
index 5a335b216..a93e3e39e 100644
--- a/api/DpiAware.md
+++ b/api/DpiAware.md
@@ -12,7 +12,6 @@ Table of contents:
* [Introduction](#introduction)
* [Static methods](#static-methods)
* [CalculateWindowSize](#calculatewindowsize)
- * [EnableHighDpiSupport](#enablehighdpisupport)
* [GetSystemDpi](#getsystemdpi)
* [IsProcessDpiAware](#isprocessdpiaware)
* [SetProcessDpiAware](#setprocessdpiaware)
@@ -46,21 +45,6 @@ OS DPI settings. For 800/600 with Win7 DPI settings
being set to "Larger 150%" will return 1200/900.
-### EnableHighDpiSupport
-
-| | |
-| --- | --- |
-| __Return__ | void |
-
-Calling this function will set current process and subprocesses
-to be DPI aware.
-
-Description from upstream CEF:
-> Call during process startup to enable High-DPI support on Windows 7 or newer.
-> Older versions of Windows should be left DPI-unaware because they do not
-> support DirectWrite and GDI fonts are kerned very badly.
-
-
### GetSystemDpi
| | |
diff --git a/api/Frame.md b/api/Frame.md
index 057129122..284f9b177 100644
--- a/api/Frame.md
+++ b/api/Frame.md
@@ -25,7 +25,6 @@ Table of contents:
* [ExecuteFunction](#executefunction)
* [ExecuteJavascript](#executejavascript)
* [GetBrowser](#getbrowser)
- * [GetParent](#getparent)
* [GetIdentifier](#getidentifier)
* [GetBrowserIdentifier](#getbrowseridentifier)
* [GetName](#getname)
@@ -36,7 +35,6 @@ Table of contents:
* [IsFocused](#isfocused)
* [IsMain](#ismain)
* [IsValid](#isvalid)
- * [LoadString](#loadstring)
* [LoadUrl](#loadurl)
* [Paste](#paste)
* [Redo](#redo)
@@ -107,15 +105,6 @@ Execute a string of JavaScript code in this frame. The sciptUrl parameter is the
Returns the browser that this frame belongs to.
-### GetParent
-
-| | |
-| --- | --- |
-| __Return__ | [Frame](Frame.md) |
-
-Returns the parent of this frame or None if this is the main (top-level) frame.
-
-
### GetIdentifier
| | |
@@ -213,32 +202,6 @@ Returns true if this is the main (top-level) frame.
True if this object is currently attached to a valid frame.
-### LoadString
-
-| Parameter | Type |
-| --- | --- |
-| value | string |
-| url | string |
-| __Return__ | void |
-
-NOTE: LoadString is problematic due to the multi-process model and the need
-to create a render process (which does not happen with LoadString). It is
-best to use instead LoadUrl with a data uri, e.g. `LoadUrl("data:text/html,some+html+code+here")`.
-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.
-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
| Parameter | Type |
@@ -246,7 +209,9 @@ in Python 3) depending on your case.
| url | string |
| __Return__ | void |
-Load the specified |url|.
+Load the specified |url|. To load HTML content directly use a data URI,
+e.g. `LoadUrl("data:text/html,some+html+code+here")`. See also
+[ResourceHandler](ResourceHandler.md) for a custom resource handler approach.
### Paste
diff --git a/api/LifespanHandler.md b/api/LifespanHandler.md
index 1794d62d7..3edd4e677 100644
--- a/api/LifespanHandler.md
+++ b/api/LifespanHandler.md
@@ -82,42 +82,48 @@ additional usage information.
| window_info_out | list[[WindowInfo](WindowInfo.md)] |
| client | None |
| browser_settings_out | list[[BrowserSettings](BrowserSettings.md)] |
+| extra_info_out | dict |
| no_javascript_access_out | list[bool] |
| __Return__ | bool |
Description from upstream CEF:
> Called on the UI thread before a new popup browser is created. The
-> |browser| and |frame| values represent the source of the popup request. The
-> |target_url| and |target_frame_name| values indicate where the popup
-> browser should navigate and may be empty if not specified with the request.
-> The |target_disposition| value indicates where the user intended to open
-> the popup (e.g. current tab, new tab, etc). The |user_gesture| value will
-> be true if the popup was opened via explicit user gesture (e.g. clicking a
-> link) or false if the popup opened automatically (e.g. via the
-> DomContentLoaded event). The |popup_features| structure contains additional
+> |browser| and |frame| values represent the source of the popup request.
+> The |target_url| and |target_frame_name| values indicate where the popup
+> browser should navigate and may be empty if not specified with the
+> request. The |target_disposition| value indicates where the user intended
+> to open the popup (e.g. current tab, new tab, etc). The |user_gesture|
+> value will be true if the popup was opened via explicit user gesture (e.g.
+> clicking a link) or false if the popup opened automatically (e.g. via the
+> DomContentLoaded event). The |popupFeatures| structure contains additional
> information about the requested popup window. To allow creation of the
-> popup browser optionally modify |windowInfo|, |client|, |browserSettings| and
+> popup browser optionally modify |windowInfo|, |client|, |settings| and
> |no_javascript_access| and return false. To cancel creation of the popup
-> browser return true. The |client| and |settings| values will default to the
-> source browser's values. If the |no_javascript_access| value is set to
+> browser return true. The |client| and |settings| values will default to
+> the source browser's values. If the |no_javascript_access| value is set to
> false the new browser will not be scriptable and may not be hosted in the
> same renderer process as the source browser. Any modifications to
-> |window_info| will be ignored if the parent browser is wrapped in a
+> |windowInfo| will be ignored if the parent browser is wrapped in a
> CefBrowserView. Popup browser creation will be canceled if the parent
-> browser is destroyed before the popup browser creation completes (indicated
-> by a call to OnAfterCreated for the popup browser).
+> browser is destroyed before the popup browser creation completes
+> (indicated by a call to OnAfterCreated for the popup browser). The
+> |extra_info| parameter provides an opportunity to specify extra
+> information specific to the created popup browser that will be passed to
+> CefRenderProcessHandler::OnBrowserCreated() in the render process.
`WindowOpenDisposition` constants in the cefpython module:
-* WOD_UNKNOWN,
-* WOD_CURRENT_TAB,
-* WOD_SINGLETON_TAB,
-* WOD_NEW_FOREGROUND_TAB,
-* WOD_NEW_BACKGROUND_TAB,
-* WOD_NEW_POPUP,
-* WOD_NEW_WINDOW,
-* WOD_SAVE_TO_DISK,
-* WOD_OFF_THE_RECORD,
-* WOD_IGNORE_ACTION
+* CEF_WOD_UNKNOWN,
+* CEF_WOD_CURRENT_TAB,
+* CEF_WOD_SINGLETON_TAB,
+* CEF_WOD_NEW_FOREGROUND_TAB,
+* CEF_WOD_NEW_BACKGROUND_TAB,
+* CEF_WOD_NEW_POPUP,
+* CEF_WOD_NEW_WINDOW,
+* CEF_WOD_SAVE_TO_DISK,
+* CEF_WOD_OFF_THE_RECORD,
+* CEF_WOD_IGNORE_ACTION,
+* CEF_WOD_SWITCH_TO_TAB,
+* CEF_WOD_NEW_PICTURE_IN_PICTURE
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.
diff --git a/api/PaintBuffer.md b/api/PaintBuffer.md
index c6509db21..838d803bb 100644
--- a/api/PaintBuffer.md
+++ b/api/PaintBuffer.md
@@ -36,7 +36,7 @@ Description from upstream CEF:
| origin="top-left" | string |
| __Return__ | object |
-Converts the `void*` buffer to string. In Py2 returns 'str' type, in Py3 returns 'bytes' type.
+Converts the `void*` buffer to bytes.
`origin` may be one of: "top-left", "bottom-left".
diff --git a/api/Request.md b/api/Request.md
index d9d0ece38..6658036b9 100644
--- a/api/Request.md
+++ b/api/Request.md
@@ -9,7 +9,6 @@ Object of this class is used in [RequestHandler](RequestHandler.md).OnBeforeBrow
Table of contents:
* [Methods](#methods)
* [CreateRequest](#createrequest)
- * [IsReadOnly](#isreadonly)
* [GetUrl](#geturl)
* [SetUrl](#seturl)
* [GetMethod](#getmethod)
@@ -24,8 +23,6 @@ Table of contents:
* [SetFlags](#setflags)
* [GetFirstPartyForCookies](#getfirstpartyforcookies)
* [SetFirstPartyForCookies](#setfirstpartyforcookies)
- * [GetResourceType](#getresourcetype)
- * [GetTransitionType](#gettransitiontype)
## Methods
@@ -41,15 +38,6 @@ You cannot instantiate `Request` class directly, use this static method
instead by calling `cefpython.Request.CreateRequest()`.
-### IsReadOnly
-
-| | |
-| --- | --- |
-| __Return__ | bool |
-
-Returns true if this object is read-only.
-
-
### GetUrl
| | |
@@ -169,7 +157,7 @@ Get the flags used in combination with WebRequest.
Available flags below. Can be accessed via `cefpython.Request.Flags["xxx"]`.
These flags are also defined as constants starting with "UR_FLAG_"
-in the cefpython module.requ
+in the cefpython module.
* **None** - Default behavior.
* **SkipCache** - If set the cache will be skipped when handling the request. Setting this value is equivalent to specifying the "Cache-Control: no-cache" request header. Setting this value in combination with UR_FLAG_ONLY_FROM_CACHE will cause the request to fail.
@@ -215,27 +203,3 @@ Set the url to the first party for cookies used in combination with
WebRequest.
-### GetResourceType
-
-| | |
-| --- | --- |
-| __Return__ | int |
-
-Not yet implemented in CEF Python.
-
-Get the resource type for this request. Only available in the browser
-process.
-
-
-### GetTransitionType
-
-| | |
-| --- | --- |
-| __Return__ | int |
-
-Not yet implemented in CEF Python.
-
-Get the transition type for this request. Only available in the browser
-process and only applies to requests that represent a main frame or
-sub-frame navigation.
-
diff --git a/api/RequestHandler.md b/api/RequestHandler.md
index f1ef8bf72..28c2dc22b 100644
--- a/api/RequestHandler.md
+++ b/api/RequestHandler.md
@@ -16,13 +16,9 @@ Available in upstream CEF, but not yet exposed to CEF Python:
Table of contents:
* [Callbacks](#callbacks)
- * [CanGetCookies](#cangetcookies)
- * [CanSetCookie](#cansetcookie)
* [GetAuthCredentials](#getauthcredentials)
- * [GetCookieManager](#getcookiemanager)
* [GetResourceHandler](#getresourcehandler)
* [OnBeforeBrowse](#onbeforebrowse)
- * [_OnBeforePluginLoad](#_onbeforepluginload)
* [OnBeforeResourceLoad](#onbeforeresourceload)
* [_OnCertificateError](#_oncertificateerror)
* [OnQuotaRequest](#onquotarequest)
@@ -36,39 +32,6 @@ Table of contents:
## Callbacks
-### CanGetCookies
-
-| Parameter | Type |
-| --- | --- |
-| browser | [Browser](Browser.md) |
-| frame | [Frame](Frame.md) |
-| request | [Request](Request.md) |
-| __Return__ | bool |
-
-Description from upstream CEF:
-> Called on the IO thread before sending a network request with a "Cookie"
-> request header. Return true to allow cookies to be included in the network
-> request or false to block cookies. The |request| object should not be
-> modified in this callback.
-
-
-### CanSetCookie
-
-| Parameter | Type |
-| --- | --- |
-| browser | [Browser](Browser.md) |
-| frame | [Frame](Frame.md) |
-| request | [Request](Request.md) |
-| cookie | [Cookie](Cookie.md) |
-| __Return__ | bool |
-
-Description from upstream CEF:
-> Called on the IO thread when receiving a network request with a
-> "Set-Cookie" response header value represented by |cookie|. Return true to
-> allow the cookie to be stored or false to block the cookie. The |request|
-> object should not be modified in this callback.
-
-
### GetAuthCredentials
| Parameter | Type |
@@ -106,44 +69,6 @@ Example implementations:
[[3]](https://github.com/cztomczak/cefpython/blob/cefpython31/cefpython/http_authentication_win.pyx).
-### GetCookieManager
-
-| Parameter | Type |
-| --- | --- |
-| browser | None or [Browser](Browser.md) |
-| main_url | string |
-| __Return__ | [CookieManager](CookieManager.md) |
-
-Called on the IO thread to retrieve the cookie manager. |main_url|
-is the URL of the top-level frame. Cookies managers can be unique
-per browser or shared across multiple browsers. The global cookie
-manager will be used if this method returns None.
-
-**IMPORTANT**: In some cases this callback is not called due to a
-race condition. See Issue [#429](../../../issues/429) for details.
-
-To successfully implement separate cookie manager per browser session,
-you have to set ApplicationSettings.`unique_request_context_per_browser`
-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
-to implement the LifespanHandler.`OnBeforePopup` callback. Return
-True in that callback to cancel popup creation and instead create
-the window on your own and embed browser in it.
-The `CreateAnotherBrowser` function from the old v31 wxpython
-example does that.
-
-
### GetResourceHandler
| Parameter | Type |
@@ -190,48 +115,6 @@ Description from upstream CEF:
> navigated automatically (e.g. via the DomContentLoaded event).
-### _OnBeforePluginLoad
-
-| Parameter | Type |
-| --- | --- |
-| browser | [Browser](Browser.md) |
-| mime_type | string |
-| plugin_url | string |
-| is_main_frame | bool |
-| top_origin_url | string |
-| plugin_info | [WebPluginInfo](WebPluginInfo.md) |
-| __Return__ | bool |
-
-Description from upstream CEF:
-> Called on multiple browser process threads before a plugin instance is
-> loaded. |mime_type| is the mime type of the plugin that will be loaded.
-> |plugin_url| is the content URL that the plugin will load and may be empty.
-> |is_main_frame| will be true if the plugin is being loaded in the main
-> (top-level) frame, |top_origin_url| is the URL for the top-level frame that
-> contains the plugin when loading a specific plugin instance or empty when
-> building the initial list of enabled plugins for 'navigator.plugins'
-> JavaScript state. |plugin_info| includes additional information about the
-> plugin that will be loaded. |plugin_policy| is the recommended policy.
-> Modify |plugin_policy| and return true to change the policy. Return false
-> to use the recommended policy. The default plugin policy can be set at
-> runtime using the `--plugin-policy=[allow|detect|block]` command-line flag.
-> Decisions to mark a plugin as disabled by setting |plugin_policy| to
-> PLUGIN_POLICY_DISABLED may be cached when |top_origin_url| is empty. To
-> purge the plugin list cache and potentially trigger new calls to this
-> method call CefRequestContext::PurgePluginListCache.
-
-Return True to block loading of the plugin.
-
-This callback will be executed during browser creation, thus you must
-call [cefpython](cefpython.md).SetGlobalClientCallback() to use it.
-The callback name was prefixed with "`_`" to distinguish this special
-behavior.
-
-Plugins are loaded on demand, only when website requires it.
-This callback is called every time the page tries to load a plugin
-(perhaps even multiple times per plugin).
-
-
### OnBeforeResourceLoad
| Parameter | Type |
diff --git a/api/Response.md b/api/Response.md
index ec742ae9a..1eb113d13 100644
--- a/api/Response.md
+++ b/api/Response.md
@@ -15,7 +15,7 @@ Table of contents:
* [SetStatusText](#setstatustext)
* [GetMimeType](#getmimetype)
* [SetMimeType](#setmimetype)
- * [GetHeader](#getheader)
+ * [GetHeaderByName](#getheaderbyname)
* [GetHeaderMap](#getheadermap)
* [GetHeaderMultimap](#getheadermultimap)
* [SetHeaderMap](#setheadermap)
@@ -91,7 +91,7 @@ Get the response mime type.
Set the response mime type.
-### GetHeader
+### GetHeaderByName
| Parameter | Type |
| --- | --- |
diff --git a/api/WebPluginInfo.md b/api/WebPluginInfo.md
deleted file mode 100644
index ad4396d35..000000000
--- a/api/WebPluginInfo.md
+++ /dev/null
@@ -1,63 +0,0 @@
-[API categories](API-categories.md) | [API index](API-index.md)
-
-
-# WebPluginInfo (object)
-
-See also [RequestHandler](RequestHandler.md)._OnBeforePluginLoad().
-
-Web Plugin API available in upstream CEF, but not yet exposed in CEF Python
-(see src/include/cef_web_plugin.h):
-
-* CefRegisterCdmCallback
-* CefRegisterWidevineCdm
-* CefIsWebPluginUnstable
-* CefRegisterWebPluginCrash
-* CefUnregisterInternalWebPlugin
-* CefRefreshWebPlugins
-* CefVisitWebPluginInfo
-
-
-Table of contents:
-* [Methods](#methods)
- * [GetName](#getname)
- * [GetPath](#getpath)
- * [GetVersion](#getversion)
- * [GetDescription](#getdescription)
-
-## Methods
-
-
-### GetName
-
-| | |
-| --- | --- |
-| __Return__ | string |
-
-Returns the plugin name (i.e. Flash).
-
-
-### GetPath
-
-| | |
-| --- | --- |
-| __Return__ | string |
-
-Returns the plugin file path (DLL/bundle/library).
-
-
-### GetVersion
-
-| | |
-| --- | --- |
-| __Return__ | string |
-
-Returns the version of the plugin (may be OS-specific).
-
-
-### GetDescription
-
-| | |
-| --- | --- |
-| __Return__ | string |
-
-Returns a description of the plugin from the version information.
diff --git a/api/cefpython.md b/api/cefpython.md
index ae7d255c3..5d508c824 100644
--- a/api/cefpython.md
+++ b/api/cefpython.md
@@ -62,9 +62,8 @@ 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.
+properly. You can use `pathlib.PurePath.as_uri` or
+`urllib.request.pathname2url` 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
diff --git a/cefpython3/__init__.py b/cefpython3/__init__.py
new file mode 100644
index 000000000..8a1043d8f
--- /dev/null
+++ b/cefpython3/__init__.py
@@ -0,0 +1,34 @@
+import ctypes
+import importlib
+import os
+import platform
+import sys
+
+__all__ = ["cefpython"]
+__author__ = "The CEF Python authors"
+
+try:
+ from importlib.metadata import version as _pkg_version
+ __version__ = _pkg_version("cefpython3")
+except Exception:
+ __version__ = "unknown"
+
+package_dir = os.path.dirname(os.path.abspath(__file__))
+
+# Let subprocess and the C extension find CEF DLLs / .so files next to this
+# __init__.py regardless of the working directory.
+os.environ["CEFPYTHON3_PATH"] = package_dir
+
+if platform.system() == "Linux":
+ # Force GDK to use X11/XWayland backend before libcef.so loads and
+ # initialises GDK. Without this, GDK picks the Wayland backend on
+ # GNOME/Wayland sessions, making gdk_x11_window_get_xid() return 0.
+ os.environ.setdefault("GDK_BACKEND", "x11")
+ _ld = os.environ.get("LD_LIBRARY_PATH", "")
+ os.environ["LD_LIBRARY_PATH"] = (
+ package_dir + os.pathsep + _ld if _ld else package_dir
+ )
+ ctypes.CDLL(os.path.join(package_dir, "libcef.so"), ctypes.RTLD_GLOBAL)
+
+_pyver = "{0}{1}".format(*sys.version_info[:2])
+cefpython = importlib.import_module(".cefpython_py{}".format(_pyver), package=__name__)
diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md
index 9ca4d6e99..be90140bc 100644
--- a/docs/Build-instructions.md
+++ b/docs/Build-instructions.md
@@ -48,8 +48,8 @@ Before you can build CEF Python or CEF you must satisfy
## Quick build instructions for Windows
-Complete steps for building CEF Python v50+ with Python 2.7 using
-prebuilt binaries and libraries from GitHub Releases.
+Complete steps for building CEF Python v50+ using prebuilt binaries
+and libraries from GitHub Releases.
When cloning repository you should checkout a stable branch which
are named "cefpythonXX" where XX is Chromium version number.
@@ -62,15 +62,7 @@ are named "cefpythonXX" where XX is Chromium version number.
3) Download [cmake](https://cmake.org/download/) and add
it to PATH.
-4) For Python 2.7 Install "Visual C++ Compiler for Python 2.7"
- from [here](https://www.microsoft.com/en-us/download/details.aspx?id=44266)
-
-5) For Python 2.7 and when using using "Visual C++ compiler for Python 2.7"
- you have to install "Visual C++ 2008 Redistributable Package"
- 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
+4) Clone cefpython, checkout for example "cefpython57" branch
that includes Chromium v57, then create a build/ directory and enter it:
```
git clone https://github.com/cztomczak/cefpython.git
@@ -94,9 +86,9 @@ pip install --upgrade -r ../tools/requirements.txt
8) Extract the archive in the "build/" directory.
-9) Build cefpython and run examples (xx.x is version number):
+9) Build cefpython and run examples:
```
-python ../tools/build.py xx.x
+python ../tools/build.py
```
@@ -143,9 +135,9 @@ sudo pip install --upgrade -r ../tools/requirements.txt
7) Extract the archive in the "build/" directory.
-8) Build cefpython and run examples (xx.x is version number):
+8) Build cefpython and run examples:
```
-python ../tools/build.py xx.x
+python ../tools/build.py
```
@@ -160,29 +152,13 @@ requirements common for all platforms.
* Download [ninja](https://github.com/ninja-build/ninja) 1.7.2 or later
and add it to PATH.
* Download [cmake](https://cmake.org/download/) and add it to PATH.
-* Install an appropriate MS compiler for a specific Python version:
- https://wiki.python.org/moin/WindowsCompilers
- * For Python 2.7 install "Microsoft Visual C++ Compiler for Python 2.7"
- from [here](https://www.microsoft.com/en-us/download/details.aspx?id=44266)
- * When using "Visual C++ compiler for Python 2.7" you have to install
- "Microsoft Visual C++ 2008 Redistributable Package" 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\"
- if does not exist
- * For Python 3.4 follow the instructions for installing Windows SDK 7.1.
- If you encounter issue with .NET Framework 4 then make registry edits
- as suggested here: [Windows SDK setup failure](http://stackoverflow.com/a/33260090/623622).
- * For Python 3.4, if getting error:
- `Cannot open include file 'ammintrin.h': No such file or directory`
- then Copy that `ammitrin.h` file from for example VS 2015 installation
- directory or find this file on the web. This is a Microsoft issue.
+* Install Visual Studio 2022 or later with C++ workload (required for
+ Python 3.10+). See https://wiki.python.org/moin/WindowsCompilers
* To build CEF from sources:
* Use Win7 x64 or later. 32-bit OS'es are not supported. For more details
see [here](https://www.chromium.org/developers/how-tos/build-instructions-windows).
- * For CEF branch >= 2704 install VS2015 Update 2 or later. Use the
- Custom Install option, see details [here](https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#Open-source-contributors).
+ * Install Visual Studio 2022 with C++ workload. Use the Custom Install
+ option, see details [here](https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#Open-source-contributors).
* Install [CMake](https://cmake.org/) 2.8.12.1 or newer and add cmake.exe
to PATH
* Install [ninja](http://martine.github.io/ninja/) and add ninja.exe
@@ -260,9 +236,9 @@ cd build/
3) Extract the downloaded archive eg. "cef55_3.2883.1553.g80bd606_win32.zip"
in the "build/" directory (using "extract here" option)
-4) Run the build.py tool (xx.x is version number):
+4) Run the build.py tool:
```
-python ../tools/build.py xx.x
+python ../tools/build.py
```
@@ -281,22 +257,36 @@ mkdir build/
cd build/
```
-2) Download CEF binaries from [Spotify Automated Builds](http://opensource.spotify.com/cefbuilds/index.html).
- The version of the binaries must match exactly the CEF version
- from the "cefpython/src/version/" directory (look for CEF_VERSION
- constant in .h file).
+2) Download and extract CEF binaries automatically using the
+ download_cef.py tool. It reads the required CEF version from
+ "cefpython/src/version/", queries the Spotify CDN index, downloads
+ the standard distribution, verifies the SHA1 checksum, and extracts
+ it to the build/ directory:
+```
+python ../tools/download_cef.py
+```
-3) Extract the downloaded archive eg.
- "cef_binary_3.2883.1553.g80bd606_windows32.tar.bz2"
- in the build/ directory (using "extract here" option)
+ Alternatively, download manually from
+ [Spotify Automated Builds](https://cef-builds.spotifycdn.com/index.html).
+ The version must match exactly the CEF version from
+ "cefpython/src/version/" (look for CEF_VERSION constant in .h file).
+ Extract the archive eg. "cef_binary_3.2883.1553.g80bd606_windows32.tar.bz2"
+ in the build/ directory (using "extract here" option).
-4) Run the automate.py tool. After it completes you should see a new
- directory eg. "cef55_3.2883.1553.g80bd606_win32/".
+3) Build libcef_dll_wrapper and prepare the CEF binaries directory using
+ the automate.py tool. This processes the downloaded "cef_binary_*"
+ directory and creates a new directory eg. "cef55_3.2883.1553.g80bd606_win32/"
+ that build.py requires. Note: this step requires cmake and ninja on PATH.
```
python ../tools/automate.py --prebuilt-cef
```
-5) Run the build.py tool (xx.x is version number):
+4) Run the build.py tool. The version number is optional and defaults to
+ {CHROME_VERSION_MAJOR}.0 read from "cefpython/src/version/":
+```
+python ../tools/build.py
+```
+ To override the patch version (eg. for a second release off the same CEF):
```
python ../tools/build.py xx.x
```
@@ -460,10 +450,8 @@ ls
```
Additional flags when using --wheel flag:
-* `--python-tag cp27` to generate Python 2.7 only package
-* `--universal` to build package for multiple Python versions
- (in such case you must first build multiple cefpython modules
- for each Python version)
+* `--python-tag cp310` to generate a Python 3.10 only package
+ (replace `310` with the appropriate version tag, e.g. `cp311`, `cp312`)
CEF Python binaries are build using similar configuration as described
on the ["Automated Build Setup"](https://bitbucket.org/chromiumembedded/cef/wiki/AutomatedBuildSetup.md#markdown-header-platform-build-configurations) wiki page in upstream CEF. The automate.py tool incorporates most of
diff --git a/docs/Knowledge-Base.md b/docs/Knowledge-Base.md
index c68f4cc06..d60f8e7be 100644
--- a/docs/Knowledge-Base.md
+++ b/docs/Knowledge-Base.md
@@ -3,7 +3,6 @@
Table of contents:
* [Notifications about new releases / commits](#notifications-about-new-releases--commits)
* [Changes in API after CEF updates](#changes-in-api-after-cef-updates)
-* [Differences between Python 2 and Python 3](#differences-between-python-2-and-python-3)
* [How to enable debug information in examples?](#how-to-enable-debug-information-in-examples)
* [Remote debugging with Google Chrome instance](#remote-debugging-with-google-chrome-instance)
* [Debugging using various chrome:// protocol uris](#debugging-using-various-chrome-protocol-uris)
@@ -45,13 +44,6 @@ to hardcode the cefpython version string. If for example using PIP's
following format if using e.g. cefpython v57.0: `cefpython3 == 57.0`.
-## Differences between Python 2 and Python 3
-
-In Python 2 all cefpython strings are byte strings, but in Python 3
-they are all unicode strings. Be aware of this when porting cefpython
-based apps to Python 3, as it may cause issues.
-
-
## How to enable debug information in examples?
You can pass "--debug" command line flag to any of CEF Python
@@ -180,7 +172,7 @@ CEF framework and in the cefpython module. Here are the default
settings:
```
cefpython_package/
- cefpython_py27.so
+ cefpython_py3XX.so
rpath=@loader_path/
load:@rpath/Chromium Embedded Framework.framework/Chromium Embedded Framework
Chromium Embedded Framework.framework/
diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md
index 5ad3d4384..03a3744c8 100644
--- a/docs/Migration-guide.md
+++ b/docs/Migration-guide.md
@@ -493,8 +493,7 @@ See Issue [#442](../../../issues/442) for more details on the issues.
[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.
+`pathlib.PurePath.as_uri` or `urllib.request.pathname2url` depending on your case.
The `cef.GetNavigateUrl` function was removed from the cefpython3 module.
diff --git a/docs/Tutorial.md b/docs/Tutorial.md
index 11cf5db71..88f43fefa 100644
--- a/docs/Tutorial.md
+++ b/docs/Tutorial.md
@@ -149,14 +149,14 @@ a line that overwrites the default exception handler in Python:
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
```
-See Python docs for [sys.excepthook](https://docs.python.org/2/library/sys.html#sys.excepthook).
+See Python docs for [sys.excepthook](https://docs.python.org/3/library/sys.html#sys.excepthook).
The cef.ExceptHook helper function does the following:
1. Writes exception to "error.log" file
2. Prints exception
3. Calls cef.[QuitMessageLoop](../api/cefpython.md#quitmessageloop)
4. Calls cef.[Shutdown](../api/cefpython.md#shutdown)
-5. Calls [os._exit(1)](https://docs.python.org/2/library/os.html#os._exit) -
+5. Calls [os._exit(1)](https://docs.python.org/3/library/os.html#os._exit) -
which exits the process with status 1, without calling
cleanup handlers, flushing stdio buffers, etc.
@@ -410,8 +410,8 @@ html_to_data_uri("test", js_callback_1);
**Communication using http requests**
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 upstream CEF there is available a fast built-in
+by running an internal web-server. See for example
+[http.server](https://docs.python.org/3/library/http.server.html) 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
@@ -612,8 +612,7 @@ In the OnPaint callback CEF provides a [PaintBufer](../api/PaintBuffer.md#paintb
browser view. This object has [GetIntPointer](../api/PaintBuffer.md#getintpointer)
and [GetString](../api/PaintBuffer.md#getstring) methods. In the
example the latter method is used which returns bytes. The method
-name is a bit confusing for Python 3 users, but in Python 2 bytes
-were strings and thus the name. Here is the code:
+name is a bit confusing since it returns bytes, not a string. Here is the code:
```Python
def OnPaint(self, browser, element_type, paint_buffer, **_):
diff --git a/examples/hello_world.py b/examples/hello_world.py
index 789f4666e..75d59a122 100644
--- a/examples/hello_world.py
+++ b/examples/hello_world.py
@@ -8,15 +8,17 @@
# Setting DPI awareness programmatically via a call to cef.DpiAware.EnableHighDpiSupport
# is problematic in Python, may not work and can cause display glitches.
+import sys
+
from cefpython3 import cefpython as cef
import platform
-import sys
def main():
check_versions()
- sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
- cef.Initialize()
+ sys.excepthook = cef.ExceptHook # shut down all CEF processes on error
+ settings = {}
+ cef.Initialize(settings=settings)
cef.CreateBrowserSync(url="https://www.google.com/",
window_title="Hello World!")
cef.MessageLoop()
@@ -31,7 +33,8 @@ def check_versions():
print("[hello_world.py] Python {ver} {arch}".format(
ver=platform.python_version(),
arch=platform.architecture()[0]))
- assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this"
+ assert tuple(int(x) for x in cef.__version__.split(".")) >= (57, 0), \
+ "CEF Python v57.0+ required to run this"
if __name__ == '__main__':
diff --git a/examples/pysdl2.py b/examples/pysdl2.py
index 97f9293dc..dfce311d2 100644
--- a/examples/pysdl2.py
+++ b/examples/pysdl2.py
@@ -127,6 +127,11 @@ def main():
dest='renderer',
choices=['software', 'hardware']
)
+ parser.add_argument(
+ '--debug',
+ help='debug app',
+ action='store_true'
+ )
args = parser.parse_args()
logLevel = logging.INFO
if args.verbose:
@@ -135,7 +140,7 @@ def main():
format='[%(filename)s %(levelname)s]: %(message)s',
level=logLevel
)
- logging.info("Using PySDL2 %s" % sdl2.__version__)
+ logging.info("Using PySDL2 %s", sdl2.__version__)
version = sdl2.SDL_version()
sdl2.SDL_GetVersion(version)
logging.info(
@@ -224,7 +229,7 @@ def main():
browser.SetClientHandler(renderHandler)
# Must call WasResized at least once to let know CEF that
# viewport size is available and that OnPaint may be called.
- browser.SendFocusEvent(True)
+ browser.SetFocus(True)
browser.WasResized()
# Begin the main rendering loop
diff --git a/examples/pywin32.py b/examples/pywin32.py
index bf8d2b9be..e261dfab4 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 tuple(int(x) for x in cef.__version__.split(".")) >= (57, 0), "CEF Python v57.0+ required to run this"
def create_browser(window_info, settings, url):
diff --git a/examples/qt.py b/examples/qt.py
index efb1b381f..c2efce944 100644
--- a/examples/qt.py
+++ b/examples/qt.py
@@ -1,37 +1,24 @@
-# Example of embedding CEF browser using PyQt4, PyQt5 and
-# PySide libraries. This example has two widgets: a navigation
-# bar and a browser.
+# Example of embedding CEF browser using PyQt5, PyQt6 and PySide6 libraries.
+# This example has two widgets: a navigation bar and a browser.
#
# Tested configurations:
# - PyQt 5.8.2 (qt 5.8.0) on Windows/Linux/Mac
-# - PyQt 4.10.4 / 4.11.4 (qt 4.8.6 / 4.8.7) on Windows/Linux
-# - PySide 1.2.1 (qt 4.8.6) on Windows/Linux/Mac
-# - PySide2 5.6.0, 5.11.2 (qt 5.6.2, 5.11.2) on Windows/Linux/Mac
+# - PyQt6 on Linux
+# - PySide6 on Linux
# - CEF Python v55.4+
-#
-# Issues with PySide 1.2:
-# - Mac: Keyboard focus issues when switching between controls (Issue #284)
-# - Mac: Mouse cursor never changes when hovering over links (Issue #311)
from cefpython3 import cefpython as cef
-import ctypes
import os
import platform
+import subprocess
import sys
# GLOBALS
-PYQT4 = False
PYQT5 = False
-PYSIDE = False
-PYSIDE2 = False
+PYQT6 = False
+PYSIDE6 = False
-if "pyqt4" in sys.argv:
- PYQT4 = True
- # noinspection PyUnresolvedReferences
- from PyQt4.QtGui import *
- # noinspection PyUnresolvedReferences
- from PyQt4.QtCore import *
-elif "pyqt5" in sys.argv:
+if "pyqt5" in sys.argv:
PYQT5 = True
# noinspection PyUnresolvedReferences
from PyQt5.QtGui import *
@@ -39,34 +26,31 @@
from PyQt5.QtCore import *
# noinspection PyUnresolvedReferences
from PyQt5.QtWidgets import *
-elif "pyside" in sys.argv:
- PYSIDE = True
+elif "pyqt6" in sys.argv:
+ PYQT6 = True
# noinspection PyUnresolvedReferences
- import PySide
+ from PyQt6.QtGui import *
# noinspection PyUnresolvedReferences
- from PySide import QtCore
+ from PyQt6.QtCore import *
# noinspection PyUnresolvedReferences
- from PySide.QtGui import *
+ from PyQt6.QtWidgets import *
+elif "pyside6" in sys.argv:
+ PYSIDE6 = True
# noinspection PyUnresolvedReferences
- from PySide.QtCore import *
-elif "pyside2" in sys.argv:
- PYSIDE2 = True
+ import PySide6
# noinspection PyUnresolvedReferences
- import PySide2
+ from PySide6 import QtCore
# noinspection PyUnresolvedReferences
- from PySide2 import QtCore
+ from PySide6.QtGui import *
# noinspection PyUnresolvedReferences
- from PySide2.QtGui import *
+ from PySide6.QtCore import *
# noinspection PyUnresolvedReferences
- from PySide2.QtCore import *
- # noinspection PyUnresolvedReferences
- from PySide2.QtWidgets import *
+ from PySide6.QtWidgets import *
else:
print("USAGE:")
- print(" qt.py pyqt4")
print(" qt.py pyqt5")
- print(" qt.py pyside")
- print(" qt.py pyside2")
+ print(" qt.py pyqt6")
+ print(" qt.py pyside6")
sys.exit(1)
# Fix for PyCharm hints warnings when using static methods
@@ -77,16 +61,37 @@
LINUX = (platform.system() == "Linux")
MAC = (platform.system() == "Darwin")
+# CEF only supports X11 on Linux. Force Qt onto the xcb (X11/XWayland)
+# backend for all bindings so that winId() returns a real X11 window ID
+# that CEF can embed into. Wayland desktops (e.g. KDE Plasma on Kubuntu)
+# often pre-set QT_QPA_PLATFORM=wayland in the session environment, so a
+# hard override is needed — setdefault would not override a pre-set value.
+# Must be set before creating QApplication.
+if LINUX:
+ os.environ["QT_QPA_PLATFORM"] = "xcb"
+
+# On Linux, query the X11 pointer button mask directly to detect outside-clicks
+# on the context menu. XQueryPointer returns real button state even while CEF
+# holds an X11 grab (grabs only affect event *delivery*, not state queries).
+if LINUX:
+ try:
+ from Xlib import display as _xlib_display_mod
+ _XLIB_DPY = _xlib_display_mod.Display()
+ def _x11_button_state():
+ try:
+ r = _XLIB_DPY.screen().root.query_pointer()
+ return r.mask & 0x1F00 # Button1Mask(256)..Button5Mask(4096)
+ except Exception:
+ return 0
+ except ImportError:
+ _XLIB_DPY = None
+ def _x11_button_state():
+ return int(QApplication.mouseButtons())
+
# Configuration
WIDTH = 800
HEIGHT = 600
-# OS differences
-CefWidgetParent = QWidget
-if LINUX and (PYQT4 or PYSIDE):
- # noinspection PyUnresolvedReferences
- CefWidgetParent = QX11EmbedContainer
-
def main():
check_versions()
@@ -97,6 +102,14 @@ def main():
# in Qt example. Calling cef.DoMessageLoopWork in a timer
# doesn't work anymore.
settings["external_message_pump"] = True
+ settings["context_menu"] = {
+ "enabled": True,
+ "navigation": True,
+ "print": True,
+ "view_source": True,
+ "external_browser": True,
+ "devtools": True,
+ }
cef.Initialize(settings)
app = CefApplication(sys.argv)
@@ -104,8 +117,11 @@ def main():
main_window.show()
main_window.activateWindow()
main_window.raise_()
- app.exec_()
- if not cef.GetAppSetting("external_message_pump"):
+ if PYQT6 or PYSIDE6:
+ app.exec()
+ else:
+ app.exec_()
+ if not cef.GetAppSetting("external_message_pump") or LINUX:
app.stopTimer()
del main_window # Just to be safe, similarly to "del app"
del app # Must destroy app object before calling Shutdown
@@ -116,37 +132,32 @@ def check_versions():
print("[qt.py] CEF Python {ver}".format(ver=cef.__version__))
print("[qt.py] Python {ver} {arch}".format(
ver=platform.python_version(), arch=platform.architecture()[0]))
- if PYQT4 or PYQT5:
+ if PYQT5 or PYQT6:
print("[qt.py] PyQt {v1} (qt {v2})".format(
v1=PYQT_VERSION_STR, v2=qVersion()))
- elif PYSIDE:
- print("[qt.py] PySide {v1} (qt {v2})".format(
- v1=PySide.__version__, v2=QtCore.__version__))
- elif PYSIDE2:
- print("[qt.py] PySide2 {v1} (qt {v2})".format(
- v1=PySide2.__version__, v2=QtCore.__version__))
+ elif PYSIDE6:
+ print("[qt.py] PySide6 {v1} (qt {v2})".format(
+ v1=PySide6.__version__, v2=QtCore.__version__))
# CEF Python version requirement
- assert cef.__version__ >= "55.4", "CEF Python v55.4+ required to run this"
+ assert tuple(int(x) for x in cef.__version__.split(".")) >= (55, 4), "CEF Python v55.4+ required to run this"
class MainWindow(QMainWindow):
def __init__(self):
# noinspection PyArgumentList
super(MainWindow, self).__init__(None)
- # Avoids crash when shutting down CEF (issue #360)
- if PYSIDE:
- self.setAttribute(Qt.WA_DeleteOnClose, True)
self.cef_widget = None
self.navigation_bar = None
- if PYQT4:
- self.setWindowTitle("PyQt4 example")
- elif PYQT5:
+ if PYQT5:
self.setWindowTitle("PyQt5 example")
- elif PYSIDE:
- self.setWindowTitle("PySide example")
- elif PYSIDE2:
- self.setWindowTitle("PySide2 example")
- self.setFocusPolicy(Qt.StrongFocus)
+ elif PYQT6:
+ self.setWindowTitle("PyQt6 example")
+ elif PYSIDE6:
+ self.setWindowTitle("PySide6 example")
+ if PYQT6 or PYSIDE6:
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ else:
+ self.setFocusPolicy(Qt.StrongFocus)
self.setupLayout()
def setupLayout(self):
@@ -167,7 +178,7 @@ def setupLayout(self):
frame.setLayout(layout)
self.setCentralWidget(frame)
- if (PYSIDE2 or PYQT5) and WINDOWS:
+ if WINDOWS:
# On Windows with PyQt5 main window must be shown first
# before CEF browser is embedded, otherwise window is
# not resized and application hangs during resize.
@@ -176,19 +187,25 @@ def setupLayout(self):
# Browser can be embedded only after layout was set up
self.cef_widget.embedBrowser()
- if (PYSIDE2 or PYQT5) and LINUX:
- # On Linux with PyQt5 the QX11EmbedContainer widget is
- # no more available. An equivalent in Qt5 is to create
- # a hidden window, embed CEF browser in it and then
- # create a container for that hidden window and replace
- # cef widget in the layout with the container.
+ if LINUX and PYQT5:
+ # On Linux with PyQt5 QX11EmbedContainer is no longer available.
+ # The equivalent is to embed CEF in a QWindow (hidden_window) and
+ # wrap it in a createWindowContainer widget.
# noinspection PyUnresolvedReferences, PyArgumentList
self.container = QWidget.createWindowContainer(
self.cef_widget.hidden_window, parent=self)
# noinspection PyArgumentList
layout.addWidget(self.container, 1, 0)
+ # The container displaces cef_widget in the layout, so
+ # cef_widget.resizeEvent never fires. Drive SetBounds from the
+ # container's resize events via this event filter.
+ self.container.installEventFilter(self.cef_widget)
def closeEvent(self, event):
+ # Dismiss any open context menu before CloseBrowser so the callback
+ # is released before CEF destroys the browser's menu-manager state.
+ if LINUX and ContextMenuHandler._active_menu is not None:
+ ContextMenuHandler._active_menu.hide()
# Close browser (force=True) and free CEF reference
if self.cef_widget.browser:
self.cef_widget.browser.CloseBrowser(True)
@@ -200,15 +217,25 @@ def clear_browser_references(self):
self.cef_widget.browser = None
-class CefWidget(CefWidgetParent):
+class CefWidget(QWidget):
def __init__(self, parent=None):
# noinspection PyArgumentList
super(CefWidget, self).__init__(parent)
self.parent = parent
self.browser = None
self.hidden_window = None # Required for PyQt5 on Linux
+ self.x = 0
+ self.y = 0
self.show()
+ def eventFilter(self, obj, event):
+ # Only installed on PyQt5/Linux where the container displaces cef_widget.
+ if event.type() == QEvent.Resize and self.browser:
+ size = event.size()
+ self.browser.SetBounds(self.x, self.y, size.width(), size.height())
+ self.browser.NotifyMoveOrResizeStarted()
+ return False
+
def focusInEvent(self, event):
# This event seems to never get called on Linux, as CEF is
# stealing all focus due to Issue #284.
@@ -228,43 +255,54 @@ def focusOutEvent(self, event):
self.browser.SetFocus(False)
def embedBrowser(self):
- if (PYSIDE2 or PYQT5) and LINUX:
+ if LINUX and PYQT5:
+ # PyQt5 uses GDK/Xlib while CEF uses XCB. Creating an XCB child
+ # under a GDK/Xlib window triggers a cross-client MatchError on
+ # Xwayland. The workaround is to create CEF under root first and
+ # then reparent into hidden_window via a GLib timer
+ # (_linux_schedule_xembed / _linux_embed_info mechanism).
# noinspection PyUnresolvedReferences
self.hidden_window = QWindow()
+ # For PyQt6/PySide6 on Linux, cef_widget already has WA_PaintOnScreen
+ # which forces a real X11 native window. CEF can be created directly
+ # as a child of cef_widget.winId() — no hidden_window or deferred
+ # reparent needed.
window_info = cef.WindowInfo()
- rect = [0, 0, self.width(), self.height()]
+ rect = [0, 0, self._phys(self.width()), self._phys(self.height())]
window_info.SetAsChild(self.getHandle(), rect)
+ if (PYQT6 or PYSIDE6) and LINUX:
+ # SetAsChild() substituted root as CEF's parent (Xwayland workaround).
+ # Qt6 uses XCB (same as CEF) so a direct parent/child relationship
+ # works. Restore cef_widget as the parent and disable the GLib-timer
+ # reparent by clearing _linux_embed_info.
+ window_info.parentWindowHandle = self.getHandle()
+ window_info._linux_embed_info = None
self.browser = cef.CreateBrowserSync(window_info,
url="https://www.google.com/")
- self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar))
- self.browser.SetClientHandler(FocusHandler(self))
+ if self.browser:
+ self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar))
+ self.browser.SetClientHandler(FocusHandler(self))
+ if LINUX:
+ self.browser.SetClientHandler(ContextMenuHandler(self))
+ if WINDOWS:
+ # Sync browser size to actual HWND client rect using device pixels.
+ # PyQt6 high-DPI scaling means self.width()/height() may be smaller
+ # than the real client rect, leaving content in a smaller area.
+ WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
+
+ def _phys(self, n):
+ # Qt6 enables AA_EnableHighDpiScaling by default, so width()/height()
+ # return logical pixels. CEF expects physical pixels. Multiply by
+ # devicePixelRatio() for PyQt6/PySide6 on Linux; PyQt5 uses the
+ # hidden_window/XReparentWindow path where X11 geometry drives sizing.
+ if LINUX and (PYQT6 or PYSIDE6):
+ return int(n * self.devicePixelRatio())
+ return n
def getHandle(self):
if self.hidden_window:
- # PyQt5 on Linux
return int(self.hidden_window.winId())
- try:
- # PyQt4 and PyQt5
- return int(self.winId())
- except:
- # PySide:
- # | QWidget.winId() returns
- # | Converting it to int using ctypes.
- if sys.version_info[0] == 2:
- # Python 2
- ctypes.pythonapi.PyCObject_AsVoidPtr.restype = (
- ctypes.c_void_p)
- ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = (
- [ctypes.py_object])
- return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId())
- else:
- # Python 3
- ctypes.pythonapi.PyCapsule_GetPointer.restype = (
- ctypes.c_void_p)
- ctypes.pythonapi.PyCapsule_GetPointer.argtypes = (
- [ctypes.py_object])
- return ctypes.pythonapi.PyCapsule_GetPointer(
- self.winId(), None)
+ return int(self.winId())
def moveEvent(self, _):
self.x = 0
@@ -274,7 +312,8 @@ def moveEvent(self, _):
WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
elif LINUX:
self.browser.SetBounds(self.x, self.y,
- self.width(), self.height())
+ self._phys(self.width()),
+ self._phys(self.height()))
self.browser.NotifyMoveOrResizeStarted()
def resizeEvent(self, event):
@@ -284,14 +323,15 @@ def resizeEvent(self, event):
WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
elif LINUX:
self.browser.SetBounds(self.x, self.y,
- size.width(), size.height())
+ self._phys(size.width()),
+ self._phys(size.height()))
self.browser.NotifyMoveOrResizeStarted()
class CefApplication(QApplication):
def __init__(self, args):
super(CefApplication, self).__init__(args)
- if not cef.GetAppSetting("external_message_pump"):
+ if not cef.GetAppSetting("external_message_pump") or LINUX:
self.timer = self.createTimer()
self.setupIcon()
@@ -326,6 +366,11 @@ def OnLoadingStateChange(self, **_):
def OnLoadStart(self, browser, **_):
self.navigation_bar.url.setText(browser.GetUrl())
+ # Dismiss any open context menu before CEF tears down its menu state
+ # during navigation — holding the callback alive past this point
+ # triggers an observers_.empty() assertion in base/observer_list.h.
+ if LINUX and ContextMenuHandler._active_menu is not None:
+ ContextMenuHandler._active_menu.hide()
if self.initial_app_loading:
self.navigation_bar.cef_widget.setFocus()
# Temporary fix no. 2 for focus issue on Linux (Issue #284)
@@ -352,9 +397,173 @@ 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)
+ # Temporary fix no. 1 for focus issues on Linux (Issue #284).
+ # Do NOT call browser.SetFocus(True) here on Linux: it calls
+ # XSetInputFocus which steals keyboard focus from any context-menu
+ # popup that is currently shown, causing the menu to close instantly.
if LINUX:
- browser.SetFocus(True)
+ if ContextMenuHandler._active_menu is not None:
+ # Fast path: button still down when OnGotFocus fires.
+ if _x11_button_state():
+ menu = ContextMenuHandler._active_menu
+ if not menu.geometry().contains(QCursor.pos()):
+ menu.hide()
+ return
+ # Start (or restart) the poll. Covers:
+ # - hover via focus-follows-mouse (button not pressed yet)
+ # - fast click where button was released before OnGotFocus fired
+ ContextMenuHandler._start_focus_poll()
+
+
+class ContextMenuHandler(object):
+ """Show a Qt context menu instead of CEF's native Aura/Ozone menu.
+
+ CEF's native context menu on Linux/Xwayland fails to display correctly
+ when the browser window has been reparented (embedded). Qt's own QMenu
+ always appears at the right position because it uses QCursor.pos().
+
+ In CEF Chrome style (116+), CefRunContextMenuCallback::Continue() does
+ not execute commands — it silently does nothing. All commands must be
+ dispatched directly through Python browser APIs.
+ """
+ SEPARATOR = None
+ _active_menu = None
+ _focus_poll = None # QTimer: polls for button press after hover-outside
+
+ # CEF standard menu command IDs (cef_types.h cef_menu_id_t)
+ _CMD_BACK = 100
+ _CMD_FORWARD = 101
+ _CMD_RELOAD = 102
+ _CMD_RELOAD_NOCACHE = 103
+ _CMD_STOPLOAD = 104
+ _CMD_PRINT = 131
+ _CMD_VIEW_SOURCE = 132
+ # Custom IDs added by context_menu_handler.cpp (MENU_ID_USER_FIRST = 26500)
+ _CMD_DEVTOOLS = 26501
+ _CMD_RELOAD_PAGE = 26502
+ _CMD_OPEN_EXTERNAL = 26503
+ _CMD_OPEN_FRAME = 26504
+
+ def __init__(self, cef_widget=None):
+ self._cef_widget = cef_widget
+
+ @staticmethod
+ def _stop_focus_poll():
+ if ContextMenuHandler._focus_poll is not None:
+ ContextMenuHandler._focus_poll.stop()
+ ContextMenuHandler._focus_poll = None
+
+ @staticmethod
+ def _start_focus_poll():
+ """Start a 5ms poll that hides the menu on an outside click."""
+ ContextMenuHandler._stop_focus_poll()
+ timer = QTimer()
+ def _check():
+ if ContextMenuHandler._active_menu is None:
+ ContextMenuHandler._stop_focus_poll()
+ return
+ if _x11_button_state():
+ menu = ContextMenuHandler._active_menu
+ # Only dismiss if cursor is outside the menu. A button press
+ # inside the menu means the user selected an item — exec_()
+ # handles that; don't interfere.
+ if not menu.geometry().contains(QCursor.pos()):
+ menu.hide()
+ # Stop poll either way once a button press is detected.
+ ContextMenuHandler._stop_focus_poll()
+ timer.timeout.connect(_check)
+ timer.start(5)
+ ContextMenuHandler._focus_poll = timer
+
+ @staticmethod
+ def _exec_cmd(browser, cmd_id, page_url):
+ if cmd_id == ContextMenuHandler._CMD_BACK:
+ browser.GoBack()
+ elif cmd_id == ContextMenuHandler._CMD_FORWARD:
+ browser.GoForward()
+ elif cmd_id in (ContextMenuHandler._CMD_RELOAD,
+ ContextMenuHandler._CMD_RELOAD_NOCACHE,
+ ContextMenuHandler._CMD_RELOAD_PAGE):
+ browser.ReloadIgnoreCache()
+ elif cmd_id == ContextMenuHandler._CMD_STOPLOAD:
+ browser.StopLoad()
+ elif cmd_id == ContextMenuHandler._CMD_PRINT:
+ browser.Print()
+ elif cmd_id == ContextMenuHandler._CMD_VIEW_SOURCE:
+ browser.LoadUrl("view-source:" + page_url)
+ elif cmd_id == ContextMenuHandler._CMD_DEVTOOLS:
+ browser.ShowDevTools()
+ elif cmd_id in (ContextMenuHandler._CMD_OPEN_EXTERNAL,
+ ContextMenuHandler._CMD_OPEN_FRAME):
+ if page_url:
+ subprocess.Popen(["xdg-open", page_url])
+ # Editing and spellcheck commands (cut/copy/paste/select-all/…)
+ # are not yet handled — they silently do nothing.
+
+ def RunContextMenu(self, browser, model, callback, **_):
+ if ContextMenuHandler.SEPARATOR is None:
+ ContextMenuHandler.SEPARATOR = cef.MENUITEMTYPE_SEPARATOR
+ sep_type = ContextMenuHandler.SEPARATOR
+
+ page_url = browser.GetUrl()
+ cef_widget = self._cef_widget
+
+ # Snapshot the model (valid only during this call).
+ items = []
+ for i in range(model.GetCount()):
+ if model.GetTypeAt(i) == sep_type:
+ items.append(None)
+ else:
+ items.append((model.GetLabelAt(i).replace("&", ""),
+ model.GetCommandIdAt(i),
+ model.IsEnabledAt(i)))
+
+ def show_menu():
+ # If a previous menu is still open (user right-clicked twice quickly
+ # before the first was dismissed), hide it now. Without this the
+ # second exec_() runs inside the first's event loop and both menus
+ # appear on screen simultaneously.
+ if ContextMenuHandler._active_menu is not None:
+ ContextMenuHandler._active_menu.hide()
+ ContextMenuHandler._stop_focus_poll()
+
+ # Cancel CEF's native context menu before displaying ours.
+ callback.Cancel()
+
+ menu = QMenu()
+ text_to_cmd = {}
+ for item in items:
+ if item is None:
+ menu.addSeparator()
+ else:
+ label, cmd_id, enabled = item
+ act = menu.addAction(label)
+ act.setEnabled(enabled)
+ text_to_cmd[label] = cmd_id
+
+ ContextMenuHandler._active_menu = menu
+ # Stop the focus-poll whenever the menu hides for any reason
+ # (item click, outside-click via poll, navigation, window close).
+ menu.aboutToHide.connect(ContextMenuHandler._stop_focus_poll)
+ # exec_() runs a nested event loop; CEF's 10ms timer keeps firing.
+ if PYQT6 or PYSIDE6:
+ act = menu.exec(QCursor.pos())
+ else:
+ act = menu.exec_(QCursor.pos())
+ ContextMenuHandler._active_menu = None
+ ContextMenuHandler._stop_focus_poll()
+
+ if act is not None:
+ cmd_id = text_to_cmd.get(act.text())
+ if cmd_id is not None:
+ b = cef_widget.browser if cef_widget else None
+ if b:
+ ContextMenuHandler._exec_cmd(b, cmd_id, page_url)
+
+ # Defer the QMenu to the next event-loop tick so that this call
+ # returns to CEF before any Qt event-loop work runs.
+ QTimer.singleShot(0, show_menu)
+ return True
class NavigationBar(QFrame):
diff --git a/examples/screenshot.py b/examples/screenshot.py
index 5ca8d4913..727cd775f 100644
--- a/examples/screenshot.py
+++ b/examples/screenshot.py
@@ -73,11 +73,26 @@ def main():
# 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
}
+ if sys.platform.startswith("darwin"):
+ # Suppress macOS keychain authorization dialogs in headless use.
+ switches["use-mock-keychain"] = ""
+ # MachPortRendezvousServer bootstrap name requires a bundle ID.
+ # Without one, renderer subprocess bootstrap_look_up fails.
+ # --single-process runs the renderer in-process, avoiding the lookup.
+ switches["single-process"] = ""
+ # --single-process puts V8 in the browser process and requires a large
+ # contiguous CodeRange; --jitless disables JIT to remove that need.
+ switches["js-flags"] = "--jitless"
+ # Run network service in-process to avoid Mach port rendezvous
+ # failures for utility subprocesses on macOS.
+ switches["enable-features"] = "NetworkServiceInProcess2"
+ else:
+ # Tweaking OSR performance (Issue #240). On macOS ARM the viz
+ # Surfaces API is required for OSR browser creation, so these
+ # switches must not be passed on macOS.
+ switches["enable-begin-frame-scheduling"] = ""
+ switches["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
@@ -99,7 +114,7 @@ def check_versions():
ver=platform.python_version(),
arch=platform.architecture()[0]))
print("[screenshot.py] Pillow {ver}".format(ver=PILLOW_VERSION))
- assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this"
+ assert tuple(int(x) for x in cef.__version__.split(".")) >= (57, 0), "CEF Python v57.0+ required to run this"
def command_line_arguments():
@@ -139,9 +154,10 @@ def create_browser(settings):
browser = cef.CreateBrowserSync(window_info=window_info,
settings=settings,
url=URL)
+ print('created browser ', browser)
browser.SetClientHandler(LoadHandler())
browser.SetClientHandler(RenderHandler())
- browser.SendFocusEvent(True)
+ browser.SetFocus(True)
# You must call WasResized at least once to let know CEF that
# viewport size is available and that OnPaint may be called.
browser.WasResized()
diff --git a/examples/snippets/README-snippets.md b/examples/snippets/README-snippets.md
index 0f41e4f3f..746013895 100644
--- a/examples/snippets/README-snippets.md
+++ b/examples/snippets/README-snippets.md
@@ -28,6 +28,10 @@ directory. If looking for non-trivial examples then see the
- [cookies.py](cookies.py) - Shows how to fetch all cookies,
all cookies for a given url and how to delete a specific cookie.
+- [crossdomain_bindings.py](crossdomain_bindings.py) - Test Javascript
+ bindings across a cross-domain navigation flow. Simulates an SSO/auth
+ redirect (app page → auth domain → back to app) and demonstrates that
+ bindings only fire for the intended target domain.
- [javascript_bindings.py](javascript_bindings.py) - Communicate
between Python and Javascript asynchronously using
inter-process messaging with the use of Javascript Bindings.
@@ -41,8 +45,9 @@ directory. If looking for non-trivial examples then see the
to execute custom code before browser window closes.
- [ondomready.py](ondomready.py) - Execute custom Python code
on a web page as soon as DOM is ready.
-- [onpagecomplete.py](onpagecomplete.py) - Execute custom
- Python code on a web page when page loading is complete.
+- [onpagecomplete.py](onpagecomplete.py) - Execute custom Python
+ code on a web page after all visible content is loaded and painted,
+ using window.load and requestAnimationFrame.
- [setcookie.py](setcookie.py) - Shows how to set a cookie
- [window_size.py](window_size.py) - Set initial window size
without use of any third party GUI framework.
diff --git a/examples/snippets/cookies.py b/examples/snippets/cookies.py
index bd732a78a..d2a0d8e5d 100644
--- a/examples/snippets/cookies.py
+++ b/examples/snippets/cookies.py
@@ -9,7 +9,7 @@
def main():
cef.Initialize()
browser = cef.CreateBrowserSync(
- url="http://www.html-kit.com/tools/cookietester/",
+ url="https://www.google.com/",
window_title="Cookies")
browser.SetClientHandler(LoadHandler())
cef.MessageLoop()
@@ -19,7 +19,7 @@ def main():
class LoadHandler(object):
def OnLoadingStateChange(self, browser, is_loading, **_):
- if is_loading:
+ if not is_loading:
print("Page loading complete - start visiting cookies")
manager = cef.CookieManager.GetGlobalManager()
# Must keep a strong reference to the CookieVisitor object
@@ -32,7 +32,7 @@ def OnLoadingStateChange(self, browser, is_loading, **_):
# To visit cookies only for a given url uncomment the
# code below.
"""
- url = "http://www.html-kit.com/tools/cookietester/"
+ url = "https://www.google.com/"
http_only_cookies = False
result = manager.VisitUrlCookies(url, http_only_cookies,
self.cookie_visitor)
diff --git a/examples/snippets/crossdomain_bindings.py b/examples/snippets/crossdomain_bindings.py
new file mode 100644
index 000000000..9f3878339
--- /dev/null
+++ b/examples/snippets/crossdomain_bindings.py
@@ -0,0 +1,127 @@
+"""
+Test JavaScript bindings across a cross-domain navigation flow.
+
+Simulates an SSO/auth redirect scenario:
+ Step 1 - Target app page loads (example.com)
+ OnContextCreated detects target domain -> injects JS -> binding fires
+ Step 2 - Simulated redirect to auth/login domain (python.org)
+ OnContextCreated detects non-target domain -> skips injection -> no callback
+ Step 3 - Simulated auth complete, return to target app page (example.com)
+ OnContextCreated detects target domain again -> binding fires again
+
+Key points demonstrated:
+- JS bindings (V8 globals) are registered for every page in the browser instance,
+ including intermediate auth/login pages on other domains.
+- It is the user's responsibility to filter in OnContextCreated by URL/domain before
+ injecting JS so that callbacks only fire for the intended landing page.
+- Each domain's V8 context is isolated: globals registered for example.com are not
+ accessible to python.org and vice versa.
+"""
+
+import threading
+from cefpython3 import cefpython as cef
+
+TARGET_HOST = "example.com"
+AUTH_URL = "https://www.python.org/" # visually distinct: simulates Okta/login page
+TARGET_URL = "https://example.com/"
+NAV_DELAY_SEC = 3.0
+
+# Navigation state machine: avoid re-triggering on multiple OnLoadEnd fires
+STATE_INIT = "init"
+STATE_ON_TARGET_1 = "on_target_1" # first example.com load complete
+STATE_GOING_AUTH = "going_auth" # timer fired, navigating to auth
+STATE_ON_AUTH = "on_auth" # auth page load complete
+STATE_GOING_TARGET = "going_target" # timer fired, navigating back
+STATE_ON_TARGET_2 = "on_target_2" # final example.com load complete
+
+
+def main():
+ print(__doc__)
+ cef.Initialize()
+ browser = cef.CreateBrowserSync(url=TARGET_URL,
+ window_title="Cross-domain JS binding test")
+ handler = CrossDomainHandler(browser)
+ browser.SetClientHandler(handler)
+ bindings = cef.JavascriptBindings()
+ bindings.SetFunction("OnTargetPageReady", handler["_OnTargetPageReady"])
+ browser.SetJavascriptBindings(bindings)
+ cef.MessageLoop()
+ del handler
+ del browser
+ cef.Shutdown()
+
+
+class CrossDomainHandler(object):
+ def __init__(self, browser):
+ self.browser = browser
+ self._state = STATE_INIT
+
+ def __getitem__(self, key):
+ return getattr(self, key)
+
+ def OnContextCreated(self, browser, frame, **_):
+ if not frame.IsMain():
+ return
+ url = frame.GetUrl()
+ if not url or url == "about:blank":
+ return
+ if TARGET_HOST in url:
+ print("[OnContextCreated] Target domain — injecting JS binding: %s" % url)
+ browser.ExecuteJavascript("""
+ if (document.readyState === "complete"
+ || document.readyState === "interactive") {
+ requestAnimationFrame(function() { OnTargetPageReady(); });
+ } else {
+ window.addEventListener("load", function() {
+ requestAnimationFrame(function() { OnTargetPageReady(); });
+ });
+ }
+ """)
+ else:
+ print("[OnContextCreated] Non-target domain — skipping JS: %s" % url)
+
+ def OnLoadEnd(self, browser, frame, http_code, **_):
+ if not frame.IsMain():
+ return
+ url = frame.GetUrl()
+ if not url or url == "about:blank":
+ return
+ print("[OnLoadEnd] state=%s url=%s" % (self._state, url))
+
+ if self._state == STATE_INIT and TARGET_HOST in url:
+ self._state = STATE_ON_TARGET_1
+ print("[OnLoadEnd] Step 1 done. Redirecting to auth in %gs..." % NAV_DELAY_SEC)
+ threading.Timer(NAV_DELAY_SEC, self._navigate_auth).start()
+
+ elif self._state == STATE_GOING_AUTH and TARGET_HOST not in url:
+ self._state = STATE_ON_AUTH
+ print("[OnLoadEnd] Step 2 done (auth page). Returning to app in %gs..." % NAV_DELAY_SEC)
+ threading.Timer(NAV_DELAY_SEC, self._navigate_target).start()
+
+ elif self._state == STATE_GOING_TARGET and TARGET_HOST in url:
+ self._state = STATE_ON_TARGET_2
+ print("[OnLoadEnd] Step 3 done. Close window to exit.")
+
+ def _navigate_auth(self):
+ self._state = STATE_GOING_AUTH
+ print("[Nav] Navigating to auth domain: %s" % AUTH_URL)
+ self.browser.GetMainFrame().LoadUrl(AUTH_URL)
+
+ def _navigate_target(self):
+ self._state = STATE_GOING_TARGET
+ print("[Nav] Navigating back to target: %s" % TARGET_URL)
+ self.browser.GetMainFrame().LoadUrl(TARGET_URL)
+
+ def _OnTargetPageReady(self):
+ url = self.browser.GetMainFrame().GetUrl()
+ print("[Python callback] OnTargetPageReady fired! state=%s url=%s"
+ % (self._state, url))
+ self.browser.ExecuteJavascript(
+ 'setTimeout(function(){'
+ ' alert("JS binding works!\\nState: %s\\nURL: " + window.location.href);'
+ ' }, 0);' % self._state
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/snippets/network_cookies.py b/examples/snippets/network_cookies.py
index c3631cff5..424a227ad 100644
--- a/examples/snippets/network_cookies.py
+++ b/examples/snippets/network_cookies.py
@@ -1,5 +1,5 @@
"""
-Implement RequestHandler.CanGetCookies and CanSetCookie
+Implement RequestHandler.CanSendCookie and CanSaveCookie
to block or allow cookies over network requests.
"""
@@ -9,7 +9,7 @@
def main():
cef.Initialize()
browser = cef.CreateBrowserSync(
- url="http://www.html-kit.com/tools/cookietester/",
+ url="https://www.google.com/",
window_title="Network cookies")
browser.SetClientHandler(RequestHandler())
cef.MessageLoop()
@@ -22,23 +22,23 @@ def __init__(self):
self.getcount = 0
self.setcount = 0
- def CanGetCookies(self, frame, request, **_):
+ def CanSendCookie(self, browser, frame, request, cookie):
# There are multiple iframes on that website, let's log
# cookies only for the main frame.
if frame.IsMain():
self.getcount += 1
- print("-- CanGetCookies #"+str(self.getcount))
+ print("-- CanSendCookie #"+str(self.getcount))
print("url="+request.GetUrl()[0:80])
print("")
# Return True to allow reading cookies or False to block
return True
- def CanSetCookie(self, frame, request, cookie, **_):
+ def CanSaveCookie(self, browser, frame, request, response, cookie):
# There are multiple iframes on that website, let's log
# cookies only for the main frame.
if frame.IsMain():
self.setcount += 1
- print("-- CanSetCookie @"+str(self.setcount))
+ print("-- CanSaveCookie @"+str(self.setcount))
print("url="+request.GetUrl()[0:80])
print("Name="+cookie.GetName())
print("Value="+cookie.GetValue())
@@ -48,4 +48,4 @@ def CanSetCookie(self, frame, request, cookie, **_):
if __name__ == '__main__':
- main()
+ main()
\ No newline at end of file
diff --git a/examples/snippets/ondomready.py b/examples/snippets/ondomready.py
index 8775129c7..1b7fb1a36 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 LoadHandler object.
+Implements a custom "_OnDomReady" event using the OnContextCreated callback.
"""
from cefpython3 import cefpython as cef
@@ -8,34 +8,37 @@
def main():
cef.Initialize()
- browser = cef.CreateBrowserSync(url="https://www.google.com/",
+ browser = cef.CreateBrowserSync(url="https://example.com/",
window_title="_OnDomReady event")
- load_handler = LoadHandler(browser)
- browser.SetClientHandler(load_handler)
+ handler = DomReadyHandler(browser)
+ browser.SetClientHandler(handler)
bindings = cef.JavascriptBindings()
bindings.SetFunction("LoadHandler_OnDomReady",
- load_handler["_OnDomReady"])
+ handler["_OnDomReady"])
browser.SetJavascriptBindings(bindings)
cef.MessageLoop()
- del load_handler
+ del handler
del browser
cef.Shutdown()
-class LoadHandler(object):
+class DomReadyHandler(object):
def __init__(self, browser):
self.browser = browser
def __getitem__(self, key):
return getattr(self, key)
- def OnLoadStart(self, browser, **_):
+ def OnContextCreated(self, browser, frame, **_):
+ if not frame.IsMain():
+ return
browser.ExecuteJavascript("""
- if (document.readyState === "complete") {
- LoadHandler_OnDomReady();
+ if (document.readyState === "complete"
+ || document.readyState === "interactive") {
+ setTimeout(function(){ LoadHandler_OnDomReady(); }, 0);
} else {
document.addEventListener("DOMContentLoaded", function() {
- LoadHandler_OnDomReady();
+ setTimeout(function(){ LoadHandler_OnDomReady(); }, 0);
});
}
""")
diff --git a/examples/snippets/onpagecomplete.py b/examples/snippets/onpagecomplete.py
index e118e8e7c..debade470 100644
--- a/examples/snippets/onpagecomplete.py
+++ b/examples/snippets/onpagecomplete.py
@@ -1,6 +1,7 @@
"""
-Execute custom Python code on a web page when page loading is complete.
-Implements a custom "_OnPageComplete" event in the LoadHandler object.
+Execute custom Python code on a web page when all visible content is loaded.
+Implements a custom "_OnPageComplete" event that fires after window.load and
+a browser paint frame, ensuring content is fully rendered before notifying.
"""
from cefpython3 import cefpython as cef
@@ -10,25 +11,43 @@ def main():
cef.Initialize()
browser = cef.CreateBrowserSync(url="https://www.google.com/",
window_title="_OnPageComplete event")
- browser.SetClientHandler(LoadHandler())
+ handler = PageCompleteHandler(browser)
+ browser.SetClientHandler(handler)
+ bindings = cef.JavascriptBindings()
+ bindings.SetFunction("LoadHandler_OnPageComplete",
+ handler["_OnPageComplete"])
+ browser.SetJavascriptBindings(bindings)
cef.MessageLoop()
+ del handler
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)
+class PageCompleteHandler(object):
+ def __init__(self, browser):
+ self.browser = browser
- def _OnPageComplete(self, browser):
+ def __getitem__(self, key):
+ return getattr(self, key)
+
+ def OnContextCreated(self, browser, frame, **_):
+ if not frame.IsMain():
+ return
+ browser.ExecuteJavascript("""
+ window.addEventListener("load", function() {
+ requestAnimationFrame(function() {
+ LoadHandler_OnPageComplete();
+ });
+ });
+ """)
+
+ def _OnPageComplete(self):
print("Page loading is complete!")
- browser.ExecuteFunction("alert", "Message from Python: Page loading"
- " is complete!")
+ self.browser.ExecuteJavascript(
+ 'setTimeout(function(){'
+ ' alert("Message from Python: Page loading is complete!");'
+ ' }, 0);'
+ )
if __name__ == '__main__':
diff --git a/examples/snippets/setcookie.py b/examples/snippets/setcookie.py
index 04a99508e..1ad1e5ae9 100644
--- a/examples/snippets/setcookie.py
+++ b/examples/snippets/setcookie.py
@@ -6,28 +6,56 @@
import datetime
+class LoadHandler(object):
+ def OnLoadEnd(self, browser, frame, http_code, **_):
+ if not frame.IsMain():
+ return
+ 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": ".google.com",
+ "path": "/",
+ "secure": True,
+ "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("https://www.google.com/", cookie)
+ print("Cookie set: my_cookie=my_value")
+
+ # Delay so SetCookie (async on IO thread) completes before visiting
+ cef.PostDelayedTask(cef.TID_UI, 200, visit_cookies)
+
+
+def visit_cookies():
+ manager = cef.CookieManager.GetGlobalManager()
+ manager.VisitUrlCookies(
+ "https://www.google.com/",
+ False,
+ CookieVisitor())
+
+
+class CookieVisitor(object):
+ def Visit(self, cookie, count, total, delete_cookie_out):
+ print("Cookie[%d/%d]: %s=%s (domain=%s)" % (
+ count + 1, total,
+ cookie.Get("name"), cookie.Get("value"),
+ cookie.Get("domain")))
+ return True # continue visiting
+
+
def main():
cef.Initialize()
- cef.CreateBrowserSync(
- url="http://www.html-kit.com/tools/cookietester/",
+ browser = cef.CreateBrowserSync(
+ url="https://www.google.com/",
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)
+ browser.SetClientHandler(LoadHandler())
cef.MessageLoop()
cef.Shutdown()
diff --git a/examples/tkinter_.py b/examples/tkinter_.py
index 327f171fb..b126e224d 100644
--- a/examples/tkinter_.py
+++ b/examples/tkinter_.py
@@ -53,7 +53,7 @@ def main():
logger.info("Python {ver} {arch}".format(
ver=platform.python_version(), arch=platform.architecture()[0]))
logger.info("Tk {ver}".format(ver=tk.Tcl().eval('info patchlevel')))
- assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this"
+ assert tuple(int(x) for x in cef.__version__.split(".")) >= (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()
diff --git a/examples/tutorial.py b/examples/tutorial.py
index 860bbe12c..7f7408130 100644
--- a/examples/tutorial.py
+++ b/examples/tutorial.py
@@ -85,7 +85,7 @@ def check_versions():
print("[tutorial.py] Python {ver} {arch}".format(
ver=platform.python_version(),
arch=platform.architecture()[0]))
- assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this"
+ assert tuple(int(x) for x in cef.__version__.split(".")) >= (57, 0), "CEF Python v57.0+ required to run this"
def html_to_data_uri(html, js_callback=None):
@@ -188,6 +188,7 @@ def __init__(self, browser):
def test_multiple_callbacks(self, js_callback):
"""Test both javascript and python callbacks."""
+ print('in test_multiple_callbacks')
js_print(self.browser, "Python", "test_multiple_callbacks",
"Called from Javascript. Will call Javascript callback now.")
diff --git a/examples/wxpython.py b/examples/wxpython.py
index 92dd7d834..f09d07344 100644
--- a/examples/wxpython.py
+++ b/examples/wxpython.py
@@ -47,9 +47,6 @@ def main():
# the same time. This is an incorrect approach
# and only a temporary fix.
settings["external_message_pump"] = True
- if WINDOWS:
- # noinspection PyUnresolvedReferences, PyArgumentList
- cef.DpiAware.EnableHighDpiSupport()
cef.Initialize(settings=settings)
app = CefApp(False)
app.MainLoop()
@@ -65,7 +62,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__ >= "66.0", "CEF Python v66.0+ required to run this"
+ assert tuple(int(x) for x in cef.__version__.split(".")) >= (66, 0), "CEF Python v66.0+ required to run this"
def scale_window_size_for_high_dpi(width, height):
@@ -195,7 +192,9 @@ def OnSize(self, _):
elif LINUX:
(x, y) = (0, 0)
(width, height) = self.browser_panel.GetSize().Get()
+ self.browser.NotifyMoveOrResizeStarted()
self.browser.SetBounds(x, y, width, height)
+ return
self.browser.NotifyMoveOrResizeStarted()
def OnClose(self, event):
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..39be9a656
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,28 @@
+[build-system]
+requires = [
+ "scikit-build-core>=0.9",
+ "cython>=3.2",
+]
+build-backend = "scikit_build_core.build"
+
+[project]
+name = "cefpython3"
+dynamic = ["version"]
+requires-python = ">=3.10"
+license = { text = "BSD-3-Clause" }
+description = "Python bindings for the Chromium Embedded Framework"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "License :: OSI Approved :: BSD Software License",
+ "Operating System :: OS Independent",
+]
+
+[tool.scikit-build]
+cmake.build-type = "Release"
+cmake.args = ["-A", "x64"]
+wheel.packages = ["cefpython3"]
diff --git a/src/app.pyx b/src/app.pyx
index a00d58bf6..ce266616e 100644
--- a/src/app.pyx
+++ b/src/app.pyx
@@ -6,7 +6,7 @@ include "cefpython.pyx"
cdef public void App_OnBeforeCommandLineProcessing_BrowserProcess(
CefRefPtr[CefCommandLine] cefCommandLine
- ) except * with gil:
+ ) noexcept with gil:
try:
AppendSwitchesToCommandLine(cefCommandLine, g_commandLineSwitches)
Debug("App_OnBeforeCommandLineProcessing_BrowserProcess()")
diff --git a/src/browser.pyx b/src/browser.pyx
index c8cba03ad..1a6c9b5f1 100644
--- a/src/browser.pyx
+++ b/src/browser.pyx
@@ -5,9 +5,9 @@
include "cefpython.pyx"
cimport cef_types
+from libc.stdint cimport uint32_t, int64_t
+from libcpp cimport nullptr
from cef_types cimport cef_state_t
-IF UNAME_SYSNAME == "Linux":
- cimport x11
# cef_mouse_button_type_t, SendMouseClickEvent().
MOUSEBUTTON_LEFT = cef_types.MBT_LEFT
@@ -59,7 +59,7 @@ cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser,
global g_pyBrowsers
- if cefBrowser == NULL or not cefBrowser.get():
+ if not cefBrowser or not cefBrowser.get():
raise Exception("{caller}: CefBrowser reference is NULL"
.format(caller=callerIdStr))
@@ -148,7 +148,7 @@ cdef void RemovePyBrowser(int browserId) except *:
# noinspection PyUnresolvedReferences
Debug("del g_pyBrowsers[%s]" % browserId)
pyBrowser = g_pyBrowsers[browserId]
- pyBrowser.cefBrowser.Assign(NULL)
+ pyBrowser.cefBrowser.Assign(nullptr)
del pyBrowser
del g_pyBrowsers[browserId]
g_unreferenced_browsers.append(browserId)
@@ -176,7 +176,7 @@ cpdef PyBrowser GetBrowserByIdentifier(int identifier):
return None
cdef public void PyBrowser_ShowDevTools(CefRefPtr[CefBrowser] cefBrowser
- ) except * with gil:
+ ) noexcept with gil:
# Called from ClientHandler::OnContextMenuCommand
cdef PyBrowser pyBrowser
try:
@@ -207,7 +207,7 @@ cdef class PyBrowser:
cdef void* imageBuffer
cdef CefRefPtr[CefBrowser] GetCefBrowser(self) except *:
- if self.cefBrowser != NULL and self.cefBrowser.get():
+ if self.cefBrowser and self.cefBrowser.get():
return self.cefBrowser
raise Exception("PyBrowser.GetCefBrowser() failed: CefBrowser "
"was destroyed")
@@ -215,7 +215,7 @@ cdef class PyBrowser:
cdef CefRefPtr[CefBrowserHost] GetCefBrowserHost(self) except *:
cdef CefRefPtr[CefBrowserHost] cefBrowserHost = (
self.GetCefBrowser().get().GetHost())
- if cefBrowserHost != NULL and cefBrowserHost.get():
+ if cefBrowserHost and cefBrowserHost.get():
return cefBrowserHost
raise Exception("PyBrowser.GetCefBrowserHost() failed: this "
"method can only be called in the browser "
@@ -230,7 +230,7 @@ cdef class PyBrowser:
if self.imageBuffer:
free(self.imageBuffer)
- cpdef py_void SetClientCallback(self, py_string name, object callback):
+ cpdef py_void SetClientCallback(self, object name, object callback):
if not self.allowedClientCallbacks:
# DisplayHandler
self.allowedClientCallbacks += [
@@ -246,9 +246,9 @@ cdef class PyBrowser:
self.allowedClientCallbacks += ["OnBeforeResourceLoad",
"OnResourceRedirect", "GetAuthCredentials",
"OnQuotaRequest", "OnProtocolExecution",
- "GetResourceHandler",
- "OnBeforeBrowse", "OnRendererProcessTerminated",
- "OnPluginCrashed", "CanGetCookies", "CanSetCookie"]
+ "GetResourceHandler", "OnBeforeBrowse",
+ "OnRendererProcessTerminated",
+ "CanSendCookie", "CanSaveCookie"]
# RequestContextHandler
self.allowedClientCallbacks += ["GetCookieManager"]
# LoadHandler
@@ -278,6 +278,8 @@ cdef class PyBrowser:
# FocusHandler
self.allowedClientCallbacks += ["OnTakeFocus", "OnSetFocus",
"OnGotFocus"]
+ # ContextMenuHandler
+ self.allowedClientCallbacks += ["RunContextMenu"]
if name not in self.allowedClientCallbacks:
raise Exception("Browser.SetClientCallback() failed: unknown "
@@ -289,7 +291,7 @@ cdef class PyBrowser:
raise Exception("Browser.SetClientHandler() failed: __class__ "
"attribute missing")
cdef dict methods = {}
- cdef py_string key
+ cdef object key
cdef object method
cdef tuple value
for value in inspect.getmembers(clientHandler,
@@ -299,7 +301,7 @@ cdef class PyBrowser:
if key and key[0] != '_':
self.SetClientCallback(key, method)
- cpdef object GetClientCallback(self, py_string name):
+ cpdef object GetClientCallback(self, object name):
if name in self.clientCallbacks:
return self.clientCallbacks[name]
@@ -345,7 +347,7 @@ cdef class PyBrowser:
NonCriticalError("GetImage not implemented on this platform")
return None
- cpdef object GetSetting(self, py_string key):
+ cpdef object GetSetting(self, object key):
cdef int browser_id = self.GetIdentifier()
if browser_id in g_browser_settings:
if key in g_browser_settings[browser_id]:
@@ -356,7 +358,7 @@ cdef class PyBrowser:
# CEF API.
# --------------
- cpdef py_void AddWordToDictionary(self, py_string word):
+ cpdef py_void AddWordToDictionary(self, object word):
cdef CefString cef_word
PyToCefString(word, cef_word)
self.GetCefBrowserHost().get().AddWordToDictionary(cef_word)
@@ -394,9 +396,9 @@ cdef class PyBrowser:
# 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)
+ .GetCookieManager(
+ nullptr) \
+ .get().FlushStore(nullptr)
cdef int browserId = self.GetCefBrowser().get().GetIdentifier()
self.GetCefBrowserHost().get().CloseBrowser(bool(forceClose))
@@ -409,16 +411,18 @@ cdef class PyBrowser:
def ExecuteFunction(self, *args):
self.GetMainFrame().ExecuteFunction(*args)
- cpdef py_void ExecuteJavascript(self, py_string jsCode,
- py_string scriptUrl="", int startLine=1):
+ cpdef py_void ExecuteJavascript(self, object jsCode,
+ object scriptUrl=None, int startLine=1):
+ if scriptUrl is None:
+ scriptUrl = u""
self.GetMainFrame().ExecuteJavascript(jsCode, scriptUrl, startLine)
- cpdef py_void Find(self, int searchId, py_string searchText,
+ cpdef py_void Find(self, object searchText,
py_bool forward, py_bool matchCase,
py_bool findNext):
cdef CefString cefSearchText
PyToCefString(searchText, cefSearchText)
- self.GetCefBrowserHost().get().Find(searchId, cefSearchText,
+ self.GetCefBrowserHost().get().Find(cefSearchText,
bool(forward), bool(matchCase), bool(findNext))
cpdef PyFrame GetFocusedFrame(self):
@@ -426,16 +430,18 @@ cdef class PyBrowser:
"Browser.GetFocusedFrame() may only be called on UI thread")
return GetPyFrame(self.GetCefBrowser().get().GetFocusedFrame())
- cpdef PyFrame GetFrame(self, py_string name):
+ cpdef PyFrame GetFrameByName(self, object name):
assert IsThread(TID_UI), (
- "Browser.GetFrame() may only be called on the UI thread")
+ "Browser.GetFrameByName() may only be called on the UI thread")
cdef CefString cefName
PyToCefString(name, cefName)
- return GetPyFrame(self.GetCefBrowser().get().GetFrame(cefName))
+ return GetPyFrame(self.GetCefBrowser().get().GetFrameByName(cefName))
cpdef object GetFrameByIdentifier(self, object identifier):
- return GetPyFrame(self.GetCefBrowser().get().GetFrame(
- identifier))
+ cdef CefString cefIdentifier
+ PyToCefString(identifier, cefIdentifier)
+ return GetPyFrame(self.GetCefBrowser().get().GetFrameByIdentifier(
+ cefIdentifier))
cpdef list GetFrameNames(self):
assert IsThread(TID_UI), (
@@ -456,7 +462,7 @@ cdef class PyBrowser:
cdef PyFrame frame
cdef list frames = []
for name in names:
- frame = self.GetFrame(name)
+ frame = self.GetFrameByName(name)
frames.append(frame)
return frames
@@ -478,7 +484,7 @@ cdef class PyBrowser:
else:
return self.GetWindowHandle()
- cpdef py_string GetUrl(self):
+ cpdef object GetUrl(self):
return self.GetMainFrame().GetUrl()
cpdef object GetUserData(self, object key):
@@ -521,10 +527,10 @@ cdef class PyBrowser:
cpdef py_bool IsWindowRenderingDisabled(self):
return self.GetCefBrowserHost().get().IsWindowRenderingDisabled()
- cpdef py_string LoadUrl(self, py_string url):
+ cpdef object LoadUrl(self, object url):
self.GetMainFrame().LoadUrl(url)
- cpdef py_void Navigate(self, py_string url):
+ cpdef py_void Navigate(self, object url):
self.LoadUrl(url)
cpdef py_void NotifyMoveOrResizeStarted(self):
@@ -539,7 +545,7 @@ cdef class PyBrowser:
cpdef py_void ReloadIgnoreCache(self):
self.GetCefBrowser().get().ReloadIgnoreCache()
- cpdef py_void ReplaceMisspelling(self, py_string word):
+ cpdef py_void ReplaceMisspelling(self, object word):
cdef CefString cef_word
PyToCefString(word, cef_word)
self.GetCefBrowserHost().get().ReplaceMisspelling(cef_word)
@@ -581,7 +587,7 @@ cdef class PyBrowser:
cdef CefBrowserSettings settings
cdef CefPoint inspect_element_at
self.GetCefBrowserHost().get().ShowDevTools(
- window_info, NULL, settings,
+ window_info, nullptr, settings,
inspect_element_at)
cpdef py_void StopLoad(self):
@@ -654,7 +660,7 @@ cdef class PyBrowser:
right = monitorInfo.rcMonitor.right
bottom = monitorInfo.rcMonitor.bottom
# noinspection PyUnresolvedReferences
- SetWindowPos(hwnd, NULL,
+ SetWindowPos(hwnd, nullptr,
left, top, right-left, bottom-top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED)
else:
@@ -664,7 +670,7 @@ cdef class PyBrowser:
if not for_metro:
(left, top, right, bottom) = self.windowRect
# noinspection PyUnresolvedReferences
- SetWindowPos(hwnd, NULL,
+ SetWindowPos(hwnd, nullptr,
int(left), int(top),
int(right-left), int(bottom-top),
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED)
@@ -679,7 +685,7 @@ cdef class PyBrowser:
if "type" in pyEvent:
cefEvent.type = int(pyEvent["type"])
if "modifiers" in pyEvent:
- cefEvent.modifiers = pyEvent["modifiers"]
+ cefEvent.modifiers = pyEvent["modifiers"]
# Always set CefKeyEvent.windows_key_code in SendKeyEvent, even on
# Linux. When sending key event for 'backspace' on Linux and setting
# "native_key_code", "character", "unmodified_character" it doesn't
@@ -728,23 +734,17 @@ cdef class PyBrowser:
self.GetCefBrowserHost().get().SendMouseWheelEvent(mouseEvent,
deltaX, deltaY)
+ # for backward compatibility
cpdef py_void SendFocusEvent(self, py_bool setFocus):
- self.GetCefBrowserHost().get().SendFocusEvent(bool(setFocus))
+ self.GetCefBrowserHost().get().SetFocus(setFocus)
cpdef py_void SendCaptureLostEvent(self):
self.GetCefBrowserHost().get().SendCaptureLostEvent()
- cpdef py_void StartDownload(self, py_string url):
+ cpdef py_void StartDownload(self, object url):
self.GetCefBrowserHost().get().StartDownload(PyToCefStringValue(
url))
- cpdef py_void SetMouseCursorChangeDisabled(self, py_bool disabled):
- self.GetCefBrowserHost().get().SetMouseCursorChangeDisabled(
- bool(disabled))
-
- cpdef py_bool IsMouseCursorChangeDisabled(self):
- return self.GetCefBrowserHost().get().IsMouseCursorChangeDisabled()
-
cpdef py_bool TryCloseBrowser(self):
return self.GetCefBrowserHost().get().TryCloseBrowser()
@@ -757,35 +757,12 @@ cdef class PyBrowser:
cpdef py_void NotifyScreenInfoChanged(self):
self.GetCefBrowserHost().get().NotifyScreenInfoChanged()
- cdef void SendProcessMessage(self, cef_process_id_t targetProcess,
- object frameId, py_string messageName, list pyArguments
- ) except *:
- cdef CefRefPtr[CefProcessMessage] message = \
- CefProcessMessage_Create(PyToCefStringValue(messageName))
- # This does not work, no idea why, the CEF implementation
- # seems not to allow it, both Assign() and swap() do not work:
- # | message.get().GetArgumentList().Assign(arguments.get())
- # | message.get().GetArgumentList().swap(arguments)
- cdef CefRefPtr[CefListValue] messageArguments = \
- message.get().GetArgumentList()
- PyListToExistingCefListValue(self.GetIdentifier(), frameId,
- pyArguments, messageArguments)
- Debug("SendProcessMessage(): message=%s, arguments size=%d" % (
- messageName,
- message.get().GetArgumentList().get().GetSize()))
- cdef cpp_bool success = \
- self.GetCefBrowser().get().SendProcessMessage(
- targetProcess, message)
- if not success:
- raise Exception("Browser.SendProcessMessage() failed: "\
- "messageName=%s" % messageName)
-
# -------------------------------------------------------------------------
# OSR drag & drop
# -------------------------------------------------------------------------
cpdef py_void DragTargetDragEnter(self, DragData drag_data, int x, int y,
- uint32 allowed_ops):
+ uint32_t allowed_ops):
cdef CefMouseEvent mouse_event
mouse_event.x = x
mouse_event.y = y
@@ -793,7 +770,7 @@ cdef class PyBrowser:
drag_data.cef_drag_data, mouse_event,
allowed_ops)
- cpdef py_void DragTargetDragOver(self, int x, int y, uint32 allowed_ops):
+ cpdef py_void DragTargetDragOver(self, int x, int y, uint32_t allowed_ops):
cdef CefMouseEvent mouse_event
mouse_event.x = x
mouse_event.y = y
@@ -809,7 +786,7 @@ cdef class PyBrowser:
mouse_event.y = y
self.GetCefBrowserHost().get().DragTargetDrop(mouse_event)
- cpdef py_void DragSourceEndedAt(self, int x, int y, uint32 operation):
+ cpdef py_void DragSourceEndedAt(self, int x, int y, uint32_t operation):
self.GetCefBrowserHost().get().DragSourceEndedAt(
x, y, operation)
diff --git a/src/cefpython.pyx b/src/cefpython.pyx
index c3b1e1a13..06902ba2a 100644
--- a/src/cefpython.pyx
+++ b/src/cefpython.pyx
@@ -137,21 +137,11 @@ 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
-# error in Python 3.6.
-IF PY_MAJOR_VERSION == 2:
- # noinspection PyUnresolvedReferences
- import urlparse
- # noinspection PyUnresolvedReferences
- from urllib import urlencode as urllib_urlencode
- from urllib import quote as urlparse_quote
-ELSE:
- # noinspection PyUnresolvedReferences
- from urllib import parse as urlparse
- from urllib.parse import quote as urlparse_quote
- # noinspection PyUnresolvedReferences
- from urllib.parse import urlencode as urllib_urlencode
+# noinspection PyUnresolvedReferences
+from urllib import parse as urlparse
+from urllib.parse import quote as urlparse_quote
+# noinspection PyUnresolvedReferences
+from urllib.parse import urlencode as urllib_urlencode
# noinspection PyUnresolvedReferences
from cpython.version cimport PY_MAJOR_VERSION
@@ -193,6 +183,8 @@ from libcpp.string cimport string as cpp_string
# noinspection PyUnresolvedReferences
from wstring cimport wstring as cpp_wstring
# noinspection PyUnresolvedReferences
+from libcpp.memory cimport unique_ptr
+# noinspection PyUnresolvedReferences
from libc.string cimport strlen
# noinspection PyUnresolvedReferences
from libc.string cimport memcpy
@@ -225,20 +217,11 @@ ctypedef uintptr_t WindowHandle
# noinspection PyUnresolvedReferences
cimport ctime
-IF UNAME_SYSNAME == "Windows":
- from windows cimport *
- from dpi_aware_win cimport *
-ELIF UNAME_SYSNAME == "Linux":
- from linux cimport *
-ELIF UNAME_SYSNAME == "Darwin":
- from mac cimport *
+include "platform_cimports.pxi"
from cpp_utils cimport *
from task cimport *
-IF UNAME_SYSNAME == "Linux":
- cimport x11
-
from cef_string cimport *
cdef extern from *:
# noinspection PyUnresolvedReferences
@@ -250,16 +233,12 @@ from cef_types cimport (
CefSettings, CefBrowserSettings, CefRect, CefSize, CefPoint,
CefKeyEvent, CefMouseEvent, CefScreenInfo,
PathKey, PK_DIR_EXE, PK_DIR_MODULE,
- int32, uint32, int64, uint64,
cef_log_severity_t,
)
# noinspection PyUnresolvedReferences
from cef_ptr cimport CefRefPtr
-# noinspection PyUnresolvedReferences
-from cef_scoped_ptr cimport scoped_ptr
-
from cef_task cimport *
from cef_platform cimport *
from cef_app cimport *
@@ -273,7 +252,6 @@ from cef_time cimport *
from cef_values cimport *
from cefpython_app cimport *
from cef_process_message cimport *
-from cef_web_plugin cimport *
from cef_request_handler cimport *
from cef_request cimport *
from cef_cookie cimport *
@@ -321,11 +299,18 @@ g_browser_settings = {}
# noinspection PyUnresolvedReferences
cdef CefRefPtr[CefRequestContext] g_shared_request_context
-cdef scoped_ptr[MainMessageLoopExternalPump] g_external_message_pump
+cdef unique_ptr[MainMessageLoopExternalPump] g_external_message_pump
cdef py_bool g_MessageLoop_called = False
cdef py_bool g_MessageLoopWork_called = False
cdef py_bool g_cef_initialized = False
+cdef py_bool g_context_initialized = False
+cdef list g_pending_browsers = []
+
+IF UNAME_SYSNAME == "Linux":
+ # Keeps ctypes callback objects alive for the duration of gtk_main() and
+ # any pending one-shot GLib timers (Xwayland XReparentWindow scheduling).
+ g_linux_reparent_callbacks = []
cdef dict g_globalClientCallbacks = {}
@@ -358,7 +343,6 @@ include "window_info.pyx"
include "process_message_utils.pyx"
include "javascript_callback.pyx"
include "python_callback.pyx"
-include "web_plugin_info.pyx"
include "request.pyx"
include "cookie.pyx"
include "string_visitor.pyx"
@@ -376,6 +360,8 @@ include "image.pyx"
# Handlers
include "handlers/accessibility_handler.pyx"
include "handlers/browser_process_handler.pyx"
+include "handlers/context_menu_handler.pyx"
+include "handlers/cookie_access_filter.pyx"
include "handlers/display_handler.pyx"
include "handlers/focus_handler.pyx"
include "handlers/javascript_dialog_handler.pyx"
@@ -393,7 +379,7 @@ include "handlers/v8function_handler.pyx"
cdef public void cefpython_GetDebugOptions(
cpp_bool* debug
- ) except * with gil:
+ ) noexcept with gil:
# Called from subprocess/cefpython_app.cpp -> CefPythonApp constructor.
try:
debug[0] = bool(g_debug)
@@ -404,15 +390,15 @@ cdef public void cefpython_GetDebugOptions(
cdef public cpp_bool ApplicationSettings_GetBool(const char* key
) except * with gil:
# Called from client_handler/client_handler.cpp for example
- cdef py_string pyKey = CharToPyString(key)
+ cdef object pyKey = CharToPyString(key)
if pyKey in g_applicationSettings:
return bool(g_applicationSettings[pyKey])
return False
cdef public cpp_bool ApplicationSettings_GetBoolFromDict(const char* key1,
const char* key2) except * with gil:
- cdef py_string pyKey1 = CharToPyString(key1)
- cdef py_string pyKey2 = CharToPyString(key2)
+ cdef object pyKey1 = CharToPyString(key1)
+ cdef object pyKey2 = CharToPyString(key2)
cdef object dictValue # Yet to be checked whether it is `dict`
if pyKey1 in g_applicationSettings:
dictValue = g_applicationSettings[pyKey1]
@@ -424,14 +410,14 @@ cdef public cpp_bool ApplicationSettings_GetBoolFromDict(const char* key1,
cdef public cpp_string ApplicationSettings_GetString(const char* key
) except * with gil:
- cdef py_string pyKey = CharToPyString(key)
+ cdef object pyKey = CharToPyString(key)
cdef cpp_string cppString
if pyKey in g_applicationSettings:
cppString = PyStringToChar(AnyToPyString(g_applicationSettings[pyKey]))
return cppString
cdef public int CommandLineSwitches_GetInt(const char* key) except * with gil:
- cdef py_string pyKey = CharToPyString(key)
+ cdef object pyKey = CharToPyString(key)
if pyKey in g_commandLineSwitches:
return int(g_commandLineSwitches[pyKey])
return 0
@@ -472,16 +458,6 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
del command_line_switches
del commandLineSwitches
- IF UNAME_SYSNAME == "Linux":
- # Fix Issue #231 - Discovery of the "icudtl.dat" file fails on Linux.
- cdef str py_module_dir = GetModuleDirectory()
- cdef CefString cef_module_dir
- PyToCefString(py_module_dir, cef_module_dir)
- CefOverridePath(PK_DIR_EXE, cef_module_dir)\
- or Debug("ERROR: CefOverridePath failed")
- CefOverridePath(PK_DIR_MODULE, cef_module_dir)\
- or Debug("ERROR: CefOverridePath failed")
- # END IF UNAME_SYSNAME == "Linux":
if not application_settings:
application_settings = {}
@@ -581,8 +557,6 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
# ------------------------------------------------------------------------
if not "multi_threaded_message_loop" in application_settings:
application_settings["multi_threaded_message_loop"] = False
- if not "single_process" in application_settings:
- application_settings["single_process"] = False
# ------------------------------------------------------------------------
# ------------------------------------------------------------------------
@@ -593,6 +567,27 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
if not application_settings["cache_path"]:
g_commandLineSwitches["disable-gpu-shader-disk-cache"] = ""
+ if sys.platform == "win32":
+ # CEF 146 / Chrome 130+ ANGLE D3D11 backend crashes with a CHECK
+ # failure (STATUS_BREAKPOINT / exit_code=-2147483645) during GPU
+ # process init, falling back to software rendering after 3 crashes.
+ # D3D9 avoids the crash but only supports ES 2.0 (ES 3.0 errors).
+ # OpenGL ANGLE supports ES 3.0 and has no crash. Users can override
+ # by passing {"use-angle": "d3d11"} in the switches dict.
+ if "use-angle" not in g_commandLineSwitches:
+ g_commandLineSwitches["use-angle"] = "gl"
+
+ IF UNAME_SYSNAME == "Linux":
+ # Initialize GTK so GDK has a display connection before CefInitialize.
+ _linux_gtk_init()
+ # Auto-apply switches/settings required for CEF 146 on Linux/Xwayland.
+ # Uses setdefault so user-supplied values are never overwritten.
+ _linux_apply_initialize_defaults(application_settings,
+ g_commandLineSwitches)
+ # Pre-seed Chrome profile files to prevent the profile-picker keepalive
+ # from blocking OnContextInitialized (Chrome 146).
+ if application_settings.get("cache_path"):
+ _linux_setup_profile(application_settings["cache_path"])
cdef CefRefPtr[CefApp] cefApp = new CefPythonApp()
@@ -600,8 +595,29 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
cdef HINSTANCE hInstance = GetModuleHandle(NULL)
cdef CefMainArgs cefMainArgs = CefMainArgs(hInstance)
ELIF UNAME_SYSNAME == "Linux":
- # TODO: use the CefMainArgs(int argc, char** argv) constructor.
- cdef CefMainArgs cefMainArgs
+ # Build a complete argv so the browser process sees all switches.
+ # OnBeforeChildProcessLaunch only reaches child processes; switches
+ # like --in-process-gpu and --single-process must be in the browser
+ # process's own command line to take effect.
+ _cefMainArgvPyList = [sys.executable.encode('utf-8')]
+ for _cefArgK, _cefArgV in g_commandLineSwitches.items():
+ if _cefArgV:
+ _cefMainArgvPyList.append(
+ ("--{}={}".format(_cefArgK, _cefArgV)).encode('utf-8'))
+ else:
+ _cefMainArgvPyList.append(
+ ("--{}".format(_cefArgK)).encode('utf-8'))
+ cdef int _cefMainArgc = len(_cefMainArgvPyList)
+ cdef char** _cefMainArgvC = \
+ malloc(_cefMainArgc * sizeof(char*))
+ cdef bytes _cefMainArgvItem
+ cdef int _cefMainArgvI
+ for _cefMainArgvI in range(_cefMainArgc):
+ _cefMainArgvItem = _cefMainArgvPyList[_cefMainArgvI]
+ _cefMainArgvC[_cefMainArgvI] = _cefMainArgvItem
+ cdef CefMainArgs cefMainArgs = CefMainArgs(_cefMainArgc, _cefMainArgvC)
+ # _cefMainArgvPyList keeps the bytes alive; freed below after
+ # CefInitialize() has processed the command line.
ELIF UNAME_SYSNAME == "Darwin":
# TODO: use the CefMainArgs(int argc, char** argv) constructor.
cdef CefMainArgs cefMainArgs
@@ -617,23 +633,36 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
g_applicationSettings[key] = copy.deepcopy(application_settings[key])
cdef CefSettings cefApplicationSettings
- # No sandboxing for the subprocesses
- cefApplicationSettings.no_sandbox = 1
+ IF UNAME_SYSNAME == "Linux":
+ # On Linux, leave no_sandbox=0 so Chrome's startup code registers the
+ # Mojo IPC bootstrap fd (GlobalDescriptors key 7) for every subprocess.
+ # Setting no_sandbox=1 would cause BasicStartupComplete() to append
+ # --no-sandbox before fd registration, causing all subprocesses to crash
+ # with "Failed global descriptor lookup: 7". Sandbox behaviour is instead
+ # controlled via --disable-setuid-sandbox / --disable-namespace-sandbox
+ # command-line switches passed by the caller.
+ pass
+ ELSE:
+ # On Windows/macOS the sandbox helper binary is not shipped with
+ # cefpython, so disable sandboxing entirely.
+ cefApplicationSettings.no_sandbox = 1
SetApplicationSettings(application_settings, &cefApplicationSettings)
# External message pump
if GetAppSetting("external_message_pump")\
and not g_external_message_pump.get():
Debug("Create external message pump")
+ global g_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())
+ g_external_message_pump = MainMessageLoopExternalPump.Create()
Debug("CefInitialize()")
cdef cpp_bool ret
with nogil:
ret = CefInitialize(cefMainArgs, cefApplicationSettings, cefApp, NULL)
+ IF UNAME_SYSNAME == "Linux":
+ free(_cefMainArgvC)
global g_cef_initialized
g_cef_initialized = True
@@ -641,10 +670,35 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs):
if not ret:
Debug("CefInitialize() failed")
- IF UNAME_SYSNAME == "Linux":
+ # Pump the message loop until OnContextInitialized fires. This
+ # guarantees that CreateBrowserSync() can be called immediately after
+ # Initialize() without hitting the deferred-creation path or getting
+ # a null browser from CefBrowserHost::CreateBrowserSync().
+ # Use a generous ceiling (30s) for CI environments where utility
+ # subprocesses (storage service) crash and delay context initialization.
+ if ret:
+ # On Linux, skip this pump entirely: the Ozone X11 backend needs
+ # gtk_main() (a blocking GLib main loop) running before
+ # OnContextInitialized can fire. The external caller (hello_world.py,
+ # test harnesses) must enter gtk_main() immediately after Initialize()
+ # and drive the loop via the GLib timer callback.
+ # On Windows/macOS, pump up to 30 s as before.
+ IF UNAME_SYSNAME != "Linux":
+ for _ in range(3000):
+ with nogil:
+ CefDoMessageLoopWork()
+ if g_context_initialized:
+ break
+ time.sleep(0.01)
+ if not g_context_initialized:
+ Debug("CefInitialize() WARNING: OnContextInitialized not received"
+ " within 30 seconds")
+
+ if sys.platform.startswith("linux"):
# Install by default.
WindowUtils.InstallX11ErrorHandlers()
+
return ret
def CreateBrowser(**kwargs):
@@ -670,8 +724,33 @@ def CreateBrowserSync(windowInfo=None,
raise Exception("Invalid argument: "+kwarg)
Debug("CreateBrowserSync() called")
- assert IsThread(TID_UI), (
- "cefpython.CreateBrowserSync() may only be called on the UI thread")
+ # CEF 146+: CefCurrentlyOn(TID_UI) returns false before MessageLoop starts,
+ # so skip the assert here and let CEF's own internal checks handle it.
+
+ # Defer browser creation until OnContextInitialized fires inside MessageLoop.
+ # In CEF 123+, browser creation before OnContextInitialized causes
+ # blink.mojom.WidgetHost rejection and renderer shows no content.
+ # Initialize() pumps the loop for up to 30s; if still not initialized
+ # (e.g. slow CI), pump an additional 30s before giving up.
+ if not g_context_initialized:
+ Debug("CreateBrowserSync(): OnContextInitialized not yet received,"
+ " pumping message loop")
+ IF UNAME_SYSNAME != "Linux":
+ for _ in range(3000):
+ with nogil:
+ CefDoMessageLoopWork()
+ if g_context_initialized:
+ break
+ time.sleep(0.01)
+ if not g_context_initialized:
+ Debug("CreateBrowserSync() deferred until OnContextInitialized")
+ g_pending_browsers.append({
+ "windowInfo": windowInfo,
+ "browserSettings": browserSettings,
+ "navigateUrl": navigateUrl,
+ "window_title": window_title,
+ })
+ return None
"""
# CEF views
@@ -720,6 +799,18 @@ def CreateBrowserSync(windowInfo=None,
elif not isinstance(windowInfo, WindowInfo):
raise Exception("CreateBrowserSync() failed: windowInfo: invalid object")
+ # On Linux, when no parent window is given, auto-create a GTK toplevel
+ # so callers need no GTK-specific code (same API as Windows/Mac).
+ _linux_toplevel_state = None
+ IF UNAME_SYSNAME == "Linux":
+ if windowInfo.windowType == "child" and windowInfo.parentWindowHandle == 0:
+ _linux_toplevel_state = _linux_create_toplevel(
+ window_title or "CEF Browser")
+ windowInfo.SetAsChild(
+ _linux_toplevel_state['xid'],
+ [0, 0, _linux_toplevel_state['width'],
+ _linux_toplevel_state['height']])
+
if window_title and windowInfo.parentWindowHandle == 0:
windowInfo.windowName = window_title
@@ -761,14 +852,16 @@ def CreateBrowserSync(windowInfo=None,
else:
cefRequestContext.Assign(g_shared_request_context.get())
+ cdef CefRefPtr[CefDictionaryValue] extra_info
+
# CEF browser creation.
with nogil:
cefBrowser = cef_browser_static.CreateBrowserSync(
cefWindowInfo, clientHandler,
- cefNavigateUrl, cefBrowserSettings,
+ cefNavigateUrl, cefBrowserSettings, extra_info,
cefRequestContext)
- if cefBrowser == NULL or not cefBrowser.get():
+ if not cefBrowser or not cefBrowser.get():
Debug("CefBrowser::CreateBrowserSync() failed")
return None
else:
@@ -816,6 +909,12 @@ def CreateBrowserSync(windowInfo=None,
MacSetWindowTitle(cefBrowser,
PyStringToChar(windowInfo.windowName))
+ IF UNAME_SYSNAME == "Linux":
+ if windowInfo._linux_embed_info:
+ _linux_schedule_xembed(pyBrowser, windowInfo._linux_embed_info)
+ if _linux_toplevel_state is not None:
+ _linux_register_window_callbacks(pyBrowser, _linux_toplevel_state)
+
return pyBrowser
def MessageLoop():
@@ -825,8 +924,11 @@ def MessageLoop():
global g_MessageLoop_called
g_MessageLoop_called = True
- with nogil:
- CefRunMessageLoop()
+ IF UNAME_SYSNAME == "Linux":
+ _linux_message_loop()
+ ELSE:
+ with nogil:
+ CefRunMessageLoop()
def MessageLoopWork():
# Perform a single iteration of CEF message loop processing.
@@ -852,6 +954,15 @@ def SingleMessageLoop():
def QuitMessageLoop():
Debug("QuitMessageLoop()")
+ IF UNAME_SYSNAME == "Linux":
+ import ctypes as _ct
+ _gtk = _ct.CDLL("libgtk-3.so.0")
+ # Only call gtk_main_quit() when a GTK main loop is actually running.
+ # _on_delete() may have already called it (via gtk_main_quit directly),
+ # and calling it a second time during the drain would generate a
+ # spurious "assertion 'main_loops != NULL' failed" warning.
+ if _gtk.gtk_main_level() > 0:
+ _gtk.gtk_main_quit()
with nogil:
CefQuitMessageLoop()
@@ -961,17 +1072,18 @@ def Shutdown():
MacShutdown()
def SetOsModalLoop(py_bool modalLoop):
- cdef cpp_bool cefModalLoop = bool(modalLoop)
- with nogil:
- CefSetOSModalLoop(cefModalLoop)
+ IF UNAME_SYSNAME == "Windows":
+ cdef cpp_bool cefModalLoop = bool(modalLoop)
+ with nogil:
+ CefSetOSModalLoop(cefModalLoop)
-cpdef py_void SetGlobalClientCallback(py_string name, object callback):
+cpdef py_void SetGlobalClientCallback(object name, object callback):
global g_globalClientCallbacks
# Global callbacks are prefixed with "_" in documentation.
# Accept both with and without a prefix.
if name.startswith("_"):
name = name[1:]
- if name in ["OnCertificateError", "OnBeforePluginLoad", "OnAfterCreated",
+ if name in ["OnCertificateError", "OnAfterCreated",
"OnAccessibilityTreeChange", "OnAccessibilityLocationChange"]:
g_globalClientCallbacks[name] = callback
else:
@@ -983,7 +1095,7 @@ cpdef py_void SetGlobalClientHandler(object clientHandler):
raise Exception("SetGlobalClientHandler() failed: __class__ "
"attribute missing")
cdef dict methods = {}
- cdef py_string key
+ cdef object key
cdef object method
cdef tuple value
for value in inspect.getmembers(clientHandler,
@@ -993,14 +1105,14 @@ cpdef py_void SetGlobalClientHandler(object clientHandler):
if key and key[0:2] != '__':
SetGlobalClientCallback(key, method)
-cpdef object GetGlobalClientCallback(py_string name):
+cpdef object GetGlobalClientCallback(object name):
global g_globalClientCallbacks
if name in g_globalClientCallbacks:
return g_globalClientCallbacks[name]
else:
return None
-cpdef object GetAppSetting(py_string key):
+cpdef object GetAppSetting(object key):
global g_applicationSettings
if key in g_applicationSettings:
return g_applicationSettings[key]
@@ -1019,7 +1131,7 @@ cpdef dict GetVersion():
cef_commit_number=__cef_commit_number__,
)
-cpdef LoadCrlSetsFile(py_string path):
+cpdef LoadCrlSetsFile(object path):
CefLoadCRLSetsFile(PyToCefStringValue(path))
cpdef GetDataUrl(data, mediatype="html"):
diff --git a/src/cefpython3.wx/chromectrl.py b/src/cefpython3.wx/chromectrl.py
index cf06f82a2..0c0129d31 100644
--- a/src/cefpython3.wx/chromectrl.py
+++ b/src/cefpython3.wx/chromectrl.py
@@ -160,9 +160,6 @@ def __init__(self, parent, url="", useTimer=True,
if not browserSettings:
browserSettings = {}
- # Disable plugins:
- # | browserSettings["plugins_disabled"] = True
-
self.browser = cefpython.CreateBrowserSync(windowInfo,
browserSettings=browserSettings, navigateUrl=url)
diff --git a/src/client_handler/CMakeLists.txt b/src/client_handler/CMakeLists.txt
new file mode 100644
index 000000000..354f7bb62
--- /dev/null
+++ b/src/client_handler/CMakeLists.txt
@@ -0,0 +1,63 @@
+file(GLOB _all_cpp "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
+if(APPLE)
+ file(GLOB _mm_files "${CMAKE_CURRENT_SOURCE_DIR}/*.mm")
+ list(APPEND _all_cpp ${_mm_files})
+endif()
+
+set(_sources "")
+foreach(_f IN LISTS _all_cpp)
+ get_filename_component(_name "${_f}" NAME)
+ set(_skip FALSE)
+ if(_name MATCHES "_win\\.cpp$" AND NOT WIN32)
+ set(_skip TRUE)
+ elseif(_name MATCHES "_linux\\.cpp$" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(_skip TRUE)
+ elseif(_name MATCHES "_mac\\.(cpp|mm)$" AND NOT APPLE)
+ set(_skip TRUE)
+ elseif(_name MATCHES "^x11\\.cpp$" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(_skip TRUE)
+ elseif(_name MATCHES "gtk" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(_skip TRUE)
+ endif()
+ if(NOT _skip)
+ list(APPEND _sources "${_f}")
+ endif()
+endforeach()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
+endif()
+
+add_library(client_handler STATIC ${_sources})
+add_dependencies(client_handler cefpython_headers)
+
+target_include_directories(client_handler PRIVATE
+ "${CEFPYTHON_SRC_DIR}"
+ "${CEFPYTHON_SRC_DIR}/common"
+ "${CEFPYTHON_PYX_STAGE_DIR}"
+ ${Python_INCLUDE_DIRS}
+)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ target_include_directories(client_handler PRIVATE ${GTK3_INCLUDE_DIRS})
+endif()
+
+if(WIN32)
+ target_compile_options(client_handler PRIVATE /EHsc /wd4190)
+ target_compile_definitions(client_handler PRIVATE
+ WIN32 _WIN32 _WINDOWS
+ NTDDI_VERSION=0x06010000 WINVER=0x0601 _WIN32_WINNT=0x0601
+ NDEBUG _NDEBUG _CRT_SECURE_NO_WARNINGS NOMINMAX
+ BROWSER_PROCESS
+ CEFPYTHON_API_H_FILE="cefpython_api_fixed.h"
+ )
+elseif(APPLE)
+ target_compile_options(client_handler PRIVATE -DNDEBUG -O3)
+ target_compile_definitions(client_handler PRIVATE
+ CEFPYTHON_API_H_FILE="cefpython_api_fixed.h")
+else()
+ target_compile_options(client_handler PRIVATE -DNDEBUG -O3 -Wno-stringop-overflow)
+ target_compile_definitions(client_handler PRIVATE
+ CEFPYTHON_API_H_FILE="cefpython_api_fixed.h")
+endif()
diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile
index 25e645ff7..84472c744 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 accessibility_handler.cpp \
+ cef_log.cpp accessibility_handler.cpp cookie_access_filter.cpp \
$(SRC_MORE)
OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o))
diff --git a/src/client_handler/client_handler.cpp b/src/client_handler/client_handler.cpp
index daf9a4e59..02190eaa1 100644
--- a/src/client_handler/client_handler.cpp
+++ b/src/client_handler/client_handler.cpp
@@ -27,6 +27,7 @@
bool ClientHandler::OnProcessMessageReceived(
CefRefPtr browser,
+ CefRefPtr frame,
CefProcessId source_process,
CefRefPtr message)
{
@@ -34,16 +35,14 @@ bool ClientHandler::OnProcessMessageReceived(
if (source_process != PID_RENDERER) {
return false;
}
- std::string messageName = message->GetName().ToString();
+ const std::string& messageName = message->GetName();
std::string logMessage = "[Browser process] OnProcessMessageReceived(): ";
logMessage.append(messageName.c_str());
LOG(INFO) << logMessage.c_str();
if (messageName == "OnContextCreated") {
CefRefPtr arguments = message->GetArgumentList();
- if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) {
- int64 frameId = arguments->GetInt(0);
- CefRefPtr frame = browser->GetFrame(frameId);
- if (!frame.get()) {
+ if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_STRING) {
+ if (!frame.get()) {
// Frame was already destroyed while IPC messaging was
// executing. Issue #431. User callback will not be
// executed in such case.
@@ -60,9 +59,9 @@ bool ClientHandler::OnProcessMessageReceived(
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 2 \
&& arguments->GetType(0) == VTYPE_INT \
- && arguments->GetType(1) == VTYPE_INT) {
+ && arguments->GetType(1) == VTYPE_STRING) {
int browserId = arguments->GetInt(0);
- int64 frameId = arguments->GetInt(1);
+ CefString frameId = arguments->GetString(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
@@ -79,12 +78,12 @@ bool ClientHandler::OnProcessMessageReceived(
CefRefPtr arguments = message->GetArgumentList();
if (arguments->GetSize() == 3
// frameId
- && arguments->GetType(0) == VTYPE_INT
+ && arguments->GetType(0) == VTYPE_STRING
// functionName
&& arguments->GetType(1) == VTYPE_STRING
// functionArguments
&& arguments->GetType(2) == VTYPE_LIST) {
- int64 frameId = arguments->GetInt(0);
+ CefString frameId = arguments->GetString(0);
CefString functionName = arguments->GetString(1);
CefRefPtr functionArguments = arguments->GetList(2);
// Even if frame was already destroyed (Issue #431) you still
diff --git a/src/client_handler/client_handler.h b/src/client_handler/client_handler.h
index 3e9e3917b..e52d0b50c 100644
--- a/src/client_handler/client_handler.h
+++ b/src/client_handler/client_handler.h
@@ -87,6 +87,7 @@ class ClientHandler : public CefClient,
}
bool OnProcessMessageReceived(CefRefPtr browser,
+ CefRefPtr frame,
CefProcessId source_process,
CefRefPtr message
) override;
diff --git a/src/client_handler/context_menu_handler.cpp b/src/client_handler/context_menu_handler.cpp
index e6be3986b..5ee2b559d 100644
--- a/src/client_handler/context_menu_handler.cpp
+++ b/src/client_handler/context_menu_handler.cpp
@@ -75,6 +75,18 @@ void ContextMenuHandler::OnBeforeContextMenu(
}
+bool ContextMenuHandler::RunContextMenu(
+ CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr params,
+ CefRefPtr model,
+ CefRefPtr callback)
+{
+ REQUIRE_UI_THREAD();
+ return ContextMenuHandler_RunContextMenu(browser, model, callback) != 0;
+}
+
+
bool ContextMenuHandler::OnContextMenuCommand(
CefRefPtr browser,
CefRefPtr frame,
diff --git a/src/client_handler/context_menu_handler.h b/src/client_handler/context_menu_handler.h
index e22f7541b..2eebcc7cd 100644
--- a/src/client_handler/context_menu_handler.h
+++ b/src/client_handler/context_menu_handler.h
@@ -21,6 +21,12 @@ class ContextMenuHandler : public CefContextMenuHandler
CefRefPtr params,
CefRefPtr model) override;
+ bool RunContextMenu(CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr params,
+ CefRefPtr model,
+ CefRefPtr callback) override;
+
bool OnContextMenuCommand(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr params,
diff --git a/src/client_handler/cookie_access_filter.cpp b/src/client_handler/cookie_access_filter.cpp
new file mode 100644
index 000000000..6eaeb70bf
--- /dev/null
+++ b/src/client_handler/cookie_access_filter.cpp
@@ -0,0 +1,24 @@
+// 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 "cookie_access_filter.h"
+#include "common/cefpython_public_api.h"
+
+
+bool CookieAccessFilter::CanSendCookie(CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request,
+ const CefCookie& cookie) {
+ REQUIRE_IO_THREAD();
+ return CookieAccessFilter_CanSendCookie(browser, frame, request, cookie);
+}
+
+bool CookieAccessFilter::CanSaveCookie(CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request,
+ CefRefPtr response,
+ const CefCookie& cookie) {
+ REQUIRE_IO_THREAD();
+ return CookieAccessFilter_CanSaveCookie(browser, frame, request, response, cookie);
+}
diff --git a/src/client_handler/cookie_access_filter.h b/src/client_handler/cookie_access_filter.h
new file mode 100644
index 000000000..e914314cf
--- /dev/null
+++ b/src/client_handler/cookie_access_filter.h
@@ -0,0 +1,28 @@
+// 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
+
+#pragma once
+#include "common/cefpython_public_api.h"
+#include "include/cef_resource_request_handler.h"
+
+
+class CookieAccessFilter : public CefCookieAccessFilter
+{
+public:
+ CookieAccessFilter(){}
+ virtual ~CookieAccessFilter(){}
+
+ virtual bool CanSendCookie(CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request,
+ const CefCookie& cookie) override;
+ virtual bool CanSaveCookie(CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request,
+ CefRefPtr response,
+ const CefCookie& cookie) override;
+
+private:
+ IMPLEMENT_REFCOUNTING(CookieAccessFilter);
+};
diff --git a/src/client_handler/cookie_visitor.cpp b/src/client_handler/cookie_visitor.cpp
index eea845f35..1ac11add8 100644
--- a/src/client_handler/cookie_visitor.cpp
+++ b/src/client_handler/cookie_visitor.cpp
@@ -11,7 +11,7 @@ bool CookieVisitor::Visit(
int total,
bool& deleteCookie
) {
- REQUIRE_IO_THREAD();
+ REQUIRE_UI_THREAD();
return CookieVisitor_Visit(cookieVisitorId_, cookie, count, total,
deleteCookie);
}
diff --git a/src/client_handler/cookie_visitor.h b/src/client_handler/cookie_visitor.h
index 54c2b53fd..0b990baca 100644
--- a/src/client_handler/cookie_visitor.h
+++ b/src/client_handler/cookie_visitor.h
@@ -24,7 +24,7 @@ class CookieVisitor : public CefCookieVisitor
int count,
int total,
bool& deleteCookie
- ) OVERRIDE;
+ ) override;
protected:
IMPLEMENT_REFCOUNTING(CookieVisitor);
diff --git a/src/client_handler/dialog_handler.cpp b/src/client_handler/dialog_handler.cpp
index ab90de9b6..4f7117590 100644
--- a/src/client_handler/dialog_handler.cpp
+++ b/src/client_handler/dialog_handler.cpp
@@ -19,7 +19,8 @@ bool DialogHandler::OnFileDialog(CefRefPtr browser,
const CefString& title,
const CefString& default_file_path,
const std::vector& accept_filters,
- int selected_accept_filter,
+ const std::vector& accept_extensions,
+ const std::vector& accept_descriptions,
CefRefPtr callback)
{
#if defined(OS_LINUX)
@@ -28,7 +29,8 @@ bool DialogHandler::OnFileDialog(CefRefPtr browser,
title,
default_file_path,
accept_filters,
- selected_accept_filter,
+ accept_extensions,
+ accept_descriptions,
callback);
#else
return false;
diff --git a/src/client_handler/dialog_handler.h b/src/client_handler/dialog_handler.h
index 21d79a60d..1698c47bf 100644
--- a/src/client_handler/dialog_handler.h
+++ b/src/client_handler/dialog_handler.h
@@ -23,7 +23,8 @@ class DialogHandler : public CefDialogHandler
const CefString& title,
const CefString& default_file_path,
const std::vector& accept_filters,
- int selected_accept_filter,
+ const std::vector& accept_extensions,
+ const std::vector& accept_descriptions,
CefRefPtr callback)
override;
diff --git a/src/client_handler/dialog_handler_gtk.cpp b/src/client_handler/dialog_handler_gtk.cpp
index ce5a4d4e0..9326940c4 100644
--- a/src/client_handler/dialog_handler_gtk.cpp
+++ b/src/client_handler/dialog_handler_gtk.cpp
@@ -30,64 +30,44 @@ std::string GetPromptText(GtkDialog* dialog) {
return std::string();
}
-std::string GetDescriptionFromMimeType(const std::string& mime_type) {
- // Check for wild card mime types and return an appropriate description.
- static const struct {
- const char* mime_type;
- const char* label;
- } kWildCardMimeTypes[] = {
- {"audio", "Audio Files"},
- {"image", "Image Files"},
- {"text", "Text Files"},
- {"video", "Video Files"},
- };
-
- for (size_t i = 0;
- i < sizeof(kWildCardMimeTypes) / sizeof(kWildCardMimeTypes[0]); ++i) {
- if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
- return std::string(kWildCardMimeTypes[i].label);
+// Split |str| on |delim| and return the parts.
+std::vector SplitString(const std::string& str, char delim) {
+ std::vector result;
+ std::string token;
+ for (char c : str) {
+ if (c == delim) {
+ if (!token.empty())
+ result.push_back(token);
+ token.clear();
+ } else {
+ token += c;
+ }
}
-
- return std::string();
+ if (!token.empty())
+ result.push_back(token);
+ return result;
}
void AddFilters(GtkFileChooser* chooser,
const std::vector& accept_filters,
+ const std::vector& accept_extensions,
+ const std::vector& accept_descriptions,
bool include_all_files,
std::vector* filters) {
bool has_filter = false;
- for (size_t i = 0; i < accept_filters.size(); ++i) {
- const std::string& filter = accept_filters[i];
+ for (size_t j = 0; j < accept_filters.size(); ++j) {
+ const std::string& filter = accept_filters[j];
if (filter.empty())
continue;
- std::vector extensions;
- std::string description;
-
- size_t sep_index = filter.find('|');
- if (sep_index != std::string::npos) {
- // Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
- description = filter.substr(0, sep_index);
-
- const std::string& exts = filter.substr(sep_index + 1);
- size_t last = 0;
- size_t size = exts.size();
- for (size_t i = 0; i <= size; ++i) {
- if (i == size || exts[i] == ';') {
- std::string ext(exts, last, i - last);
- if (!ext.empty() && ext[0] == '.')
- extensions.push_back(ext);
- last = i + 1;
- }
- }
- } else if (filter[0] == '.') {
- // Treat as an extension beginning with the '.' character.
- extensions.push_back(filter);
- } else {
- // Otherwise convert mime type to one or more extensions.
- description = GetDescriptionFromMimeType(filter);
+ // Use pre-parsed extensions when available.
+ std::vector extensions =
+ SplitString(accept_extensions[j], ';');
+ std::string description = accept_descriptions[j];
+ if (extensions.empty()) {
+ // Fallback: convert MIME type to extensions.
std::vector ext;
CefGetExtensionsForMimeType(filter, ext);
for (size_t x = 0; x < ext.size(); ++x)
@@ -101,7 +81,9 @@ void AddFilters(GtkFileChooser* chooser,
std::string ext_str;
for (size_t x = 0; x < extensions.size(); ++x) {
- const std::string& pattern = "*" + extensions[x];
+ std::string pattern = extensions[x];
+ if (pattern[0] != '*')
+ pattern = "*" + pattern;
if (x != 0)
ext_str += ";";
ext_str += pattern;
@@ -133,7 +115,7 @@ void AddFilters(GtkFileChooser* chooser,
} // namespace
-ClientDialogHandlerGtk::ClientDialogHandlerGtk() : gtk_dialog_(NULL) {}
+ClientDialogHandlerGtk::ClientDialogHandlerGtk() : gtk_dialog_(nullptr) {}
bool ClientDialogHandlerGtk::OnFileDialog(
CefRefPtr browser,
@@ -141,26 +123,23 @@ bool ClientDialogHandlerGtk::OnFileDialog(
const CefString& title,
const CefString& default_file_path,
const std::vector& accept_filters,
- int selected_accept_filter,
+ const std::vector& accept_extensions,
+ const std::vector& accept_descriptions,
CefRefPtr callback) {
std::vector files;
GtkFileChooserAction action;
const gchar* accept_button;
- // Remove any modifier flags.
- FileDialogMode mode_type =
- static_cast(mode & FILE_DIALOG_TYPE_MASK);
-
- if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
+ if (mode == FILE_DIALOG_OPEN || mode == FILE_DIALOG_OPEN_MULTIPLE) {
action = GTK_FILE_CHOOSER_ACTION_OPEN;
- accept_button = GTK_STOCK_OPEN;
- } else if (mode_type == FILE_DIALOG_OPEN_FOLDER) {
+ accept_button = "_Open";
+ } else if (mode == FILE_DIALOG_OPEN_FOLDER) {
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
- accept_button = GTK_STOCK_OPEN;
- } else if (mode_type == FILE_DIALOG_SAVE) {
+ accept_button = "_Open";
+ } else if (mode == FILE_DIALOG_SAVE) {
action = GTK_FILE_CHOOSER_ACTION_SAVE;
- accept_button = GTK_STOCK_SAVE;
+ accept_button = "_Save";
} else {
NOTREACHED();
return false;
@@ -170,7 +149,7 @@ bool ClientDialogHandlerGtk::OnFileDialog(
if (!title.empty()) {
title_str = title;
} else {
- switch (mode_type) {
+ switch (mode) {
case FILE_DIALOG_OPEN:
title_str = "Open File";
break;
@@ -193,21 +172,13 @@ 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, "_Cancel",
+ GTK_RESPONSE_CANCEL, accept_button, GTK_RESPONSE_ACCEPT, nullptr);
- if (mode_type == FILE_DIALOG_OPEN_MULTIPLE)
+ if (mode == 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_set_show_hidden(GTK_FILE_CHOOSER(dialog),
- !(mode & FILE_DIALOG_HIDEREADONLY_FLAG));
-
- if (!default_file_path.empty() && mode_type == FILE_DIALOG_SAVE) {
+ if (!default_file_path.empty() && mode == FILE_DIALOG_SAVE) {
const std::string& file_path = default_file_path;
bool exists = false;
@@ -227,25 +198,22 @@ bool ClientDialogHandlerGtk::OnFileDialog(
}
std::vector filters;
- AddFilters(GTK_FILE_CHOOSER(dialog), accept_filters, true, &filters);
- if (selected_accept_filter < static_cast(filters.size())) {
- gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog),
- filters[selected_accept_filter]);
- }
+ AddFilters(GTK_FILE_CHOOSER(dialog), accept_filters, accept_extensions,
+ accept_descriptions, true, &filters);
bool success = false;
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
- if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_FOLDER ||
- mode_type == FILE_DIALOG_SAVE) {
+ if (mode == FILE_DIALOG_OPEN || mode == FILE_DIALOG_OPEN_FOLDER ||
+ mode == FILE_DIALOG_SAVE) {
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
files.push_back(std::string(filename));
success = true;
- } else if (mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
+ } else if (mode == FILE_DIALOG_OPEN_MULTIPLE) {
GSList* filenames =
gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
if (filenames) {
- for (GSList* iter = filenames; iter != NULL;
+ for (GSList* iter = filenames; iter != nullptr;
iter = g_slist_next(iter)) {
std::string path(static_cast(iter->data));
g_free(iter->data);
@@ -257,24 +225,10 @@ bool ClientDialogHandlerGtk::OnFileDialog(
}
}
- int filter_index = selected_accept_filter;
- if (success) {
- GtkFileFilter* selected_filter =
- gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
- if (selected_filter != NULL) {
- for (size_t x = 0; x < filters.size(); ++x) {
- if (filters[x] == selected_filter) {
- filter_index = x;
- break;
- }
- }
- }
- }
-
gtk_widget_destroy(dialog);
if (success)
- callback->Continue(filter_index, files);
+ callback->Continue(files);
else
callback->Cancel();
@@ -316,11 +270,6 @@ bool ClientDialogHandlerGtk::OnJSDialog(CefRefPtr browser,
js_dialog_callback_ = callback;
- if (!origin_url.empty()) {
- // title += " - ";
- // title += CefFormatUrlForSecurityDisplay(origin_url).ToString();
- }
-
GtkWindow* window = CefBrowser_GetGtkWindow(browser);
if (!window)
return false;
@@ -329,12 +278,12 @@ bool ClientDialogHandlerGtk::OnJSDialog(CefRefPtr browser,
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_CALLBACK(gtk_widget_hide_on_delete), nullptr);
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);
+ "_OK", GTK_RESPONSE_OK);
if (dialog_type != JSDIALOGTYPE_PROMPT)
gtk_widget_grab_focus(ok_button);
@@ -378,8 +327,8 @@ void ClientDialogHandlerGtk::OnResetDialogState(CefRefPtr browser) {
if (!gtk_dialog_)
return;
gtk_widget_destroy(gtk_dialog_);
- gtk_dialog_ = NULL;
- js_dialog_callback_ = NULL;
+ gtk_dialog_ = nullptr;
+ js_dialog_callback_ = nullptr;
}
// static
@@ -401,5 +350,5 @@ void ClientDialogHandlerGtk::OnDialogResponse(GtkDialog* dialog,
NOTREACHED();
}
- handler->OnResetDialogState(NULL);
+ handler->OnResetDialogState(nullptr);
}
diff --git a/src/client_handler/dialog_handler_gtk.h b/src/client_handler/dialog_handler_gtk.h
index 59f65eda1..85c0fed42 100644
--- a/src/client_handler/dialog_handler_gtk.h
+++ b/src/client_handler/dialog_handler_gtk.h
@@ -25,8 +25,9 @@ class ClientDialogHandlerGtk : public CefDialogHandler,
const CefString& title,
const CefString& default_file_path,
const std::vector& accept_filters,
- int selected_accept_filter,
- CefRefPtr callback) OVERRIDE;
+ const std::vector& accept_extensions,
+ const std::vector& accept_descriptions,
+ CefRefPtr callback) override;
// CefJSDialogHandler methods.
bool OnJSDialog(CefRefPtr browser,
@@ -35,12 +36,12 @@ class ClientDialogHandlerGtk : public CefDialogHandler,
const CefString& message_text,
const CefString& default_prompt_text,
CefRefPtr callback,
- bool& suppress_message) OVERRIDE;
+ bool& suppress_message) override;
bool OnBeforeUnloadDialog(CefRefPtr browser,
const CefString& message_text,
bool is_reload,
- CefRefPtr callback) OVERRIDE;
- void OnResetDialogState(CefRefPtr browser) OVERRIDE;
+ CefRefPtr callback) override;
+ void OnResetDialogState(CefRefPtr browser) override;
private:
static void OnDialogResponse(GtkDialog* dialog,
diff --git a/src/client_handler/display_handler.cpp b/src/client_handler/display_handler.cpp
index b00bf6a51..573549e26 100644
--- a/src/client_handler/display_handler.cpp
+++ b/src/client_handler/display_handler.cpp
@@ -59,4 +59,13 @@ void DisplayHandler::OnLoadingProgressChange(CefRefPtr browser,
double progress) {
REQUIRE_UI_THREAD();
return DisplayHandler_OnLoadingProgressChange(browser, progress);
+}
+
+bool DisplayHandler::OnCursorChange(CefRefPtr browser,
+ CefCursorHandle cursor,
+ cef_cursor_type_t type,
+ const CefCursorInfo& custom_cursor_info)
+{
+ REQUIRE_UI_THREAD();
+ return DisplayHandler_OnCursorChange(browser, cursor);
}
\ No newline at end of file
diff --git a/src/client_handler/display_handler.h b/src/client_handler/display_handler.h
index a281713d9..382bceeaa 100644
--- a/src/client_handler/display_handler.h
+++ b/src/client_handler/display_handler.h
@@ -38,6 +38,12 @@ class DisplayHandler : public CefDisplayHandler
void OnLoadingProgressChange(CefRefPtr browser,
double progress) override;
+
+ bool OnCursorChange(CefRefPtr browser,
+ CefCursorHandle cursor,
+ cef_cursor_type_t type,
+ const CefCursorInfo& custom_cursor_info
+ ) override;
private:
IMPLEMENT_REFCOUNTING(DisplayHandler);
diff --git a/src/client_handler/download_handler.cpp b/src/client_handler/download_handler.cpp
index 9e887e467..ac63a4eee 100644
--- a/src/client_handler/download_handler.cpp
+++ b/src/client_handler/download_handler.cpp
@@ -6,7 +6,7 @@
#include "include/base/cef_logging.h"
-void DownloadHandler::OnBeforeDownload(
+bool DownloadHandler::OnBeforeDownload(
CefRefPtr browser,
CefRefPtr download_item,
const CefString& suggested_name,
@@ -23,6 +23,7 @@ void DownloadHandler::OnBeforeDownload(
LOG(INFO) << "[Browser process] Tried to download file,"
" but downloads are disabled";
}
+ return false;
}
diff --git a/src/client_handler/download_handler.h b/src/client_handler/download_handler.h
index e4fc5faf1..a64ba17fd 100644
--- a/src/client_handler/download_handler.h
+++ b/src/client_handler/download_handler.h
@@ -12,7 +12,7 @@ class DownloadHandler : public CefDownloadHandler
DownloadHandler(){}
virtual ~DownloadHandler(){}
- void OnBeforeDownload(CefRefPtr browser,
+ bool OnBeforeDownload(CefRefPtr browser,
CefRefPtr download_item,
const CefString& suggested_name,
CefRefPtr callback
diff --git a/src/client_handler/dpi_aware.cpp b/src/client_handler/dpi_aware.cpp
index 48e2a13f7..a7854079f 100644
--- a/src/client_handler/dpi_aware.cpp
+++ b/src/client_handler/dpi_aware.cpp
@@ -4,6 +4,8 @@
// Windows only
+#ifdef _WIN32
+
#pragma comment(lib, "Gdi32.lib")
#include
@@ -11,6 +13,7 @@
#include "include/wrapper/cef_closure_task.h"
#include "include/base/cef_bind.h"
#include "include/base/cef_logging.h"
+#include "include/base/cef_callback.h"
const int DEFAULT_DPIX = 96;
@@ -219,10 +222,11 @@ void SetBrowserDpiSettings(CefRefPtr cefBrowser,
CefPostDelayedTask(
TID_UI,
CefCreateClosureTask(
- base::Bind(&SetBrowserDpiSettings,
+ base::BindOnce(&SetBrowserDpiSettings,
cefBrowser, autoZooming)
),
50
);
}
+#endif // _WIN32
diff --git a/src/client_handler/lifespan_handler.cpp b/src/client_handler/lifespan_handler.cpp
index b4d7d52f6..1298a4112 100644
--- a/src/client_handler/lifespan_handler.cpp
+++ b/src/client_handler/lifespan_handler.cpp
@@ -11,6 +11,7 @@
bool LifespanHandler::OnBeforePopup(CefRefPtr browser,
CefRefPtr frame,
+ int popup_id,
const CefString& target_url,
const CefString& target_frame_name,
WindowOpenDisposition target_disposition,
@@ -19,6 +20,7 @@ bool LifespanHandler::OnBeforePopup(CefRefPtr browser,
CefWindowInfo& windowInfo,
CefRefPtr& client,
CefBrowserSettings& settings,
+ CefRefPtr& extra_info,
bool* no_javascript_access)
{
REQUIRE_UI_THREAD();
@@ -27,7 +29,7 @@ bool LifespanHandler::OnBeforePopup(CefRefPtr browser,
return LifespanHandler_OnBeforePopup(browser, frame, target_url,
target_frame_name, target_disposition, user_gesture,
popupFeaturesNotImpl, windowInfo, client, settings,
- no_javascript_access);
+ extra_info, no_javascript_access);
}
diff --git a/src/client_handler/lifespan_handler.h b/src/client_handler/lifespan_handler.h
index 91244eff9..ddef2bc91 100644
--- a/src/client_handler/lifespan_handler.h
+++ b/src/client_handler/lifespan_handler.h
@@ -16,6 +16,7 @@ class LifespanHandler : public CefLifeSpanHandler
bool OnBeforePopup(CefRefPtr browser,
CefRefPtr frame,
+ int popup_id,
const CefString& target_url,
const CefString& target_frame_name,
WindowOpenDisposition target_disposition,
@@ -24,6 +25,7 @@ class LifespanHandler : public CefLifeSpanHandler
CefWindowInfo& windowInfo,
CefRefPtr& client,
CefBrowserSettings& settings,
+ CefRefPtr& extra_info,
bool* no_javascript_access) override;
void OnAfterCreated(CefRefPtr browser) override;
bool DoClose(CefRefPtr browser) override;
diff --git a/src/client_handler/render_handler.cpp b/src/client_handler/render_handler.cpp
index cf45d15ee..1935a3baf 100644
--- a/src/client_handler/render_handler.cpp
+++ b/src/client_handler/render_handler.cpp
@@ -13,11 +13,11 @@ bool RenderHandler::GetRootScreenRect(CefRefPtr browser,
}
-bool RenderHandler::GetViewRect(CefRefPtr browser,
+void RenderHandler::GetViewRect(CefRefPtr browser,
CefRect& rect)
{
REQUIRE_UI_THREAD();
- return RenderHandler_GetViewRect(browser, rect);
+ RenderHandler_GetViewRect(browser, rect);
}
@@ -69,16 +69,6 @@ void RenderHandler::OnPaint(CefRefPtr browser,
}
-void RenderHandler::OnCursorChange(CefRefPtr browser,
- CefCursorHandle cursor,
- CursorType type,
- const CefCursorInfo& custom_cursor_info)
-{
- REQUIRE_UI_THREAD();
- RenderHandler_OnCursorChange(browser, cursor);
-}
-
-
void RenderHandler::OnScrollOffsetChanged(CefRefPtr browser,
double x,
double y)
diff --git a/src/client_handler/render_handler.h b/src/client_handler/render_handler.h
index 75eee86c5..c87c52354 100644
--- a/src/client_handler/render_handler.h
+++ b/src/client_handler/render_handler.h
@@ -22,7 +22,7 @@ class RenderHandler : public CefRenderHandler,
bool GetRootScreenRect(CefRefPtr browser,
CefRect& rect) override;
- bool GetViewRect(CefRefPtr browser,
+ void GetViewRect(CefRefPtr browser,
CefRect& rect) override;
bool GetScreenPoint(CefRefPtr browser,
@@ -46,12 +46,6 @@ class RenderHandler : public CefRenderHandler,
const void* buffer,
int width, int height) override;
- void OnCursorChange(CefRefPtr browser,
- CefCursorHandle cursor,
- CursorType type,
- const CefCursorInfo& custom_cursor_info
- ) override;
-
void OnScrollOffsetChanged(CefRefPtr browser,
double x,
double y) override;
diff --git a/src/client_handler/request_context_handler.cpp b/src/client_handler/request_context_handler.cpp
deleted file mode 100644
index bf816cf51..000000000
--- a/src/client_handler/request_context_handler.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2013 CEF Python, see the Authors file.
-// All rights reserved. Licensed under BSD 3-clause license.
-// Project website: https://github.com/cztomczak/cefpython
-
-#include "request_context_handler.h"
-#include "common/cefpython_public_api.h"
-
-// --------------------------------------------------------------------------
-// CefRequestContextHandler
-// --------------------------------------------------------------------------
-
-CefRefPtr RequestContextHandler::GetCookieManager() {
- REQUIRE_IO_THREAD();
- if (browser_.get()) {
- return RequestHandler_GetCookieManager(browser_,
- browser_->GetMainFrame()->GetURL());
- } else {
- CefString mainUrl;
- return RequestHandler_GetCookieManager(browser_, mainUrl);
- }
- // Default: return NULL.
-}
-
-bool RequestContextHandler::OnBeforePluginLoad(
- const CefString& mime_type,
- const CefString& plugin_url,
- bool is_main_frame,
- const CefString& top_origin_url,
- CefRefPtr plugin_info,
- PluginPolicy* plugin_policy) {
- // Called on multiple threads
- return RequestHandler_OnBeforePluginLoad(browser_,
- mime_type,
- plugin_url,
- is_main_frame,
- top_origin_url,
- plugin_info,
- plugin_policy);
-}
diff --git a/src/client_handler/request_context_handler.h b/src/client_handler/request_context_handler.h
index b8bf25f3a..6c4e3218d 100644
--- a/src/client_handler/request_context_handler.h
+++ b/src/client_handler/request_context_handler.h
@@ -10,12 +10,14 @@
#include "common/cefpython_public_api.h"
+#include "include/cef_request_context_handler.h"
+#include "include/base/cef_callback.h"
+
class RequestContextHandler :
public CefRequestContextHandler
{
private:
CefRefPtr browser_;
- typedef cef_plugin_policy_t PluginPolicy;
public:
// Browser may be NULL when instantiated from cefpython.CreateBrowserSync.
@@ -29,14 +31,6 @@ class RequestContextHandler :
browser_ = browser;
}
- virtual CefRefPtr GetCookieManager() OVERRIDE;
- virtual bool OnBeforePluginLoad(const CefString& mime_type,
- const CefString& plugin_url,
- bool is_main_frame,
- const CefString& top_origin_url,
- CefRefPtr plugin_info,
- PluginPolicy* plugin_policy) OVERRIDE;
-
private:
IMPLEMENT_REFCOUNTING(RequestContextHandler);
};
diff --git a/src/client_handler/request_handler.cpp b/src/client_handler/request_handler.cpp
index 5227b8921..964dacf19 100644
--- a/src/client_handler/request_handler.cpp
+++ b/src/client_handler/request_handler.cpp
@@ -4,6 +4,19 @@
#include "request_handler.h"
#include "include/base/cef_logging.h"
+#include "include/base/cef_callback.h"
+
+
+CefRefPtr RequestHandler::GetResourceRequestHandler(
+ CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request,
+ bool is_navigation,
+ bool is_download,
+ const CefString& request_initiator,
+ bool& disable_default_handling) {
+ return new ResourceRequestHandler();
+}
bool RequestHandler::OnBeforeBrowse(CefRefPtr browser,
@@ -22,7 +35,7 @@ ReturnValue RequestHandler::OnBeforeResourceLoad(
CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
- CefRefPtr callback)
+ CefRefPtr callback)
{
REQUIRE_IO_THREAD();
bool retval = RequestHandler_OnBeforeResourceLoad(browser, frame, request);
@@ -73,8 +86,8 @@ bool RequestHandler::GetAuthCredentials(CefRefPtr browser,
bool RequestHandler::OnQuotaRequest(CefRefPtr browser,
const CefString& origin_url,
- int64 new_size,
- CefRefPtr callback) {
+ int64_t new_size,
+ CefRefPtr callback) {
REQUIRE_IO_THREAD();
return RequestHandler_OnQuotaRequest(browser, origin_url, new_size,
callback);
@@ -94,7 +107,7 @@ bool RequestHandler::OnCertificateError(
cef_errorcode_t cert_error,
const CefString& request_url,
CefRefPtr ssl_info, // not used
- CefRefPtr callback)
+ CefRefPtr callback)
{
REQUIRE_UI_THREAD();
return RequestHandler_OnCertificateError(cert_error, request_url,
@@ -103,32 +116,11 @@ bool RequestHandler::OnCertificateError(
void RequestHandler::OnRenderProcessTerminated(CefRefPtr browser,
- cef_termination_status_t status)
+ cef_termination_status_t status,
+ int error_code,
+ const CefString& error_string)
{
REQUIRE_UI_THREAD();
LOG(ERROR) << "[Browser process] OnRenderProcessTerminated()";
RequestHandler_OnRendererProcessTerminated(browser, status);
}
-
-
-void RequestHandler::OnPluginCrashed(CefRefPtr browser,
- const CefString& plugin_path)
-{
- REQUIRE_UI_THREAD();
- RequestHandler_OnPluginCrashed(browser, plugin_path);
-}
-
-bool RequestHandler::CanGetCookies(CefRefPtr browser,
- CefRefPtr frame,
- CefRefPtr request) {
- REQUIRE_IO_THREAD();
- return RequestHandler_CanGetCookies(browser, frame, request);
-}
-
-bool RequestHandler::CanSetCookie(CefRefPtr browser,
- CefRefPtr frame,
- CefRefPtr request,
- const CefCookie& cookie) {
- REQUIRE_IO_THREAD();
- return RequestHandler_CanSetCookie(browser, frame, request, cookie);
-}
diff --git a/src/client_handler/request_handler.h b/src/client_handler/request_handler.h
index 7e5e0e6c3..b8c10d877 100644
--- a/src/client_handler/request_handler.h
+++ b/src/client_handler/request_handler.h
@@ -4,16 +4,29 @@
#include "common/cefpython_public_api.h"
#include "include/cef_request_handler.h"
+#include "include/base/cef_callback.h"
+#include "cookie_access_filter.h"
+#include "resource_request_handler.h"
typedef cef_return_value_t ReturnValue;
-class RequestHandler : public CefRequestHandler
+class RequestHandler : public CefRequestHandler,
+ public CookieAccessFilter
{
public:
RequestHandler(){}
virtual ~RequestHandler(){}
+ CefRefPtr GetResourceRequestHandler(
+ CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request,
+ bool is_navigation,
+ bool is_download,
+ const CefString& request_initiator,
+ bool& disable_default_handling) override;
+
bool OnBeforeBrowse(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
@@ -23,19 +36,19 @@ class RequestHandler : public CefRequestHandler
ReturnValue OnBeforeResourceLoad(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
- CefRefPtr callback
- ) override;
+ CefRefPtr callback
+ ) ;
CefRefPtr GetResourceHandler(
CefRefPtr browser,
CefRefPtr frame,
- CefRefPtr request) override;
+ CefRefPtr request) ;
void OnResourceRedirect(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr request,
CefRefPtr response,
- CefString& new_url) override;
+ CefString& new_url) ;
bool GetAuthCredentials(CefRefPtr browser,
CefRefPtr frame,
@@ -44,37 +57,27 @@ class RequestHandler : public CefRequestHandler
int port,
const CefString& realm,
const CefString& scheme,
- CefRefPtr callback) override;
+ CefRefPtr callback) ;
bool OnQuotaRequest(CefRefPtr browser,
const CefString& origin_url,
- int64 new_size,
- CefRefPtr callback) override;
+ int64_t new_size,
+ CefRefPtr callback) ;
void OnProtocolExecution(CefRefPtr browser,
const CefString& url,
- bool& allow_os_execution) override;
+ bool& allow_os_execution) ;
bool OnCertificateError(CefRefPtr browser,
cef_errorcode_t cert_error,
const CefString& request_url,
CefRefPtr ssl_info,
- CefRefPtr callback) override;
+ CefRefPtr callback) override;
void OnRenderProcessTerminated(CefRefPtr browser,
- cef_termination_status_t status) override;
-
- void OnPluginCrashed(CefRefPtr browser,
- const CefString& plugin_path) override;
-
- bool CanGetCookies(CefRefPtr browser,
- CefRefPtr frame,
- CefRefPtr request) override;
-
- bool CanSetCookie(CefRefPtr browser,
- CefRefPtr frame,
- CefRefPtr request,
- const CefCookie& cookie) override;
+ cef_termination_status_t status,
+ int error_code,
+ const CefString& error_string) override;
private:
IMPLEMENT_REFCOUNTING(RequestHandler);
diff --git a/src/client_handler/resource_handler.cpp b/src/client_handler/resource_handler.cpp
index 963575928..08d2565a0 100644
--- a/src/client_handler/resource_handler.cpp
+++ b/src/client_handler/resource_handler.cpp
@@ -12,7 +12,7 @@ bool ResourceHandler::ProcessRequest(CefRefPtr request,
}
void ResourceHandler::GetResponseHeaders(CefRefPtr response,
- int64& response_length,
+ int64_t& response_length,
CefString& redirectUrl) {
REQUIRE_IO_THREAD();
ResourceHandler_GetResponseHeaders(resourceHandlerId_, response,
@@ -28,16 +28,6 @@ bool ResourceHandler::ReadResponse(void* data_out,
bytes_to_read, bytes_read, callback);
}
-bool ResourceHandler::CanGetCookie(const CefCookie& cookie) {
- REQUIRE_IO_THREAD();
- return ResourceHandler_CanGetCookie(resourceHandlerId_, cookie);
-}
-
-bool ResourceHandler::CanSetCookie(const CefCookie& cookie) {
- REQUIRE_IO_THREAD();
- return ResourceHandler_CanSetCookie(resourceHandlerId_, cookie);
-}
-
void ResourceHandler::Cancel() {
REQUIRE_IO_THREAD();
return ResourceHandler_Cancel(resourceHandlerId_);
diff --git a/src/client_handler/resource_handler.h b/src/client_handler/resource_handler.h
index 3bf5e41f6..fc81264ad 100644
--- a/src/client_handler/resource_handler.h
+++ b/src/client_handler/resource_handler.h
@@ -23,7 +23,7 @@ class ResourceHandler : public CefResourceHandler
CefRefPtr callback) override;
virtual void GetResponseHeaders(CefRefPtr response,
- int64& response_length,
+ int64_t& response_length,
CefString& redirectUrl) override;
virtual bool ReadResponse(void* data_out,
@@ -31,11 +31,7 @@ class ResourceHandler : public CefResourceHandler
int& bytes_read,
CefRefPtr callback) override;
- virtual bool CanGetCookie(const CefCookie& cookie) override;
-
- virtual bool CanSetCookie(const CefCookie& cookie) override;
-
- virtual void Cancel() OVERRIDE;
+ virtual void Cancel() override;
private:
IMPLEMENT_REFCOUNTING(ResourceHandler);
diff --git a/src/client_handler/resource_request_handler.h b/src/client_handler/resource_request_handler.h
new file mode 100644
index 000000000..33eb44ca5
--- /dev/null
+++ b/src/client_handler/resource_request_handler.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 CEF Python, see the Authors file.
+// All rights reserved. Licensed under BSD 3-clause license.
+// Project website: https://github.com/cztomczak/cefpython
+
+#pragma once
+#include "include/cef_resource_request_handler.h"
+#include "cookie_access_filter.h"
+
+// Minimal CefResourceRequestHandler that returns a CookieAccessFilter.
+// CEF 146 moved CanSendCookie/CanSaveCookie out of CefRequestHandler into
+// CefCookieAccessFilter, reachable only via this intermediate interface.
+class ResourceRequestHandler : public CefResourceRequestHandler {
+public:
+ ResourceRequestHandler() {}
+ virtual ~ResourceRequestHandler() {}
+
+ CefRefPtr GetCookieAccessFilter(
+ CefRefPtr browser,
+ CefRefPtr frame,
+ CefRefPtr request) override {
+ return new CookieAccessFilter();
+ }
+
+private:
+ IMPLEMENT_REFCOUNTING(ResourceRequestHandler);
+};
diff --git a/src/client_handler/string_visitor.h b/src/client_handler/string_visitor.h
index 3765442c5..c36cf40f6 100644
--- a/src/client_handler/string_visitor.h
+++ b/src/client_handler/string_visitor.h
@@ -21,7 +21,7 @@ class StringVisitor : public CefStringVisitor
virtual void Visit(
const CefString& string
- ) OVERRIDE;
+ ) override;
protected:
IMPLEMENT_REFCOUNTING(StringVisitor);
diff --git a/src/client_handler/task.cpp b/src/client_handler/task.cpp
index ec032a2d6..62a9fcbff 100644
--- a/src/client_handler/task.cpp
+++ b/src/client_handler/task.cpp
@@ -5,21 +5,22 @@
#include "task.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/base/cef_bind.h"
+#include "include/base/cef_callback.h"
void PostTaskWrapper(int threadId, int taskId) {
CefPostTask(
static_cast(threadId),
- CefCreateClosureTask(base::Bind(
+ CefCreateClosureTask(base::BindOnce(
&PyTaskRunnable,
taskId
))
);
}
-void PostDelayedTaskWrapper(int threadId, int64 delay_ms, int taskId) {
+void PostDelayedTaskWrapper(int threadId, int64_t delay_ms, int taskId) {
CefPostDelayedTask(
static_cast(threadId),
- CefCreateClosureTask(base::Bind(
+ CefCreateClosureTask(base::BindOnce(
&PyTaskRunnable,
taskId
)),
@@ -33,7 +34,7 @@ CefRefPtr CreateTask_SetCookie(
const CefCookie& cookie,
CefRefPtr callback)
{
- return CefCreateClosureTask(base::Bind(
+ return CefCreateClosureTask(base::BindOnce(
base::IgnoreResult(&CefCookieManager::SetCookie), obj,
url,
cookie,
@@ -47,7 +48,7 @@ CefRefPtr CreateTask_DeleteCookies(
const CefString& cookie_name,
CefRefPtr callback)
{
- return CefCreateClosureTask(base::Bind(
+ return CefCreateClosureTask(base::BindOnce(
base::IgnoreResult(&CefCookieManager::DeleteCookies), obj,
url,
cookie_name,
diff --git a/src/client_handler/task.h b/src/client_handler/task.h
index 1839d9e32..a7d8c5b38 100644
--- a/src/client_handler/task.h
+++ b/src/client_handler/task.h
@@ -9,7 +9,7 @@
#include "include/cef_task.h"
void PostTaskWrapper(int threadId, int taskId);
-void PostDelayedTaskWrapper(int threadId, int64 delay_ms, int taskId);
+void PostDelayedTaskWrapper(int threadId, int64_t delay_ms, int taskId);
CefRefPtr CreateTask_SetCookie(
CefCookieManager* obj,
diff --git a/src/client_handler/util_mac.mm b/src/client_handler/util_mac.mm
index 4916b253a..7727105e0 100644
--- a/src/client_handler/util_mac.mm
+++ b/src/client_handler/util_mac.mm
@@ -77,6 +77,30 @@ void MacInitialize() {
// OFF: it's causing a crash during shutdown release
// g_autopool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
+
+ // CEF 130+ builds the MachPortRendezvousServer bootstrap service name as
+ // BaseBundleID() + ".MachPortRendezvousServer." + pid
+ // When the process has no app bundle (e.g. a bare Python script),
+ // BaseBundleID() returns "" and the name starts with ".", which
+ // bootstrap_register rejects as invalid. All renderer subprocesses then
+ // crash with "Unknown service name" (1102) on bootstrap_look_up.
+ // Inject a synthetic CFBundleIdentifier before CefInitialize() so the
+ // service name becomes a valid reverse-DNS label ("org.cefpython...").
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ if (mainBundle) {
+ CFStringRef bundleID = CFBundleGetIdentifier(mainBundle);
+ if (!bundleID || CFStringGetLength(bundleID) == 0) {
+ // CFBundleGetInfoDictionary returns the bundle's internal mutable
+ // dictionary; casting to CFMutableDictionaryRef is safe here.
+ CFMutableDictionaryRef infoDict =
+ (CFMutableDictionaryRef)CFBundleGetInfoDictionary(mainBundle);
+ if (infoDict) {
+ CFDictionarySetValue(infoDict,
+ CFSTR("CFBundleIdentifier"),
+ CFSTR("org.cefpython"));
+ }
+ }
+ }
}
void MacShutdown() {
@@ -85,7 +109,7 @@ void MacShutdown() {
}
void MacSetWindowTitle(CefRefPtr browser, char* title) {
- NSView* view = browser->GetHost()->GetWindowHandle();
+ NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetHost()->GetWindowHandle());
NSString* nstitle = [NSString stringWithFormat:@"%s" , title];
view.window.title = nstitle;
}
diff --git a/src/client_handler/web_request_client.cpp b/src/client_handler/web_request_client.cpp
index 965de919b..509a72eda 100644
--- a/src/client_handler/web_request_client.cpp
+++ b/src/client_handler/web_request_client.cpp
@@ -9,14 +9,14 @@ void WebRequestClient::OnRequestComplete(CefRefPtr request) {
}
void WebRequestClient::OnUploadProgress(CefRefPtr request,
- int64 current,
- int64 total) {
+ int64_t current,
+ int64_t total) {
WebRequestClient_OnUploadProgress(webRequestId_, request, current, total);
}
void WebRequestClient::OnDownloadProgress(CefRefPtr request,
- int64 current,
- int64 total) {
+ int64_t current,
+ int64_t total) {
WebRequestClient_OnDownloadProgress(webRequestId_, request, current,
total);
}
diff --git a/src/client_handler/web_request_client.h b/src/client_handler/web_request_client.h
index 8b815a968..8f5bce96a 100644
--- a/src/client_handler/web_request_client.h
+++ b/src/client_handler/web_request_client.h
@@ -20,26 +20,26 @@ class WebRequestClient : public CefURLRequestClient
}
virtual ~WebRequestClient(){}
- virtual void OnRequestComplete(CefRefPtr request) OVERRIDE;
+ virtual void OnRequestComplete(CefRefPtr request) override;
virtual void OnUploadProgress(CefRefPtr request,
- int64 current,
- int64 total) OVERRIDE;
+ int64_t current,
+ int64_t total) override;
virtual void OnDownloadProgress(CefRefPtr request,
- int64 current,
- int64 total) OVERRIDE;
+ int64_t current,
+ int64_t total) override;
virtual void OnDownloadData(CefRefPtr request,
const void* data,
- size_t data_length) OVERRIDE;
+ size_t data_length) override;
virtual bool GetAuthCredentials(bool isProxy,
const CefString& host,
int port,
const CefString& realm,
const CefString& scheme,
- CefRefPtr callback) OVERRIDE;
+ CefRefPtr callback) override;
protected:
IMPLEMENT_REFCOUNTING(WebRequestClient);
diff --git a/src/client_handler/x11.cpp b/src/client_handler/x11.cpp
index 2e97a6e91..4db68dd80 100644
--- a/src/client_handler/x11.cpp
+++ b/src/client_handler/x11.cpp
@@ -36,6 +36,7 @@ void SetX11WindowBounds(CefRefPtr browser,
int x, int y, int width, int height) {
::Window xwindow = browser->GetHost()->GetWindowHandle();
::Display* xdisplay = cef_get_xdisplay();
+ if (!xdisplay || !xwindow) return;
XWindowChanges changes = {0};
changes.x = x;
changes.y = y;
@@ -43,17 +44,27 @@ void SetX11WindowBounds(CefRefPtr browser,
changes.height = static_cast(height);
XConfigureWindow(xdisplay, xwindow,
CWX | CWY | CWHeight | CWWidth, &changes);
+ XFlush(xdisplay);
}
void SetX11WindowTitle(CefRefPtr browser, char* title) {
::Window xwindow = browser->GetHost()->GetWindowHandle();
::Display* xdisplay = cef_get_xdisplay();
+ if (!xdisplay || !xwindow) return;
XStoreName(xdisplay, xwindow, title);
}
GtkWindow* CefBrowser_GetGtkWindow(CefRefPtr browser) {
// TODO: Should return NULL when using the Views framework
// -- REWRITTEN FOR CEF PYTHON USE CASE --
+ //
+ // WARNING (CEF 146 Ozone X11): gtk_plug_new_for_display() below sends an
+ // XEMBED_EMBEDDED_NOTIFY to the browser's X11 window, which causes GTK to
+ // call XReparentWindow and move the browser window into a new GtkSocket.
+ // This breaks embedded-window positioning. Only call this function when
+ // showing a transient dialog (file chooser, print dialog) where the browser
+ // window displacement is acceptable or the dialog is temporary.
+ //
// X11 window handle
::Window xwindow = browser->GetHost()->GetWindowHandle();
// X11 display
@@ -99,7 +110,7 @@ GtkWindow* CefBrowser_GetGtkWindow(CefRefPtr browser) {
XImage* CefBrowser_GetImage(CefRefPtr browser) {
::Display* display = cef_get_xdisplay();
if (!display) {
- LOG(ERROR) << "XOpenDisplay failed in CefBrowser_GetImage";
+ LOG(ERROR) << "cef_get_xdisplay() returned NULL in CefBrowser_GetImage";
return NULL;
}
::Window browser_window = browser->GetHost()->GetWindowHandle();
diff --git a/src/client_handler/x11.h b/src/client_handler/x11.h
index d92ee41ed..9c922aaae 100644
--- a/src/client_handler/x11.h
+++ b/src/client_handler/x11.h
@@ -4,12 +4,17 @@
#pragma once
+// CEF headers must come before X11/GTK headers: X11/Xlib.h defines 'Status'
+// (typedef int Status) which conflicts with CefURLRequest::Status typedef.
+// Pre-including cef_urlrequest.h ensures it is processed first.
+#include "include/cef_urlrequest.h"
+#include "include/cef_browser.h"
+
#include
#include
+#include
#include
-#include "include/cef_browser.h"
-
void InstallX11ErrorHandlers();
void SetX11WindowBounds(CefRefPtr browser,
int x, int y, int width, int height);
diff --git a/src/command_line.pyx b/src/command_line.pyx
index dd26b61d3..1662fcc8d 100644
--- a/src/command_line.pyx
+++ b/src/command_line.pyx
@@ -12,10 +12,10 @@ cdef void AppendSwitchesToCommandLine(
# 1. App_OnBeforeCommandLineProcessing_BrowserProcess()
# 2. BrowserProcessHandler_OnRenderProcessThreadCreated()
cdef PyCommandLine pyCommandLine = CreatePyCommandLine(cefCommandLine)
- cdef py_string switch
- cdef py_string value
- for switch, value in switches.iteritems():
- if not isinstance(switch, basestring) or switch[0] == '-':
+ cdef object switch
+ cdef object value
+ for switch, value in switches.items():
+ if not isinstance(switch, (str, bytes)) or switch[0] == '-':
Debug("Invalid command line switch: %s" % switch)
continue
if value:
@@ -39,27 +39,27 @@ cdef PyCommandLine CreatePyCommandLine(
cdef class PyCommandLine:
cdef CefRefPtr[CefCommandLine] cefCommandLine
- cdef py_void AppendSwitch(self, py_string switch):
+ cdef py_void AppendSwitch(self, object switch):
cdef CefString cefSwitch
cefSwitch = PyToCefStringValue(switch)
self.cefCommandLine.get().AppendSwitch(cefSwitch)
- cdef py_void AppendSwitchWithValue(self, py_string switch, py_string value):
+ cdef py_void AppendSwitchWithValue(self, object switch, object value):
cdef CefString cefSwitch
cdef CefString cefValue
cefSwitch = PyToCefStringValue(switch)
cefValue = PyToCefStringValue(value)
self.cefCommandLine.get().AppendSwitchWithValue(cefSwitch, cefValue)
- cdef py_string GetCommandLineString(self):
+ cdef object GetCommandLineString(self):
return CefToPyString(self.cefCommandLine.get().GetCommandLineString())
- cdef py_bool HasSwitch(self, py_string switch):
+ cdef py_bool HasSwitch(self, object switch):
cdef CefString cefSwitch
cefSwitch = PyToCefStringValue(switch)
return self.cefCommandLine.get().HasSwitch(cefSwitch)
- cdef py_string GetSwitchValue(self, py_string switch):
+ cdef object GetSwitchValue(self, object switch):
cdef CefString cefValue
cdef CefString cefSwitch
cefSwitch = PyToCefStringValue(switch)
diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h
index c796388e6..f46f340da 100644
--- a/src/common/cefpython_public_api.h
+++ b/src/common/cefpython_public_api.h
@@ -2,9 +2,8 @@
// All rights reserved. Licensed under BSD 3-clause license.
// Project website: https://github.com/cztomczak/cefpython
-/* This is a wrapper around including cefpython_fixed.h that is generated
- by Cython. Functions marked with the 'public' keyword are exposed
- to C through that header file. */
+/* Wrapper around the Cython-generated public API header.
+ Functions marked 'public' in cefpython.pyx are exposed to C++ through it. */
#ifndef CEFPYTHON_PUBLIC_API_H
#define CEFPYTHON_PUBLIC_API_H
@@ -13,19 +12,16 @@
#pragma warning(disable:4190) // cefpython API extern C-linkage warnings
#endif
-// Python.h must be included first otherwise error on Linux:
-// >> error: "_POSIX_C_SOURCE" redefined
+// Python.h must be included first (avoids _POSIX_C_SOURCE redefinition on Linux)
#include "Python.h"
-
-// Includes required by "cefpython_fixed.h".
+// Includes required by the generated cefpython_*_fixed.h
#include "include/cef_client.h"
#include "include/cef_urlrequest.h"
#include "include/cef_command_line.h"
#include "util.h"
-// cefpython_fixed.h declares public functions using DL_IMPORT and these
-// macros are not available in Python 3.
+// cefpython_fixed.h uses DL_IMPORT/DL_EXPORT which were removed in Python 3.
#ifndef DL_IMPORT
#define DL_IMPORT(RTYPE) RTYPE
#endif
@@ -33,24 +29,9 @@
#define DL_EXPORT(RTYPE) RTYPE
#endif
-#if PY_MAJOR_VERSION == 2
-#if PY_MINOR_VERSION == 7
-#include "../../build/build_cefpython/cefpython_py27_fixed.h"
-#endif // PY_MINOR_VERSION
-#elif PY_MAJOR_VERSION == 3
-#if PY_MINOR_VERSION == 4
-#include "../../build/build_cefpython/cefpython_py34_fixed.h"
-#elif PY_MINOR_VERSION == 5
-#include "../../build/build_cefpython/cefpython_py35_fixed.h"
-#elif PY_MINOR_VERSION == 6
-#include "../../build/build_cefpython/cefpython_py36_fixed.h"
-#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"
-#elif PY_MINOR_VERSION == 9
-#include "../../build/build_cefpython/cefpython_py39_fixed.h"
-#endif // PY_MINOR_VERSION
-#endif // PY_MAJOR_VERSION
+// CMake sets CEFPYTHON_API_H_FILE to "cefpython_api_fixed.h", a generated
+// stable-name wrapper in the pyx_stage/ build directory resolved via the
+// target's include path.
+#include CEFPYTHON_API_H_FILE
#endif // CEFPYTHON_PUBLIC_API_H
diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi
index bf130d6ed..45e11d1f3 100644
--- a/src/compile_time_constants.pxi
+++ b/src/compile_time_constants.pxi
@@ -1,10 +1,3 @@
-# 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
-cdef extern from "limits.h":
- cdef int INT_MIN
- cdef int INT_MAX
+# UNAME_SYSNAME and PY_MAJOR_VERSION are Cython built-in compile-time
+# constants; no DEF needed. INT_MIN/INT_MAX come from libc.limits.
+from libc.limits cimport INT_MIN, INT_MAX
diff --git a/src/cookie.pyx b/src/cookie.pyx
index 6d90144b0..ba14d5bf4 100644
--- a/src/cookie.pyx
+++ b/src/cookie.pyx
@@ -67,6 +67,10 @@ cdef class Cookie:
self.SetHasExpires(cookie[key])
elif key == "expires":
self.SetExpires(cookie[key])
+ elif key == "sameSite":
+ self.SetSameSite(cookie[key])
+ elif key == "priority":
+ self.SetPriority(cookie[key])
else:
raise Exception("Invalid key: %s" % key)
@@ -82,9 +86,11 @@ cdef class Cookie:
"lastAccess": self.GetLastAccess(),
"hasExpires": self.GetHasExpires(),
"expires": self.GetExpires(),
+ "sameSite": self.GetSameSite(),
+ "priority": self.GetPriority(),
}
- cpdef py_void SetName(self, py_string name):
+ cpdef py_void SetName(self, object name):
# This works:
# | CefString(&self.cefCookie.name).FromString(name)
# This does not work:
@@ -108,7 +114,7 @@ cdef class Cookie:
cefString.Attach(&self.cefCookie.name, False)
return CefToPyString(cefString)
- cpdef py_void SetValue(self, py_string value):
+ cpdef py_void SetValue(self, object value):
cdef CefString cefString
cefString.Attach(&self.cefCookie.value, False)
PyToCefString(value, cefString)
@@ -118,20 +124,7 @@ cdef class Cookie:
cefString.Attach(&self.cefCookie.value, False)
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))
+ cpdef py_void SetDomain(self, object domain):
cdef CefString cefString
cefString.Attach(&self.cefCookie.domain, False)
PyToCefString(domain, cefString)
@@ -141,7 +134,7 @@ cdef class Cookie:
cefString.Attach(&self.cefCookie.domain, False)
return CefToPyString(cefString)
- cpdef py_void SetPath(self, py_string path):
+ cpdef py_void SetPath(self, object path):
cdef CefString cefString
cefString.Attach(&self.cefCookie.path, False)
PyToCefString(path, cefString)
@@ -152,43 +145,52 @@ cdef class Cookie:
return CefToPyString(cefString)
cpdef py_void SetSecure(self, py_bool secure):
- # Need to wrap it with bool() to get rid of the C++ compiler
- # warnings: "cefpython.cpp(24740) : warning C4800: 'int' :
- # forcing value to bool 'true' or 'false' (performance warning)".
- self.cefCookie.secure = bool(secure)
+ self.cefCookie.secure = int(bool(secure))
cpdef py_bool GetSecure(self):
- return self.cefCookie.secure
+ return bool(self.cefCookie.secure)
cpdef py_void SetHttpOnly(self, py_bool httpOnly):
- self.cefCookie.httponly = bool(httpOnly)
+ self.cefCookie.httponly = int(bool(httpOnly))
cpdef py_bool GetHttpOnly(self):
- return self.cefCookie.httponly
+ return bool(self.cefCookie.httponly)
cpdef py_void SetCreation(self, object creation):
- DatetimeToCefTimeT(creation, self.cefCookie.creation)
+ DatetimeToCefBasetimeT(creation, self.cefCookie.creation)
cpdef object GetCreation(self):
- return CefTimeTToDatetime(self.cefCookie.creation)
+ return CefBasetimeTToDatetime(self.cefCookie.creation)
cpdef py_void SetLastAccess(self, object lastAccess):
- DatetimeToCefTimeT(lastAccess, self.cefCookie.last_access)
+ DatetimeToCefBasetimeT(lastAccess, self.cefCookie.last_access)
cpdef object GetLastAccess(self):
- return CefTimeTToDatetime(self.cefCookie.last_access)
+ return CefBasetimeTToDatetime(self.cefCookie.last_access)
cpdef py_void SetHasExpires(self, py_bool hasExpires):
- self.cefCookie.has_expires = bool(hasExpires)
+ self.cefCookie.has_expires = int(bool(hasExpires))
cpdef py_bool GetHasExpires(self):
- return self.cefCookie.has_expires
+ return bool(self.cefCookie.has_expires)
cpdef py_void SetExpires(self, object expires):
- DatetimeToCefTimeT(expires, self.cefCookie.expires)
+ DatetimeToCefBasetimeT(expires, self.cefCookie.expires)
cpdef object GetExpires(self):
- return CefTimeTToDatetime(self.cefCookie.expires)
+ return CefBasetimeTToDatetime(self.cefCookie.expires)
+
+ cpdef int GetSameSite(self) except *:
+ return self.cefCookie.same_site
+
+ cpdef py_void SetSameSite(self, int sameSite):
+ self.cefCookie.same_site =