diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index f867f0eb621a4..4eaf46700b472 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: buildtools: - image: ghcr.io/electron/devcontainer:77262e58c37631ab082482f42c33cdf68c6c394b + image: ghcr.io/electron/devcontainer:424eedbf277ad9749ffa9219068aa72ed4a5e373 volumes: - ..:/workspaces/gclient/src/electron:cached diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79dce49f46f72..e558ae9717861 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,7 @@ Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING. - [ ] PR description included and stakeholders cc'd - [ ] `npm test` passes - [ ] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md) -- [ ] relevant documentation, tutorials, templates and examples are changed or added +- [ ] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.md) - [ ] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples). #### Release Notes diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 58a416e01f404..50b2decbcdcfd 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -38,6 +38,15 @@ runs: run: | GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"x64\" v8_snapshot_toolchain=\"//build/toolchain/mac:clang_x64\"" echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV + - name: Set GN_EXTRA_ARGS for Windows + shell: bash + if: ${{inputs.target-arch != 'x64' && inputs.target-platform == 'win' }} + run: | + GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"${{ inputs.target-arch }}\"" + echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV + - name: Add Clang problem matcher + shell: bash + run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json" - name: Build Electron ${{ inputs.step-suffix }} shell: bash run: | @@ -181,8 +190,8 @@ runs: electron/script/zip-symbols.py -b $BUILD_PATH fi - name: Generate FFMpeg ${{ inputs.step-suffix }} - shell: bash if: ${{ inputs.is-release == 'true' }} + shell: bash run: | cd src gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true $GN_EXTRA_ARGS" @@ -199,6 +208,9 @@ runs: e build --target electron:libcxx_headers_zip -j $NUMBER_OF_NINJA_PROCESSES e build --target electron:libcxxabi_headers_zip -j $NUMBER_OF_NINJA_PROCESSES e build --target electron:libcxx_objects_zip -j $NUMBER_OF_NINJA_PROCESSES + - name: Remove Clang problem matcher + shell: bash + run: echo "::remove-matcher owner=clang::" - name: Generate TypeScript Definitions ${{ inputs.step-suffix }} if: ${{ inputs.is-release == 'true' }} shell: bash diff --git a/.github/actions/build-git-cache/action.yml b/.github/actions/build-git-cache/action.yml new file mode 100644 index 0000000000000..6a50666a50fbd --- /dev/null +++ b/.github/actions/build-git-cache/action.yml @@ -0,0 +1,83 @@ +name: 'Build Git Cache' +description: 'Runs a gclient sync to build the git cache for Electron' +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' +runs: + using: "composite" + steps: + - name: Set GIT_CACHE_PATH to make gclient to use the cache + shell: bash + run: | + echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Install Build Tools + uses: ./src/electron/.github/actions/install-build-tools + - name: Set up cache drive + shell: bash + run: | + if [ "${{ inputs.target-platform }}" = "win" ]; then + echo "CACHE_DRIVE=/mnt/win-cache" >> $GITHUB_ENV + else + echo "CACHE_DRIVE=/mnt/cross-instance-cache" >> $GITHUB_ENV + fi + - name: Check cross instance cache disk space + shell: bash + run: | + # if there is less than 35 GB free space then creating the cache might fail so exit early + freespace=`df -m $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` + freespace_human=`df -h $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` + if [ $freespace -le 35000 ]; then + echo "The cross mount cache has $freespace_human free space which is not enough - exiting" + exit 1 + else + echo "The cross mount cache has $freespace_human free space - continuing" + fi + - name: Restore gitcache + shell: bash + run: | + GIT_CACHE_TAR="$CACHE_DRIVE/gitcache.tar" + if [ ! -f "$GIT_CACHE_TAR" ]; then + echo "Git cache tar file does not exist, skipping restore" + exit 0 + fi + echo "Restoring git cache from $GIT_CACHE_TAR to $GIT_CACHE_PATH" + mkdir -p $GIT_CACHE_PATH + tar -xf $GIT_CACHE_TAR -C $GIT_CACHE_PATH + - name: Gclient Sync + shell: bash + run: | + e d gclient config \ + --name "src/electron" \ + --unmanaged \ + ${GCLIENT_EXTRA_ARGS} \ + "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" + + if [ "$TARGET_OS" != "" ]; then + echo "target_os=['$TARGET_OS']" >> ./.gclient + fi + + ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags --nohooks -vv + - name: Compress Git Cache Directory + shell: bash + run: | + echo "Uncompressed gitcache size: $(du -sh $GIT_CACHE_PATH | cut -f1 -d' ')" + cd $GIT_CACHE_PATH + tar -cf ../gitcache.tar . + cd .. + echo "Compressed gitcache to $(du -sh gitcache.tar | cut -f1 -d' ')" + # remove the old cache file if it exists + if [ -f $CACHE_DRIVE/gitcache.tar ]; then + echo "Removing old gitcache.tar from $CACHE_DRIVE" + rm $CACHE_DRIVE/gitcache.tar + fi + cp ./gitcache.tar $CACHE_DRIVE/ + - name: Wait for active SSH sessions + shell: bash + if: always() && !cancelled() + run: | + while [ -f /var/.ssh-lock ] + do + sleep 60 + done diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index d43907f07c19f..e0d66fa041f21 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -9,6 +9,8 @@ inputs: description: 'Whether to persist the cache to the shared drive' required: false default: 'true' + target-platform: + description: 'Target platform, should be linux, win, macos' runs: using: "composite" steps: @@ -18,40 +20,34 @@ runs: echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Install Build Tools - uses: ./src/electron/.github/actions/install-build-tools - name: Set Chromium Git Cookie uses: ./src/electron/.github/actions/set-chromium-cookie - - name: Get Depot Tools - shell: bash - run: | - if [[ ! -d depot_tools ]]; then - git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git - - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - fi - - name: Add Depot Tools to PATH - shell: bash - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH + - name: Install Build Tools + uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash shell: bash run: | node src/electron/script/generate-deps-hash.js - echo "DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)" >> $GITHUB_ENV + DEPSHASH="v1-src-cache-$(cat src/electron/.depshash)" + echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV + echo "CACHE_FILE=$DEPSHASH.tar" >> $GITHUB_ENV + if [ "${{ inputs.target-platform }}" = "win" ]; then + echo "CACHE_DRIVE=/mnt/win-cache" >> $GITHUB_ENV + else + echo "CACHE_DRIVE=/mnt/cross-instance-cache" >> $GITHUB_ENV + fi - name: Generate SAS Key if: ${{ inputs.generate-sas-token == 'true' }} shell: bash run: | - curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$DEPSHASH.tar" > sas-token + curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token - name: Save SAS Key if: ${{ inputs.generate-sas-token == 'true' }} - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - path: | - sas-token - key: sas-key-${{ github.run_number }}-${{ github.run_attempt }} + path: sas-token + key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }} + enableCrossOsArchive: true - name: Check If Cache Exists id: check-cache shell: bash @@ -60,7 +56,7 @@ runs: echo "Not using cache this time..." echo "cache_exists=false" >> $GITHUB_OUTPUT else - cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + cache_path=$CACHE_DRIVE/$CACHE_FILE echo "Using cache key: $DEPSHASH" echo "Checking for cache in: $cache_path" if [ ! -f "$cache_path" ] || [ `du $cache_path | cut -f1` = "0" ]; then @@ -76,14 +72,29 @@ runs: shell: bash run: | # if there is less than 35 GB free space then creating the cache might fail so exit early - freespace=`df -m /mnt/cross-instance-cache | grep -w /mnt/cross-instance-cache | awk '{print $4}'` - freespace_human=`df -h /mnt/cross-instance-cache | grep -w /mnt/cross-instance-cache | awk '{print $4}'` + freespace=`df -m $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` + freespace_human=`df -h $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` if [ $freespace -le 35000 ]; then echo "The cross mount cache has $freespace_human free space which is not enough - exiting" exit 1 else echo "The cross mount cache has $freespace_human free space - continuing" fi + - name: Add patch conflict problem matcher + shell: bash + run: echo "::add-matcher::src/electron/.github/problem-matchers/patch-conflict.json" + - name: Restore gitcache + if: steps.check-cache.outputs.cache_exists == 'false' + shell: bash + run: | + GIT_CACHE_TAR="$CACHE_DRIVE/gitcache.tar" + if [ ! -f "$GIT_CACHE_TAR" ]; then + echo "Git cache tar file does not exist, skipping restore" + exit 0 + fi + echo "Restoring git cache from $GIT_CACHE_TAR to $GIT_CACHE_PATH" + mkdir -p $GIT_CACHE_PATH + tar -xf $GIT_CACHE_TAR -C $GIT_CACHE_PATH - name: Gclient Sync if: steps.check-cache.outputs.cache_exists == 'false' shell: bash @@ -99,19 +110,14 @@ runs: fi ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv - if [ "${{ inputs.is-release }}" != "true" && -n "${{ env.PATCH_UP_APP_CREDS }}" ]; then + if [[ "${{ inputs.is-release }}" != "true" ]]; then # Re-export all the patches to check if there were changes. python3 src/electron/script/export_all_patches.py src/electron/patches/config.json cd src/electron git update-index --refresh || true if ! git diff-index --quiet HEAD --; then # There are changes to the patches. Make a git commit with the updated patches - git add patches - GIT_COMMITTER_NAME="PatchUp" GIT_COMMITTER_EMAIL="73610968+patchup[bot]@users.noreply.github.com" git commit -m "chore: update patches" --author="PatchUp <73610968+patchup[bot]@users.noreply.github.com>" - # Export it - mkdir -p ../../patches - git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch - if node ./script/push-patch.js; then + if node ./script/patch-up.js; then echo echo "======================================================================" echo "Changes to the patches when applying, we have auto-pushed the diff to the current branch" @@ -119,6 +125,11 @@ runs: echo "======================================================================" exit 1 else + git add patches + GIT_COMMITTER_NAME="PatchUp" GIT_COMMITTER_EMAIL="73610968+patchup[bot]@users.noreply.github.com" git commit -m "chore: update patches" --author="PatchUp <73610968+patchup[bot]@users.noreply.github.com>" + # Export it + mkdir -p ../../patches + git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch echo echo "======================================================================" echo "There were changes to the patches when applying." @@ -128,9 +139,15 @@ runs: cat ../../patches/update-patches.patch exit 1 fi + else + echo "No changes to patches detected" fi fi - + - name: Remove patch conflict problem matcher + shell: bash + run: | + echo "::remove-matcher owner=merge-conflict::" + echo "::remove-matcher owner=patch-conflict::" # delete all .git directories under src/ except for # third_party/angle/ and third_party/dawn/ because of build time generation of files # gen/angle/commit.h depends on third_party/angle/.git/HEAD @@ -165,14 +182,14 @@ runs: shell: bash run: | echo "Uncompressed src size: $(du -sh src | cut -f1 -d' ')" - tar -cf $DEPSHASH.tar src - echo "Compressed src to $(du -sh $DEPSHASH.tar | cut -f1 -d' ')" - cp ./$DEPSHASH.tar /mnt/cross-instance-cache/ + tar -cf $CACHE_FILE src + echo "Compressed src to $(du -sh $CACHE_FILE | cut -f1 -d' ')" + cp ./$CACHE_FILE $CACHE_DRIVE/ - name: Persist Src Cache if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }} shell: bash run: | - final_cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + final_cache_path=$CACHE_DRIVE/$CACHE_FILE echo "Using cache key: $DEPSHASH" echo "Checking path: $final_cache_path" if [ ! -f "$final_cache_path" ]; then @@ -181,3 +198,11 @@ runs: else echo "Cache key persisted in $final_cache_path" fi + - name: Wait for active SSH sessions + shell: bash + if: always() && !cancelled() + run: | + while [ -f /var/.ssh-lock ] + do + sleep 60 + done diff --git a/.github/actions/cipd-install/action.yml b/.github/actions/cipd-install/action.yml new file mode 100644 index 0000000000000..327e904be473e --- /dev/null +++ b/.github/actions/cipd-install/action.yml @@ -0,0 +1,40 @@ +name: 'CIPD install' +description: 'Installs the specified CIPD package' +inputs: + cipd-root-prefix-path: + description: 'Path to prepend to installation directory' + default: '' + dependency: + description: 'Name of dependency to install' + deps-file: + description: 'Location of DEPS file that defines the dependency' + installation-dir: + description: 'Location to install dependency' + target-platform: + description: 'Target platform, should be linux, win, macos' + package: + description: 'Package to install' +runs: + using: "composite" + steps: + - name: Delete wrong ${{ inputs.dependency }} + shell: bash + run : | + rm -rf ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} + - name: Create ensure file for ${{ inputs.dependency }} + shell: bash + run: | + echo '${{ inputs.package }}' `e d gclient getdep --deps-file=${{ inputs.deps-file }} -r '${{ inputs.installation-dir }}:${{ inputs.package }}'` > ${{ inputs.dependency }}_ensure_file + cat ${{ inputs.dependency }}_ensure_file + - name: CIPD installation of ${{ inputs.dependency }} (macOS) + if: ${{ inputs.target-platform == 'macos' }} + shell: bash + run: | + echo "ensuring ${{ inputs.dependency }} on macOS" + e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file + - name: CIPD installation of ${{ inputs.dependency }} (Windows) + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: | + echo "ensuring ${{ inputs.dependency }} on Windows" + e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file diff --git a/.github/actions/fix-sync-macos/action.yml b/.github/actions/fix-sync-macos/action.yml deleted file mode 100644 index e9e2c922280bd..0000000000000 --- a/.github/actions/fix-sync-macos/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: 'Fix Sync macOS' -description: 'Checks out Electron and stores it in the AKS Cache' -runs: - using: "composite" - steps: - - name: Fix Sync - shell: bash - # This step is required to correct for differences between "gclient sync" - # on Linux and the expected state on macOS. This requires: - # 1. Fixing Clang Install (wrong binary) - # 2. Fixing esbuild (wrong binary) - # 3. Fixing rustc (wrong binary) - # 4. Fixing gn (wrong binary) - # 5. Fix reclient (wrong binary) - # 6. Fixing dsymutil (wrong binary) - # 7. Ensuring we are using the correct ninja and adding it to PATH - # 8. Fixing angle (wrong remote) - run : | - SEDOPTION="-i ''" - rm -rf src/third_party/llvm-build - python3 src/tools/clang/scripts/update.py - - echo 'infra/3pp/tools/esbuild/${platform}' `gclient getdep --deps-file=src/third_party/devtools-frontend/src/DEPS -r 'third_party/esbuild:infra/3pp/tools/esbuild/${platform}'` > esbuild_ensure_file - # Remove extra output from calling gclient getdep which always calls update_depot_tools - sed -i '' "s/Updating depot_tools... //g" esbuild_ensure_file - cipd ensure --root src/third_party/devtools-frontend/src/third_party/esbuild -ensure-file esbuild_ensure_file - - rm -rf src/third_party/rust-toolchain - python3 src/tools/rust/update_rust.py - - # Prevent calling gclient getdep which always calls update_depot_tools - echo 'gn/gn/mac-${arch}' `gclient getdep --deps-file=src/DEPS -r 'src/buildtools/mac:gn/gn/mac-${arch}'` > gn_ensure_file - sed -i '' "s/Updating depot_tools... //g" gn_ensure_file - cipd ensure --root src/buildtools/mac -ensure-file gn_ensure_file - - # Prevent calling gclient getdep which always calls update_depot_tools - echo 'infra/rbe/client/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/buildtools/reclient:infra/rbe/client/${platform}'` > gn_ensure_file - sed -i '' "s/Updating depot_tools... //g" gn_ensure_file - cipd ensure --root src/buildtools/reclient -ensure-file gn_ensure_file - python3 src/buildtools/reclient_cfgs/configure_reclient_cfgs.py --rbe_instance "projects/rbe-chrome-untrusted/instances/default_instance" --reproxy_cfg_template reproxy.cfg.template --rewrapper_cfg_project "" --skip_remoteexec_cfg_fetch - - if [ "${{ env.TARGET_ARCH }}" == "arm64" ]; then - DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1 - else - DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.x64.sha1 - fi - python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang -s $DSYM_SHA_FILE -o src/tools/clang/dsymutil/bin/dsymutil - - echo 'infra/3pp/tools/ninja/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/third_party/ninja:infra/3pp/tools/ninja/${platform}'` > ninja_ensure_file - sed $SEDOPTION "s/Updating depot_tools... //g" ninja_ensure_file - cipd ensure --root src/third_party/ninja -ensure-file ninja_ensure_file - - echo "$(pwd)/src/third_party/ninja" >> $GITHUB_PATH - - cd src/third_party/angle - rm -f .git/objects/info/alternates - git remote set-url origin https://chromium.googlesource.com/angle/angle.git - cp .git/config .git/config.backup - git remote remove origin - mv .git/config.backup .git/config - git fetch diff --git a/.github/actions/fix-sync/action.yml b/.github/actions/fix-sync/action.yml new file mode 100644 index 0000000000000..23bd710e20141 --- /dev/null +++ b/.github/actions/fix-sync/action.yml @@ -0,0 +1,120 @@ +name: 'Fix Sync' +description: 'Ensures proper binaries are in place' +# This action is required to correct for differences between "gclient sync" +# on Linux and the expected state on macOS/windows. This requires: +# 1. Fixing Clang Install (wrong binary) +# 2. Fixing esbuild (wrong binary) +# 3. Fixing rustc (wrong binary) +# 4. Fixing gn (wrong binary) +# 5. Fix reclient (wrong binary) +# 6. Fixing dsymutil (wrong binary) +# 7. Ensuring we are using the correct ninja and adding it to PATH +# 8. Fixing angle (wrong remote) +# 9. Install windows toolchain on Windows +# 10. Fix node binary on Windows +# 11. Fix rc binary on Windows +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' +runs: + using: "composite" + steps: + - name: Fix clang + shell: bash + run : | + rm -rf src/third_party/llvm-build + python3 src/tools/clang/scripts/update.py + - name: Fix esbuild + uses: ./src/electron/.github/actions/cipd-install + with: + cipd-root-prefix-path: src/third_party/devtools-frontend/src/ + dependency: esbuild + deps-file: src/third_party/devtools-frontend/src/DEPS + installation-dir: third_party/esbuild + target-platform: ${{ inputs.target-platform }} + package: infra/3pp/tools/esbuild/${platform} + - name: Fix rustc + shell: bash + run : | + rm -rf src/third_party/rust-toolchain + python3 src/tools/rust/update_rust.py + - name: Fix gn (macOS) + if: ${{ inputs.target-platform == 'macos' }} + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: gn + deps-file: src/DEPS + installation-dir: src/buildtools/mac + target-platform: ${{ inputs.target-platform }} + package: gn/gn/mac-${arch} + - name: Fix gn (Windows) + if: ${{ inputs.target-platform == 'win' }} + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: gn + deps-file: src/DEPS + installation-dir: src/buildtools/win + target-platform: ${{ inputs.target-platform }} + package: gn/gn/windows-amd64 + - name: Fix reclient + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: reclient + deps-file: src/DEPS + installation-dir: src/buildtools/reclient + target-platform: ${{ inputs.target-platform }} + package: infra/rbe/client/${platform} + - name: Configure reclient configs + shell: bash + run : | + python3 src/buildtools/reclient_cfgs/configure_reclient_cfgs.py --rbe_instance "projects/rbe-chrome-untrusted/instances/default_instance" --reproxy_cfg_template reproxy.cfg.template --rewrapper_cfg_project "" --skip_remoteexec_cfg_fetch + - name: Fix dsymutil (macOS) + if: ${{ inputs.target-platform == 'macos' }} + shell: bash + run : | + # Fix dsymutil + if [ "${{ inputs.target-platform }}" = "macos" ]; then + if [ "${{ env.TARGET_ARCH }}" == "arm64" ]; then + DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1 + else + DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.x64.sha1 + fi + python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang -s $DSYM_SHA_FILE -o src/tools/clang/dsymutil/bin/dsymutil + fi + - name: Fix ninja + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: ninja + deps-file: src/DEPS + installation-dir: src/third_party/ninja + target-platform: ${{ inputs.target-platform }} + package: infra/3pp/tools/ninja/${platform} + - name: Set ninja in path + shell: bash + run : | + echo "$(pwd)/src/third_party/ninja" >> $GITHUB_PATH + - name: Fixup angle git + shell: bash + run : | + cd src/third_party/angle + rm -f .git/objects/info/alternates + git remote set-url origin https://chromium.googlesource.com/angle/angle.git + cp .git/config .git/config.backup + git remote remove origin + mv .git/config.backup .git/config + git fetch + - name: Get Windows toolchain + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: e d vpython3 src\build\vs_toolchain.py update --force + - name: Download nodejs + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: | + $nodedeps = e d gclient getdep --deps-file=src/DEPS -r src/third_party/node/win | ConvertFrom-JSON + python3 src\third_party\depot_tools\download_from_google_storage.py --no_resume --no_auth --bucket chromium-nodejs -o src\third_party\node\win\node.exe $nodedeps.object_name + - name: Install rc + if: ${{ inputs.target-platform == 'win' }} + shell: bash + run: | + python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang/rc -s src/build/toolchain/win/rc/win/rc.exe.sha1 diff --git a/.github/actions/free-space-macos/action.yml b/.github/actions/free-space-macos/action.yml index 75350ca796bad..166f7877d32c7 100644 --- a/.github/actions/free-space-macos/action.yml +++ b/.github/actions/free-space-macos/action.yml @@ -6,6 +6,8 @@ runs: - name: Free Space on MacOS shell: bash run: | + echo "Disk usage before cleanup:" + df -h sudo mkdir -p $TMPDIR/del-target tmpify() { @@ -62,4 +64,5 @@ runs: # lipo off some huge binaries arm64 versions to save space strip_universal_deep $(xcode-select -p)/../SharedFrameworks - # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr \ No newline at end of file + # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr + sudo mdutil -a -i off diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index 0dca6d013010e..b401df9717820 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -10,11 +10,22 @@ runs: git config --global core.filemode false git config --global core.autocrlf false git config --global branch.autosetuprebase always + git config --global core.fscache true + git config --global core.longpaths true + git config --global core.preloadindex true fi - export BUILD_TOOLS_SHA=8246e57791b0af4ae5975eb96f09855f9269b1cd + export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c npm i -g @electron/build-tools + # Update depot_tools to ensure python + e d update_depot_tools e auto-update disable + # Disable further updates of depot_tools + e d auto-update disable if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then e d cipd.bat --version cp "C:\Python311\python.exe" "C:\Python311\python3.exe" - fi + echo "C:\Users\ContainerAdministrator\.electron_build_tools\third_party\depot_tools" >> $GITHUB_PATH + else + echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH + echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH + fi \ No newline at end of file diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 25f288c2a7fa1..ff0f5581472db 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -7,7 +7,7 @@ runs: shell: bash id: yarn-cache-dir-path run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT - - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} diff --git a/.github/actions/restore-cache-aks/action.yml b/.github/actions/restore-cache-aks/action.yml index 70c67f1d1c91b..b614b3a076dce 100644 --- a/.github/actions/restore-cache-aks/action.yml +++ b/.github/actions/restore-cache-aks/action.yml @@ -1,12 +1,20 @@ name: 'Restore Cache AKS' description: 'Restores Electron src cache via AKS' +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' runs: using: "composite" steps: - name: Restore and Ensure Src Cache shell: bash run: | - cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + if [ "${{ inputs.target-platform }}" = "win" ]; then + cache_path=/mnt/win-cache/$DEPSHASH.tar + else + cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + fi + echo "Using cache key: $DEPSHASH" echo "Checking for cache in: $cache_path" if [ ! -f "$cache_path" ]; then diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index 0ccfd851ea4d2..62ba8332f021f 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -1,22 +1,25 @@ name: 'Restore Cache AZCopy' description: 'Restores Electron src cache via AZCopy' +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' runs: using: "composite" steps: - name: Obtain SAS Key continue-on-error: true - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - path: | - sas-token - key: sas-key-${{ github.run_number }}-1 + path: sas-token + key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-1 + enableCrossOsArchive: true - name: Obtain SAS Key continue-on-error: true - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - path: | - sas-token - key: sas-key-${{ github.run_number }}-${{ github.run_attempt }} + path: sas-token + key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }} + enableCrossOsArchive: true - name: Download Src Cache from AKS # The cache will always exist here as a result of the checkout job # Either it was uploaded to Azure in the checkout job for this commit @@ -26,21 +29,31 @@ runs: timeout_minutes: 30 max_attempts: 3 retry_on: error + shell: bash command: | sas_token=$(cat sas-token) - if [ -z $sas-token ]; then + if [ -z "$sas_token" ]; then echo "SAS Token not found; exiting src cache download early..." exit 1 + else + sas_token=$(jq -r '.sasToken' sas-token) + account_name=$(jq -r '.accountName' sas-token) + if [ "${{ inputs.target-platform }}" = "win" ]; then + azcopy copy --log-level=ERROR \ + "https://$account_name.file.core.windows.net/${{ env.AZURE_AKS_WIN_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar + else + azcopy copy --log-level=ERROR \ + "https://$account_name.file.core.windows.net/${{ env.AZURE_AKS_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar + fi fi - azcopy copy --log-level=ERROR \ - "https://${{ env.AZURE_AKS_CACHE_STORAGE_ACCOUNT }}.file.core.windows.net/${{ env.AZURE_AKS_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar env: - AZURE_AKS_CACHE_STORAGE_ACCOUNT: f723719aa87a34622b5f7f3 - AZURE_AKS_CACHE_SHARE_NAME: pvc-f6a4089f-b082-4bee-a3f9-c3e1c0c02d8f + AZURE_AKS_CACHE_SHARE_NAME: linux-cache + AZURE_AKS_WIN_CACHE_SHARE_NAME: windows-cache - name: Clean SAS Key shell: bash run: rm -f sas-token - name: Unzip and Ensure Src Cache + if: ${{ inputs.target-platform == 'macos' }} shell: bash run: | echo "Downloaded cache is $(du -sh $DEPSHASH.tar | cut -f1)" @@ -68,4 +81,45 @@ runs: fi echo "Wiping Electron Directory" - rm -rf src/electron \ No newline at end of file + rm -rf src/electron + + - name: Unzip and Ensure Src Cache (Windows) + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: | + $src_cache = "$env:DEPSHASH.tar" + $cache_size = $(Get-Item $src_cache).length + Write-Host "Downloaded cache is $cache_size" + if ($cache_size -eq 0) { + Write-Host "Cache is empty - exiting" + exit 1 + } + + $TEMP_DIR=New-Item -ItemType Directory -Path temp-cache + $TEMP_DIR_PATH = $TEMP_DIR.FullName + C:\ProgramData\Chocolatey\bin\7z.exe -y -snld x $src_cache -o"$TEMP_DIR_PATH" + + - name: Move Src Cache (Windows) + if: ${{ inputs.target-platform == 'win' }} + uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 + with: + timeout_minutes: 30 + max_attempts: 3 + retry_on: error + shell: powershell + command: | + if (Test-Path "temp-cache\src") { + Write-Host "Relocating Cache" + Remove-Item -Recurse -Force src + Move-Item temp-cache\src src + + Write-Host "Deleting zip file" + Remove-Item -Force $src_cache + } + if (-Not (Test-Path "src\third_party\blink")) { + Write-Host "Cache was not correctly restored - exiting" + exit 1 + } + + Write-Host "Wiping Electron Directory" + Remove-Item -Recurse -Force src\electron diff --git a/.github/actions/set-chromium-cookie/action.yml b/.github/actions/set-chromium-cookie/action.yml index 70d0852624a64..2011655e29b59 100644 --- a/.github/actions/set-chromium-cookie/action.yml +++ b/.github/actions/set-chromium-cookie/action.yml @@ -4,9 +4,14 @@ runs: using: "composite" steps: - name: Set the git cookie from chromium.googlesource.com (Unix) - if: ${{ runner.os != 'Windows' && env.CHROMIUM_GIT_COOKIE }} + if: ${{ runner.os != 'Windows' }} shell: bash run: | + if [[ -z "${{ env.CHROMIUM_GIT_COOKIE }}" ]]; then + echo "CHROMIUM_GIT_COOKIE is not set - cannot authenticate." + exit 0 + fi + eval 'set +o history' 2>/dev/null || setopt HIST_IGNORE_SPACE 2>/dev/null touch ~/.gitcookies chmod 0600 ~/.gitcookies @@ -17,10 +22,37 @@ runs: ${{ env.CHROMIUM_GIT_COOKIE }} __END__ eval 'set -o history' 2>/dev/null || unsetopt HIST_IGNORE_SPACE 2>/dev/null + + RESPONSE=$(curl -s -b ~/.gitcookies https://chromium-review.googlesource.com/a/accounts/self) + if [[ $RESPONSE == ")]}'"* ]]; then + # Extract account email for verification + EMAIL=$(echo "$RESPONSE" | tail -c +5 | jq -r '.email // "No email found"') + echo "Cookie authentication successful - authenticated as: $EMAIL" + else + echo "Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE is set correctly" + echo $RESPONSE + fi - name: Set the git cookie from chromium.googlesource.com (Windows) - if: ${{ runner.os == 'Windows' && env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} + if: ${{ runner.os == 'Windows' }} shell: cmd run: | + if "%CHROMIUM_GIT_COOKIE_WINDOWS_STRING%"=="" ( + echo CHROMIUM_GIT_COOKIE_WINDOWS_STRING is not set - cannot authenticate. + exit /b 0 + ) + git config --global http.cookiefile "%USERPROFILE%\.gitcookies" powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies" + curl -s -b "%USERPROFILE%\.gitcookies" https://chromium-review.googlesource.com/a/accounts/self > response.txt + + findstr /B /C:")]}'" response.txt > nul + if %ERRORLEVEL% EQU 0 ( + echo Cookie authentication successful + powershell -NoProfile -Command "& {$content = Get-Content -Raw response.txt; $content = $content.Substring(4); try { $json = ConvertFrom-Json $content; if($json.email) { Write-Host 'Authenticated as:' $json.email } else { Write-Host 'No email found in response' } } catch { Write-Host 'Error parsing JSON:' $_ }}" + ) else ( + echo Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE_WINDOWS_STRING is set correctly + type response.txt + ) + + del response.txt diff --git a/.github/actions/ssh-debug/action.yml b/.github/actions/ssh-debug/action.yml new file mode 100644 index 0000000000000..96b99671fddd6 --- /dev/null +++ b/.github/actions/ssh-debug/action.yml @@ -0,0 +1,20 @@ +name: Debug via SSH +description: Setup a SSH server with a tunnel to access it to debug via SSH. +inputs: + tunnel: + description: 'Enable SSH tunneling via cloudflared' + required: true + default: 'false' + timeout: + description: 'SSH session timeout in minutes' + required: false + type: number + default: 60 +runs: + using: composite + steps: + - run: $GITHUB_ACTION_PATH/setup-ssh.sh + shell: bash + env: + TUNNEL: ${{ inputs.tunnel }} + TIMEOUT: ${{ inputs.timeout }} diff --git a/.github/actions/ssh-debug/bashrc b/.github/actions/ssh-debug/bashrc new file mode 100644 index 0000000000000..52ecb9592040f --- /dev/null +++ b/.github/actions/ssh-debug/bashrc @@ -0,0 +1,4 @@ +# If we're in an interactive SSH session and we're not already in tmux and there's no explicit SSH command, auto attach tmux +if [ -n "$SSH_TTY" ] && [ -z "$TMUX" ] && [ -z "$SSH_ORIGINAL_COMMAND" ]; then + exec tmux attach || exec tmux +fi diff --git a/.github/actions/ssh-debug/setup-ssh.sh b/.github/actions/ssh-debug/setup-ssh.sh new file mode 100755 index 0000000000000..7407dc20955ea --- /dev/null +++ b/.github/actions/ssh-debug/setup-ssh.sh @@ -0,0 +1,140 @@ +#!/bin/bash -e + +get_authorized_keys() { + if [ -z "$AUTHORIZED_USERS" ] || ! echo "$AUTHORIZED_USERS" | grep -q "\b$GITHUB_ACTOR\b"; then + return 1 + fi + + api_response=$(curl -s "https://api.github.com/users/$GITHUB_ACTOR/keys") + + if echo "$api_response" | jq -e 'type == "object" and has("message")' >/dev/null; then + error_msg=$(echo "$api_response" | jq -r '.message') + echo "Error: $error_msg" + return 1 + else + echo "$api_response" | jq -r '.[].key' + fi +} + +authorized_keys=$(get_authorized_keys "$GITHUB_ACTOR") + +if [ -n "$authorized_keys" ]; then + echo "Configured SSH key(s) for user: $GITHUB_ACTOR" +else + echo "Error: User '$GITHUB_ACTOR' is not authorized to access this debug session." + echo "Authorized users: $AUTHORIZED_USERS" + exit 1 +fi + +if [ "$TUNNEL" != "true" ]; then + echo "SSH tunneling is disabled. Set enable-tunnel: true to enable remote access." + echo "Local SSH server would be available on localhost:2222 if this were a local environment." + exit 0 +fi + +echo "SSH tunneling enabled. Setting up remote access..." + +EXTERNAL_DEPS="curl jq ssh-keygen" + +for dep in $EXTERNAL_DEPS; do + if ! command -v "$dep" > /dev/null 2>&1; then + echo "Command $dep not installed on the system!" >&2 + exit 1 + fi +done + +cd "$GITHUB_ACTION_PATH" + +bashrc_path=$(pwd)/bashrc + +# Source `bashrc` to auto start tmux on SSH login. +if ! grep -q "$bashrc_path" ~/.bash_profile; then + echo >> ~/.bash_profile # On macOS runner there's no newline at the end of the file + echo "source \"$bashrc_path\"" >> ~/.bash_profile +fi + +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) + +if [ "$ARCH" = "x86_64" ]; then + ARCH="amd64" +elif [ "$ARCH" = "aarch64" ]; then + ARCH="arm64" +fi + +# Install tmux on macOS runners if not present. +if [ "$OS" = "darwin" ] && ! command -v tmux > /dev/null 2>&1; then + echo "Installing tmux..." + brew install tmux +fi + +if [ "$OS" = "darwin" ]; then + cloudflared_url="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-${OS}-${ARCH}.tgz" + echo "Downloading \`cloudflared\` from <$cloudflared_url>..." + curl --location --silent --output cloudflared.tgz "$cloudflared_url" + tar xf cloudflared.tgz + rm cloudflared.tgz +else + cloudflared_url="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-${OS}-${ARCH}" + echo "Downloading \`cloudflared\` from <$cloudflared_url>..." + curl --location --silent --output cloudflared "$cloudflared_url" +fi + +chmod +x cloudflared + +echo "Setting up SSH key for authorized user: $GITHUB_ACTOR" +echo "$authorized_keys" > authorized_keys + +echo 'Creating SSH server key...' +ssh-keygen -q -f ssh_host_rsa_key -N '' + +echo 'Creating SSH server config...' +sed "s,\$PWD,$PWD,;s,\$USER,$USER," sshd_config.template > sshd_config + +echo 'Starting SSH server...' +/usr/sbin/sshd -f sshd_config -D & +sshd_pid=$! + +echo 'Starting tmux session...' +(cd "$GITHUB_WORKSPACE" && tmux new-session -d -s debug) + +#if no cloudflare tunnel token is provided, exit +if [ -z "$CLOUDFLARE_TUNNEL_TOKEN" ]; then + echo "Error: required CLOUDFLARE_TUNNEL_TOKEN not found" + exit 1 +fi + +echo 'Starting Cloudflare tunnel...' + +./cloudflared tunnel --no-autoupdate run --token "$CLOUDFLARE_TUNNEL_TOKEN" 2>&1 | tee cloudflared.log | sed -u 's/^/cloudflared: /' & +cloudflared_pid=$! + +url="$TUNNEL_HOSTNAME" + +public_key=$(cut -d' ' -f1,2 < ssh_host_rsa_key.pub) + +( + echo ' ' + echo ' ' + echo '🔗 SSH Debug Session Ready!' + echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + echo ' ' + echo '📋 Copy and run this command to connect:' + echo ' ' + if [ -n "$TUNNEL_HOSTNAME" ]; then + echo "ssh-keygen -R action-ssh-debug && echo 'action-ssh-debug $public_key' >> ~/.ssh/known_hosts && ssh -o ProxyCommand='cloudflared access tcp --hostname $url' runner@action-ssh-debug" + else + echo "ssh-keygen -R action-ssh-debug && echo 'action-ssh-debug $public_key' >> ~/.ssh/known_hosts && ssh -o ProxyCommand='cloudflared access tcp --hostname $url' runner@action-ssh-debug" + fi + echo ' ' + echo "⏰ Session expires automatically in $TIMEOUT minutes" + echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + echo ' ' + echo ' ' +) | cat + +echo 'Starting SSH session in background...' +./ssh-session.sh "$sshd_pid" "$cloudflared_pid" $TIMEOUT & + +echo 'SSH session is running in background. GitHub Action will continue.' +echo 'Session will auto-cleanup after timeout or when processes end.' diff --git a/.github/actions/ssh-debug/ssh-session.sh b/.github/actions/ssh-debug/ssh-session.sh new file mode 100755 index 0000000000000..875acf1c66b70 --- /dev/null +++ b/.github/actions/ssh-debug/ssh-session.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +SSHD_PID=$1 +CLOUDFLARED_PID=$2 +SESSION_TIMEOUT=${3:-3600} + +# Wait for timeout or until processes die. +sleep "$SESSION_TIMEOUT" & +SLEEP_PID=$! + +# Monitor if SSH or cloudflared dies early. +while kill -0 "$SSHD_PID" 2>/dev/null && kill -0 "$CLOUDFLARED_PID" 2>/dev/null && kill -0 "$SLEEP_PID" 2>/dev/null; do + sleep 10 +done + +# Cleanup. +kill "$SLEEP_PID" 2>/dev/null || true +kill "$SSHD_PID" 2>/dev/null || true +kill "$CLOUDFLARED_PID" 2>/dev/null || true + +echo "SSH session ended" diff --git a/.github/actions/ssh-debug/sshd_config.template b/.github/actions/ssh-debug/sshd_config.template new file mode 100644 index 0000000000000..9c7949d8b886a --- /dev/null +++ b/.github/actions/ssh-debug/sshd_config.template @@ -0,0 +1,9 @@ +Port 2222 +HostKey $PWD/ssh_host_rsa_key +PidFile $PWD/sshd.pid + +# Only allow single user +AllowUsers $USER + +# Only allow those keys +AuthorizedKeysFile $PWD/authorized_keys diff --git a/.github/problem-matchers/clang.json b/.github/problem-matchers/clang.json new file mode 100644 index 0000000000000..35cf775305bd5 --- /dev/null +++ b/.github/problem-matchers/clang.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "clang", + "fromPath": "src/out/Default/args.gn", + "pattern": [ + { + "regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ] +} diff --git a/.github/problem-matchers/eslint-stylish.json b/.github/problem-matchers/eslint-stylish.json new file mode 100644 index 0000000000000..a98afcfb11b94 --- /dev/null +++ b/.github/problem-matchers/eslint-stylish.json @@ -0,0 +1,22 @@ +{ + "problemMatcher": [ + { + "owner": "eslint-stylish", + "pattern": [ + { + "regexp": "^\\s*([^\\s].*)$", + "file": 1 + }, + { + "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$", + "line": 1, + "column": 2, + "severity": 3, + "message": 4, + "code": 5, + "loop": true + } + ] + } + ] +} diff --git a/.github/problem-matchers/patch-conflict.json b/.github/problem-matchers/patch-conflict.json new file mode 100644 index 0000000000000..e8324448cbbfa --- /dev/null +++ b/.github/problem-matchers/patch-conflict.json @@ -0,0 +1,24 @@ +{ + "problemMatcher": [ + { + "owner": "merge-conflict", + "pattern": [ + { + "regexp": "^CONFLICT\\s\\(\\S+\\): (Merge conflict in \\S+)$", + "message": 1 + } + ] + }, + { + "owner": "patch-conflict", + "pattern": [ + { + "regexp": "^error: (patch failed: (\\S+):(\\d+))$", + "message": 1, + "file": 2, + "line": 3 + } + ] + } + ] +} diff --git a/.github/workflows/archaeologist-dig.yml b/.github/workflows/archaeologist-dig.yml index f23d7d35c5e33..32563abf96b51 100644 --- a/.github/workflows/archaeologist-dig.yml +++ b/.github/workflows/archaeologist-dig.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Node.js/npm uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a with: - node-version: 20.11.x + node-version: 20.19.x - name: Setting Up Dig Site run: | echo "remote: ${{ github.event.pull_request.head.repo.clone_url }}" diff --git a/.github/workflows/build-git-cache.yml b/.github/workflows/build-git-cache.yml new file mode 100644 index 0000000000000..43daf56e5aa36 --- /dev/null +++ b/.github/workflows/build-git-cache.yml @@ -0,0 +1,74 @@ +name: Build Git Cache +# This workflow updates git cache on the cross-instance cache volumes +# It runs daily at midnight. + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + build-git-cache-linux: + runs-on: electron-arc-centralus-linux-amd64-32core + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root + volumes: + - /mnt/cross-instance-cache:/mnt/cross-instance-cache + env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' + steps: + - name: Checkout Electron + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: src/electron + fetch-depth: 0 + - name: Build Git Cache + uses: ./src/electron/.github/actions/build-git-cache + with: + target-platform: linux + + build-git-cache-windows: + runs-on: electron-arc-centralus-linux-amd64-32core + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root --device /dev/fuse --cap-add SYS_ADMIN + volumes: + - /mnt/win-cache:/mnt/win-cache + env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' + TARGET_OS: 'win' + steps: + - name: Checkout Electron + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: src/electron + fetch-depth: 0 + - name: Build Git Cache + uses: ./src/electron/.github/actions/build-git-cache + with: + target-platform: win + + build-git-cache-macos: + runs-on: electron-arc-centralus-linux-amd64-32core + # This job updates the same git cache as linux, so it needs to run after the linux one. + needs: build-git-cache-linux + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root + volumes: + - /mnt/cross-instance-cache:/mnt/cross-instance-cache + env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' + steps: + - name: Checkout Electron + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: src/electron + fetch-depth: 0 + - name: Build Git Cache + uses: ./src/electron/.github/actions/build-git-cache + with: + target-platform: macos \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d540b5980b4db..4c15823e382a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' required: true skip-macos: type: boolean @@ -64,7 +64,7 @@ jobs: id: set-output run: | if [ -z "${{ inputs.build-image-sha }}" ]; then - echo "build-image-sha=bc2f48b2415a670de18d13605b1cf0eb5fdbaae1" >> "$GITHUB_OUTPUT" + echo "build-image-sha=424eedbf277ad9749ffa9219068aa72ed4a5e373" >> "$GITHUB_OUTPUT" else echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT" fi @@ -92,7 +92,7 @@ jobs: checkout-macos: needs: setup if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-macos}} - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root @@ -115,11 +115,12 @@ jobs: uses: ./src/electron/.github/actions/checkout with: generate-sas-token: 'true' + target-platform: macos checkout-linux: needs: setup if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-linux}} - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root @@ -127,7 +128,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' PATCH_UP_APP_CREDS: ${{ secrets.PATCH_UP_APP_CREDS }} outputs: @@ -145,12 +146,13 @@ jobs: checkout-windows: needs: setup if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root --device /dev/fuse --cap-add SYS_ADMIN volumes: - - /mnt/cross-instance-cache:/mnt/cross-instance-cache + - /mnt/win-cache:/mnt/win-cache + - /var/run/sas:/var/run/sas env: CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} @@ -168,6 +170,9 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout & Sync & Save uses: ./src/electron/.github/actions/checkout + with: + generate-sas-token: 'true' + target-platform: win # GN Check Jobs macos-gn-check: @@ -186,7 +191,7 @@ jobs: with: target-platform: linux target-archs: x64 arm arm64 - check-runs-on: electron-arc-linux-amd64-8core + check-runs-on: electron-arc-centralus-linux-amd64-8core check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' gn-build-type: testing secrets: inherit @@ -197,8 +202,8 @@ jobs: with: target-platform: win target-archs: x64 x86 arm64 - check-runs-on: electron-arc-linux-amd64-8core - check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' + check-runs-on: electron-arc-centralus-linux-amd64-8core + check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}' gn-build-type: testing secrets: inherit @@ -247,8 +252,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test-and-nan.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-amd64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-amd64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}' target-platform: linux @@ -267,8 +272,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-amd64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-amd64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}' target-platform: linux @@ -288,8 +293,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-arm64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-arm64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init","volumes":["/home/runner/externals:/mnt/runner-externals"]}' target-platform: linux @@ -308,8 +313,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-arm64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-arm64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/test:arm64v8-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}' target-platform: linux @@ -326,10 +331,10 @@ jobs: issues: read pull-requests: read uses: ./.github/workflows/pipeline-electron-build-and-test.yml - needs: setup + needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core test-runs-on: windows-latest target-platform: win target-arch: x64 @@ -345,10 +350,10 @@ jobs: issues: read pull-requests: read uses: ./.github/workflows/pipeline-electron-build-and-test.yml - needs: setup + needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core test-runs-on: windows-latest target-platform: win target-arch: x86 @@ -364,10 +369,10 @@ jobs: issues: read pull-requests: read uses: ./.github/workflows/pipeline-electron-build-and-test.yml - needs: setup + needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core test-runs-on: electron-hosted-windows-arm64-4core target-platform: win target-arch: arm64 diff --git a/.github/workflows/clean-src-cache.yml b/.github/workflows/clean-src-cache.yml index f1bd158a4eb21..9a1bfddccc888 100644 --- a/.github/workflows/clean-src-cache.yml +++ b/.github/workflows/clean-src-cache.yml @@ -10,12 +10,13 @@ on: jobs: clean-src-cache: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 options: --user root volumes: - /mnt/cross-instance-cache:/mnt/cross-instance-cache + - /mnt/win-cache:/mnt/win-cache steps: - name: Cleanup Source Cache shell: bash @@ -23,3 +24,6 @@ jobs: df -h /mnt/cross-instance-cache find /mnt/cross-instance-cache -type f -mtime +15 -delete df -h /mnt/cross-instance-cache + df -h /mnt/win-cache + find /mnt/win-cache -type f -mtime +15 -delete + df -h /mnt/win-cache diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index cfba90632237e..7bf533f8eeae3 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' upload-to-storage: description: 'Uploads to Azure storage' required: false @@ -19,7 +19,7 @@ on: jobs: checkout-linux: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ inputs.build-image-sha }} options: --user root @@ -27,6 +27,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - name: Checkout Electron @@ -42,7 +43,7 @@ jobs: needs: checkout-linux with: environment: production-release - build-runs-on: electron-arc-linux-amd64-32core + build-runs-on: electron-arc-centralus-linux-amd64-32core build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' target-platform: linux target-arch: x64 @@ -58,7 +59,7 @@ jobs: needs: checkout-linux with: environment: production-release - build-runs-on: electron-arc-linux-amd64-32core + build-runs-on: electron-arc-centralus-linux-amd64-32core build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' target-platform: linux target-arch: arm @@ -74,7 +75,7 @@ jobs: needs: checkout-linux with: environment: production-release - build-runs-on: electron-arc-linux-amd64-32core + build-runs-on: electron-arc-centralus-linux-amd64-32core build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' target-platform: linux target-arch: arm64 diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index 34baead94a33b..3c156fa2a3f61 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' required: true upload-to-storage: description: 'Uploads to Azure storage' @@ -20,7 +20,7 @@ on: jobs: checkout-macos: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ inputs.build-image-sha }} options: --user root @@ -28,6 +28,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' steps: - name: Checkout Electron @@ -39,6 +40,7 @@ jobs: uses: ./src/electron/.github/actions/checkout with: generate-sas-token: 'true' + target-platform: macos publish-x64-darwin: uses: ./.github/workflows/pipeline-segment-electron-build.yml diff --git a/.github/workflows/pipeline-electron-docs-only.yml b/.github/workflows/pipeline-electron-docs-only.yml index eb5441d148222..062f3af2f57e7 100644 --- a/.github/workflows/pipeline-electron-docs-only.yml +++ b/.github/workflows/pipeline-electron-docs-only.yml @@ -15,7 +15,7 @@ concurrency: jobs: docs-only: name: Docs Only Compile - runs-on: electron-arc-linux-amd64-4core + runs-on: electron-arc-centralus-linux-amd64-4core timeout-minutes: 20 container: ${{ fromJSON(inputs.container) }} steps: diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml index acbf2a6510945..7c1b27d0a33b8 100644 --- a/.github/workflows/pipeline-electron-lint.yml +++ b/.github/workflows/pipeline-electron-lint.yml @@ -18,7 +18,7 @@ env: jobs: lint: name: Lint - runs-on: electron-arc-linux-amd64-4core + runs-on: electron-arc-centralus-linux-amd64-4core timeout-minutes: 20 container: ${{ fromJSON(inputs.container) }} steps: @@ -61,6 +61,9 @@ jobs: curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]" + - name: Add ESLint problem matcher + shell: bash + run: echo "::add-matcher::src/electron/.github/problem-matchers/eslint-stylish.json" - name: Run Lint shell: bash run: | diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml index 66ac1202ce7a1..7df57eb3ecceb 100644 --- a/.github/workflows/pipeline-segment-electron-build.yml +++ b/.github/workflows/pipeline-segment-electron-build.yml @@ -73,6 +73,7 @@ env: SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }} GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || inputs.target-platform == 'win' && '--custom-var=checkout_win=True' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }} ELECTRON_OUT_DIR: Default + ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }} jobs: build: @@ -94,6 +95,15 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Setup SSH Debugging + if: ${{ inputs.target-platform == 'macos' && env.ACTIONS_STEP_DEBUG == 'true' }} + uses: ./src/electron/.github/actions/ssh-debug + with: + tunnel: 'true' + env: + CLOUDFLARE_TUNNEL_TOKEN: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN }} + TUNNEL_HOSTNAME: ${{ secrets.CLOUDFLARED_SSH_HOSTNAME }} + AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }} - name: Free up space (macOS) if: ${{ inputs.target-platform == 'macos' }} uses: ./src/electron/.github/actions/free-space-macos @@ -104,7 +114,7 @@ jobs: if: ${{ inputs.target-platform == 'macos' }} uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a with: - node-version: 20.11.x + node-version: 20.19.x cache: yarn cache-dependency-path: src/electron/yarn.lock - name: Install Dependencies @@ -129,24 +139,8 @@ jobs: echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV - name: Set Chromium Git Cookie uses: ./src/electron/.github/actions/set-chromium-cookie - - name: Get Depot Tools - timeout-minutes: 5 - run: | - git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git - - SEDOPTION="-i" - if [ "`uname`" = "Darwin" ]; then - SEDOPTION="-i ''" - fi - - # remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems - sed $SEDOPTION '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja - - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - - name: Add Depot Tools to PATH - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH + - name: Install Build Tools + uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash run: | node src/electron/script/generate-deps-hash.js @@ -154,24 +148,26 @@ jobs: echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV - name: Restore src cache via AZCopy - if: ${{ inputs.target-platform == 'macos' }} + if: ${{ inputs.target-platform != 'linux' }} uses: ./src/electron/.github/actions/restore-cache-azcopy + with: + target-platform: ${{ inputs.target-platform }} - name: Restore src cache via AKS if: ${{ inputs.target-platform == 'linux' }} uses: ./src/electron/.github/actions/restore-cache-aks - - name: Checkout src via gclient sync - if: ${{ inputs.target-platform == 'win' }} - uses: ./src/electron/.github/actions/checkout - with: - use-cache: 'false' - name: Checkout Electron uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Install Build Tools - uses: ./src/electron/.github/actions/install-build-tools + - name: Fix Sync + if: ${{ inputs.target-platform != 'linux' }} + uses: ./src/electron/.github/actions/fix-sync + with: + target-platform: ${{ inputs.target-platform }} + env: + ELECTRON_DEPOT_TOOLS_DISABLE_LOG: true - name: Init Build Tools run: | e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }} @@ -184,9 +180,6 @@ jobs: echo "DEPSHASH=$(cat src/electron/.depshash)" >> $GITHUB_ENV - name: Add CHROMIUM_BUILDTOOLS_PATH to env run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV - - name: Fix Sync (macOS) - if: ${{ inputs.target-platform == 'macos' }} - uses: ./src/electron/.github/actions/fix-sync-macos - name: Setup Number of Ninja Processes run: | echo "NUMBER_OF_NINJA_PROCESSES=${{ inputs.target-platform != 'macos' && '300' || '200' }}" >> $GITHUB_ENV diff --git a/.github/workflows/pipeline-segment-electron-gn-check.yml b/.github/workflows/pipeline-segment-electron-gn-check.yml index 550cd42b7a859..48fe703078145 100644 --- a/.github/workflows/pipeline-segment-electron-gn-check.yml +++ b/.github/workflows/pipeline-segment-electron-gn-check.yml @@ -65,7 +65,9 @@ jobs: sudo rm -rf $TMPDIR/del-target - name: Check disk space after freeing up space if: ${{ inputs.target-platform == 'macos' }} - run: df -h + run: df -h + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Enable windows toolchain @@ -81,9 +83,13 @@ jobs: - name: Restore src cache via AZCopy if: ${{ inputs.target-platform == 'macos' }} uses: ./src/electron/.github/actions/restore-cache-azcopy + with: + target-platform: ${{ inputs.target-platform }} - name: Restore src cache via AKS if: ${{ inputs.target-platform == 'linux' || inputs.target-platform == 'win' }} uses: ./src/electron/.github/actions/restore-cache-aks + with: + target-platform: ${{ inputs.target-platform }} - name: Run Electron Only Hooks run: | echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]" > tmpgclient diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 087e7af3460e6..65220fc48e60f 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -40,6 +40,7 @@ env: CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} ELECTRON_OUT_DIR: Default ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} + ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }} jobs: test: @@ -81,7 +82,7 @@ jobs: if: ${{ inputs.target-platform == 'win' }} uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a with: - node-version: 20.11.x + node-version: 20.19.x - name: Add TCC permissions on macOS if: ${{ inputs.target-platform == 'macos' }} run: | @@ -100,9 +101,11 @@ jobs: } userValuesArray=( - "'kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceCamera','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceBluetoothAlways','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" ) for values in "${userValuesArray[@]}"; do # Sonoma and higher have a few extra values @@ -124,6 +127,15 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Setup SSH Debugging + if: ${{ inputs.target-platform == 'macos' && env.ACTIONS_STEP_DEBUG == 'true' }} + uses: ./src/electron/.github/actions/ssh-debug + with: + tunnel: 'true' + env: + CLOUDFLARE_TUNNEL_TOKEN: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN }} + TUNNEL_HOSTNAME: ${{ secrets.CLOUDFLARED_SSH_HOSTNAME }} + AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - name: Set Chromium Git Cookie @@ -134,6 +146,9 @@ jobs: git config --global core.filemode false git config --global core.autocrlf false git config --global branch.autosetuprebase always + git config --global core.fscache true + git config --global core.longpaths true + git config --global core.preloadindex true git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git # Ensure depot_tools does not update. test -d depot_tools && cd depot_tools @@ -190,7 +205,6 @@ jobs: MOCHA_REPORTER: mocha-multi-reporters MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap ELECTRON_DISABLE_SECURITY_WARNINGS: 1 - ELECTRON_SKIP_NATIVE_MODULE_TESTS: true DISPLAY: ':99.0' NPM_CONFIG_MSVS_VERSION: '2022' run: | diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index ce99158af8d03..b33c31ab99556 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -38,7 +38,7 @@ env: jobs: node-tests: name: Run Node.js Tests - runs-on: electron-arc-linux-amd64-8core + runs-on: electron-arc-centralus-linux-amd64-8core timeout-minutes: 30 env: TARGET_ARCH: ${{ inputs.target-arch }} @@ -51,6 +51,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools @@ -58,17 +60,6 @@ jobs: e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie - - name: Get Depot Tools - timeout-minutes: 5 - run: | - git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - - name: Add Depot Tools to PATH - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH - name: Download Generated Artifacts uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 with: @@ -101,7 +92,7 @@ jobs: done nan-tests: name: Run Nan Tests - runs-on: electron-arc-linux-amd64-4core + runs-on: electron-arc-centralus-linux-amd64-4core timeout-minutes: 30 env: TARGET_ARCH: ${{ inputs.target-arch }} @@ -114,6 +105,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools @@ -121,17 +114,6 @@ jobs: e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie - - name: Get Depot Tools - timeout-minutes: 5 - run: | - git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - - name: Add Depot Tools to PATH - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH - name: Download Generated Artifacts uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 with: diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index 1ee5845eaff40..407ee819dc753 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' required: true upload-to-storage: description: 'Uploads to Azure storage' @@ -20,13 +20,15 @@ on: jobs: checkout-windows: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ inputs.build-image-sha }} options: --user root --device /dev/fuse --cap-add SYS_ADMIN volumes: - - /mnt/cross-instance-cache:/mnt/cross-instance-cache + - /mnt/win-cache:/mnt/win-cache + - /var/run/sas:/var/run/sas env: + CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' TARGET_OS: 'win' ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1' @@ -40,13 +42,16 @@ jobs: fetch-depth: 0 - name: Checkout & Sync & Save uses: ./src/electron/.github/actions/checkout + with: + generate-sas-token: 'true' + target-platform: win publish-x64-win: uses: ./.github/workflows/pipeline-segment-electron-build.yml needs: checkout-windows with: environment: production-release - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core target-platform: win target-arch: x64 is-release: true @@ -60,7 +65,7 @@ jobs: needs: checkout-windows with: environment: production-release - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core target-platform: win target-arch: arm64 is-release: true @@ -74,7 +79,7 @@ jobs: needs: checkout-windows with: environment: production-release - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core target-platform: win target-arch: x86 is-release: true diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc index aa44ae70b8780..11be56cea63f2 100644 --- a/.markdownlint-cli2.jsonc +++ b/.markdownlint-cli2.jsonc @@ -1,6 +1,7 @@ { "config": { "extends": "@electron/lint-roller/configs/markdownlint.json", + "descriptive-link-text": false, "link-image-style": { "autolink": false, "shortcut": false @@ -26,6 +27,6 @@ "no-newline-in-links": true }, "customRules": [ - "@electron/lint-roller/markdownlint-rules/" + "./node_modules/@electron/lint-roller/markdownlint-rules/index.mjs" ] } diff --git a/BUILD.gn b/BUILD.gn index a449ee53b5717..1e5061a0c53b5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -670,6 +670,8 @@ source_set("electron_lib") { sources += [ "shell/browser/certificate_manager_model.cc", "shell/browser/certificate_manager_model.h", + "shell/browser/linux/x11_util.cc", + "shell/browser/linux/x11_util.h", "shell/browser/ui/gtk_util.cc", "shell/browser/ui/gtk_util.h", ] @@ -1246,7 +1248,7 @@ if (is_mac) { "//components/crash/core/app:run_as_crashpad_handler", ] - ldflags = [] + ldflags = [ "/DELAYLOAD:ffmpeg.dll" ] libs = [ "comctl32.lib", diff --git a/DEPS b/DEPS index 176b3014c68d1..dc1d62f7dd5d0 100644 --- a/DEPS +++ b/DEPS @@ -2,9 +2,9 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '135.0.7049.5', + '136.0.7103.177', 'node_version': - 'v22.14.0', + 'v22.18.0', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': @@ -31,7 +31,7 @@ vars = { 'sysroots_json_path': 'electron/script/sysroots.json', # KEEP IN SYNC WITH utils.js FILE - 'yarn_version': '1.15.2', + 'yarn_version': '1.22.22', # To be able to build clean Chromium from sources. 'apply_patches': True, diff --git a/README.md b/README.md index 07edb95ea80da..2ab98ce41009b 100644 --- a/README.md +++ b/README.md @@ -44,29 +44,17 @@ Each Electron release provides binaries for macOS, Windows, and Linux. * Fedora 32 and newer * Debian 10 and newer -## Quick start & Electron Fiddle +## Electron Fiddle Use [`Electron Fiddle`](https://github.com/electron/fiddle) to build, run, and package small Electron experiments, to see code examples for all of Electron's APIs, and to try out different versions of Electron. It's designed to make the start of your journey with Electron easier. -Alternatively, clone and run the -[electron/electron-quick-start](https://github.com/electron/electron-quick-start) -repository to see a minimal Electron app in action: - -```sh -git clone https://github.com/electron/electron-quick-start -cd electron-quick-start -npm install -npm start -``` - ## Resources for learning Electron * [electronjs.org/docs](https://electronjs.org/docs) - All of Electron's documentation * [electron/fiddle](https://github.com/electron/fiddle) - A tool to build, run, and package small Electron experiments -* [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - A very basic starter Electron app * [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - Sample starter apps created by the community ## Programmatic usage diff --git a/build/.eslintrc.json b/build/.eslintrc.json index dc7dde78dc189..35c8df531937d 100644 --- a/build/.eslintrc.json +++ b/build/.eslintrc.json @@ -1,8 +1,8 @@ { "plugins": [ - "unicorn" + "import" ], "rules": { - "unicorn/prefer-node-protocol": "error" + "import/enforce-node-protocol-usage": ["error", "always"] } } diff --git a/default_app/.eslintrc.json b/default_app/.eslintrc.json index dc7dde78dc189..35c8df531937d 100644 --- a/default_app/.eslintrc.json +++ b/default_app/.eslintrc.json @@ -1,8 +1,8 @@ { "plugins": [ - "unicorn" + "import" ], "rules": { - "unicorn/prefer-node-protocol": "error" + "import/enforce-node-protocol-usage": ["error", "always"] } } diff --git a/docs/README.md b/docs/README.md index 98ae1ad9a79a4..02d5d1331ca29 100644 --- a/docs/README.md +++ b/docs/README.md @@ -96,8 +96,9 @@ These individual tutorials expand on topics discussed in the guide above. * [Chrome Extensions Support](api/extensions.md) * [Breaking API Changes](breaking-changes.md) -### Custom DOM Elements: +### Custom Web Features: +* [`-electron-corner-smoothing` CSS Rule](api/corner-smoothing-css.md) * [`` Tag](api/webview-tag.md) * [`window.open` Function](api/window-open.md) @@ -112,6 +113,7 @@ These individual tutorials expand on topics discussed in the guide above. * [dialog](api/dialog.md) * [globalShortcut](api/global-shortcut.md) * [inAppPurchase](api/in-app-purchase.md) +* [ImageView](api/image-view.md) * [ipcMain](api/ipc-main.md) * [Menu](api/menu.md) * [MenuItem](api/menu-item.md) diff --git a/docs/api/app.md b/docs/api/app.md index e7e635e21bd55..0ff66e518e1fe 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -41,9 +41,10 @@ that was used to open the application, if it was launched from Notification Cent You can also call `app.isReady()` to check if this event has already fired and `app.whenReady()` to get a Promise that is fulfilled when Electron is initialized. -**Note**: The `ready` event is only fired after the main process has finished running the first -tick of the event loop. If an Electron API needs to be called before the `ready` event, ensure -that it is called synchronously in the top-level context of the main process. +> [!NOTE] +> The `ready` event is only fired after the main process has finished running the first +> tick of the event loop. If an Electron API needs to be called before the `ready` event, ensure +> that it is called synchronously in the top-level context of the main process. ### Event: 'window-all-closed' @@ -66,12 +67,14 @@ Emitted before the application starts closing its windows. Calling `event.preventDefault()` will prevent the default behavior, which is terminating the application. -**Note:** If application quit was initiated by `autoUpdater.quitAndInstall()`, -then `before-quit` is emitted _after_ emitting `close` event on all windows and -closing them. +> [!NOTE] +> If application quit was initiated by `autoUpdater.quitAndInstall()`, +> then `before-quit` is emitted _after_ emitting `close` event on all windows and +> closing them. -**Note:** On Windows, this event will not be emitted if the app is closed due -to a shutdown/restart of the system or a user logout. +> [!NOTE] +> On Windows, this event will not be emitted if the app is closed due +> to a shutdown/restart of the system or a user logout. ### Event: 'will-quit' @@ -86,8 +89,9 @@ terminating the application. See the description of the `window-all-closed` event for the differences between the `will-quit` and `window-all-closed` events. -**Note:** On Windows, this event will not be emitted if the app is closed due -to a shutdown/restart of the system or a user logout. +> [!NOTE] +> On Windows, this event will not be emitted if the app is closed due +> to a shutdown/restart of the system or a user logout. ### Event: 'quit' @@ -98,8 +102,9 @@ Returns: Emitted when the application is quitting. -**Note:** On Windows, this event will not be emitted if the app is closed due -to a shutdown/restart of the system or a user logout. +> [!NOTE] +> On Windows, this event will not be emitted if the app is closed due +> to a shutdown/restart of the system or a user logout. ### Event: 'open-file' _macOS_ @@ -470,24 +475,28 @@ and `workingDirectory` is its current working directory. Usually applications respond to this by making their primary window focused and non-minimized. -**Note:** `argv` will not be exactly the same list of arguments as those passed -to the second instance. The order might change and additional arguments might be appended. -If you need to maintain the exact same arguments, it's advised to use `additionalData` instead. +> [!NOTE] +> `argv` will not be exactly the same list of arguments as those passed +> to the second instance. The order might change and additional arguments might be appended. +> If you need to maintain the exact same arguments, it's advised to use `additionalData` instead. -**Note:** If the second instance is started by a different user than the first, the `argv` array will not include the arguments. +> [!NOTE] +> If the second instance is started by a different user than the first, the `argv` array will not include the arguments. This event is guaranteed to be emitted after the `ready` event of `app` gets emitted. -**Note:** Extra command line arguments might be added by Chromium, -such as `--original-process-start-time`. +> [!NOTE] +> Extra command line arguments might be added by Chromium, +> such as `--original-process-start-time`. ## Methods The `app` object has the following methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some methods are only available on specific operating systems and are +> labeled as such. ### `app.quit()` @@ -679,7 +688,8 @@ preferred over `name` by Electron. Overrides the current application's name. -**Note:** This function overrides the name used internally by Electron; it does not affect the name that the OS uses. +> [!NOTE] +> This function overrides the name used internally by Electron; it does not affect the name that the OS uses. ### `app.getLocale()` @@ -688,18 +698,22 @@ Possible return values are documented [here](https://source.chromium.org/chromiu To set the locale, you'll want to use a command line switch at app startup, which may be found [here](command-line-switches.md). -**Note:** When distributing your packaged app, you have to also ship the -`locales` folder. +> [!NOTE] +> When distributing your packaged app, you have to also ship the +> `locales` folder. -**Note:** This API must be called after the `ready` event is emitted. +> [!NOTE] +> This API must be called after the `ready` event is emitted. -**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). +> [!NOTE] +> To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). ### `app.getLocaleCountryCode()` Returns `string` - User operating system's locale two-letter [ISO 3166](https://www.iso.org/iso-3166-country-codes.html) country code. The value is taken from native OS APIs. -**Note:** When unable to detect locale country code, it returns empty string. +> [!NOTE] +> When unable to detect locale country code, it returns empty string. ### `app.getSystemLocale()` @@ -712,9 +726,11 @@ Different operating systems also use the regional data differently: Therefore, this API can be used for purposes such as choosing a format for rendering dates and times in a calendar app, especially when the developer wants the format to be consistent with the OS. -**Note:** This API must be called after the `ready` event is emitted. +> [!NOTE] +> This API must be called after the `ready` event is emitted. -**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). +> [!NOTE] +> To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). ### `app.getPreferredSystemLanguages()` @@ -759,6 +775,22 @@ bar, and on macOS, you can visit it from dock menu. Clears the recent documents list. +### `app.getRecentDocuments()` _macOS_ _Windows_ + +Returns `string[]` - An array containing documents in the most recent documents list. + +```js +const { app } = require('electron') + +const path = require('node:path') + +const file = path.join(app.getPath('desktop'), 'foo.txt') +app.addRecentDocument(file) + +const recents = app.getRecentDocuments() +console.log(recents) // ['/path/to/desktop/foo.txt'} +``` + ### `app.setAsDefaultProtocolClient(protocol[, path, args])` * `protocol` string - The name of your protocol, without `://`. For example, @@ -777,16 +809,18 @@ Once registered, all links with `your-protocol://` will be opened with the current executable. The whole link, including protocol, will be passed to your application as a parameter. -**Note:** On macOS, you can only register protocols that have been added to -your app's `info.plist`, which cannot be modified at runtime. However, you can -change the file during build time via [Electron Forge][electron-forge], -[Electron Packager][electron-packager], or by editing `info.plist` with a text -editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details. +> [!NOTE] +> On macOS, you can only register protocols that have been added to +> your app's `info.plist`, which cannot be modified at runtime. However, you can +> change the file during build time via [Electron Forge][electron-forge], +> [Electron Packager][electron-packager], or by editing `info.plist` with a text +> editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details. -**Note:** In a Windows Store environment (when packaged as an `appx`) this API -will return `true` for all calls but the registry key it sets won't be accessible -by other applications. In order to register your Windows Store application -as a default protocol handler you must [declare the protocol in your manifest](https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol). +> [!NOTE] +> In a Windows Store environment (when packaged as an `appx`) this API +> will return `true` for all calls but the registry key it sets won't be accessible +> by other applications. In order to register your Windows Store application +> as a default protocol handler you must [declare the protocol in your manifest](https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol). The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally. @@ -810,11 +844,12 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler. Returns `boolean` - Whether the current executable is the default handler for a protocol (aka URI scheme). -**Note:** On macOS, you can use this method to check if the app has been -registered as the default protocol handler for a protocol. You can also verify -this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the -macOS machine. Please refer to -[Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. +> [!NOTE] +> On macOS, you can use this method to check if the app has been +> registered as the default protocol handler for a protocol. You can also verify +> this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the +> macOS machine. Please refer to +> [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. The API uses the Windows Registry and `LSCopyDefaultHandlerForURLScheme` internally. @@ -858,8 +893,9 @@ Adds `tasks` to the [Tasks][tasks] category of the Jump List on Windows. Returns `boolean` - Whether the call succeeded. -**Note:** If you'd like to customize the Jump List even more use -`app.setJumpList(categories)` instead. +> [!NOTE] +> If you'd like to customize the Jump List even more use +> `app.setJumpList(categories)` instead. ### `app.getJumpListSettings()` _Windows_ @@ -897,21 +933,24 @@ following strings: If `categories` is `null` the previously set custom Jump List (if any) will be replaced by the standard Jump List for the app (managed by Windows). -**Note:** If a `JumpListCategory` object has neither the `type` nor the `name` -property set then its `type` is assumed to be `tasks`. If the `name` property +> [!NOTE] +> If a `JumpListCategory` object has neither the `type` nor the `name` +> property set then its `type` is assumed to be `tasks`. If the `name` property is set but the `type` property is omitted then the `type` is assumed to be `custom`. -**Note:** Users can remove items from custom categories, and Windows will not -allow a removed item to be added back into a custom category until **after** -the next successful call to `app.setJumpList(categories)`. Any attempt to -re-add a removed item to a custom category earlier than that will result in the -entire custom category being omitted from the Jump List. The list of removed -items can be obtained using `app.getJumpListSettings()`. +> [!NOTE] +> Users can remove items from custom categories, and Windows will not +> allow a removed item to be added back into a custom category until **after** +> the next successful call to `app.setJumpList(categories)`. Any attempt to +> re-add a removed item to a custom category earlier than that will result in the +> entire custom category being omitted from the Jump List. The list of removed +> items can be obtained using `app.getJumpListSettings()`. -**Note:** The maximum length of a Jump List item's `description` property is -260 characters. Beyond this limit, the item will not be added to the Jump -List, nor will it be displayed. +> [!NOTE] +> The maximum length of a Jump List item's `description` property is +> 260 characters. Beyond this limit, the item will not be added to the Jump +> List, nor will it be displayed. Here's a very simple example of creating a custom Jump List: @@ -1110,6 +1149,11 @@ indicates success while any other value indicates failure according to Chromium resolver will attempt to use the system's DNS settings to do DNS lookups itself. Enabled by default on macOS, disabled by default on Windows and Linux. + * `enableHappyEyeballs` boolean (optional) - Whether the + [Happy Eyeballs V3][happy-eyeballs-v3] algorithm should be used in creating + network connections. When enabled, hostnames resolving to multiple IP + addresses will be attempted in parallel to have a chance at establishing a + connection more quickly. * `secureDnsMode` string (optional) - Can be 'off', 'automatic' or 'secure'. Configures the DNS-over-HTTP mode. When 'off', no DoH lookups will be performed. When 'automatic', DoH lookups will be performed first if DoH is @@ -1183,7 +1227,8 @@ Returns [`ProcessMetric[]`](structures/process-metric.md): Array of `ProcessMetr Returns [`GPUFeatureStatus`](structures/gpu-feature-status.md) - The Graphics Feature Status from `chrome://gpu/`. -**Note:** This information is only usable after the `gpu-info-update` event is emitted. +> [!NOTE] +> This information is only usable after the `gpu-info-update` event is emitted. ### `app.getGPUInfo(infoType)` @@ -1237,11 +1282,13 @@ badge. On macOS, it shows on the dock icon. On Linux, it only works for Unity launcher. -**Note:** Unity launcher requires a `.desktop` file to work. For more information, -please read the [Unity integration documentation][unity-requirement]. +> [!NOTE] +> Unity launcher requires a `.desktop` file to work. For more information, +> please read the [Unity integration documentation][unity-requirement]. -**Note:** On macOS, you need to ensure that your application has the permission -to display notifications for this method to work. +> [!NOTE] +> On macOS, you need to ensure that your application has the permission +> to display notifications for this method to work. ### `app.getBadgeCount()` _Linux_ _macOS_ @@ -1343,7 +1390,8 @@ details. Disabled by default. This API must be called after the `ready` event is emitted. -**Note:** Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. +> [!NOTE] +> Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. ### `app.showAboutPanel()` @@ -1471,7 +1519,8 @@ By using this API, important information such as password and other sensitive in See [Apple's documentation](https://developer.apple.com/library/archive/technotes/tn2150/_index.html) for more details. -**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed. +> [!NOTE] +> Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed. ### `app.setProxy(config)` @@ -1485,7 +1534,7 @@ and internal requests made by the runtime (ex: geolocation queries). This method can only be called after app is ready. -#### `app.resolveProxy(url)` +### `app.resolveProxy(url)` * `url` URL @@ -1533,7 +1582,8 @@ See [Chromium's accessibility docs](https://www.chromium.org/developers/design-d This API must be called after the `ready` event is emitted. -**Note:** Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. +> [!NOTE] +> Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. ### `app.applicationMenu` @@ -1546,11 +1596,13 @@ An `Integer` property that returns the badge count for current app. Setting the On macOS, setting this with any nonzero integer shows on the dock icon. On Linux, this property only works for Unity launcher. -**Note:** Unity launcher requires a `.desktop` file to work. For more information, -please read the [Unity integration documentation][unity-requirement]. +> [!NOTE] +> Unity launcher requires a `.desktop` file to work. For more information, +> please read the [Unity integration documentation][unity-requirement]. -**Note:** On macOS, you need to ensure that your application has the permission -to display notifications for this property to take effect. +> [!NOTE] +> On macOS, you need to ensure that your application has the permission +> to display notifications for this property to take effect. ### `app.commandLine` _Readonly_ @@ -1559,8 +1611,8 @@ command line arguments that Chromium uses. ### `app.dock` _macOS_ _Readonly_ -A [`Dock`](./dock.md) `| undefined` object that allows you to perform actions on your app icon in the user's -dock on macOS. +A `Dock | undefined` property ([`Dock`](./dock.md) on macOS, `undefined` on all other +platforms) that allows you to perform actions on your app icon in the user's dock. ### `app.isPackaged` _Readonly_ @@ -1579,6 +1631,7 @@ A `boolean` property that returns `true` if the app is packaged, `false` otherw [Squirrel-Windows]: https://github.com/Squirrel/Squirrel.Windows [JumpListBeginListMSDN]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-beginlist [about-panel-options]: https://developer.apple.com/reference/appkit/nsapplication/1428479-orderfrontstandardaboutpanelwith?language=objc +[happy-eyeballs-v3]: https://datatracker.ietf.org/doc/draft-pauly-happy-happyeyeballs-v3/ ### `app.name` diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index bf419f33ad70a..f2e4a37945037 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -26,8 +26,9 @@ requirements, you can read [Server Support][server-support]. Note that update process. Apps that need to disable ATS can add the `NSAllowsArbitraryLoads` key to their app's plist. -**Note:** Your application must be signed for automatic updates on macOS. -This is a requirement of `Squirrel.Mac`. +> [!IMPORTANT] +> Your application must be signed for automatic updates on macOS. +> This is a requirement of `Squirrel.Mac`. ### Windows @@ -93,8 +94,9 @@ Emitted when an update has been downloaded. On Windows only `releaseName` is available. -**Note:** It is not strictly necessary to handle this event. A successfully -downloaded update will still be applied the next time the application starts. +> [!NOTE] +> It is not strictly necessary to handle this event. A successfully +> downloaded update will still be applied the next time the application starts. ### Event: 'before-quit-for-update' @@ -125,8 +127,9 @@ Returns `string` - The current update feed URL. Asks the server whether there is an update. You must call `setFeedURL` before using this API. -**Note:** If an update is available it will be downloaded automatically. -Calling `autoUpdater.checkForUpdates()` twice will download the update two times. +> [!NOTE] +> If an update is available it will be downloaded automatically. +> Calling `autoUpdater.checkForUpdates()` twice will download the update two times. ### `autoUpdater.quitAndInstall()` @@ -137,9 +140,10 @@ Under the hood calling `autoUpdater.quitAndInstall()` will close all application windows first, and automatically call `app.quit()` after all windows have been closed. -**Note:** It is not strictly necessary to call this function to apply an update, -as a successfully downloaded update will always be applied the next time the -application starts. +> [!NOTE] +> It is not strictly necessary to call this function to apply an update, +> as a successfully downloaded update will always be applied the next time the +> application starts. [squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac [server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 1cf5cb18e331f..8ba0be4e6a099 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -4,7 +4,7 @@ Process: [Main](../glossary.md#main-process) -> **Note** +> [!NOTE] > `BaseWindow` provides a flexible way to compose multiple web views in a > single window. For windows with only a single, full-size web view, the > [`BrowserWindow`](browser-window.md) class may be a simpler option. @@ -99,6 +99,10 @@ Process: [Main](../glossary.md#main-process) It creates a new `BaseWindow` with native properties as set by the `options`. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new BaseWindow([options])` * `options` [BaseWindowConstructorOptions](structures/base-window-options.md?inline) (optional) @@ -107,8 +111,9 @@ It creates a new `BaseWindow` with native properties as set by the `options`. Objects created with `new BaseWindow` emit the following events: -**Note:** Some events are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some events are only available on specific operating systems and are +> labeled as such. #### Event: 'close' @@ -137,7 +142,11 @@ window.onbeforeunload = (e) => { } ``` -_**Note**: There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and `window.addEventListener('beforeunload', handler)`. It is recommended to always set the `event.returnValue` explicitly, instead of only returning a value, as the former works more consistently within Electron._ +> [!NOTE] +> There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and +> `window.addEventListener('beforeunload', handler)`. It is recommended to always set the +> `event.returnValue` explicitly, instead of only returning a value, as the former works more +> consistently within Electron. #### Event: 'closed' @@ -252,7 +261,8 @@ Emitted when the window is being moved to a new position. Emitted once when the window is moved to a new position. -**Note**: On macOS this event is an alias of `move`. +> [!NOTE] +> On macOS, this event is an alias of `move`. #### Event: 'enter-full-screen' @@ -342,12 +352,12 @@ Emitted when the window has closed a sheet. Emitted when the native new tab button is clicked. -#### Event: 'system-context-menu' _Windows_ +#### Event: 'system-context-menu' _Windows_ _Linux_ Returns: * `event` Event -* `point` [Point](structures/point.md) - The screen coordinates the context menu was triggered at +* `point` [Point](structures/point.md) - The screen coordinates where the context menu was triggered. Emitted when the system context menu is triggered on the window, this is normally only triggered when the user right clicks on the non-client area @@ -356,6 +366,8 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows-linux). + ### Static Methods The `BaseWindow` class has the following static methods: @@ -396,7 +408,7 @@ A `View` property for the content view of the window. A `string` (optional) property that is equal to the `tabbingIdentifier` passed to the `BrowserWindow` constructor or `undefined` if none was set. -#### `win.autoHideMenuBar` +#### `win.autoHideMenuBar` _Linux_ _Windows_ A `boolean` property that determines whether the window menu bar should hide itself automatically. Once set, the menu bar will only show when users press the single `Alt` key. @@ -419,7 +431,8 @@ A `boolean` property that determines whether the window is focusable. A `boolean` property that determines whether the window is visible on all workspaces. -**Note:** Always returns false on Windows. +> [!NOTE] +> Always returns false on Windows. #### `win.shadow` @@ -429,7 +442,8 @@ A `boolean` property that determines whether the window has a shadow. A `boolean` property that determines whether the menu bar should be visible. -**Note:** If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. +> [!NOTE] +> If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. #### `win.kiosk` @@ -450,7 +464,8 @@ and the icon of the file will show in window's title bar. A `string` property that determines the title of the native window. -**Note:** The title of the web page can be different from the title of the native window. +> [!NOTE] +> The title of the web page can be different from the title of the native window. #### `win.minimizable` _macOS_ _Windows_ @@ -511,12 +526,17 @@ A `string` property that defines an alternative title provided only to accessibility tools such as screen readers. This string is not directly visible to users. +#### `win.snapped` _Windows_ _Readonly_ + +A `boolean` property that indicates whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241) + ### Instance Methods Objects created with `new BaseWindow` have the following instance methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some methods are only available on specific operating systems and are +> labeled as such. #### `win.setContentView(view)` @@ -608,7 +628,8 @@ Returns `boolean` - Whether the window is minimized. Sets whether the window should be in fullscreen mode. -**Note:** On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](base-window.md#event-enter-full-screen) or ['leave-full-screen'](base-window.md#event-leave-full-screen) events. +> [!NOTE] +> On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](base-window.md#event-enter-full-screen) or > ['leave-full-screen'](base-window.md#event-leave-full-screen) events. #### `win.isFullScreen()` @@ -722,13 +743,15 @@ win.setBounds({ width: 100 }) console.log(win.getBounds()) ``` -**Note:** On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. +> [!NOTE] +> On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. #### `win.getBounds()` Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window as `Object`. -**Note:** On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. +> [!NOTE] +> On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. #### `win.getBackgroundColor()` @@ -736,7 +759,8 @@ Returns `string` - Gets the background color of the window in Hex (`#RRGGBB`) fo See [Setting `backgroundColor`](browser-window.md#setting-the-backgroundcolor-property). -**Note:** The alpha value is _not_ returned alongside the red, green, and blue values. +> [!NOTE] +> The alpha value is _not_ returned alongside the red, green, and blue values. #### `win.setContentBounds(bounds[, animate])` @@ -754,7 +778,8 @@ Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window's cl Returns [`Rectangle`](structures/rectangle.md) - Contains the window bounds of the normal state -**Note:** whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). +> [!NOTE] +> Whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). #### `win.setEnabled(enable)` @@ -951,8 +976,9 @@ Changes the title of native window to `title`. Returns `string` - The title of the native window. -**Note:** The title of the web page can be different from the title of the native -window. +> [!NOTE] +> The title of the web page can be different from the title of the native +> window. #### `win.setSheetOffset(offsetY[, offsetX])` _macOS_ @@ -1226,8 +1252,46 @@ in the taskbar. Sets the properties for the window's taskbar button. -**Note:** `relaunchCommand` and `relaunchDisplayName` must always be set -together. If one of those properties is not set, then neither will be used. +> [!NOTE] +> `relaunchCommand` and `relaunchDisplayName` must always be set +> together. If one of those properties is not set, then neither will be used. + +#### `win.setAccentColor(accentColor)` _Windows_ + +* `accentColor` boolean | string - The accent color for the window. By default, follows user preference in System Settings. + +Sets the system accent color and highlighting of active window border. + +The `accentColor` parameter accepts the following values: + +* **Color string** - Sets a custom accent color using standard CSS color formats (Hex, RGB, RGBA, HSL, HSLA, or named colors). Alpha values in RGBA/HSLA formats are ignored and the color is treated as fully opaque. +* **`true`** - Uses the system's default accent color from user preferences in System Settings. +* **`false`** - Explicitly disables accent color highlighting for the window. + +Examples: + +```js +const win = new BrowserWindow({ frame: false }) + +// Set red accent color. +win.setAccentColor('#ff0000') + +// RGB format (alpha ignored if present). +win.setAccentColor('rgba(255,0,0,0.5)') + +// Use system accent color. +win.setAccentColor(true) + +// Disable accent color. +win.setAccentColor(false) +``` + +#### `win.getAccentColor()` _Windows_ + +Returns `string | boolean` - the system accent color and highlighting of active window border in Hex RGB format. + +If a color has been set for the window that differs from the system accent color, the window accent color will +be returned. Otherwise, a boolean will be returned, with `true` indicating that the window uses the global system accent color, and `false` indicating that accent color highlighting is disabled for this window. #### `win.setIcon(icon)` _Windows_ _Linux_ @@ -1264,6 +1328,13 @@ Sets whether the menu bar should be visible. If the menu bar is auto-hide, users Returns `boolean` - Whether the menu bar is visible. +#### `win.isSnapped()` _Windows_ + +Returns `boolean` - whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241) + +The window is snapped via buttons shown when the mouse is hovered over window +maximize button, or by dragging it to the edges of the screen. + #### `win.setVisibleOnAllWorkspaces(visible[, options])` _macOS_ _Linux_ * `visible` boolean @@ -1280,13 +1351,15 @@ Returns `boolean` - Whether the menu bar is visible. Sets whether the window should be visible on all workspaces. -**Note:** This API does nothing on Windows. +> [!NOTE] +> This API does nothing on Windows. #### `win.isVisibleOnAllWorkspaces()` _macOS_ _Linux_ Returns `boolean` - Whether the window is visible on all workspaces. -**Note:** This API always returns false on Windows. +> [!NOTE] +> This API always returns false on Windows. #### `win.setIgnoreMouseEvents(ignore[, options])` @@ -1314,6 +1387,10 @@ On Windows it calls SetWindowDisplayAffinity with `WDA_EXCLUDEFROMCAPTURE`. For Windows 10 version 2004 and up the window will be removed from capture entirely, older Windows versions behave as if `WDA_MONITOR` is applied capturing a black window. +#### `win.isContentProtected()` _macOS_ _Windows_ + +Returns `boolean` - whether or not content protection is currently enabled. + #### `win.setFocusable(focusable)` _macOS_ _Windows_ * `focusable` boolean @@ -1403,7 +1480,8 @@ This method sets the browser window's system-drawn background material, includin See the [Windows documentation](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type) for more details. -**Note:** This method is only supported on Windows 11 22H2 and up. +> [!NOTE] +> This method is only supported on Windows 11 22H2 and up. #### `win.setWindowButtonPosition(position)` _macOS_ @@ -1425,8 +1503,9 @@ Sets the touchBar layout for the current window. Specifying `null` or `undefined` clears the touch bar. This method only has an effect if the machine has a touch bar. -**Note:** The TouchBar API is currently experimental and may change or be -removed in future Electron releases. +> [!NOTE] +> The TouchBar API is currently experimental and may change or be +> removed in future Electron releases. #### `win.setTitleBarOverlay(options)` _Windows_ _Linux_ diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md index f3873c538a69b..87f6ead865960 100644 --- a/docs/api/browser-view.md +++ b/docs/api/browser-view.md @@ -8,7 +8,7 @@ deprecated: ``` --> -> **Note** +> [!NOTE] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -29,7 +29,7 @@ deprecated: > Create and control views. -> **Note** +> [!NOTE] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -38,6 +38,10 @@ Process: [Main](../glossary.md#main-process) This module cannot be used until the `ready` event of the `app` module is emitted. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Example ```js @@ -176,4 +180,5 @@ Examples of valid `color` values: * Similar to CSS Color Module Level 3 keywords, but case-sensitive. * e.g. `blueviolet` or `red` -**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. +> [!NOTE] +> Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 7742039cf6710..442f9860c585d 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -150,6 +150,10 @@ Process: [Main](../glossary.md#main-process) It creates a new `BrowserWindow` with native properties as set by the `options`. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new BrowserWindow([options])` * `options` [BrowserWindowConstructorOptions](structures/browser-window-options.md?inline) (optional) @@ -158,7 +162,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. Objects created with `new BrowserWindow` emit the following events: -**Note:** Some events are only available on specific operating systems and are +> [!NOTE] +> Some events are only available on specific operating systems and are labeled as such. #### Event: 'page-title-updated' @@ -200,7 +205,11 @@ window.onbeforeunload = (e) => { } ``` -_**Note**: There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and `window.addEventListener('beforeunload', handler)`. It is recommended to always set the `event.returnValue` explicitly, instead of only returning a value, as the former works more consistently within Electron._ +> [!NOTE] +> There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and +> `window.addEventListener('beforeunload', handler)`. It is recommended to always set the +> `event.returnValue` explicitly, instead of only returning a value, as the former works more +> consistently within Electron. #### Event: 'closed' @@ -323,7 +332,8 @@ Emitted when the window is being moved to a new position. Emitted once when the window is moved to a new position. -**Note**: On macOS this event is an alias of `move`. +> [!NOTE] +> On macOS, this event is an alias of `move`. #### Event: 'enter-full-screen' @@ -421,12 +431,12 @@ Emitted when the window has closed a sheet. Emitted when the native new tab button is clicked. -#### Event: 'system-context-menu' _Windows_ +#### Event: 'system-context-menu' _Windows_ _Linux_ Returns: * `event` Event -* `point` [Point](structures/point.md) - The screen coordinates the context menu was triggered at +* `point` [Point](structures/point.md) - The screen coordinates where the context menu was triggered. Emitted when the system context menu is triggered on the window, this is normally only triggered when the user right clicks on the non-client area @@ -435,6 +445,8 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows-linux). + ### Static Methods The `BrowserWindow` class has the following static methods: @@ -458,7 +470,7 @@ or `null` if the contents are not owned by a window. * `browserView` [BrowserView](browser-view.md) -> **Note** +> [!NOTE] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -497,7 +509,7 @@ A `Integer` property representing the unique ID of the window. Each ID is unique A `string` (optional) property that is equal to the `tabbingIdentifier` passed to the `BrowserWindow` constructor or `undefined` if none was set. -#### `win.autoHideMenuBar` +#### `win.autoHideMenuBar` _Linux_ _Windows_ A `boolean` property that determines whether the window menu bar should hide itself automatically. Once set, the menu bar will only show when users press the single `Alt` key. @@ -520,7 +532,8 @@ A `boolean` property that determines whether the window is focusable. A `boolean` property that determines whether the window is visible on all workspaces. -**Note:** Always returns false on Windows. +> [!NOTE] +> Always returns false on Windows. #### `win.shadow` @@ -530,7 +543,8 @@ A `boolean` property that determines whether the window has a shadow. A `boolean` property that determines whether the menu bar should be visible. -**Note:** If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. +> [!NOTE] +> If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. #### `win.kiosk` @@ -551,7 +565,8 @@ and the icon of the file will show in window's title bar. A `string` property that determines the title of the native window. -**Note:** The title of the web page can be different from the title of the native window. +> [!NOTE] +> The title of the web page can be different from the title of the native window. #### `win.minimizable` _macOS_ _Windows_ @@ -611,12 +626,17 @@ A `string` property that defines an alternative title provided only to accessibility tools such as screen readers. This string is not directly visible to users. +#### `win.snapped` _Windows_ _Readonly_ + +A `boolean` property that indicates whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241) + ### Instance Methods Objects created with `new BrowserWindow` have the following instance methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some methods are only available on specific operating systems and are +> labeled as such. #### `win.destroy()` @@ -698,13 +718,15 @@ Returns `boolean` - Whether the window is minimized. Sets whether the window should be in fullscreen mode. -**Note:** On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events. +> [!NOTE] +> On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events. #### `win.isFullScreen()` Returns `boolean` - Whether the window is in fullscreen mode. -**Note:** On macOS, fullscreen transitions take place asynchronously. When querying for a BrowserWindow's fullscreen status, you should ensure that either the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events have been emitted. +> [!NOTE] +> On macOS, fullscreen transitions take place asynchronously. When querying for a BrowserWindow's fullscreen status, you should ensure that either the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events have been emitted. #### `win.setSimpleFullScreen(flag)` _macOS_ @@ -814,13 +836,15 @@ win.setBounds({ width: 100 }) console.log(win.getBounds()) ``` -**Note:** On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. +> [!NOTE] +> On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. #### `win.getBounds()` Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window as `Object`. -**Note:** On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. +> [!NOTE] +> On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. #### `win.getBackgroundColor()` @@ -828,7 +852,8 @@ Returns `string` - Gets the background color of the window in Hex (`#RRGGBB`) fo See [Setting `backgroundColor`](#setting-the-backgroundcolor-property). -**Note:** The alpha value is _not_ returned alongside the red, green, and blue values. +> [!NOTE] +> The alpha value is _not_ returned alongside the red, green, and blue values. #### `win.setContentBounds(bounds[, animate])` @@ -846,7 +871,8 @@ Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window's cl Returns [`Rectangle`](structures/rectangle.md) - Contains the window bounds of the normal state -**Note:** whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). +> [!NOTE] +> Whatever the current state of the window (maximized, minimized or in fullscreen), this function always returns the position and size of the window in normal state. In normal state, `getBounds` and `getNormalBounds` return the same [`Rectangle`](structures/rectangle.md). #### `win.setEnabled(enable)` @@ -1043,8 +1069,9 @@ Changes the title of native window to `title`. Returns `string` - The title of the native window. -**Note:** The title of the web page can be different from the title of the native -window. +> [!NOTE] +> The title of the web page can be different from the title of the native +> window. #### `win.setSheetOffset(offsetY[, offsetX])` _macOS_ @@ -1403,8 +1430,46 @@ in the taskbar. Sets the properties for the window's taskbar button. -**Note:** `relaunchCommand` and `relaunchDisplayName` must always be set -together. If one of those properties is not set, then neither will be used. +> [!NOTE] +> `relaunchCommand` and `relaunchDisplayName` must always be set +> together. If one of those properties is not set, then neither will be used. + +#### `win.setAccentColor(accentColor)` _Windows_ + +* `accentColor` boolean | string - The accent color for the window. By default, follows user preference in System Settings. + +Sets the system accent color and highlighting of active window border. + +The `accentColor` parameter accepts the following values: + +* **Color string** - Sets a custom accent color using standard CSS color formats (Hex, RGB, RGBA, HSL, HSLA, or named colors). Alpha values in RGBA/HSLA formats are ignored and the color is treated as fully opaque. +* **`true`** - Uses the system's default accent color from user preferences in System Settings. +* **`false`** - Explicitly disables accent color highlighting for the window. + +Examples: + +```js +const win = new BrowserWindow({ frame: false }) + +// Set red accent color. +win.setAccentColor('#ff0000') + +// RGB format (alpha ignored if present). +win.setAccentColor('rgba(255,0,0,0.5)') + +// Use system accent color. +win.setAccentColor(true) + +// Disable accent color. +win.setAccentColor(false) +``` + +#### `win.getAccentColor()` _Windows_ + +Returns `string | boolean` - the system accent color and highlighting of active window border in Hex RGB format. + +If a color has been set for the window that differs from the system accent color, the window accent color will +be returned. Otherwise, a boolean will be returned, with `true` indicating that the window uses the global system accent color, and `false` indicating that accent color highlighting is disabled for this window. #### `win.showDefinitionForSelection()` _macOS_ @@ -1445,6 +1510,13 @@ Sets whether the menu bar should be visible. If the menu bar is auto-hide, users Returns `boolean` - Whether the menu bar is visible. +#### `win.isSnapped()` _Windows_ + +Returns `boolean` - whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241) + +The window is snapped via buttons shown when the mouse is hovered over window +maximize button, or by dragging it to the edges of the screen. + #### `win.setVisibleOnAllWorkspaces(visible[, options])` _macOS_ _Linux_ * `visible` boolean @@ -1461,13 +1533,15 @@ Returns `boolean` - Whether the menu bar is visible. Sets whether the window should be visible on all workspaces. -**Note:** This API does nothing on Windows. +> [!NOTE] +> This API does nothing on Windows. #### `win.isVisibleOnAllWorkspaces()` _macOS_ _Linux_ Returns `boolean` - Whether the window is visible on all workspaces. -**Note:** This API always returns false on Windows. +> [!NOTE] +> This API always returns false on Windows. #### `win.setIgnoreMouseEvents(ignore[, options])` @@ -1495,6 +1569,10 @@ On Windows it calls SetWindowDisplayAffinity with `WDA_EXCLUDEFROMCAPTURE`. For Windows 10 version 2004 and up the window will be removed from capture entirely, older Windows versions behave as if `WDA_MONITOR` is applied capturing a black window. +#### `win.isContentProtected()` _macOS_ _Windows_ + +Returns `boolean` - whether or not content protection is currently enabled. + #### `win.setFocusable(focusable)` _macOS_ _Windows_ * `focusable` boolean @@ -1588,7 +1666,8 @@ This method sets the browser window's system-drawn background material, includin See the [Windows documentation](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type) for more details. -**Note:** This method is only supported on Windows 11 22H2 and up. +> [!NOTE] +> This method is only supported on Windows 11 22H2 and up. #### `win.setWindowButtonPosition(position)` _macOS_ @@ -1610,8 +1689,9 @@ Sets the touchBar layout for the current window. Specifying `null` or `undefined` clears the touch bar. This method only has an effect if the machine has a touch bar. -**Note:** The TouchBar API is currently experimental and may change or be -removed in future Electron releases. +> [!NOTE] +> The TouchBar API is currently experimental and may change or be +> removed in future Electron releases. #### `win.setBrowserView(browserView)` _Experimental_ _Deprecated_ @@ -1619,7 +1699,7 @@ removed in future Electron releases. If there are other `BrowserView`s attached, they will be removed from this window. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1628,7 +1708,7 @@ this window. Returns `BrowserView | null` - The `BrowserView` attached to `win`. Returns `null` if one is not attached. Throws an error if multiple `BrowserView`s are attached. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1638,7 +1718,7 @@ if one is not attached. Throws an error if multiple `BrowserView`s are attached. Replacement API for setBrowserView supporting work with multi browser views. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1646,7 +1726,7 @@ Replacement API for setBrowserView supporting work with multi browser views. * `browserView` [BrowserView](browser-view.md) -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1657,7 +1737,7 @@ Replacement API for setBrowserView supporting work with multi browser views. Raises `browserView` above other `BrowserView`s attached to `win`. Throws an error if `browserView` is not attached to `win`. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1666,7 +1746,7 @@ Throws an error if `browserView` is not attached to `win`. Returns `BrowserView[]` - a sorted by z-index array of all BrowserViews that have been attached with `addBrowserView` or `setBrowserView`. The top-most BrowserView is the last element of the array. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. diff --git a/docs/api/client-request.md b/docs/api/client-request.md index 21490bf1f1018..d6e27d6deb951 100644 --- a/docs/api/client-request.md +++ b/docs/api/client-request.md @@ -60,6 +60,10 @@ following properties: `strict-origin-when-cross-origin`. * `cache` string (optional) - can be `default`, `no-store`, `reload`, `no-cache`, `force-cache` or `only-if-cached`. + * `priority` string (optional) - can be `throttled`, `idle`, `lowest`, + `low`, `medium`, or `highest`. Defaults to `idle`. + * `priorityIncremental` boolean (optional) - the incremental loading flag as part + of HTTP extensible priorities (RFC 9218). Default is `true`. `options` properties such as `protocol`, `host`, `hostname`, `port` and `path` strictly follow the Node.js model as described in the diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index c4328e2871720..275a7afe62108 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -18,7 +18,8 @@ console.log(clipboard.readText('selection')) The `clipboard` module has the following methods: -**Note:** Experimental APIs are marked as such and could be removed in future. +> [!NOTE] +> Experimental APIs are marked as such and could be removed in future. ### `clipboard.readText([type])` @@ -141,9 +142,10 @@ bookmark is unavailable. The `title` value will always be empty on Windows. Writes the `title` (macOS only) and `url` into the clipboard as a bookmark. -**Note:** Most apps on Windows don't support pasting bookmarks into them so -you can use `clipboard.write` to write both a bookmark and fallback text to the -clipboard. +> [!NOTE] +> Most apps on Windows don't support pasting bookmarks into them so +> you can use `clipboard.write` to write both a bookmark and fallback text to the +> clipboard. ```js const { clipboard } = require('electron') diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index a07a3eb9523df..e5ab9dcbe94d7 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -73,7 +73,8 @@ Passing `--enable-logging=file` will result in logs being saved to the file specified by `--log-file=...`, or to `electron_debug.log` in the user-data directory if `--log-file` is not specified. -> **Note:** On Windows, logs from child processes cannot be sent to stderr. +> [!NOTE] +> On Windows, logs from child processes cannot be sent to stderr. > Logging to a file is the most reliable way to collect logs on Windows. See also `--log-file`, `--log-level`, `--v`, and `--vmodule`. @@ -252,9 +253,10 @@ the required version is unavailable. Current default is set to `3`. Electron supports some of the [CLI flags][node-cli] supported by Node.js. -**Note:** Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect. +> [!NOTE] +> Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect. -### `--inspect-brk\[=\[host:]port]` +### `--inspect-brk[=[host:]port]` Activate inspector on host:port and break at start of user script. Default host:port is 127.0.0.1:9229. @@ -266,13 +268,13 @@ Activate inspector on `host:port` and break at start of the first internal JavaScript script executed when the inspector is available. Default `host:port` is `127.0.0.1:9229`. -### `--inspect-port=\[host:]port` +### `--inspect-port=[host:]port` Set the `host:port` to be used when the inspector is activated. Useful when activating the inspector by sending the SIGUSR1 signal. Default host is `127.0.0.1`. Aliased to `--debug-port=[host:]port`. -### `--inspect\[=\[host:]port]` +### `--inspect[=[host:]port]` Activate inspector on `host:port`. Default is `127.0.0.1:9229`. @@ -288,6 +290,10 @@ Specify ways of the inspector web socket url exposure. By default inspector websocket url is available in stderr and under /json/list endpoint on `http://host:port/json/list`. +### `--experimental-network-inspector` + +Enable support for devtools network inspector events, for visibility into requests made by the nodejs `http` and `https` modules. + ### `--no-deprecation` Silence deprecation warnings. @@ -319,6 +325,10 @@ Set the directory to which all Node.js diagnostic output files are written. Defa Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https://nodejs.org/docs/latest/api/v8.html#v8setheapsnapshotnearheaplimitlimit). +### `--no-experimental-global-navigator` + +Disable exposition of [Navigator API][] on the global scope from Node.js. + [app]: app.md [append-switch]: command-line.md#commandlineappendswitchswitch-value [debugging-main-process]: ../tutorial/debugging-main-process.md @@ -327,3 +337,4 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https: [play-silent-audio]: https://github.com/atom/atom/pull/9485/files [ready]: app.md#event-ready [severities]: https://source.chromium.org/chromium/chromium/src/+/main:base/logging.h?q=logging::LogSeverity&ss=chromium +[Navigator API]: https://github.com/nodejs/node/blob/main/doc/api/globals.md#navigator diff --git a/docs/api/command-line.md b/docs/api/command-line.md index 63046d734624d..373bee8417423 100644 --- a/docs/api/command-line.md +++ b/docs/api/command-line.md @@ -20,45 +20,91 @@ document. #### `commandLine.appendSwitch(switch[, value])` -* `switch` string - A command-line switch, without the leading `--` -* `value` string (optional) - A value for the given switch +* `switch` string - A command-line switch, without the leading `--`. +* `value` string (optional) - A value for the given switch. Append a switch (with optional `value`) to Chromium's command line. -**Note:** This will not affect `process.argv`. The intended usage of this function is to -control Chromium's behavior. +> [!NOTE] +> This will not affect `process.argv`. The intended usage of this function is to +> control Chromium's behavior. + +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +``` #### `commandLine.appendArgument(value)` -* `value` string - The argument to append to the command line +* `value` string - The argument to append to the command line. Append an argument to Chromium's command line. The argument will be quoted correctly. Switches will precede arguments regardless of appending order. If you're appending an argument like `--switch=value`, consider using `appendSwitch('switch', 'value')` instead. -**Note:** This will not affect `process.argv`. The intended usage of this function is to -control Chromium's behavior. +```js +const { app } = require('electron') + +app.commandLine.appendArgument('--enable-experimental-web-platform-features') +``` + +> [!NOTE] +> This will not affect `process.argv`. The intended usage of this function is to +> control Chromium's behavior. #### `commandLine.hasSwitch(switch)` -* `switch` string - A command-line switch +* `switch` string - A command-line switch. Returns `boolean` - Whether the command-line switch is present. +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +const hasPort = app.commandLine.hasSwitch('remote-debugging-port') +console.log(hasPort) // true +``` + #### `commandLine.getSwitchValue(switch)` -* `switch` string - A command-line switch +* `switch` string - A command-line switch. Returns `string` - The command-line switch value. -**Note:** When the switch is not present or has no value, it returns empty string. +This function is meant to obtain Chromium command line switches. It is not +meant to be used for application-specific command line arguments. For the +latter, please use `process.argv`. + +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +const portValue = app.commandLine.getSwitchValue('remote-debugging-port') +console.log(portValue) // '8315' +``` + +> [!NOTE] +> When the switch is not present or has no value, it returns empty string. #### `commandLine.removeSwitch(switch)` -* `switch` string - A command-line switch +* `switch` string - A command-line switch. Removes the specified switch from Chromium's command line. -**Note:** This will not affect `process.argv`. The intended usage of this function is to -control Chromium's behavior. +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +console.log(app.commandLine.hasSwitch('remote-debugging-port')) // true + +app.commandLine.removeSwitch('remote-debugging-port') +console.log(app.commandLine.hasSwitch('remote-debugging-port')) // false +``` + +> [!NOTE] +> This will not affect `process.argv`. The intended usage of this function is to +> control Chromium's behavior. diff --git a/docs/api/content-tracing.md b/docs/api/content-tracing.md index f4646f2ad3e67..9ccb2c1cbdd0d 100644 --- a/docs/api/content-tracing.md +++ b/docs/api/content-tracing.md @@ -7,8 +7,9 @@ Process: [Main](../glossary.md#main-process) This module does not include a web interface. To view recorded traces, use [trace viewer][], available at `chrome://tracing` in Chrome. -**Note:** You should not use this module until the `ready` event of the app -module is emitted. +> [!NOTE] +> You should not use this module until the `ready` event of the app +> module is emitted. ```js const { app, contentTracing } = require('electron') diff --git a/docs/api/corner-smoothing-css.md b/docs/api/corner-smoothing-css.md new file mode 100644 index 0000000000000..029e74657ff3c --- /dev/null +++ b/docs/api/corner-smoothing-css.md @@ -0,0 +1,78 @@ +## CSS Rule: `-electron-corner-smoothing` + +> Smoothes out the corner rounding of the `border-radius` CSS rule. + +The rounded corners of elements with [the `border-radius` CSS rule](https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius) can be smoothed out using the `-electron-corner-smoothing` CSS rule. This smoothness is very similar to Apple's "continuous" rounded corners in SwiftUI and Figma's "corner smoothing" control on design elements. + +![There is a black rectangle on the left using simple rounded corners, and a blue rectangle on the right using smooth rounded corners. In between those rectangles is a magnified view of the same corner from both rectangles overlapping to show the subtle difference in shape.](../images/corner-smoothing-summary.svg) + +Integrating with the operating system and its design language is important to many desktop applications. The shape of a rounded corner can be a subtle detail to many users. However, aligning closely to the system's design language that users are familiar with makes the application's design feel familiar too. Beyond matching the design language of macOS, designers may decide to use smoother round corners for many other reasons. + +`-electron-corner-smoothing` affects the shape of borders, outlines, and shadows on the target element. Mirroring the behavior of `border-radius`, smoothing will gradually back off if an element's size is too small for the chosen value. + +The `-electron-corner-smoothing` CSS rule is **only implemented for Electron** and has no effect in browsers. Avoid using this rule outside of Electron. This CSS rule is considered experimental and may require migration in the future if replaced by a CSS standard. + +### Example + +The following example shows the effect of corner smoothing at different percents. + +```css +.box { + width: 128px; + height: 128px; + background-color: cornflowerblue; + border-radius: 24px; + -electron-corner-smoothing: var(--percent); /* Column header in table below. */ +} +``` + +| 0% | 30% | 60% | 100% | +| --- | --- | --- | --- | +| ![A rectangle with round corners at 0% smoothness](../images/corner-smoothing-example-0.svg) | ![A rectangle with round corners at 30% smoothness](../images/corner-smoothing-example-30.svg) | ![A rectangle with round corners at 60% smoothness](../images/corner-smoothing-example-60.svg) | ![A rectangle with round corners at 100% smoothness](../images/corner-smoothing-example-100.svg) | + +### Matching the system UI + +Use the `system-ui` keyword to match the smoothness to the OS design language. + +```css +.box { + width: 128px; + height: 128px; + background-color: cornflowerblue; + border-radius: 24px; + -electron-corner-smoothing: system-ui; /* Match the system UI design. */ +} +``` + +| OS: | macOS | Windows, Linux | +| --- | --- | --- | +| Value: | `60%` | `0%` | +| Example: | ![A rectangle with round corners whose smoothness matches macOS](../images/corner-smoothing-example-60.svg) | ![A rectangle with round corners whose smoothness matches Windows and Linux](../images/corner-smoothing-example-0.svg) | + +### Controlling availibility + +This CSS rule can be disabled by setting [the `cornerSmoothingCSS` web preference](./structures/web-preferences.md) to `false`. + +```js +const myWindow = new BrowserWindow({ + // [...] + webPreferences: { + enableCornerSmoothingCSS: false // Disables the `-electron-corner-smoothing` CSS rule + } +}) +``` + +The CSS rule will still parse, but will have no visual effect. + +### Formal reference + +* **Initial value**: `0%` +* **Inherited**: No +* **Animatable**: No +* **Computed value**: As specified + +```css +-electron-corner-smoothing = + | + system-ui +``` diff --git a/docs/api/crash-reporter.md b/docs/api/crash-reporter.md index 26c685f8a48a3..2af51c383617c 100644 --- a/docs/api/crash-reporter.md +++ b/docs/api/crash-reporter.md @@ -19,7 +19,8 @@ following projects: * [socorro](https://github.com/mozilla-services/socorro) * [mini-breakpad-server](https://github.com/electron/mini-breakpad-server) -> **Note:** Electron uses Crashpad, not Breakpad, to collect and upload +> [!NOTE] +> Electron uses Crashpad, not Breakpad, to collect and upload > crashes, but for the time being, the [upload protocol is the same](https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/doc/overview_design.md#Upload-to-collection-server). Or use a 3rd party hosted solution: @@ -84,19 +85,23 @@ before `app.on('ready')`. If the crash reporter is not initialized at the time a renderer process is created, then that renderer process will not be monitored by the crash reporter. -**Note:** You can test out the crash reporter by generating a crash using -`process.crash()`. +> [!NOTE] +> You can test out the crash reporter by generating a crash using +> `process.crash()`. -**Note:** If you need to send additional/updated `extra` parameters after your -first call `start` you can call `addExtraParameter`. +> [!NOTE] +> If you need to send additional/updated `extra` parameters after your +> first call `start` you can call `addExtraParameter`. -**Note:** Parameters passed in `extra`, `globalExtra` or set with -`addExtraParameter` have limits on the length of the keys and values. Key names -must be at most 39 bytes long, and values must be no longer than 127 bytes. -Keys with names longer than the maximum will be silently ignored. Key values -longer than the maximum length will be truncated. +> [!NOTE] +> Parameters passed in `extra`, `globalExtra` or set with +> `addExtraParameter` have limits on the length of the keys and values. Key names +> must be at most 39 bytes long, and values must be no longer than 127 bytes. +> Keys with names longer than the maximum will be silently ignored. Key values +> longer than the maximum length will be truncated. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.getLastCrashReport()` @@ -105,7 +110,8 @@ last crash report. Only crash reports that have been uploaded will be returned; even if a crash report is present on disk it will not be returned until it is uploaded. In the case that there are no uploaded reports, `null` is returned. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.getUploadedReports()` @@ -114,14 +120,16 @@ Returns [`CrashReport[]`](structures/crash-report.md): Returns all uploaded crash reports. Each report contains the date and uploaded ID. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.getUploadToServer()` Returns `boolean` - Whether reports should be submitted to the server. Set through the `start` method or `setUploadToServer`. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.setUploadToServer(uploadToServer)` @@ -130,7 +138,8 @@ the `start` method or `setUploadToServer`. This would normally be controlled by user preferences. This has no effect if called before `start` is called. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.addExtraParameter(key, value)` @@ -148,10 +157,11 @@ with crashes from renderer or other child processes. Similarly, adding extra parameters in a renderer process will not result in those parameters being sent with crashes that occur in other renderer processes or in the main process. -**Note:** Parameters have limits on the length of the keys and values. Key -names must be no longer than 39 bytes, and values must be no longer than 20320 -bytes. Keys with names longer than the maximum will be silently ignored. Key -values longer than the maximum length will be truncated. +> [!NOTE] +> Parameters have limits on the length of the keys and values. Key +> names must be no longer than 39 bytes, and values must be no longer than 20320 +> bytes. Keys with names longer than the maximum will be silently ignored. Key +> values longer than the maximum length will be truncated. ### `crashReporter.removeExtraParameter(key)` diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md index 89f6f120062f7..822eabeac8751 100644 --- a/docs/api/desktop-capturer.md +++ b/docs/api/desktop-capturer.md @@ -70,8 +70,9 @@ stopButton.addEventListener('click', () => { See [`navigator.mediaDevices.getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) for more information. -**Note:** `navigator.mediaDevices.getDisplayMedia` does not permit the use of `deviceId` for -selection of a source - see [specification](https://w3c.github.io/mediacapture-screen-share/#constraints). +> [!NOTE] +> `navigator.mediaDevices.getDisplayMedia` does not permit the use of `deviceId` for +> selection of a source - see [specification](https://w3c.github.io/mediacapture-screen-share/#constraints). ## Methods @@ -92,8 +93,9 @@ The `desktopCapturer` module has the following methods: Returns `Promise` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured. -**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, -which can detected by [`systemPreferences.getMediaAccessStatus`][]. +> [!NOTE] +> Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, +> which can detected by [`systemPreferences.getMediaAccessStatus`][]. [`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia [`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-windows-macos diff --git a/docs/api/dialog.md b/docs/api/dialog.md index a3a18584e648d..e1c641c724731 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -67,10 +67,11 @@ The `extensions` array should contain extensions without wildcards or dots (e.g. `'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the `'*'` wildcard (no other wildcard is supported). -**Note:** On Windows and Linux an open dialog can not be both a file selector -and a directory selector, so if you set `properties` to -`['openFile', 'openDirectory']` on these platforms, a directory selector will be -shown. +> [!NOTE] +> On Windows and Linux an open dialog can not be both a file selector +> and a directory selector, so if you set `properties` to +> `['openFile', 'openDirectory']` on these platforms, a directory selector will be +> shown. ```js @ts-type={mainWindow:Electron.BaseWindow} dialog.showOpenDialogSync(mainWindow, { @@ -78,10 +79,11 @@ dialog.showOpenDialogSync(mainWindow, { }) ``` -**Note:** On Linux `defaultPath` is not supported when using portal file chooser -dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` -[command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) -to force gtk or kde dialogs. +> [!NOTE] +> On Linux `defaultPath` is not supported when using portal file chooser +> dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` +> [command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) +> to force gtk or kde dialogs. ### `dialog.showOpenDialog([window, ]options)` @@ -139,10 +141,11 @@ The `extensions` array should contain extensions without wildcards or dots (e.g. `'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the `'*'` wildcard (no other wildcard is supported). -**Note:** On Windows and Linux an open dialog can not be both a file selector -and a directory selector, so if you set `properties` to -`['openFile', 'openDirectory']` on these platforms, a directory selector will be -shown. +> [!NOTE] +> On Windows and Linux an open dialog can not be both a file selector +> and a directory selector, so if you set `properties` to +> `['openFile', 'openDirectory']` on these platforms, a directory selector will be +> shown. ```js @ts-type={mainWindow:Electron.BaseWindow} dialog.showOpenDialog(mainWindow, { @@ -155,10 +158,11 @@ dialog.showOpenDialog(mainWindow, { }) ``` -**Note:** On Linux `defaultPath` is not supported when using portal file chooser -dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` -[command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) -to force gtk or kde dialogs. +> [!NOTE] +> On Linux `defaultPath` is not supported when using portal file chooser +> dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` +> [command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) +> to force gtk or kde dialogs. ### `dialog.showSaveDialogSync([window, ]options)` @@ -225,8 +229,9 @@ The `window` argument allows the dialog to attach itself to a parent window, mak The `filters` specifies an array of file types that can be displayed, see `dialog.showOpenDialog` for an example. -**Note:** On macOS, using the asynchronous version is recommended to avoid issues when -expanding and collapsing the dialog. +> [!NOTE] +> On macOS, using the asynchronous version is recommended to avoid issues when +> expanding and collapsing the dialog. ### `dialog.showMessageBoxSync([window, ]options)` diff --git a/docs/api/dock.md b/docs/api/dock.md index ea6ad93db7857..6bac22e9b9f47 100644 --- a/docs/api/dock.md +++ b/docs/api/dock.md @@ -9,7 +9,7 @@ The following example shows how to bounce your icon on the dock. ```js const { app } = require('electron') -app.dock.bounce() +app.dock?.bounce() ``` ### Instance Methods @@ -28,7 +28,8 @@ When `informational` is passed, the dock icon will bounce for one second. However, the request remains active until either the application becomes active or the request is canceled. -**Note:** This method can only be used while the app is not focused; when the app is focused it will return -1. +> [!NOTE] +> This method can only be used while the app is not focused; when the app is focused it will return -1. #### `dock.cancelBounce(id)` _macOS_ diff --git a/docs/api/download-item.md b/docs/api/download-item.md index dc2c0d1a893e4..06d52a44ea803 100644 --- a/docs/api/download-item.md +++ b/docs/api/download-item.md @@ -115,7 +115,8 @@ Returns `boolean` - Whether the download is paused. Resumes the download that has been paused. -**Note:** To enable resumable downloads the server you are downloading from must support range requests and provide both `Last-Modified` and `ETag` header values. Otherwise `resume()` will dismiss previously received bytes and restart the download from the beginning. +> [!NOTE] +> To enable resumable downloads the server you are downloading from must support range requests and provide both `Last-Modified` and `ETag` header values. Otherwise `resume()` will dismiss previously received bytes and restart the download from the beginning. #### `downloadItem.canResume()` @@ -141,9 +142,10 @@ Returns `boolean` - Whether the download has user gesture. Returns `string` - The file name of the download item. -**Note:** The file name is not always the same as the actual one saved in local -disk. If user changes the file name in a prompted download saving dialog, the -actual name of saved file will be different. +> [!NOTE] +> The file name is not always the same as the actual one saved in local +> disk. If user changes the file name in a prompted download saving dialog, the +> actual name of saved file will be different. #### `downloadItem.getCurrentBytesPerSecond()` @@ -172,8 +174,9 @@ header. Returns `string` - The current state. Can be `progressing`, `completed`, `cancelled` or `interrupted`. -**Note:** The following methods are useful specifically to resume a -`cancelled` item when session is restarted. +> [!NOTE] +> The following methods are useful specifically to resume a +> `cancelled` item when session is restarted. #### `downloadItem.getURLChain()` diff --git a/docs/api/environment-variables.md b/docs/api/environment-variables.md index 643b5fb7be419..f0efc56404ac6 100644 --- a/docs/api/environment-variables.md +++ b/docs/api/environment-variables.md @@ -104,7 +104,7 @@ you would when running the normal Node.js executable, with the exception of the These flags are disabled owing to the fact that Electron uses BoringSSL instead of OpenSSL when building Node.js' `crypto` module, and so will not work as designed. -If the [`runAsNode` fuse](../tutorial/fuses.md#L13) is disabled, `ELECTRON_RUN_AS_NODE` will be ignored. +If the [`runAsNode` fuse](../tutorial/fuses.md#runasnode) is disabled, `ELECTRON_RUN_AS_NODE` will be ignored. ### `ELECTRON_NO_ATTACH_CONSOLE` _Windows_ diff --git a/docs/api/extensions-api.md b/docs/api/extensions-api.md index 30add8f9bcd9d..afbb40574e17b 100644 --- a/docs/api/extensions-api.md +++ b/docs/api/extensions-api.md @@ -92,11 +92,13 @@ app.whenReady().then(async () => { This API does not support loading packed (.crx) extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. -**Note:** Loading extensions into in-memory (non-persistent) sessions is not -supported and will throw an error. +> [!NOTE] +> Loading extensions into in-memory (non-persistent) sessions is not +> supported and will throw an error. #### `extensions.removeExtension(extensionId)` @@ -104,8 +106,9 @@ supported and will throw an error. Unloads an extension. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. #### `extensions.getExtension(extensionId)` @@ -113,12 +116,14 @@ is emitted. Returns `Extension | null` - The loaded extension with the given ID. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. #### `extensions.getAllExtensions()` Returns `Extension[]` - A list of all loaded extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. diff --git a/docs/api/extensions.md b/docs/api/extensions.md index 2bc93d499a2d2..43f7ccfb40d8a 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -6,7 +6,8 @@ but it also happens to support some other extension capabilities. [chrome-extensions-api-index]: https://developer.chrome.com/extensions/api_index -> **Note:** Electron does not support arbitrary Chrome extensions from the +> [!NOTE] +> Electron does not support arbitrary Chrome extensions from the > store, and it is a **non-goal** of the Electron project to be perfectly > compatible with Chrome's implementation of Extensions. @@ -160,7 +161,8 @@ The following methods of `chrome.tabs` are supported: - `chrome.tabs.update` (partial support) - supported properties: `url`, `muted`. -> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active +> [!NOTE] +> In Chrome, passing `-1` as a tab ID signifies the "currently active > tab". Since Electron has no such concept, passing `-1` as a tab ID is not > supported and will raise an error. @@ -170,6 +172,7 @@ See [official documentation](https://developer.chrome.com/docs/extensions/refere All features of this API are supported. -> **NOTE:** Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers. +> [!NOTE] +> Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers. See [official documentation](https://developer.chrome.com/docs/extensions/reference/webRequest) for more information. diff --git a/docs/api/global-shortcut.md b/docs/api/global-shortcut.md index 142bdbaba1adf..50a5dacdfd246 100644 --- a/docs/api/global-shortcut.md +++ b/docs/api/global-shortcut.md @@ -8,13 +8,13 @@ The `globalShortcut` module can register/unregister a global keyboard shortcut with the operating system so that you can customize the operations for various shortcuts. -**Note:** The shortcut is global; it will work even if the app does -not have the keyboard focus. This module cannot be used before the `ready` -event of the app module is emitted. - -Please also note that it is also possible to use Chromium's -`GlobalShortcutsPortal` implementation, which allows apps to bind global -shortcuts when running within a Wayland session. +> [!NOTE] +> The shortcut is global; it will work even if the app does +> not have the keyboard focus. This module cannot be used before the `ready` +> event of the app module is emitted. +> Please also note that it is also possible to use Chromium's +> `GlobalShortcutsPortal` implementation, which allows apps to bind global +> shortcuts when running within a Wayland session. ```js const { app, globalShortcut } = require('electron') diff --git a/docs/api/image-view.md b/docs/api/image-view.md new file mode 100644 index 0000000000000..fcc7135ecfa7a --- /dev/null +++ b/docs/api/image-view.md @@ -0,0 +1,65 @@ +# ImageView + +> A View that displays an image. + +Process: [Main](../glossary.md#main-process) + +This module cannot be used until the `ready` event of the `app` +module is emitted. + +Useful for showing splash screens that will be swapped for `WebContentsView`s +when the content finishes loading. + +Note that `ImageView` is experimental and may be changed or removed in the future. + +```js +const { BaseWindow, ImageView, nativeImage, WebContentsView } = require('electron') +const path = require('node:path') + +const win = new BaseWindow({ width: 800, height: 600 }) + +// Create a "splash screen" image to display while the WebContentsView loads +const splashView = new ImageView() +const splashImage = nativeImage.createFromPath(path.join(__dirname, 'loading.png')) +splashView.setImage(splashImage) +win.setContentView(splashView) + +const webContentsView = new WebContentsView() +webContentsView.webContents.once('did-finish-load', () => { + // Now that the WebContentsView has loaded, swap out the "splash screen" ImageView + win.setContentView(webContentsView) +}) +webContentsView.webContents.loadURL('https://electronjs.org') +``` + +## Class: ImageView extends `View` + +> A View that displays an image. + +Process: [Main](../glossary.md#main-process) + +`ImageView` inherits from [`View`](view.md). + +`ImageView` is an [EventEmitter][event-emitter]. + +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + +### `new ImageView()` _Experimental_ + +Creates an ImageView. + +### Instance Methods + +The following methods are available on instances of the `ImageView` class, in +addition to those inherited from [View](view.md): + +#### `image.setImage(image)` _Experimental_ + +* `image` NativeImage + +Sets the image for this `ImageView`. Note that only image formats supported by +`NativeImage` can be used with an `ImageView`. + +[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter diff --git a/docs/api/ipc-main-service-worker.md b/docs/api/ipc-main-service-worker.md index 8995d66ba7a7c..08a9d63a98174 100644 --- a/docs/api/ipc-main-service-worker.md +++ b/docs/api/ipc-main-service-worker.md @@ -11,6 +11,10 @@ Process: [Main](../glossary.md#main-process) +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Instance Methods #### `ipcMainServiceWorker.on(channel, listener)` diff --git a/docs/api/ipc-renderer.md b/docs/api/ipc-renderer.md index 7f2afc41e7c10..53722a414bbea 100644 --- a/docs/api/ipc-renderer.md +++ b/docs/api/ipc-renderer.md @@ -156,7 +156,7 @@ If you need to transfer a [`MessagePort`][] to the main process, use [`ipcRender If you do not need a response to the message, consider using [`ipcRenderer.send`](#ipcrenderersendchannel-args). -> **Note** +> [!NOTE] > Sending non-standard JavaScript types such as DOM objects or > special Electron objects will throw an exception. > @@ -165,7 +165,7 @@ If you do not need a response to the message, consider using [`ipcRenderer.send` > Electron's IPC to the main process, as the main process would have no way to decode > them. Attempting to send such objects over IPC will result in an error. -> **Note** +> [!NOTE] > If the handler in the main process throws an error, > the promise returned by `invoke` will reject. > However, the `Error` object in the renderer process @@ -195,7 +195,8 @@ throw an exception. The main process handles it by listening for `channel` with [`ipcMain`](./ipc-main.md) module, and replies by setting `event.returnValue`. -> :warning: **WARNING**: Sending a synchronous message will block the whole +> [!WARNING] +> Sending a synchronous message will block the whole > renderer process until the reply is received, so use this method only as a > last resort. It's much better to use the asynchronous version, > [`invoke()`](./ipc-renderer.md#ipcrendererinvokechannel-args). diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 0bc80b2bd37c1..8ee760d1cb44a 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -6,6 +6,10 @@ Process: [Main](../glossary.md#main-process) See [`Menu`](menu.md) for examples. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new MenuItem(options)` * `options` Object @@ -19,7 +23,7 @@ See [`Menu`](menu.md) for examples. * `type` string (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or `radio`. * `label` string (optional) - * `sublabel` string (optional) + * `sublabel` string (optional) _macOS_ - Available in macOS >= 14.4 * `toolTip` string (optional) _macOS_ - Hover text for this menu item. * `accelerator` [Accelerator](accelerator.md) (optional) * `icon` ([NativeImage](native-image.md) | string) (optional) @@ -51,7 +55,8 @@ See [`Menu`](menu.md) for examples. the placement of their containing group after the containing group of the item with the specified id. -**Note:** `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development. +> [!NOTE] +> `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development. ### Roles @@ -125,7 +130,8 @@ When specifying a `role` on macOS, `label` and `accelerator` are the only options that will affect the menu item. All other options will be ignored. Lowercase `role`, e.g. `toggledevtools`, is still supported. -**Note:** The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS. +> [!NOTE] +> The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS. ### Instance Properties @@ -170,7 +176,8 @@ An `Accelerator` (optional) indicating the item's accelerator, if set. An `Accelerator | null` indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item. -**Note:** This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`. +> [!NOTE] +> This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`. #### `menuItem.icon` diff --git a/docs/api/menu.md b/docs/api/menu.md index b9a8a7b09e07f..9ba6ce00242c0 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -6,6 +6,10 @@ Process: [Main](../glossary.md#main-process) +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new Menu()` Creates a new menu. @@ -32,16 +36,18 @@ In order to escape the `&` character in an item name, add a proceeding `&`. For Passing `null` will suppress the default menu. On Windows and Linux, this has the additional effect of removing the menu bar from the window. -**Note:** The default menu will be created automatically if the app does not set one. -It contains standard items such as `File`, `Edit`, `View`, `Window` and `Help`. +> [!NOTE] +> The default menu will be created automatically if the app does not set one. +> It contains standard items such as `File`, `Edit`, `View`, `Window` and `Help`. #### `Menu.getApplicationMenu()` Returns `Menu | null` - The application menu, if set, or `null`, if not set. -**Note:** The returned `Menu` instance doesn't support dynamic addition or -removal of menu items. [Instance properties](#instance-properties) can still -be dynamically modified. +> [!NOTE] +> The returned `Menu` instance doesn't support dynamic addition or +> removal of menu items. [Instance properties](#instance-properties) can still +> be dynamically modified. #### `Menu.sendActionToFirstResponder(action)` _macOS_ @@ -73,6 +79,8 @@ The `menu` object has the following instance methods: * `options` Object (optional) * `window` [BaseWindow](base-window.md) (optional) - Default is the focused window. + * `frame` [WebFrameMain](web-frame-main.md) (optional) - Provide the relevant frame + if you want certain OS-level features such as Writing Tools on macOS to function correctly. Typically, this should be `params.frame` from the [`context-menu` event](web-contents.md#event-context-menu) on a WebContents, or the [`focusedFrame` property](web-contents.md#contentsfocusedframe-readonly) of a WebContents. * `x` number (optional) - Default is the current mouse cursor position. Must be declared if `y` is declared. * `y` number (optional) - Default is the current mouse cursor position. @@ -117,8 +125,9 @@ Inserts the `menuItem` to the `pos` position of the menu. Objects created with `new Menu` or returned by `Menu.buildFromTemplate` emit the following events: -**Note:** Some events are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some events are only available on specific operating systems and are +> labeled as such. #### Event: 'menu-will-show' @@ -327,6 +336,27 @@ name, no matter what label you set. To change it, modify your app bundle's [About Information Property List Files][AboutInformationPropertyListFiles] for more information. +### Menu Sublabels + +Menu sublabels, or [subtitles](https://developer.apple.com/documentation/appkit/nsmenuitem/subtitle?language=objc), can be added to menu items using the `sublabel` option. Below is an example based on the renderer example above: + +```js @ts-expect-error=[12] +// main +ipcMain.on('show-context-menu', (event) => { + const template = [ + { + label: 'Menu Item 1', + sublabel: 'Subtitle 1', + click: () => { event.sender.send('context-menu-command', 'menu-item-1') } + }, + { type: 'separator' }, + { label: 'Menu Item 2', sublabel: 'Subtitle 2', type: 'checkbox', checked: true } + ] + const menu = Menu.buildFromTemplate(template) + menu.popup({ window: BrowserWindow.fromWebContents(event.sender) }) +}) +``` + ## Setting Menu for Specific Browser Window (_Linux_ _Windows_) The [`setMenu` method][setMenu] of browser windows can set the menu of certain diff --git a/docs/api/message-channel-main.md b/docs/api/message-channel-main.md index 18339848db6ac..3dff6a6269629 100644 --- a/docs/api/message-channel-main.md +++ b/docs/api/message-channel-main.md @@ -33,6 +33,10 @@ ipcRenderer.on('port', (e) => { }) ``` +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Instance Properties #### `channel.port1` diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 91ee4e68ef042..a867cfca5e2f1 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -134,7 +134,8 @@ Creates an empty `NativeImage` instance. Returns `Promise` - fulfilled with the file's thumbnail preview image, which is a [NativeImage](native-image.md). -Note: The Windows implementation will ignore `size.height` and scale the height according to `size.width`. +> [!NOTE] +> Windows implementation will ignore `size.height` and scale the height according to `size.width`. ### `nativeImage.createFromPath(path)` @@ -142,8 +143,8 @@ Note: The Windows implementation will ignore `size.height` and scale the height Returns `NativeImage` -Creates a new `NativeImage` instance from a file located at `path`. This method -returns an empty image if the `path` does not exist, cannot be read, or is not +Creates a new `NativeImage` instance from an image file (e.g., PNG or JPEG) located at `path`. +This method returns an empty image if the `path` does not exist, cannot be read, or is not a valid image. ```js @@ -271,16 +272,12 @@ changes: Returns `string` - The [Data URL][data-url] of the image. -#### `image.getBitmap([options])` +#### `image.getBitmap([options])` _Deprecated_ * `options` Object (optional) * `scaleFactor` Number (optional) - Defaults to 1.0. -Returns `Buffer` - A [Buffer][buffer] that contains the image's raw bitmap pixel data. - -The difference between `getBitmap()` and `toBitmap()` is that `getBitmap()` does not -copy the bitmap data, so you have to use the returned Buffer immediately in -current event loop tick; otherwise the data might be changed or destroyed. +Legacy alias for `image.toBitmap()`. #### `image.getNativeHandle()` _macOS_ diff --git a/docs/api/native-theme.md b/docs/api/native-theme.md index e6bdd6c806067..7860c870307b4 100644 --- a/docs/api/native-theme.md +++ b/docs/api/native-theme.md @@ -63,6 +63,14 @@ Your application should then always use `shouldUseDarkColors` to determine what A `boolean` for if the OS / Chromium currently has high-contrast mode enabled or is being instructed to show a high-contrast UI. +### `nativeTheme.shouldUseDarkColorsForSystemIntegratedUI` _macOS_ _Windows_ _Readonly_ + +A `boolean` property indicating whether or not the system theme has been set to dark or light. + +On Windows this property distinguishes between system and app light/dark theme, returning +`true` if the system theme is set to dark theme and `false` otherwise. On macOS the return +value will be the same as `nativeTheme.shouldUseDarkColors`. + ### `nativeTheme.shouldUseInvertedColorScheme` _macOS_ _Windows_ _Readonly_ A `boolean` for if the OS / Chromium currently has an inverted color scheme diff --git a/docs/api/net-log.md b/docs/api/net-log.md index f9b04212c043f..2ae5beabfc9ee 100644 --- a/docs/api/net-log.md +++ b/docs/api/net-log.md @@ -17,8 +17,9 @@ app.whenReady().then(async () => { See [`--log-net-log`](command-line-switches.md#--log-net-logpath) to log network events throughout the app's lifecycle. -**Note:** All methods unless specified can only be used after the `ready` event -of the `app` module gets emitted. +> [!NOTE] +> All methods unless specified can only be used after the `ready` event +> of the `app` module gets emitted. ## Methods diff --git a/docs/api/net.md b/docs/api/net.md index 464c078a54258..2b42e445ff19c 100644 --- a/docs/api/net.md +++ b/docs/api/net.md @@ -117,8 +117,9 @@ protocol.handle('https', (req) => { }) ``` -Note: in the [utility process](../glossary.md#utility-process) custom protocols -are not supported. +> [!NOTE] +> In the [utility process](../glossary.md#utility-process), custom protocols +> are not supported. ### `net.isOnline()` diff --git a/docs/api/notification.md b/docs/api/notification.md index efaa93b39e217..1c5a6e9c98162 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -4,12 +4,9 @@ Process: [Main](../glossary.md#main-process) -:::info Renderer process notifications - -If you want to show notifications from a renderer process you should use the -[web Notifications API](../tutorial/notifications.md) - -::: +> [!NOTE] +> If you want to show notifications from a renderer process you should use the +> [web Notifications API](../tutorial/notifications.md) ## Class: Notification @@ -21,6 +18,10 @@ Process: [Main](../glossary.md#main-process) It creates a new `Notification` with native properties as set by the `options`. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Static Methods The `Notification` class has the following static methods: diff --git a/docs/api/power-save-blocker.md b/docs/api/power-save-blocker.md index ee57287258df0..348a6b78907a6 100644 --- a/docs/api/power-save-blocker.md +++ b/docs/api/power-save-blocker.md @@ -33,10 +33,11 @@ Returns `Integer` - The blocker ID that is assigned to this power blocker. Starts preventing the system from entering lower-power mode. Returns an integer identifying the power save blocker. -**Note:** `prevent-display-sleep` has higher precedence over -`prevent-app-suspension`. Only the highest precedence type takes effect. In -other words, `prevent-display-sleep` always takes precedence over -`prevent-app-suspension`. +> [!NOTE] +> `prevent-display-sleep` has higher precedence over +> `prevent-app-suspension`. Only the highest precedence type takes effect. In +> other words, `prevent-display-sleep` always takes precedence over +> `prevent-app-suspension`. For example, an API calling A requests for `prevent-app-suspension`, and another calling B requests for `prevent-display-sleep`. `prevent-display-sleep` diff --git a/docs/api/process.md b/docs/api/process.md index 03a606de9ac19..3c24d5db57985 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -233,7 +233,8 @@ console.log(version) // On Linux -> '4.15.0-45-generic' ``` -**Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`. +> [!NOTE] +> It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`. ### `process.takeHeapSnapshot(filePath)` diff --git a/docs/api/protocol.md b/docs/api/protocol.md index 1af780827c80b..a14cd01b6250c 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -20,8 +20,9 @@ app.whenReady().then(() => { }) ``` -**Note:** All methods unless specified can only be used after the `ready` event -of the `app` module gets emitted. +> [!NOTE] +> All methods unless specified can only be used after the `ready` event +> of the `app` module gets emitted. ## Using `protocol` with a custom `partition` or `session` @@ -61,8 +62,9 @@ The `protocol` module has the following methods: * `customSchemes` [CustomScheme[]](structures/custom-scheme.md) -**Note:** This method can only be used before the `ready` event of the `app` -module gets emitted and can be called only once. +> [!NOTE] +> This method can only be used before the `ready` event of the `app` +> module gets emitted and can be called only once. Registers the `scheme` as standard, secure, bypasses content security policy for resources, allows registering ServiceWorker, supports fetch API, streaming diff --git a/docs/api/push-notifications.md b/docs/api/push-notifications.md index 01c0e830e5b87..099689d927300 100644 --- a/docs/api/push-notifications.md +++ b/docs/api/push-notifications.md @@ -46,4 +46,7 @@ See: https://developer.apple.com/documentation/appkit/nsapplication/1428476-regi ### `pushNotifications.unregisterForAPNSNotifications()` _macOS_ Unregisters the app from notifications received from APNS. + +Apps unregistered through this method can always reregister. + See: https://developer.apple.com/documentation/appkit/nsapplication/1428747-unregisterforremotenotifications?language=objc diff --git a/docs/api/safe-storage.md b/docs/api/safe-storage.md index d9c7dc3feab47..4f985db5e1951 100644 --- a/docs/api/safe-storage.md +++ b/docs/api/safe-storage.md @@ -73,5 +73,6 @@ command line flag is provided `--password-store="basic"`. is provided `--password-store="kwallet"`. * `kwallet5` - When the desktop session is `kde5` or if the following command line flag is provided `--password-store="kwallet5"`. -* `kwallet6` - When the desktop session is `kde6`. +* `kwallet6` - When the desktop session is `kde6` or if the following command line flag +is provided `--password-store="kwallet6"`. * `unknown` - When the function is called before app has emitted the `ready` event. diff --git a/docs/api/screen.md b/docs/api/screen.md index 4f68a2018b062..c3bc334887eeb 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -9,8 +9,9 @@ module is emitted. `screen` is an [EventEmitter][event-emitter]. -**Note:** In the renderer / DevTools, `window.screen` is a reserved DOM -property, so writing `let { screen } = require('electron')` will not work. +> [!NOTE] +> In the renderer / DevTools, `window.screen` is a reserved DOM +> property, so writing `let { screen } = require('electron')` will not work. An example of creating a window that fills the whole screen: @@ -57,6 +58,14 @@ app.whenReady().then(() => { }) ``` +> [!NOTE] +> Screen coordinates used by this module are [`Point`](structures/point.md) structures. +> There are two kinds of coordinates available to the process: +> +> * **Physical screen points** are raw hardware pixels on a display. +> * **Device-independent pixel (DIP) points** are virtualized screen points scaled based on the DPI +> (dots per inch) of the display. + ## Events The `screen` module emits the following events: @@ -101,7 +110,8 @@ Returns [`Point`](structures/point.md) The current absolute position of the mouse pointer. -**Note:** The return value is a DIP point, not a screen physical point. +> [!NOTE] +> The return value is a DIP point, not a screen physical point. ### `screen.getPrimaryDisplay()` @@ -124,7 +134,7 @@ Returns [`Display`](structures/display.md) - The display nearest the specified p Returns [`Display`](structures/display.md) - The display that most closely intersects the provided bounds. -### `screen.screenToDipPoint(point)` _Windows_ +### `screen.screenToDipPoint(point)` _Windows_ _Linux_ * `point` [Point](structures/point.md) @@ -133,7 +143,10 @@ Returns [`Point`](structures/point.md) Converts a screen physical point to a screen DIP point. The DPI scale is performed relative to the display containing the physical point. -### `screen.dipToScreenPoint(point)` _Windows_ +Not currently supported on Wayland - if used there it will return the point passed +in with no changes. + +### `screen.dipToScreenPoint(point)` _Windows_ _Linux_ * `point` [Point](structures/point.md) @@ -142,6 +155,8 @@ Returns [`Point`](structures/point.md) Converts a screen DIP point to a screen physical point. The DPI scale is performed relative to the display containing the DIP point. +Not currently supported on Wayland. + ### `screen.screenToDipRect(window, rect)` _Windows_ * `window` [BrowserWindow](browser-window.md) | null diff --git a/docs/api/session.md b/docs/api/session.md index 65a3a36be0063..105dfb5f7673c 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -651,7 +651,7 @@ Clears the session’s HTTP cache. `shadercache`, `websql`, `serviceworkers`, `cachestorage`. If not specified, clear all storage types. * `quotas` string[] (optional) - The types of quotas to clear, can be - `temporary`, `syncable`. If not specified, clear all quotas. + `temporary`. If not specified, clear all quotas. Returns `Promise` - resolves when the storage data has been cleared. @@ -762,7 +762,8 @@ Preconnects the given number of sockets to an origin. Returns `Promise` - Resolves when all connections are closed. -**Note:** It will terminate / fail all requests currently in flight. +> [!NOTE] +> It will terminate / fail all requests currently in flight. #### `ses.fetch(input[, init])` @@ -1305,8 +1306,9 @@ Initiates a download of the resource at `url`. The API will generate a [DownloadItem](download-item.md) that can be accessed with the [will-download](#event-will-download) event. -**Note:** This does not perform any security checks that relate to a page's origin, -unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl-options). +> [!NOTE] +> This does not perform any security checks that relate to a page's origin, +> unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl-options). #### `ses.createInterruptedDownload(options)` @@ -1434,7 +1436,8 @@ The built in spellchecker does not automatically detect what language a user is spell checker to correctly check their words you must call this API with an array of language codes. You can get the list of supported language codes with the `ses.availableSpellCheckerLanguages` property. -**Note:** On macOS the OS spellchecker is used and will detect your language automatically. This API is a no-op on macOS. +> [!NOTE] +> On macOS, the OS spellchecker is used and will detect your language automatically. This API is a no-op on macOS. #### `ses.getSpellCheckerLanguages()` @@ -1442,7 +1445,8 @@ Returns `string[]` - An array of language codes the spellchecker is enabled for. will fallback to using `en-US`. By default on launch if this setting is an empty list Electron will try to populate this setting with the current OS locale. This setting is persisted across restarts. -**Note:** On macOS the OS spellchecker is used and has its own list of languages. On macOS, this API will return whichever languages have been configured by the OS. +> [!NOTE] +> On macOS, the OS spellchecker is used and has its own list of languages. On macOS, this API will return whichever languages have been configured by the OS. #### `ses.setSpellCheckerDictionaryDownloadURL(url)` @@ -1460,7 +1464,8 @@ If the files present in `hunspell_dictionaries.zip` are available at `https://ex then you should call this api with `ses.setSpellCheckerDictionaryDownloadURL('https://example.com/dictionaries/')`. Please note the trailing slash. The URL to the dictionaries is formed as `${url}${filename}`. -**Note:** On macOS the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS. +> [!NOTE] +> On macOS, the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS. #### `ses.listWordsInSpellCheckerDictionary()` @@ -1474,7 +1479,8 @@ Resolves when the full dictionary is loaded from disk. Returns `boolean` - Whether the word was successfully written to the custom dictionary. This API will not work on non-persistent (in-memory) sessions. -**Note:** On macOS and Windows 10 this word will be written to the OS custom dictionary as well +> [!NOTE] +> On macOS and Windows, this word will be written to the OS custom dictionary as well. #### `ses.removeWordFromSpellCheckerDictionary(word)` @@ -1483,7 +1489,8 @@ will not work on non-persistent (in-memory) sessions. Returns `boolean` - Whether the word was successfully removed from the custom dictionary. This API will not work on non-persistent (in-memory) sessions. -**Note:** On macOS and Windows 10 this word will be removed from the OS custom dictionary as well +> [!NOTE] +> On macOS and Windows, this word will be removed from the OS custom dictionary as well. #### `ses.loadExtension(path[, options])` _Deprecated_ @@ -1526,11 +1533,13 @@ app.whenReady().then(async () => { This API does not support loading packed (.crx) extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. -**Note:** Loading extensions into in-memory (non-persistent) sessions is not -supported and will throw an error. +> [!NOTE] +> Loading extensions into in-memory (non-persistent) sessions is not +> supported and will throw an error. **Deprecated:** Use the new `ses.extensions.loadExtension` API. @@ -1540,8 +1549,9 @@ supported and will throw an error. Unloads an extension. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. **Deprecated:** Use the new `ses.extensions.removeExtension` API. @@ -1551,8 +1561,9 @@ is emitted. Returns `Extension | null` - The loaded extension with the given ID. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. **Deprecated:** Use the new `ses.extensions.getExtension` API. @@ -1560,8 +1571,9 @@ is emitted. Returns `Extension[]` - A list of all loaded extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. **Deprecated:** Use the new `ses.extensions.getAllExtensions` API. @@ -1599,9 +1611,11 @@ Clears various different types of data. This method clears more types of data and is more thorough than the `clearStorageData` method. -**Note:** Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`. +> [!NOTE] +> Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`. -**Note:** Clearing cache data will also clear the shared dictionary cache. This means that any dictionaries used for compression may be reloaded after clearing the cache. If you wish to clear the shared dictionary cache but leave other cached data intact, you may want to use the `clearSharedDictionaryCache` method. +> [!NOTE] +> Clearing cache data will also clear the shared dictionary cache. This means that any dictionaries used for compression may be reloaded after clearing the cache. If you wish to clear the shared dictionary cache but leave other cached data intact, you may want to use the `clearSharedDictionaryCache` method. For more information, refer to Chromium's [`BrowsingDataRemover` interface][browsing-data-remover]. diff --git a/docs/api/share-menu.md b/docs/api/share-menu.md index a886ea52682af..c1ffd455743b0 100644 --- a/docs/api/share-menu.md +++ b/docs/api/share-menu.md @@ -13,6 +13,10 @@ For including the share menu as a submenu of other menus, please use the Process: [Main](../glossary.md#main-process) +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new ShareMenu(sharingItem)` * `sharingItem` SharingItem - The item to share. diff --git a/docs/api/shell.md b/docs/api/shell.md index 1008b73c164d2..80e720b8f419a 100644 --- a/docs/api/shell.md +++ b/docs/api/shell.md @@ -14,7 +14,8 @@ const { shell } = require('electron') shell.openExternal('https://github.com') ``` -**Note:** While the `shell` module can be used in the renderer process, it will not function in a sandboxed renderer. +> [!WARNING] +> While the `shell` module can be used in the renderer process, it will not function in a sandboxed renderer. ## Methods diff --git a/docs/api/structures/base-window-options.md b/docs/api/structures/base-window-options.md index 3876d69225278..375c6adc3b59b 100644 --- a/docs/api/structures/base-window-options.md +++ b/docs/api/structures/base-window-options.md @@ -58,8 +58,8 @@ `false` on macOS. This option is not configurable on other platforms. * `disableAutoHideCursor` boolean (optional) - Whether to hide cursor when typing. Default is `false`. -* `autoHideMenuBar` boolean (optional) - Auto hide the menu bar unless the `Alt` - key is pressed. Default is `false`. +* `autoHideMenuBar` boolean (optional) _Linux_ _Windows_ - Auto hide the menu bar + unless the `Alt` key is pressed. Default is `false`. * `enableLargerThanScreen` boolean (optional) _macOS_ - Enable the window to be resized larger than screen. Only relevant for macOS, as other OSes allow larger-than-screen windows by default. Default is `false`. @@ -93,8 +93,9 @@ **Note:** This option is currently experimental. * `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjunction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`. * `color` String (optional) _Windows_ _Linux_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color. - * `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color. + * `symbolColor` String (optional) _Windows_ _Linux_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color. * `height` Integer (optional) - The height of the title bar and Window Controls Overlay in pixels. Default is system height. +* `accentColor` boolean | string (optional) _Windows_ - The accent color for the window. By default, follows user preference in System Settings. Set to `false` to explicitly disable, or set the color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha values will be ignored. * `trafficLightPosition` [Point](point.md) (optional) _macOS_ - Set a custom position for the traffic light buttons in frameless windows. * `roundedCorners` boolean (optional) _macOS_ _Windows_ - Whether frameless window diff --git a/docs/api/structures/hid-device.md b/docs/api/structures/hid-device.md index a6a097061dc22..7c8712cd8605b 100644 --- a/docs/api/structures/hid-device.md +++ b/docs/api/structures/hid-device.md @@ -6,3 +6,11 @@ * `productId` Integer - The USB product ID. * `serialNumber` string (optional) - The USB device serial number. * `guid` string (optional) - Unique identifier for the HID interface. A device may have multiple HID interfaces. +* `collections` Object[] - an array of report formats. See [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/HIDDevice/collections) for more. + * `usage` Integer - An integer representing the usage ID component of the HID usage associated with this collection. + * `usagePage` Integer - An integer representing the usage page component of the HID usage associated with this collection. + * `type` Integer - An 8-bit value representing the collection type, which describes a different relationship between the grouped items. + * `children` Object[] - An array of sub-collections which takes the same format as a top-level collection. + * `inputReports` Object[] - An array of inputReport items which represent individual input reports described in this collection. + * `outputReports` Object[] - An array of outputReport items which represent individual output reports described in this collection. + * `featureReports` Object[] - An array of featureReport items which represent individual feature reports described in this collection. diff --git a/docs/api/structures/input-event.md b/docs/api/structures/input-event.md index a68a9304dfca1..34b6891faf283 100644 --- a/docs/api/structures/input-event.md +++ b/docs/api/structures/input-event.md @@ -12,6 +12,6 @@ `pointerDown`, `pointerUp`, `pointerMove`, `pointerRawUpdate`, `pointerCancel` or `pointerCausedUaAction`. * `modifiers` string[] (optional) - An array of modifiers of the event, can - be `shift`, `control`, `ctrl`, `alt`, `meta`, `command`, `cmd`, `isKeypad`, - `isAutoRepeat`, `leftButtonDown`, `middleButtonDown`, `rightButtonDown`, - `capsLock`, `numLock`, `left`, `right`. + be `shift`, `control`, `ctrl`, `alt`, `meta`, `command`, `cmd`, `iskeypad`, + `isautorepeat`, `leftbuttondown`, `middlebuttondown`, `rightbuttondown`, + `capslock`, `numlock`, `left`, `right`. diff --git a/docs/api/structures/jump-list-category.md b/docs/api/structures/jump-list-category.md index 117483f1a8e5e..ad5747ceed62f 100644 --- a/docs/api/structures/jump-list-category.md +++ b/docs/api/structures/jump-list-category.md @@ -15,11 +15,13 @@ * `items` JumpListItem[] (optional) - Array of [`JumpListItem`](jump-list-item.md) objects if `type` is `tasks` or `custom`, otherwise it should be omitted. -**Note:** If a `JumpListCategory` object has neither the `type` nor the `name` -property set then its `type` is assumed to be `tasks`. If the `name` property -is set but the `type` property is omitted then the `type` is assumed to be -`custom`. +> [!NOTE] +> If a `JumpListCategory` object has neither the `type` nor the `name` +> property set then its `type` is assumed to be `tasks`. If the `name` property +> is set but the `type` property is omitted then the `type` is assumed to be +> `custom`. -**Note:** The maximum length of a Jump List item's `description` property is -260 characters. Beyond this limit, the item will not be added to the Jump -List, nor will it be displayed. +> [!NOTE] +> The maximum length of a Jump List item's `description` property is +> 260 characters. Beyond this limit, the item will not be added to the Jump +> List, nor will it be displayed. diff --git a/docs/api/structures/point.md b/docs/api/structures/point.md index 5b792cea0f9c7..9294dc7db54ba 100644 --- a/docs/api/structures/point.md +++ b/docs/api/structures/point.md @@ -3,6 +3,7 @@ * `x` number * `y` number -**Note:** Both `x` and `y` must be whole integers, when providing a point object -as input to an Electron API we will automatically round your `x` and `y` values -to the nearest whole integer. +> [!NOTE] +> Both `x` and `y` must be whole integers, when providing a point object +> as input to an Electron API we will automatically round your `x` and `y` values +> to the nearest whole integer. diff --git a/docs/api/structures/web-preferences.md b/docs/api/structures/web-preferences.md index 26ead57c2329f..4e6710523615d 100644 --- a/docs/api/structures/web-preferences.md +++ b/docs/api/structures/web-preferences.md @@ -149,6 +149,7 @@ `WebContents` when the preferred size changes. Default is `false`. * `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent. * `enableDeprecatedPaste` boolean (optional) _Deprecated_ - Whether to enable the `paste` [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). Default is `false`. +* `enableCornerSmoothingCSS` boolean (optional) _Experimental_ - Whether the [`-electron-corner-smoothing` CSS rule](../corner-smoothing-css.md) is enabled. Default is `true`. [chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment [runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5 diff --git a/docs/api/touch-bar-other-items-proxy.md b/docs/api/touch-bar-other-items-proxy.md index a00cd4b0b1588..efad02d070c70 100644 --- a/docs/api/touch-bar-other-items-proxy.md +++ b/docs/api/touch-bar-other-items-proxy.md @@ -4,8 +4,9 @@ > from Chromium at the space indicated by the proxy. By default, this proxy is added > to each TouchBar at the end of the input. For more information, see the AppKit docs on > [NSTouchBarItemIdentifierOtherItemsProxy](https://developer.apple.com/documentation/appkit/nstouchbaritemidentifierotheritemsproxy) -> -> Note: Only one instance of this class can be added per TouchBar. + +> [!NOTE] +> Only one instance of this class can be added per TouchBar. Process: [Main](../glossary.md#main-process)
_This class is not exported from the `'electron'` module. It is only available as a return value of other methods in the Electron API._ diff --git a/docs/api/touch-bar.md b/docs/api/touch-bar.md index c229430326437..35f3105416ba3 100644 --- a/docs/api/touch-bar.md +++ b/docs/api/touch-bar.md @@ -1,5 +1,9 @@ # TouchBar +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ## Class: TouchBar > Create TouchBar layouts for native macOS applications @@ -15,12 +19,14 @@ Process: [Main](../glossary.md#main-process) Creates a new touch bar with the specified items. Use `BrowserWindow.setTouchBar` to add the `TouchBar` to a window. -**Note:** The TouchBar API is currently experimental and may change or be -removed in future Electron releases. +> [!NOTE] +> The TouchBar API is currently experimental and may change or be +> removed in future Electron releases. -**Tip:** If you don't have a MacBook with Touch Bar, you can use -[Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator) -to test Touch Bar usage in your app. +> [!TIP] +> If you don't have a MacBook with Touch Bar, you can use +> [Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator) +> to test Touch Bar usage in your app. ### Static Properties diff --git a/docs/api/tray.md b/docs/api/tray.md index 6d11505ac5602..2ffc4dc671847 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -25,6 +25,10 @@ app.whenReady().then(() => { }) ``` +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + **Platform Considerations** **Linux** @@ -176,7 +180,8 @@ Returns: Emitted when the mouse is released from clicking the tray icon. -Note: This will not be emitted if you have set a context menu for your Tray using `tray.setContextMenu`, as a result of macOS-level constraints. +> [!NOTE] +> This will not be emitted if you have set a context menu for your Tray using `tray.setContextMenu`, as a result of macOS-level constraints. #### Event: 'mouse-down' _macOS_ @@ -238,7 +243,7 @@ Sets the `image` associated with this tray icon when pressed on macOS. * `toolTip` string -Sets the hover text for this tray icon. +Sets the hover text for this tray icon. Setting the text to an empty string will remove the tooltip. #### `tray.setTitle(title[, options])` _macOS_ diff --git a/docs/api/utility-process.md b/docs/api/utility-process.md index 0b500b58fa9fc..e6cf1b79bab87 100644 --- a/docs/api/utility-process.md +++ b/docs/api/utility-process.md @@ -44,6 +44,9 @@ Process: [Main](../glossary.md#main-process)
Returns [`UtilityProcess`](utility-process.md#class-utilityprocess) +> [!NOTE] +> `utilityProcess.fork` can only be called after the `ready` event has been emitted on `App`. + ## Class: UtilityProcess > Instances of the `UtilityProcess` represent the Chromium spawned child process @@ -106,7 +109,8 @@ child.on('exit', () => { }) ``` -**Note:** You can use the `pid` to determine if the process is currently running. +> [!NOTE] +> You can use the `pid` to determine if the process is currently running. #### `child.stdout` diff --git a/docs/api/view.md b/docs/api/view.md index 32ac190d618e6..5858d08c0abb8 100644 --- a/docs/api/view.md +++ b/docs/api/view.md @@ -25,6 +25,10 @@ Process: [Main](../glossary.md#main-process) `View` is an [EventEmitter][event-emitter]. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new View()` Creates a new `View`. @@ -94,13 +98,15 @@ Examples of valid `color` values: * Similar to CSS Color Module Level 3 keywords, but case-sensitive. * e.g. `blueviolet` or `red` -**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. +> [!NOTE] +> Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. #### `view.setBorderRadius(radius)` * `radius` Integer - Border radius size in pixels. -**Note:** The area cutout of the view's border still captures clicks. +> [!NOTE] +> The area cutout of the view's border still captures clicks. #### `view.setVisible(visible)` diff --git a/docs/api/web-contents-view.md b/docs/api/web-contents-view.md index 66bb257cf0edb..802580e26b9bd 100644 --- a/docs/api/web-contents-view.md +++ b/docs/api/web-contents-view.md @@ -32,6 +32,10 @@ Process: [Main](../glossary.md#main-process) `WebContentsView` is an [EventEmitter][event-emitter]. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new WebContentsView([options])` * `options` Object (optional) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index bcb733a8ad985..37876c608966e 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -463,7 +463,8 @@ win.webContents.on('will-prevent-unload', (event) => { }) ``` -**Note:** This will be emitted for `BrowserViews` but will _not_ be respected - this is because we have chosen not to tie the `BrowserView` lifecycle to its owning BrowserWindow should one exist per the [specification](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event). +> [!NOTE] +> This will be emitted for `BrowserViews` but will _not_ be respected - this is because we have chosen not to tie the `BrowserView` lifecycle to its owning BrowserWindow should one exist per the [specification](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event). #### Event: 'render-process-gone' @@ -533,14 +534,55 @@ To only prevent the menu shortcuts, use [`setIgnoreMenuShortcuts`](#contentssetignoremenushortcutsignore): ```js -const { BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron') -const win = new BrowserWindow({ width: 800, height: 600 }) +app.whenReady().then(() => { + const win = new BrowserWindow({ width: 800, height: 600 }) + + win.webContents.on('before-input-event', (event, input) => { + // Enable application menu keyboard shortcuts when Ctrl/Cmd are down. + win.webContents.setIgnoreMenuShortcuts(!input.control && !input.meta) + }) +}) +``` + +#### Event: 'before-mouse-event' + +Returns: + +* `event` Event +* `mouse` [MouseInputEvent](structures/mouse-input-event.md) + +Emitted before dispatching mouse events in the page. + +Calling `event.preventDefault` will prevent the page mouse events. + +```js +const { app, BrowserWindow } = require('electron') -win.webContents.on('before-input-event', (event, input) => { - // For example, only enable application menu keyboard shortcuts when - // Ctrl/Cmd are down. - win.webContents.setIgnoreMenuShortcuts(!input.control && !input.meta) +app.whenReady().then(() => { + const win = new BrowserWindow({ width: 800, height: 600 }) + + win.webContents.on('before-mouse-event', (event, mouse) => { + // Prevent mouseDown events. + if (mouse.type === 'mouseDown') { + console.log(mouse) + /* + { + type: 'mouseDown', + clickCount: 1, + movementX: 0, + movementY: 0, + button: 'left', + x: 632.359375, + y: 480.6875, + globalX: 168.359375, + globalY: 193.6875 + } + */ + event.preventDefault() + } + }) }) ``` @@ -838,9 +880,10 @@ Emitted when a bluetooth device needs to be selected when a call to the `deviceId` of the device to be selected. Passing an empty string to `callback` will cancel the request. -If an event listener is not added for this event, or if `event.preventDefault` -is not called when handling this event, the first available device will be -automatically selected. +If no event listener is added for this event, all bluetooth requests will be cancelled. + +If `event.preventDefault` is not called when handling this event, the first available +device will be automatically selected. Due to the nature of bluetooth, scanning for devices when `navigator.bluetooth.requestDevice` is called may take time and will cause @@ -887,7 +930,7 @@ const { BrowserWindow } = require('electron') const win = new BrowserWindow({ webPreferences: { offscreen: true } }) win.webContents.on('paint', (event, dirty, image) => { - // updateBitmap(dirty, image.getBitmap()) + // updateBitmap(dirty, image.toBitmap()) }) win.loadURL('https://github.com') ``` @@ -1491,7 +1534,8 @@ increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. The formula for this is `scale := 1.2 ^ level`. -> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the +> [!NOTE] +> The zoom policy at the Chromium level is same-origin, meaning that the > zoom level for a specific domain propagates across all instances of windows with > the same domain. Differentiating the window URLs will make zoom work per-window. @@ -1508,7 +1552,8 @@ Returns `Promise` Sets the maximum and minimum pinch-to-zoom level. -> **NOTE**: Visual zoom is disabled by default in Electron. To re-enable it, call: +> [!NOTE] +> Visual zoom is disabled by default in Electron. To re-enable it, call: > > ```js > const win = new BrowserWindow() @@ -2076,7 +2121,9 @@ Disable device emulation enabled by `webContents.enableDeviceEmulation`. * `inputEvent` [MouseInputEvent](structures/mouse-input-event.md) | [MouseWheelInputEvent](structures/mouse-wheel-input-event.md) | [KeyboardInputEvent](structures/keyboard-input-event.md) Sends an input `event` to the page. -**Note:** The [`BrowserWindow`](browser-window.md) containing the contents needs to be focused for + +> [!NOTE] +> The [`BrowserWindow`](browser-window.md) containing the contents needs to be focused for `sendInputEvent()` to work. #### `contents.beginFrameSubscription([onlyDirty ,]callback)` @@ -2218,7 +2265,9 @@ By default this value is `{ min: 0, max: 0 }` , which would apply no restriction * `max` Integer - The maximum UDP port number that WebRTC should use. Setting the WebRTC UDP Port Range allows you to restrict the udp port range used by WebRTC. By default the port range is unrestricted. -**Note:** To reset to an unrestricted port range this value should be set to `{ min: 0, max: 0 }`. + +> [!NOTE] +> To reset to an unrestricted port range this value should be set to `{ min: 0, max: 0 }`. #### `contents.getMediaSourceId(requestWebContents)` @@ -2364,8 +2413,9 @@ A [`WebContents`](web-contents.md) instance that might own this `WebContents`. A `WebContents | null` property that represents the of DevTools `WebContents` associated with a given `WebContents`. -**Note:** Users should never store this object because it may become `null` -when the DevTools has been closed. +> [!NOTE] +> Users should never store this object because it may become `null` +> when the DevTools has been closed. #### `contents.debugger` _Readonly_ diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index b0148f4bfe043..301c002bd1401 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -41,7 +41,8 @@ Changes the zoom level to the specified level. The original size is 0 and each increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. -> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the +> [!NOTE] +> The zoom policy at the Chromium level is same-origin, meaning that the > zoom level for a specific domain propagates across all instances of windows with > the same domain. Differentiating the window URLs will make zoom work per-window. @@ -56,13 +57,15 @@ Returns `number` - The current zoom level. Sets the maximum and minimum pinch-to-zoom level. -> **NOTE**: Visual zoom is disabled by default in Electron. To re-enable it, call: +> [!NOTE] +> Visual zoom is disabled by default in Electron. To re-enable it, call: > > ```js > webFrame.setVisualZoomLevelLimits(1, 3) > ``` -> **NOTE**: Visual zoom only applies to pinch-to-zoom behavior. Cmd+/-/0 zoom shortcuts are +> [!NOTE] +> Visual zoom only applies to pinch-to-zoom behavior. Cmd+/-/0 zoom shortcuts are > controlled by the 'zoomIn', 'zoomOut', and 'resetZoom' MenuItem roles in the application > Menu. To disable shortcuts, manually [define the Menu](./menu.md#examples) and omit zoom roles > from the definition. @@ -189,7 +192,9 @@ dispatch errors of isolated worlds to foreign worlds. * `name` string (optional) - Name for isolated world. Useful in devtools. Set the security origin, content security policy and name of the isolated world. -Note: If the `csp` is specified, then the `securityOrigin` also has to be specified. + +> [!NOTE] +> If the `csp` is specified, then the `securityOrigin` also has to be specified. ### `webFrame.getResourceUsage()` diff --git a/docs/api/web-utils.md b/docs/api/web-utils.md index 2162d9b36cbfc..54ff8c03ababb 100644 --- a/docs/api/web-utils.md +++ b/docs/api/web-utils.md @@ -16,7 +16,7 @@ Returns `string` - The file system path that this `File` object points to. In th This method superseded the previous augmentation to the `File` object with the `path` property. An example is included below. -```js +```js @ts-nocheck // Before const oldPath = document.querySelector('input').files[0].path diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index 14103680f7f4e..385358728a2ed 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -30,8 +30,10 @@ rendered. Unlike an `iframe`, the `webview` runs in a separate process than your app. It doesn't have the same permissions as your web page and all interactions between your app and embedded content will be asynchronous. This keeps your app -safe from the embedded content. **Note:** Most methods called on the -webview from the host page require a synchronous call to the main process. +safe from the embedded content. + +> [!NOTE] +> Most methods called on the webview from the host page require a synchronous call to the main process. ## Example @@ -252,7 +254,8 @@ The full list of supported feature strings can be found in the The `webview` tag has the following methods: -**Note:** The webview element must be loaded before using the methods. +> [!NOTE] +> The webview element must be loaded before using the methods. **Example** @@ -679,7 +682,8 @@ increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. The formula for this is `scale := 1.2 ^ level`. -> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the +> [!NOTE] +> The zoom policy at the Chromium level is same-origin, meaning that the > zoom level for a specific domain propagates across all instances of windows with > the same domain. Differentiating the window URLs will make zoom work per-window. diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index e7d3eeff07e82..537b4a74c8cd4 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -12,6 +12,34 @@ This document uses the following convention to categorize breaking changes: * **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release. * **Removed:** An API or feature was removed, and is no longer supported by Electron. +## Planned Breaking API Changes (37.0) + +### Behavior Changed: `BrowserWindow.IsVisibleOnAllWorkspaces()` on Linux + +`BrowserWindow.IsVisibleOnAllWorkspaces()` will now return false on Linux if the +window is not currently visible. + +### Behavior Changes: `app.commandLine` + +`app.commandLine` will convert upper-cases switches and arguments to lowercase. + +`app.commandLine` was only meant to handle chromium switches (which aren't case-sensitive) and switches passed via `app.commandLine` will not be passed down to any of the child processes. + +If you were using `app.commandLine` to control the behavior of the main process, you should do this via `process.argv`. + +### Deprecated: `NativeImage.getBitmap()` + +`NativeImage.toBitmap()` returns a newly-allocated copy of the bitmap. `NativeImage.getBitmap()` was originally an alternative function that returned the original instead of a copy. This changed when sandboxing was introduced, so both return a copy and are functionally equivalent. + +Client code should call `NativeImage.toBitmap()` instead: + +```js +// Deprecated +bitmap = image.getBitmap() +// Use this instead +bitmap = image.toBitmap() +``` + ## Planned Breaking API Changes (36.0) ### Removed:`isDefault` and `status` properties on `PrinterInfo` @@ -19,6 +47,29 @@ This document uses the following convention to categorize breaking changes: These properties have been removed from the PrinterInfo Object because they have been removed from upstream Chromium. +### Removed: `quota` type `syncable` in `Session.clearStorageData(options)` + +When calling `Session.clearStorageData(options)`, the `options.quota` type +`syncable` is no longer supported because it has been +[removed](https://chromium-review.googlesource.com/c/chromium/src/+/6309405) +from upstream Chromium. + +### Deprecated: `quota` property in `Session.clearStorageData(options)` + +When calling `Session.clearStorageData(options)`, the `options.quota` +property is deprecated. Since the `syncable` type was removed, there +is only type left -- `'temporary'` -- so specifying it is unnecessary. + +### Deprecated: `null` value for `session` property in `ProtocolResponse` + +Previously, setting the ProtocolResponse.session property to `null` +Would create a random independent session. This is no longer supported. + +Using single-purpose sessions here is discouraged due to overhead costs; +however, old code that needs to preserve this behavior can emulate it by +creating a random session with `session.fromPartition(some_random_string)` +and then using it in `ProtocolResponse.session`. + ### Deprecated: Extension methods and events on `session` `session.loadExtension`, `session.removeExtension`, `session.getExtension`, @@ -33,6 +84,24 @@ It has been always returning `true` since Electron 23, which only supports Windo https://learn.microsoft.com/en-us/windows/win32/dwm/composition-ovw#disabling-dwm-composition-windows7-and-earlier +### Changed: GTK 4 is default when running GNOME + +After an [upstream change](https://chromium-review.googlesource.com/c/chromium/src/+/6310469), GTK 4 is now the default when running GNOME. + +In rare cases, this may cause some applications or configurations to [error](https://github.com/electron/electron/issues/46538) with the following message: + +```stderr +Gtk-ERROR **: 11:30:38.382: GTK 2/3 symbols detected. Using GTK 2/3 and GTK 4 in the same process is not supported +``` + +Affected users can work around this by specifying the `gtk-version` command-line flag: + +```shell +$ electron --gtk-version=3 # or --gtk-version=2 +``` + +The same can be done with the [`app.commandLine.appendSwitch`](https://www.electronjs.org/docs/latest/api/command-line#commandlineappendswitchswitch-value) function. + ## Planned Breaking API Changes (35.0) ### Behavior Changed: Dialog API's `defaultPath` option on Linux @@ -41,7 +110,7 @@ On Linux, the required portal version for file dialogs has been reverted to 3 from 4. Using the `defaultPath` option of the Dialog API is not supported when using portal file chooser dialogs unless the portal backend is version 4 or higher. The `--xdg-portal-required-version` -[command-line switch](/api/command-line-switches.md#--xdg-portal-required-versionversion) +[command-line switch](api/command-line-switches.md#--xdg-portal-required-versionversion) can be used to force a required version for your application. See [#44426](https://github.com/electron/electron/pull/44426) for more details. diff --git a/docs/development/api-history-migration-guide.md b/docs/development/api-history-migration-guide.md index f4128dbf527de..0c1ae7c54a25e 100644 --- a/docs/development/api-history-migration-guide.md +++ b/docs/development/api-history-migration-guide.md @@ -65,7 +65,7 @@ Verify that the Pull Request is correct and make a corresponding entry in the API History: > [!NOTE] -> Refer to the [API History section of `styleguide.md`](../styleguide.md#api-history) +> Refer to the [API History section of `style-guide.md`](./style-guide.md#api-history) for information on how to create API History blocks. `````markdown diff --git a/docs/development/build-instructions-gn.md b/docs/development/build-instructions-gn.md index 64c897b5ea59f..2efd0ba8d9dc8 100644 --- a/docs/development/build-instructions-gn.md +++ b/docs/development/build-instructions-gn.md @@ -155,7 +155,8 @@ $ gn gen out/Release --args="import(\"//electron/build/args/release.gn\")" $ gn gen out/Release --args="import(\`"//electron/build/args/release.gn\`")" ``` -**Note:** This will generate a `out/Testing` or `out/Release` build directory under `src/` with the testing or release build depending upon the configuration passed above. You can replace `Testing|Release` with another names, but it should be a subdirectory of `out`. +> [!NOTE] +> This will generate a `out/Testing` or `out/Release` build directory under `src/` with the testing or release build depending upon the configuration passed above. You can replace `Testing|Release` with another names, but it should be a subdirectory of `out`. Also you shouldn't have to run `gn gen` again—if you want to change the build arguments, you can run `gn args out/Testing` to bring up an editor. To see the list of available build configuration options, run `gn args out/Testing --list`. diff --git a/docs/development/build-instructions-linux.md b/docs/development/build-instructions-linux.md index 8bfd6349a511a..07cd2c49d091a 100644 --- a/docs/development/build-instructions-linux.md +++ b/docs/development/build-instructions-linux.md @@ -7,11 +7,8 @@ Follow the guidelines below for building **Electron itself** on Linux, for the p ## Prerequisites * At least 25GB disk space and 8GB RAM. -* Python >= 3.7. -* Node.js. There are various ways to install Node. You can download - source code from [nodejs.org](https://nodejs.org) and compile it. - Doing so permits installing Node on your own home directory as a standard user. - Or try repositories such as [NodeSource](https://nodesource.com/blog/nodejs-v012-iojs-and-the-nodesource-linux-repositories). +* Python >= 3.9. +* [Node.js](https://nodejs.org/download/) >= 22.12.0 * [clang](https://clang.llvm.org/get_started.html) 3.4 or later. * Development headers of GTK 3 and libnotify. diff --git a/docs/development/build-instructions-macos.md b/docs/development/build-instructions-macos.md index 8ab4670d1971c..cc3ab4fc8a7dc 100644 --- a/docs/development/build-instructions-macos.md +++ b/docs/development/build-instructions-macos.md @@ -10,8 +10,8 @@ Follow the guidelines below for building **Electron itself** on macOS, for the p * [Xcode](https://developer.apple.com/technologies/tools/). The exact version needed depends on what branch you are building, but the latest version of Xcode is generally a good bet for building `main`. -* [node.js](https://nodejs.org) (external) -* Python >= 3.7 +* Python >= 3.9 +* [Node.js](https://nodejs.org/download/) >= 22.12.0 ### Arm64-specific prerequisites diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 8d7e7943308a4..3ff011c45e04e 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -14,7 +14,7 @@ Follow the guidelines below for building **Electron itself** on Windows, for the set a few environment variables to point the toolchains to your installation path. * `vs2022_install = DRIVE:\path\to\Microsoft Visual Studio\2022\Community`, replacing `2022` and `Community` with your installed versions and replacing `DRIVE:` with the drive that Visual Studio is on. Often, this will be `C:`. * `WINDOWSSDKDIR = DRIVE:\path\to\Windows Kits\10`, replacing `DRIVE:` with the drive that Windows Kits is on. Often, this will be `C:`. -* [Node.js](https://nodejs.org/download/) +* [Node.js](https://nodejs.org/download/) >= 22.12.0 * [Git](https://git-scm.com) * Debugging Tools for Windows of Windows SDK 10.0.15063.468 if you plan on creating a full distribution since `symstore.exe` is used for creating a symbol @@ -39,8 +39,9 @@ Building Electron is done entirely with command-line scripts and cannot be done with Visual Studio. You can develop Electron with any editor but support for building with Visual Studio will come in the future. -**Note:** Even though Visual Studio is not used for building, it's still -**required** because we need the build toolchains it provides. +> [!NOTE] +> Even though Visual Studio is not used for building, it's still +> **required** because we need the build toolchains it provides. ## Exclude source tree from Windows Security diff --git a/docs/development/creating-api.md b/docs/development/creating-api.md index 62af7250c15b0..49f383f33f923 100644 --- a/docs/development/creating-api.md +++ b/docs/development/creating-api.md @@ -148,7 +148,8 @@ In your [`shell/common/node_bindings.cc`](https://github.com/electron/electron/b V(electron_browser_{api_name}) ``` -> Note: More technical details on how Node links with Electron can be found on [our blog](https://www.electronjs.org/blog/electron-internals-using-node-as-a-library#link-node-with-electron). +> [!NOTE] +> More technical details on how Node links with Electron can be found on [our blog](https://www.electronjs.org/blog/electron-internals-using-node-as-a-library#link-node-with-electron). ## Expose your API to TypeScript diff --git a/docs/development/patches.md b/docs/development/patches.md index e7c744f34a551..e32549e1fe7a7 100644 --- a/docs/development/patches.md +++ b/docs/development/patches.md @@ -49,7 +49,8 @@ $ git commit $ ../../electron/script/git-export-patches -o ../../electron/patches/node ``` -> **NOTE**: `git-export-patches` ignores any uncommitted files, so you must create a commit if you want your changes to be exported. The subject line of the commit message will be used to derive the patch file name, and the body of the commit message should include the reason for the patch's existence. +> [!NOTE] +> `git-export-patches` ignores any uncommitted files, so you must create a commit if you want your changes to be exported. The subject line of the commit message will be used to derive the patch file name, and the body of the commit message should include the reason for the patch's existence. Re-exporting patches will sometimes cause shasums in unrelated patches to change. This is generally harmless and can be ignored (but go ahead and add those changes to your PR, it'll stop them from showing up for other people). diff --git a/docs/styleguide.md b/docs/development/style-guide.md similarity index 98% rename from docs/styleguide.md rename to docs/development/style-guide.md index 18e5ebb98631c..cac7c7520e474 100644 --- a/docs/styleguide.md +++ b/docs/development/style-guide.md @@ -195,7 +195,7 @@ required[, optional] More detailed information on each of the arguments is noted in an unordered list below the method. The type of argument is notated by either JavaScript primitives (e.g. `string`, `Promise`, or `Object`), a custom API structure like Electron's -[`Cookie`](api/structures/cookie.md), or the wildcard `any`. +[`Cookie`](../api/structures/cookie.md), or the wildcard `any`. If the argument is of type `Array`, use `[]` shorthand with the type of value inside the array (for example,`any[]` or `string[]`). @@ -290,7 +290,7 @@ The purpose of the API History block is to describe when/where/how/why an API wa Each API change listed in the block should include a link to the PR where that change was made along with an optional short description of the change. If applicable, include the [heading id](https://gist.github.com/asabaylus/3071099) -for that change from the [breaking changes documentation](./breaking-changes.md). +for that change from the [breaking changes documentation](../breaking-changes.md). The [API History linting script][api-history-linting-script] (`lint:api-history`) validates API History blocks in the Electron documentation against the schema and diff --git a/docs/faq.md b/docs/faq.md index 114731d69d689..9b5e656d81659 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -152,6 +152,14 @@ The effect is visible only on (some?) LCD screens. Even if you don't see a diffe Notice that just setting the background in the CSS does not have the desired effect. +## Class inheritance does not work with Electron built-in modules + +Electron classes cannot be subclassed with the [`extends`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) +keyword (also known as class inheritance). This feature was never implemented in Electron due +to the added complexity it would add to C++/JavaScript interop in Electron's internals. + +For more information, see [electron/electron#23](https://github.com/electron/electron/issues/23). + [memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management [closures]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures [storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage diff --git a/docs/fiddles/features/macos-dock-menu/main.js b/docs/fiddles/features/macos-dock-menu/main.js index 5b8b154fe4a8f..4b9503471bca2 100644 --- a/docs/fiddles/features/macos-dock-menu/main.js +++ b/docs/fiddles/features/macos-dock-menu/main.js @@ -24,9 +24,7 @@ const dockMenu = Menu.buildFromTemplate([ ]) app.whenReady().then(() => { - if (process.platform === 'darwin') { - app.dock.setMenu(dockMenu) - } + app.dock?.setMenu(dockMenu) }).then(createWindow) app.on('window-all-closed', () => { diff --git a/docs/images/corner-smoothing-example-0.svg b/docs/images/corner-smoothing-example-0.svg new file mode 100644 index 0000000000000..928435ed0b006 --- /dev/null +++ b/docs/images/corner-smoothing-example-0.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-example-100.svg b/docs/images/corner-smoothing-example-100.svg new file mode 100644 index 0000000000000..34d0802239bef --- /dev/null +++ b/docs/images/corner-smoothing-example-100.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-example-30.svg b/docs/images/corner-smoothing-example-30.svg new file mode 100644 index 0000000000000..4996a25253ce0 --- /dev/null +++ b/docs/images/corner-smoothing-example-30.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-example-60.svg b/docs/images/corner-smoothing-example-60.svg new file mode 100644 index 0000000000000..cb1e68a8ff688 --- /dev/null +++ b/docs/images/corner-smoothing-example-60.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-summary.svg b/docs/images/corner-smoothing-summary.svg new file mode 100644 index 0000000000000..75bffa8e259d3 --- /dev/null +++ b/docs/images/corner-smoothing-summary.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/tutorial/code-signing.md b/docs/tutorial/code-signing.md index 880b36f16f53e..d0eff31e5fe38 100644 --- a/docs/tutorial/code-signing.md +++ b/docs/tutorial/code-signing.md @@ -78,6 +78,8 @@ See the [Mac App Store Guide][]. ## Signing Windows builds +### Using traditional certificates + Before you can code sign your application, you need to acquire a code signing certificate. Unlike Apple, Microsoft allows developers to purchase those certificates on the open market. They are usually sold by the same companies @@ -117,13 +119,13 @@ expose configuration options through a `windowsSign` property. You can either us to sign files directly - or use the same `windowsSign` configuration across Electron Forge, [`@electron/packager`][], [`electron-winstaller`][], and [`electron-wix-msi`][]. -### Using Electron Forge +#### Using Electron Forge Electron Forge is the recommended way to sign your app as well as your `Squirrel.Windows` and `WiX MSI` installers. Detailed instructions on how to configure your application can be found in the [Electron Forge Code Signing Tutorial](https://www.electronforge.io/guides/code-signing/code-signing-windows). -### Using Electron Packager +#### Using Electron Packager If you're not using an integrated build pipeline like Forge, you are likely using [`@electron/packager`][], which includes [`@electron/windows-sign`][]. @@ -146,7 +148,7 @@ packager({ }) ``` -### Using electron-winstaller (Squirrel.Windows) +#### Using electron-winstaller (Squirrel.Windows) [`electron-winstaller`][] is a package that can generate Squirrel.Windows installers for your Electron app. This is the tool used under the hood by Electron Forge's @@ -178,7 +180,7 @@ try { For full configuration options, check out the [`electron-winstaller`][] repository! -### Using electron-wix-msi (WiX MSI) +#### Using electron-wix-msi (WiX MSI) [`electron-wix-msi`][] is a package that can generate MSI installers for your Electron app. This is the tool used under the hood by Electron Forge's [MSI Maker][maker-msi]. @@ -221,11 +223,32 @@ await msiCreator.compile() For full configuration options, check out the [`electron-wix-msi`][] repository! -### Using Electron Builder +#### Using Electron Builder Electron Builder comes with a custom solution for signing your application. You can find [its documentation here](https://www.electron.build/code-signing). +### Using Azure Trusted Signing + +[Azure Trusted Signing][] is Microsoft's modern cloud-based alternative to EV certificates. +It is the cheapest option for code signing on Windows, and it gets rid of SmartScreen warnings. + +As of May 2025, Azure Trusted Signing is [available][trusted-signing-availability] to US and +Canada-based organizations with 3+ years of verifiable business history. Microsoft is looking +to make the program more widely available. If you're reading this at a later point, it could +make sense to check if the eligibility criteria have changed. + +#### Using Electron Forge + +Electron Forge is the recommended way to sign your app as well as your `Squirrel.Windows` +and `WiX MSI` installers. Instructions for Azure Trusted Signing can be found +[here][forge-trusted-signing]. + +#### Using Electron Builder + +The Electron Builder documentation for Azure Trusted Signing can be found +[here][builder-trusted-signing]. + ### Signing Windows Store applications See the [Windows Store Guide][]. @@ -243,3 +266,7 @@ See the [Windows Store Guide][]. [windows store guide]: ./windows-store-guide.md [maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows [maker-msi]: https://www.electronforge.io/config/makers/wix-msi +[azure trusted signing]: https://azure.microsoft.com/en-us/products/trusted-signing +[trusted-signing-availability]: https://techcommunity.microsoft.com/blog/microsoft-security-blog/trusted-signing-public-preview-update/4399713 +[forge-trusted-signing]: https://www.electronforge.io/guides/code-signing/code-signing-windows#using-azure-trusted-signing +[builder-trusted-signing]: https://www.electron.build/code-signing-win#using-azure-trusted-signing-beta diff --git a/docs/tutorial/custom-window-styles.md b/docs/tutorial/custom-window-styles.md index 8e4503cbb5cc8..e07a06230bcf0 100644 --- a/docs/tutorial/custom-window-styles.md +++ b/docs/tutorial/custom-window-styles.md @@ -37,7 +37,6 @@ the illusion of a circular window. open on the user's system). * The window will not be transparent when DevTools is opened. * On _Windows_: - * Transparent windows will not work when DWM is disabled. * Transparent windows can not be maximized using the Windows system menu or by double clicking the title bar. The reasoning behind this can be seen on PR [#28207](https://github.com/electron/electron/pull/28207). diff --git a/docs/tutorial/debugging-vscode.md b/docs/tutorial/debugging-vscode.md index fa5ccd8f76879..9f8d29d25c22c 100644 --- a/docs/tutorial/debugging-vscode.md +++ b/docs/tutorial/debugging-vscode.md @@ -1,6 +1,7 @@ # Debugging in VSCode -This guide goes over how to set up VSCode debugging for both your own Electron project as well as the native Electron codebase. +This guide goes over how to set up VSCode debugging for both your own Electron +project as well as the native Electron codebase. ## Debugging your Electron app @@ -9,8 +10,8 @@ This guide goes over how to set up VSCode debugging for both your own Electron p #### 1. Open an Electron project in VSCode. ```sh -$ git clone git@github.com:electron/electron-quick-start.git -$ code electron-quick-start +$ npx create-electron-app@latest my-app +$ code my-app ``` #### 2. Add a file `.vscode/launch.json` with the following configuration: @@ -37,23 +38,27 @@ $ code electron-quick-start #### 3. Debugging -Set some breakpoints in `main.js`, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). You should be able to hit the breakpoints. - -Here is a pre-configured project that you can download and directly debug in VSCode: https://github.com/octref/vscode-electron-debug/tree/master/electron-quick-start +Set some breakpoints in `main.js`, and start debugging in the +[Debug View](https://code.visualstudio.com/docs/editor/debugging). You should +be able to hit the breakpoints. ## Debugging the Electron codebase -If you want to build Electron from source and modify the native Electron codebase, this section will help you in testing your modifications. +If you want to build Electron from source and modify the native Electron codebase, +this section will help you in testing your modifications. -For those unsure where to acquire this code or how to build it, [Electron's Build Tools](https://github.com/electron/build-tools) automates and explains most of this process. If you wish to manually set up the environment, you can instead use these [build instructions](../development/build-instructions-gn.md). +For those unsure where to acquire this code or how to build it, +[Electron's Build Tools](https://github.com/electron/build-tools) automates and +explains most of this process. If you wish to manually set up the environment, +you can instead use these [build instructions](../development/build-instructions-gn.md). ### Windows (C++) #### 1. Open an Electron project in VSCode. ```sh -$ git clone git@github.com:electron/electron-quick-start.git -$ code electron-quick-start +$ npx create-electron-app@latest my-app +$ code my-app ``` #### 2. Add a file `.vscode/launch.json` with the following configuration: @@ -86,14 +91,22 @@ $ code electron-quick-start **Configuration Notes** -* `cppvsdbg` requires the [built-in C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) be enabled. +* `cppvsdbg` requires the +[built-in C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) +be enabled. * `${workspaceFolder}` is the full path to Chromium's `src` directory. * `your-executable-location` will be one of the following depending on a few items: - * `Testing`: If you are using the default settings of [Electron's Build-Tools](https://github.com/electron/build-tools) or the default instructions when [building from source](../development/build-instructions-gn.md#building). + * `Testing`: If you are using the default settings of + [Electron's Build-Tools](https://github.com/electron/build-tools) or the default + instructions when [building from source](../development/build-instructions-gn.md#building). * `Release`: If you built a Release build rather than a Testing build. - * `your-directory-name`: If you modified this during your build process from the default, this will be whatever you specified. -* The `args` array string `"your-electron-project-path"` should be the absolute path to either the directory or `main.js` file of the Electron project you are using for testing. In this example, it should be your path to `electron-quick-start`. + * `your-directory-name`: If you modified this during your build process from + the default, this will be whatever you specified. +* The `args` array string `"your-electron-project-path"` should be the absolute +path to either the directory or `main.js` file of the Electron project you are +using for testing. In this example, it should be your path to `my-app`. #### 3. Debugging -Set some breakpoints in the .cc files of your choosing in the native Electron C++ code, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). +Set some breakpoints in the .cc files of your choosing in the native Electron C++ +code, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). diff --git a/docs/tutorial/electron-timelines.md b/docs/tutorial/electron-timelines.md index 7d596fb3baf61..893b7891a0fd4 100644 --- a/docs/tutorial/electron-timelines.md +++ b/docs/tutorial/electron-timelines.md @@ -9,10 +9,11 @@ check out our [Electron Versioning](./electron-versioning.md) doc. | Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported | | ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- | -| 36.0.0 | 2025-Mar-06 | 2025-Apr-02 | 2025-Apr-29 | 2025-Oct-28 | M136 | TBD | ✅ | +| 37.0.0 | 2025-May-01 | 2025-May-28 | 2025-Jun-24 | 2026-Jan-13 | M138 | TBD | ✅ | +| 36.0.0 | 2025-Mar-06 | 2025-Apr-02 | 2025-Apr-29 | 2025-Oct-28 | M136 | v22.14 | ✅ | | 35.0.0 | 2025-Jan-16 | 2025-Feb-05 | 2025-Mar-04 | 2025-Sep-02 | M134 | v22.14 | ✅ | | 34.0.0 | 2024-Oct-17 | 2024-Nov-13 | 2025-Jan-14 | 2025-Jun-24 | M132 | v20.18 | ✅ | -| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | ✅ | +| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | 🚫 | | 32.0.0 | 2024-Jun-14 | 2024-Jul-24 | 2024-Aug-20 | 2025-Mar-04 | M128 | v20.16 | 🚫 | | 31.0.0 | 2024-Apr-18 | 2024-May-15 | 2024-Jun-11 | 2025-Jan-14 | M126 | v20.14 | 🚫 | | 30.0.0 | 2024-Feb-22 | 2024-Mar-20 | 2024-Apr-16 | 2024-Oct-15 | M124 | v20.11 | 🚫 | diff --git a/docs/tutorial/fuses.md b/docs/tutorial/fuses.md index 1afa53d6a7df3..c810d1af2a660 100644 --- a/docs/tutorial/fuses.md +++ b/docs/tutorial/fuses.md @@ -48,7 +48,7 @@ The nodeCliInspect fuse toggles whether the `--inspect`, `--inspect-brk`, etc. f **@electron/fuses:** `FuseV1Options.EnableEmbeddedAsarIntegrityValidation` -The embeddedAsarIntegrityValidation fuse toggles an experimental feature on macOS that validates the content of the `app.asar` file when it is loaded. This feature is designed to have a minimal performance impact but may marginally slow down file reads from inside the `app.asar` archive. +The embeddedAsarIntegrityValidation fuse toggles an experimental feature on macOS and Windows that validates the content of the `app.asar` file when it is loaded. This feature is designed to have a minimal performance impact but may marginally slow down file reads from inside the `app.asar` archive. For more information on how to use asar integrity validation please read the [Asar Integrity](asar-integrity.md) documentation. diff --git a/docs/tutorial/keyboard-shortcuts.md b/docs/tutorial/keyboard-shortcuts.md index 8caa93f65c040..687454c7ae75c 100644 --- a/docs/tutorial/keyboard-shortcuts.md +++ b/docs/tutorial/keyboard-shortcuts.md @@ -133,9 +133,10 @@ function handleKeyPress (event) { window.addEventListener('keyup', handleKeyPress, true) ``` -> Note: the third parameter `true` indicates that the listener will always receive -key presses before other listeners so they can't have `stopPropagation()` -called on them. +> [!NOTE] +> The third parameter `true` indicates that the listener will always receive +> key presses before other listeners so they can't have `stopPropagation()` +> called on them. #### Intercepting events in the main process diff --git a/docs/tutorial/macos-dock.md b/docs/tutorial/macos-dock.md index 6df03e3fd1cf1..97b5e5db5b014 100644 --- a/docs/tutorial/macos-dock.md +++ b/docs/tutorial/macos-dock.md @@ -50,9 +50,7 @@ const dockMenu = Menu.buildFromTemplate([ ]) app.whenReady().then(() => { - if (process.platform === 'darwin') { - app.dock.setMenu(dockMenu) - } + app.dock?.setMenu(dockMenu) }).then(createWindow) app.on('window-all-closed', () => { diff --git a/docs/tutorial/multithreading.md b/docs/tutorial/multithreading.md index ab70b88a502c9..fd0a52205f48b 100644 --- a/docs/tutorial/multithreading.md +++ b/docs/tutorial/multithreading.md @@ -20,7 +20,8 @@ const win = new BrowserWindow({ The `nodeIntegrationInWorker` can be used independent of `nodeIntegration`, but `sandbox` must not be set to `true`. -**Note:** This option is not available in [`SharedWorker`s](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) or [`Service Worker`s](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker) owing to incompatibilities in sandboxing policies. +> [!NOTE] +> This option is not available in [`SharedWorker`s](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) or [`Service Worker`s](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker) owing to incompatibilities in sandboxing policies. ## Available APIs diff --git a/docs/tutorial/native-code-and-electron-cpp-linux.md b/docs/tutorial/native-code-and-electron-cpp-linux.md new file mode 100644 index 0000000000000..b40fa8f6b4afc --- /dev/null +++ b/docs/tutorial/native-code-and-electron-cpp-linux.md @@ -0,0 +1,1638 @@ +# Native Code and Electron: C++ (Linux) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for Linux using C++ and GTK3. To illustrate how you can embed native Linux code in your Electron app, we'll be building a basic native GTK3 GUI that communicates with Electron's JavaScript. + +Specifically, we'll be using GTK3 for our GUI interface, which provides: + +* A comprehensive set of UI widgets like buttons, entry fields, and lists +* Cross-desktop compatibility across various Linux distributions +* Integration with the native theming and accessibility features of Linux desktops + +> [!NOTE] +> We specifically use GTK3 because that's what Chromium (and by extension, Electron) uses internally. Using GTK4 would cause runtime conflicts since both GTK3 and GTK4 would be loaded in the same process. If and when Chromium upgrades to GTK4, you will likely be able to easily upgrade your native code to GTK4, too. + +This tutorial will be most useful to those who already have some familiarity with GTK development on Linux. You should have experience with basic GTK concepts like widgets, signals, and the main event loop. In the interest of brevity, we're not spending too much time explaining the individual GTK elements we're using or the code we're writing for them. This allows this tutorial to be really helpful for those who already know GTK development and want to use their skills with Electron - without having to also be an entire GTK documentation. + +> [!NOTE] +> If you're not already familiar with these concepts, the [GTK3 documentation](https://docs.gtk.org/gtk3/) and [GTK3 tutorials](https://docs.gtk.org/gtk3/getting_started.html) are excellent resources to get started. The [GNOME Developer Documentation](https://developer.gnome.org/) also provides comprehensive guides for GTK development. + +## Requirements + +Just like our general introduction to Native Code and Electron, this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code. Since this tutorial discusses writing native code that interacts with GTK3, you'll need: + +* A Linux distribution with GTK3 development files installed +* The [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) tool +* G++ compiler and build tools + +On Ubuntu/Debian, you can install these with: + +```sh +sudo apt-get install build-essential pkg-config libgtk-3-dev +``` + +On Fedora/RHEL/CentOS: + +```sh +sudo dnf install gcc-c++ pkgconfig gtk3-devel +``` + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +cpp-linux/ +├── binding.gyp # Configuration file for node-gyp to build the native addon +├── include/ +│ └── cpp_code.h # Header file with declarations for our C++ native code +├── js/ +│ └── index.js # JavaScript interface that loads and exposes our native addon +├── package.json # Node.js package configuration and dependencies +└── src/ + ├── cpp_addon.cc # C++ code that bridges Node.js/Electron with our native code + └── cpp_code.cc # Implementation of our native C++ functionality using GTK3 +``` + +Our package.json should look like this: + +```json title='package.json' +{ + "name": "cpp-linux", + "version": "1.0.0", + "description": "A demo module that exposes C++ code to Electron", + "main": "js/index.js", + "scripts": { + "clean": "rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "bindings": "^1.5.0" + } +} +``` + +## 2) Setting up the build configuration + +For a Linux-specific addon using GTK3, we need to configure our `binding.gyp` file correctly to ensure our addon is only compiled on Linux systems - doing ideally nothing on other platforms. This involves using conditional compilation flags, leveraging `pkg-config` to automatically locate and include the GTK3 libraries and header paths on the user's system, and setting appropriate compiler flags to enable features like exception handling and threading support. The configuration will ensure that our native code can properly interface with both the Node.js/Electron runtime and the GTK3 libraries that provide the native GUI capabilities. + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "cpp_addon", + "conditions": [ + ['OS=="linux"', { + "sources": [ + "src/cpp_addon.cc", + "src/cpp_code.cc" + ], + "include_dirs": [ + " +#include + +namespace cpp_code { + +std::string hello_world(const std::string& input); +void hello_gui(); + +// Callback function types +using TodoCallback = std::function; + +// Callback setters +void setTodoAddedCallback(TodoCallback callback); +void setTodoUpdatedCallback(TodoCallback callback); +void setTodoDeletedCallback(TodoCallback callback); + +} // namespace cpp_code +``` + +This header defines: + +* A basic `hello_world` function +* A `hello_gui` function to create a GTK3 GUI +* Callback types for Todo operations (add, update, delete) +* Setter functions for the callback + +## 4) Implementing GTK3 GUI Code + +Now, let's implement our GTK3 GUI in `src/cpp_code.cc`. We'll break this into manageable sections. We'll start with a number of includes as well as the basic setup. + +### Basic Setup and Data Structures + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using TodoCallback = std::function; + +namespace cpp_code +{ + // Basic functions + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + // Data structures + struct TodoItem + { + uuid_t id; + std::string text; + int64_t date; + + std::string toJson() const + { + char uuid_str[37]; + uuid_unparse(id, uuid_str); + return "{" + "\"id\":\"" + + std::string(uuid_str) + "\"," + "\"text\":\"" + + text + "\"," + "\"date\":" + + std::to_string(date) + + "}"; + } + + static std::string formatDate(int64_t timestamp) + { + char date_str[64]; + time_t unix_time = timestamp / 1000; + strftime(date_str, sizeof(date_str), "%Y-%m-%d", localtime(&unix_time)); + return date_str; + } + }; +``` + +In this section: + +* We include necessary headers for GTK3, standard library components, and UUID generation. +* Define a `TodoCallback` type to handle communication back to JavaScript. +* Create a `TodoItem` struct to store our todo data with: + * A UUID for unique identification + * Text content and a timestamp + * A method to convert to JSON for sending to JavaScript + * A static helper to format dates for display + +The `toJson()` method is particularly important as it's what allows our C++ objects to be serialized for transmission to JavaScript. There are probably better ways to do that, but this tutorial is about combining C++ for native Linux UI development with Electron, so we'll give ourselves a pass for not writing better JSON serialization code here. There are many libraries to work with JSON in C++ with different trade-offs. See https://www.json.org/json-en.html for a list. + +Notably, we haven't actually added any user interface yet - which we'll do in the next step. GTK code tends to be verbose, so bear with us - despite the length. + +### Global state and forward declarations + +Below the code already in your `src/cpp_code.cc`, add the following: + +```cpp title='src/cpp_code.cc' + // Forward declarations + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo); + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo); + + // Global state + namespace + { + TodoCallback g_todoAddedCallback; + TodoCallback g_todoUpdatedCallback; + TodoCallback g_todoDeletedCallback; + GMainContext *g_gtk_main_context = nullptr; + GMainLoop *g_main_loop = nullptr; + std::thread *g_gtk_thread = nullptr; + std::vector g_todos; + } +``` + +Here we: + +* Forward-declare helper functions we'll use later +* Set up global state in an anonymous namespace, including: + * Callbacks for the `add`, `update`, and `delete` todo operations + * GTK main context and loop pointers for thread management + * A pointer to the GTK thread itself + * A vector to store our todos + +These global variables keep track of application state and allow different parts of our code to interact with each other. The thread management variables (`g_gtk_main_context`, `g_main_loop`, and `g_gtk_thread`) are particularly important because GTK requires running in its own event loop. Since our code will be called from Node.js/Electron's main thread, we need to run GTK in a separate thread to avoid blocking the JavaScript event loop. This separation ensures that our native UI remains responsive while still allowing bidirectional communication with the Electron application. The callbacks enable us to send events back to JavaScript when the user interacts with our native GTK interface. + +### Helper Functions + +Moving on, we're adding more code below the code we've already written. In this section, we're adding three static helper methods - and also start setting up some actual native user interface. We'll add a helper function that'll notify a callback in a thread-safe way, a function to update a row label, and a function to create the whole "Add Todo" dialog. + +```cpp title='src/cpp_code.cc' + // Helper functions + static void notify_callback(const TodoCallback &callback, const std::string &json) + { + if (callback && g_gtk_main_context) + { + g_main_context_invoke(g_gtk_main_context, [](gpointer data) -> gboolean + { + auto* cb_data = static_cast*>(data); + cb_data->first(cb_data->second); + delete cb_data; + return G_SOURCE_REMOVE; }, new std::pair(callback, json)); + } + } + + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo) + { + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + auto *old_label = GTK_WIDGET(gtk_container_get_children(GTK_CONTAINER(row))->data); + gtk_container_remove(GTK_CONTAINER(row), old_label); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_widget_show_all(GTK_WIDGET(row)); + } + + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo = nullptr) + { + auto *dialog = gtk_dialog_new_with_buttons( + existing_todo ? "Edit Todo" : "Add Todo", + parent, + GTK_DIALOG_MODAL, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + nullptr); + + auto *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_set_border_width(GTK_CONTAINER(content_area), 10); + + auto *entry = gtk_entry_new(); + if (existing_todo) + { + gtk_entry_set_text(GTK_ENTRY(entry), existing_todo->text.c_str()); + } + gtk_container_add(GTK_CONTAINER(content_area), entry); + + auto *calendar = gtk_calendar_new(); + if (existing_todo) + { + time_t unix_time = existing_todo->date / 1000; + struct tm *timeinfo = localtime(&unix_time); + gtk_calendar_select_month(GTK_CALENDAR(calendar), timeinfo->tm_mon, timeinfo->tm_year + 1900); + gtk_calendar_select_day(GTK_CALENDAR(calendar), timeinfo->tm_mday); + } + gtk_container_add(GTK_CONTAINER(content_area), calendar); + + gtk_widget_show_all(dialog); + return dialog; + } +``` + +These helper functions are crucial for our application: + +* `notify_callback`: Safely invokes JavaScript callbacks from the GTK thread using `g_main_context_invoke`, which schedules function execution in the GTK main context. As a reminder, the GTK main context is the environment where GTK operations must be performed to ensure thread safety, as GTK is not thread-safe and all UI operations must happen on the main thread. +* `update_todo_row_label`: Updates a row in the todo list with new text and formatted date. +* `create_todo_dialog`: Creates a dialog for adding or editing todos with: + * A text entry field for the todo text + * A calendar widget for selecting the date + * Appropriate buttons for saving or canceling + +### Event handlers + +Our native user interface has events - and those events must be handled. The only Electron-specific thing in this code is that we're notifying our JS callbacks. + +```cpp title='src/cpp_code.cc' + static void edit_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + auto *dialog = create_todo_dialog( + GTK_WINDOW(gtk_builder_get_object(builder, "window")), + &g_todos[index]); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + auto *entry = GTK_ENTRY(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->data); + auto *calendar = GTK_CALENDAR(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->next->data); + + const char *new_text = gtk_entry_get_text(entry); + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + gint64 new_date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos[index].text = new_text; + g_todos[index].date = new_date; + + update_todo_row_label(row, g_todos[index]); + notify_callback(g_todoUpdatedCallback, g_todos[index].toJson()); + } + + gtk_widget_destroy(dialog); + } + + static void delete_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + std::string json = g_todos[index].toJson(); + gtk_container_remove(GTK_CONTAINER(list), GTK_WIDGET(row)); + g_todos.erase(g_todos.begin() + index); + notify_callback(g_todoDeletedCallback, json); + } + + static void on_add_clicked(GtkButton *button, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *entry = GTK_ENTRY(gtk_builder_get_object(builder, "todo_entry")); + auto *calendar = GTK_CALENDAR(gtk_builder_get_object(builder, "todo_calendar")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + const char *text = gtk_entry_get_text(entry); + if (strlen(text) > 0) + { + TodoItem todo; + uuid_generate(todo.id); + todo.text = text; + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + todo.date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos.push_back(todo); + + auto *row = gtk_list_box_row_new(); + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_container_add(GTK_CONTAINER(list), row); + gtk_widget_show_all(row); + + gtk_entry_set_text(entry, ""); + + notify_callback(g_todoAddedCallback, todo.toJson()); + } + } + + static void on_row_activated(GtkListBox *list_box, GtkListBoxRow *row, gpointer user_data) + { + GMenu *menu = g_menu_new(); + g_menu_append(menu, "Edit", "app.edit"); + g_menu_append(menu, "Delete", "app.delete"); + + auto *popover = gtk_popover_new_from_model(GTK_WIDGET(row), G_MENU_MODEL(menu)); + gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_RIGHT); + gtk_popover_popup(GTK_POPOVER(popover)); + + g_object_unref(menu); + } +``` + +These event handlers manage user interactions: + +`edit_action`: Handles editing a todo by: + +* Getting the selected row +* Creating a dialog with the current todo data +* Updating the todo if the user confirms +* Notifying JavaScript via callback + +`delete_action`: Removes a todo and notifies JavaScript. + +`on_add_clicked`: Adds a new todo when the user clicks the Add button: + +* Gets text and date from input fields +* Creates a new TodoItem with a unique ID +* Adds it to the list and the underlying data store +* Notifies JavaScript + +`on_row_activated`: Shows a popup menu when a todo is clicked, with options to edit or delete. + +### GTK application setup + +Now, we'll need to setup our GTK application. This might be counter-intuitive, given that we already have a GTK application running. The activation code here is necessary because this is native C++ code running alongside Electron, not within it. While Electron does have its own main process and renderer processes, this GTK application operates as a native OS window that's launched from the Electron application but runs in its own process or thread. The `hello_gui()` function specifically starts the GTK application with its own thread (`g_gtk_thread`), application loop, and UI context. + +```cpp title='src/cpp_code.cc' + static gboolean init_gtk_app(gpointer user_data) + { + auto *app = static_cast(user_data); + g_application_run(G_APPLICATION(app), 0, nullptr); + g_object_unref(app); + if (g_main_loop) + { + g_main_loop_quit(g_main_loop); + } + return G_SOURCE_REMOVE; + } + + static void activate_handler(GtkApplication *app, gpointer user_data) + { + auto *builder = gtk_builder_new(); + + const GActionEntry app_actions[] = { + {"edit", edit_action, nullptr, nullptr, nullptr, {0, 0, 0}}, + {"delete", delete_action, nullptr, nullptr, nullptr, {0, 0, 0}}}; + g_action_map_add_action_entries(G_ACTION_MAP(app), app_actions, + G_N_ELEMENTS(app_actions), builder); + + gtk_builder_add_from_string(builder, + "" + "" + " " + " Todo List" + " 400" + " 500" + " " + " " + " true" + " vertical" + " 6" + " 12" + " " + " " + " true" + " 6" + " " + " " + " true" + " true" + " Enter todo item..." + " " + " " + " " + " " + " true" + " " + " " + " " + " " + " true" + " Add" + " " + " " + " " + " " + " " + " " + " true" + " true" + " " + " " + " true" + " single" + " " + " " + " " + " " + " " + " " + " " + "", + -1, nullptr); + + auto *window = GTK_WINDOW(gtk_builder_get_object(builder, "window")); + auto *button = GTK_BUTTON(gtk_builder_get_object(builder, "add_button")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + gtk_window_set_application(window, app); + + g_signal_connect(button, "clicked", G_CALLBACK(on_add_clicked), builder); + g_signal_connect(list, "row-activated", G_CALLBACK(on_row_activated), nullptr); + + gtk_widget_show_all(GTK_WIDGET(window)); + } +``` + +Let's take a closer look at the code above: + +* `init_gtk_app`: Runs the GTK application main loop. +* `activate_handler`: Sets up the application UI when activated: + * Creates a GtkBuilder for loading the UI + * Registers edit and delete actions + * Defines the UI layout using GTK's XML markup language + * Connects signals to our event handlers + +The UI layout is defined inline using XML, which is a common pattern in GTK applications. It creates a main window, input controls (text entry, calendar, and add button), a list box for displaying todos, and proper layout containers and scrolling. + +### Main GUI function and thread management + +Now that we have everything wired, up, we can add our two core GUI functions: `hello_gui()` (which we'll call from JavaScript) and `cleanup_gui()` to get rid of everything. You'll be hopefully delighted to hear that our careful setup of GTK app, context, and threads makes this straightforward: + +```cpp title='src/cpp_code.cc' + void hello_gui() + { + if (g_gtk_thread != nullptr) + { + g_print("GTK application is already running.\n"); + return; + } + + if (!gtk_init_check(0, nullptr)) + { + g_print("Failed to initialize GTK.\n"); + return; + } + + g_gtk_main_context = g_main_context_new(); + g_main_loop = g_main_loop_new(g_gtk_main_context, FALSE); + + g_gtk_thread = new std::thread([]() + { + GtkApplication* app = gtk_application_new("com.example.todo", G_APPLICATION_NON_UNIQUE); + g_signal_connect(app, "activate", G_CALLBACK(activate_handler), nullptr); + + g_idle_add_full(G_PRIORITY_DEFAULT, init_gtk_app, app, nullptr); + + if (g_main_loop) { + g_main_loop_run(g_main_loop); + } }); + + g_gtk_thread->detach(); + } + + void cleanup_gui() + { + if (g_main_loop && g_main_loop_is_running(g_main_loop)) + { + g_main_loop_quit(g_main_loop); + } + + if (g_main_loop) + { + g_main_loop_unref(g_main_loop); + g_main_loop = nullptr; + } + + if (g_gtk_main_context) + { + g_main_context_unref(g_gtk_main_context); + g_gtk_main_context = nullptr; + } + + g_gtk_thread = nullptr; + } +``` + +These functions manage the GTK application lifecycle: + +* `hello_gui`: The entry point exposed to JavaScript that checks if GTK is already running, initializes GTK, creates a new main context and loop, launches a thread to run the GTK application, and detaches the thread so it runs independently. +* `cleanup_gui`: Properly cleans up GTK resources when the application closes. + +Running GTK in a separate thread is crucial for Electron integration, as it prevents the GTK main loop from blocking Node.js's event loop. + +### Callback management + +Previously, we setup global variables to hold our callbacks. Now, we'll add functions that assign those callbacks. These callbacks form the bridge between our native GTK code and JavaScript, allowing bidirectional communication. + +```cpp title='src/cpp_code.cc' + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + void setTodoUpdatedCallback(TodoCallback callback) + { + g_todoUpdatedCallback = callback; + } + + void setTodoDeletedCallback(TodoCallback callback) + { + g_todoDeletedCallback = callback; + } +``` + +### Putting `cpp_code.cc` together + +We've now finished the GTK and native part of our addon - that is, the code that's most concerned with interacting with the operating system (and by contrast, less so with bridging the native C++ and JavaScript worlds). After adding all the sections above, your `src/cpp_code.cc` should look like this: + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using TodoCallback = std::function; + +namespace cpp_code +{ + + // Basic functions + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + // Data structures + struct TodoItem + { + uuid_t id; + std::string text; + int64_t date; + + std::string toJson() const + { + char uuid_str[37]; + uuid_unparse(id, uuid_str); + return "{" + "\"id\":\"" + + std::string(uuid_str) + "\"," + "\"text\":\"" + + text + "\"," + "\"date\":" + + std::to_string(date) + + "}"; + } + + static std::string formatDate(int64_t timestamp) + { + char date_str[64]; + time_t unix_time = timestamp / 1000; + strftime(date_str, sizeof(date_str), "%Y-%m-%d", localtime(&unix_time)); + return date_str; + } + }; + + // Forward declarations + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo); + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo); + + // Global state + namespace + { + TodoCallback g_todoAddedCallback; + TodoCallback g_todoUpdatedCallback; + TodoCallback g_todoDeletedCallback; + GMainContext *g_gtk_main_context = nullptr; + GMainLoop *g_main_loop = nullptr; + std::thread *g_gtk_thread = nullptr; + std::vector g_todos; + } + + // Helper functions + static void notify_callback(const TodoCallback &callback, const std::string &json) + { + if (callback && g_gtk_main_context) + { + g_main_context_invoke(g_gtk_main_context, [](gpointer data) -> gboolean + { + auto* cb_data = static_cast*>(data); + cb_data->first(cb_data->second); + delete cb_data; + return G_SOURCE_REMOVE; }, new std::pair(callback, json)); + } + } + + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo) + { + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + auto *old_label = GTK_WIDGET(gtk_container_get_children(GTK_CONTAINER(row))->data); + gtk_container_remove(GTK_CONTAINER(row), old_label); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_widget_show_all(GTK_WIDGET(row)); + } + + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo = nullptr) + { + auto *dialog = gtk_dialog_new_with_buttons( + existing_todo ? "Edit Todo" : "Add Todo", + parent, + GTK_DIALOG_MODAL, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + nullptr); + + auto *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_set_border_width(GTK_CONTAINER(content_area), 10); + + auto *entry = gtk_entry_new(); + if (existing_todo) + { + gtk_entry_set_text(GTK_ENTRY(entry), existing_todo->text.c_str()); + } + gtk_container_add(GTK_CONTAINER(content_area), entry); + + auto *calendar = gtk_calendar_new(); + if (existing_todo) + { + time_t unix_time = existing_todo->date / 1000; + struct tm *timeinfo = localtime(&unix_time); + gtk_calendar_select_month(GTK_CALENDAR(calendar), timeinfo->tm_mon, timeinfo->tm_year + 1900); + gtk_calendar_select_day(GTK_CALENDAR(calendar), timeinfo->tm_mday); + } + gtk_container_add(GTK_CONTAINER(content_area), calendar); + + gtk_widget_show_all(dialog); + return dialog; + } + + static void edit_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + auto *dialog = create_todo_dialog( + GTK_WINDOW(gtk_builder_get_object(builder, "window")), + &g_todos[index]); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + auto *entry = GTK_ENTRY(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->data); + auto *calendar = GTK_CALENDAR(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->next->data); + + const char *new_text = gtk_entry_get_text(entry); + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + gint64 new_date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos[index].text = new_text; + g_todos[index].date = new_date; + + update_todo_row_label(row, g_todos[index]); + notify_callback(g_todoUpdatedCallback, g_todos[index].toJson()); + } + + gtk_widget_destroy(dialog); + } + + static void delete_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + std::string json = g_todos[index].toJson(); + gtk_container_remove(GTK_CONTAINER(list), GTK_WIDGET(row)); + g_todos.erase(g_todos.begin() + index); + notify_callback(g_todoDeletedCallback, json); + } + + static void on_add_clicked(GtkButton *button, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *entry = GTK_ENTRY(gtk_builder_get_object(builder, "todo_entry")); + auto *calendar = GTK_CALENDAR(gtk_builder_get_object(builder, "todo_calendar")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + const char *text = gtk_entry_get_text(entry); + if (strlen(text) > 0) + { + TodoItem todo; + uuid_generate(todo.id); + todo.text = text; + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + todo.date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos.push_back(todo); + + auto *row = gtk_list_box_row_new(); + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_container_add(GTK_CONTAINER(list), row); + gtk_widget_show_all(row); + + gtk_entry_set_text(entry, ""); + + notify_callback(g_todoAddedCallback, todo.toJson()); + } + } + + static void on_row_activated(GtkListBox *list_box, GtkListBoxRow *row, gpointer user_data) + { + GMenu *menu = g_menu_new(); + g_menu_append(menu, "Edit", "app.edit"); + g_menu_append(menu, "Delete", "app.delete"); + + auto *popover = gtk_popover_new_from_model(GTK_WIDGET(row), G_MENU_MODEL(menu)); + gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_RIGHT); + gtk_popover_popup(GTK_POPOVER(popover)); + + g_object_unref(menu); + } + + static gboolean init_gtk_app(gpointer user_data) + { + auto *app = static_cast(user_data); + g_application_run(G_APPLICATION(app), 0, nullptr); + g_object_unref(app); + if (g_main_loop) + { + g_main_loop_quit(g_main_loop); + } + return G_SOURCE_REMOVE; + } + + static void activate_handler(GtkApplication *app, gpointer user_data) + { + auto *builder = gtk_builder_new(); + + const GActionEntry app_actions[] = { + {"edit", edit_action, nullptr, nullptr, nullptr, {0, 0, 0}}, + {"delete", delete_action, nullptr, nullptr, nullptr, {0, 0, 0}}}; + g_action_map_add_action_entries(G_ACTION_MAP(app), app_actions, + G_N_ELEMENTS(app_actions), builder); + + gtk_builder_add_from_string(builder, + "" + "" + " " + " Todo List" + " 400" + " 500" + " " + " " + " true" + " vertical" + " 6" + " 12" + " " + " " + " true" + " 6" + " " + " " + " true" + " true" + " Enter todo item..." + " " + " " + " " + " " + " true" + " " + " " + " " + " " + " true" + " Add" + " " + " " + " " + " " + " " + " " + " true" + " true" + " " + " " + " true" + " single" + " " + " " + " " + " " + " " + " " + " " + "", + -1, nullptr); + + auto *window = GTK_WINDOW(gtk_builder_get_object(builder, "window")); + auto *button = GTK_BUTTON(gtk_builder_get_object(builder, "add_button")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + gtk_window_set_application(window, app); + + g_signal_connect(button, "clicked", G_CALLBACK(on_add_clicked), builder); + g_signal_connect(list, "row-activated", G_CALLBACK(on_row_activated), nullptr); + + gtk_widget_show_all(GTK_WIDGET(window)); + } + + void hello_gui() + { + if (g_gtk_thread != nullptr) + { + g_print("GTK application is already running.\n"); + return; + } + + if (!gtk_init_check(0, nullptr)) + { + g_print("Failed to initialize GTK.\n"); + return; + } + + g_gtk_main_context = g_main_context_new(); + g_main_loop = g_main_loop_new(g_gtk_main_context, FALSE); + + g_gtk_thread = new std::thread([]() + { + GtkApplication* app = gtk_application_new("com.example.todo", G_APPLICATION_NON_UNIQUE); + g_signal_connect(app, "activate", G_CALLBACK(activate_handler), nullptr); + + g_idle_add_full(G_PRIORITY_DEFAULT, init_gtk_app, app, nullptr); + + if (g_main_loop) { + g_main_loop_run(g_main_loop); + } }); + + g_gtk_thread->detach(); + } + + void cleanup_gui() + { + if (g_main_loop && g_main_loop_is_running(g_main_loop)) + { + g_main_loop_quit(g_main_loop); + } + + if (g_main_loop) + { + g_main_loop_unref(g_main_loop); + g_main_loop = nullptr; + } + + if (g_gtk_main_context) + { + g_main_context_unref(g_gtk_main_context); + g_gtk_main_context = nullptr; + } + + g_gtk_thread = nullptr; + } + + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + void setTodoUpdatedCallback(TodoCallback callback) + { + g_todoUpdatedCallback = callback; + } + + void setTodoDeletedCallback(TodoCallback callback) + { + g_todoDeletedCallback = callback; + } + +} // namespace cpp_code +``` + +## 5) Creating the Node.js addon bridge + +Now let's implement the bridge between our C++ code and Node.js in `src/cpp_addon.cc`. Let's start by creating a basic skeleton for our addon: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +// Class to wrap our C++ code will go here + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + // We'll add code here later + return exports; +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +This is the minimal structure required for a Node.js addon using `node-addon-api`. The `Init` function is called when the addon is loaded, and the `NODE_API_MODULE` macro registers our initializer. This basic skeleton doesn't do anything yet, but it provides the entry point for Node.js to load our native code. + +### Create a class to wrap our C++ code + +Let's create a class that will wrap our C++ code and expose it to JavaScript. In our previous step, we've added a comment reading "Class to wrap our C++ code will go here" - replace it with the code below. + +```cpp title='src/cpp_addon.cc' +class CppAddon : public Napi::ObjectWrap +{ +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) + { + Napi::Function func = DefineClass(env, "CppLinuxAddon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + Napi::FunctionReference *constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppLinuxAddon", func); + return exports; + } + + CppAddon(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info), + env_(info.Env()), + emitter(Napi::Persistent(Napi::Object::New(info.Env()))), + callbacks(Napi::Persistent(Napi::Object::New(info.Env()))), + tsfn_(nullptr) + { + // We'll implement the constructor together with a callback struct later + } + + ~CppAddon() + { + if (tsfn_ != nullptr) + { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + // Method implementations will go here +}; +``` + +Here, we create a C++ class that inherits from `Napi::ObjectWrap`: + +`static Napi::Object Init` defines our JavaScript interface with three methods: + +* `helloWorld`: A simple function to test the bridge +* `helloGui`: The function to launch our GTK3 UI +* `on`: A method to register event callbacks + +The constructor initializes: + +* `emitter`: An object that will emit events to JavaScript +* `callbacks`: A map of registered JavaScript callback functions +* `tsfn_`: A thread-safe function handle (crucial for GTK3 thread communication) + +The destructor properly cleans up the thread-safe function when the object is garbage collected. + +### Implement basic functionality - HelloWorld + +Next, we'll add our two main methods, `HelloWorld()` and `HelloGui()`. We'll add these to our `private` scope, right where we have a comment reading "Method implementations will go here". + +```cpp title='src/cpp_addon.cc' +Napi::Value HelloWorld(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) + { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); +} + +void HelloGui(const Napi::CallbackInfo &info) +{ + cpp_code::hello_gui(); +} + +// On() method implementation will go here +``` + +`HelloWorld()`: + +* Validates the input argument (must be a string) +* Calls our C++ hello_world function +* Returns the result as a JavaScript string + +`HelloGui()`: + +* Simply calls our C++ hello_gui function without arguments +* Returns nothing (void) as the function just launches the UI +* These methods form the direct bridge between JavaScript calls and our native C++ functions. + +You might be wondering what `Napi::CallbackInfo` is or where it comes from. This is a class provided by the Node-API (N-API) C++ wrapper, specifically from the [`node-addon-api`](https://github.com/nodejs/node-addon-api) package. It encapsulates all the information about a JavaScript function call, including: + +* The arguments passed from JavaScript +* The JavaScript execution environment (via `info.Env()`) +* The `this` value of the function call +* The number of arguments (via `info.Length()`) + +This class is fundamental to the Node.js native addon development as it serves as the bridge between JavaScript function calls and C++ method implementations. Every native method that can be called from JavaScript receives a `CallbackInfo` object as its parameter, allowing the C++ code to access and validate the JavaScript arguments before processing them. You can see us using it in `HelloWorld()` to get function parameters and other information about the function call. Our `HelloGui()` function doesn't use it, but if it did, it'd follow the same pattern. + +### Setting up the event system + +Now we'll tackle the tricky part of native development: setting up the event system. Previously, we added native callbacks to our `cpp_code.cc` code - and in our bridge code in `cpp_addon.cc`, we'll need to find a way to have those callbacks ultimately trigger a JavaScript method. + +Let's start with the `On()` method, which we'll call from JavaScript. In our previously written code, you'll find a comment reading `On() method implementation will go here`. Replace it with the following method: + +```cpp title='src/cpp_addon.cc' +Napi::Value On(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) + { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); +} +``` + +This method allows JavaScript to register callbacks for different event types and stores the JavaScript function in our `callbacks` map for later use. So far, so good - but now we need to let `cpp_code.cc` know about these callbacks. We also need to figure out a way to coordinate our threads, because the actual `cpp_code.cc` will be doing most of its work on its own thread. + +In our code, find the section where we're declaring the constructor `CppAddon(const Napi::CallbackInfo &info)`, which you'll find in the `public` section. It should have a comment reading `We'll implement the constructor together with a callback struct later`. Then, replace that part with the following code: + +```cpp title='src/cpp_addon.cc' + struct CallbackData + { + std::string eventType; + std::string payload; + CppAddon *addon; + }; + + CppAddon(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info), + env_(info.Env()), + emitter(Napi::Persistent(Napi::Object::New(info.Env()))), + callbacks(Napi::Persistent(Napi::Object::New(info.Env()))), + tsfn_(nullptr) + { + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void *context, void *data) + { + auto *callbackData = static_cast(data); + if (!callbackData) + return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) + { + delete callbackData; + return; + } + + try + { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) + { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } + catch (...) + { + } + + delete callbackData; + }, + &tsfn_); + + if (status != napi_ok) + { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks here + auto makeCallback = [this](const std::string &eventType) + { + return [this, eventType](const std::string &payload) + { + if (tsfn_ != nullptr) + { + auto *data = new CallbackData{ + eventType, + payload, + this}; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); + cpp_code::setTodoUpdatedCallback(makeCallback("todoUpdated")); + cpp_code::setTodoDeletedCallback(makeCallback("todoDeleted")); + } +``` + +This is the most complex part of our bridge: implementing bidirectional communication. There are a few things worth noting going on here, so let's take them step by step: + +`CallbackData` struct: + +* Holds the event type, JSON payload, and a reference to our addon. + +In the constructor: + +* We create a thread-safe function (`napi_create_threadsafe_function`) which is crucial for calling into JavaScript from the GTK3 thread +* The thread-safe function callback unpacks the data and calls the appropriate JavaScript callback +* We create a lambda `makeCallback` that produces callback functions for different event types +* We register these callbacks with our C++ code using the setter functions + +Let's talk about `napi_create_threadsafe_function`. The orchestration of different threads is maybe the most difficult part about native addon development - and in our experience, the place where developers are most likely to give up. `napi_create_threadsafe_function` is provided by the N-API and allows you to safely call JavaScript functions from any thread. This is essential when working with GUI frameworks like GTK3 that run on their own thread. Here's why it's important: + +1. **Thread Safety**: JavaScript in Electron runs on a single thread (exceptions apply, but this is a generally useful rule). Without thread-safe functions, calling JavaScript from another thread would cause crashes or race conditions. +1. **Queue Management**: It automatically queues function calls and executes them on the JavaScript thread. +1. **Resource Management**: It handles proper reference counting to ensure objects aren't garbage collected while still needed. + +In our code, we're using it to bridge the gap between GTK3's event loop and Node.js's event loop, allowing events from our GUI to safely trigger JavaScript callbacks. + +For developers wanting to learn more, you can refer to the [official N-API documentation](https://nodejs.org/api/n-api.html#n_api_napi_create_threadsafe_function) for detailed information about thread-safe functions, the [node-addon-api wrapper documentation](https://github.com/nodejs/node-addon-api/blob/main/doc/threadsafe_function.md) for the C++ wrapper implementation, and the [Node.js Threading Model article](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/) to understand how Node.js handles concurrency and why thread-safe functions are necessary. + +### Putting `cpp_addon.cc` together + +We've now finished the bridge part our addon - that is, the code that's most concerned with being the bridge between your JavaScript and C++ code (and by contrast, less so actually interacting with the operating system or GTK). After adding all the sections above, your `src/cpp_addon.cc` should look like this: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +class CppAddon : public Napi::ObjectWrap +{ +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) + { + Napi::Function func = DefineClass(env, "CppLinuxAddon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + Napi::FunctionReference *constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppLinuxAddon", func); + return exports; + } + + struct CallbackData + { + std::string eventType; + std::string payload; + CppAddon *addon; + }; + + CppAddon(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info), + env_(info.Env()), + emitter(Napi::Persistent(Napi::Object::New(info.Env()))), + callbacks(Napi::Persistent(Napi::Object::New(info.Env()))), + tsfn_(nullptr) + { + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void *context, void *data) + { + auto *callbackData = static_cast(data); + if (!callbackData) + return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) + { + delete callbackData; + return; + } + + try + { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) + { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } + catch (...) + { + } + + delete callbackData; + }, + &tsfn_); + + if (status != napi_ok) + { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks here + auto makeCallback = [this](const std::string &eventType) + { + return [this, eventType](const std::string &payload) + { + if (tsfn_ != nullptr) + { + auto *data = new CallbackData{ + eventType, + payload, + this}; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); + cpp_code::setTodoUpdatedCallback(makeCallback("todoUpdated")); + cpp_code::setTodoDeletedCallback(makeCallback("todoDeleted")); + } + + ~CppAddon() + { + if (tsfn_ != nullptr) + { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo &info) + { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) + { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); + } + + void HelloGui(const Napi::CallbackInfo &info) + { + cpp_code::hello_gui(); + } + + Napi::Value On(const Napi::CallbackInfo &info) + { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) + { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) +{ + return CppAddon::Init(env, exports); +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +## 6) Creating a JavaScript wrapper + +Let's finish things off by adding a JavaScript wrapper in `js/index.js`. As we could all see, C++ requires a lot of boilerplate code that might be easier or faster to write in JavaScript - and you will find that many production applications end up transforming data or requests in JavaScript before invoking native code. We, for instance, turn our timestamp into a proper JavaScript date. + +```cpp title='js/index.js' +const EventEmitter = require('events'); + +class CppLinuxAddon extends EventEmitter { + constructor() { + super() + + if (process.platform !== 'linux') { + throw new Error('This module is only available on Linux'); + } + + const native = require('bindings')('cpp_addon') + this.addon = new native.CppLinuxAddon() + + // Set up event forwarding + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.parse(payload)) + }); + + this.addon.on('todoUpdated', (payload) => { + this.emit('todoUpdated', this.parse(payload)) + }) + + this.addon.on('todoDeleted', (payload) => { + this.emit('todoDeleted', this.parse(payload)) + }) + } + + helloWorld(input = "") { + return this.addon.helloWorld(input) + } + + helloGui() { + return this.addon.helloGui() + } + + // Parse JSON and convert date to JavaScript Date object + parse(payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'linux') { + module.exports = new CppLinuxAddon() +} else { + // Return empty object on non-Linux platforms + module.exports = {} +} +``` + +This wrapper: + +* Extends EventEmitter for native event handling +* Only loads on Linux platforms +* Forwards events from C++ to JavaScript +* Provides clean methods to call into C++ +* Converts JSON data into proper JavaScript objects + +## 7) Building and testing the addon + +With all files in place, you can build the addon: + +```sh +npm run build +``` + +If the build completes, you can now add the addon to your Electron app and `import` or `require` it there. + +## Usage Example + +Once you've built the addon, you can use it in your Electron application. Here's a complete example: + +```js @ts-expect-error=[2] +// In your Electron main process or renderer process +import cppLinux from 'cpp-linux' + +// Test the basic functionality +console.log(cppLinux.helloWorld('Hi!')) +// Output: "Hello from C++! You said: Hi!" + +// Set up event listeners for GTK GUI interactions +cppLinux.on('todoAdded', (todo) => { + console.log('New todo added:', todo) + // todo: { id: "uuid-string", text: "Todo text", date: Date object } +}) + +cppLinux.on('todoUpdated', (todo) => { + console.log('Todo updated:', todo) +}) + +cppLinux.on('todoDeleted', (todo) => { + console.log('Todo deleted:', todo) +}) + +// Launch the native GTK GUI +cppLinux.helloGui() +``` + +When you run this code: + +1. The `helloWorld()` call will return a greeting from C++ +2. The event listeners will be triggered when users interact with the GTK3 GUI +3. The `helloGui()` call will open a native GTK3 window with: + * A text entry field for todo items + * A calendar widget for selecting dates + * An "Add" button to create new todos + * A scrollable list showing all todos + * Right-click context menus for editing and deleting todos + +All interactions with the native GTK3 interface will trigger the corresponding JavaScript events, allowing your Electron application to respond to native GUI actions in real-time. + +## Conclusion + +You've now built a complete native Node.js addon for Linux using C++ and GTK3. This addon: + +1. Provides a bidirectional bridge between JavaScript and C++ +1. Creates a native GTK3 GUI that runs in its own thread +1. Implements a simple Todo application with add functionality +1. Uses GTK3, which is compatible with Electron's Chromium runtime +1. Handles callbacks from C++ to JavaScript safely + +This foundation can be extended to implement more complex Linux-specific features in your Electron applications. You can access system features, integrate with Linux-specific libraries, or create performant native UIs while maintaining the flexibility and ease of development that Electron provides. +For more information on GTK3 development, refer to the [GTK3 Documentation](https://docs.gtk.org/gtk3/) and the [GLib/GObject documentation](https://docs.gtk.org/gobject/). You may also find the [Node.js N-API documentation](https://nodejs.org/api/n-api.html) and [node-addon-api](https://github.com/nodejs/node-addon-api) helpful for extending your native addons. diff --git a/docs/tutorial/native-code-and-electron-cpp-win32.md b/docs/tutorial/native-code-and-electron-cpp-win32.md new file mode 100644 index 0000000000000..3d7fab89b932b --- /dev/null +++ b/docs/tutorial/native-code-and-electron-cpp-win32.md @@ -0,0 +1,1346 @@ +# Native Code and Electron: C++ (Windows) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for Windows using C++ and the [Win32 API](https://learn.microsoft.com/en-us/windows/win32/). To illustrate how you can embed native Win32 code in your Electron app, we'll be building a basic native Windows GUI (using the Windows Common Controls) that communicates with Electron's JavaScript. + +Specifically, we'll be integrating with two commonly used native Windows libraries: + +* `comctl32.lib`, which contains common controls and user interface components. It provides various UI elements like buttons, scrollbars, toolbars, status bars, progress bars, and tree views. As far as GUI development on Windows goes, this library is very low-level and basic - more modern frameworks like WinUI or WPF are advanced and alternatives but require a lot more C++ and Windows version considerations than are useful for this tutorial. This way, we can avoid the many perils of building native interfaces for multiple Windows versions! +* `shcore.lib`, a library that provides high-DPI awareness functionality and other Shell-related features around managing displays and UI elements. + +This tutorial will be most useful to those who already have some familiarity with native C++ GUI development on Windows. You should have experience with basic window classes and procedures, like `WNDCLASSEXW` and `WindowProc` functions. You should also be familiar with the Windows message loop, which is the heart of any native application - our code will be using `GetMessage`, `TranslateMessage`, and `DispatchMessage` to handle messages. Lastly, we'll be using (but not explaining) standard Win32 controls like `WC_EDITW` or `WC_BUTTONW`. + +> [!NOTE] +> If you're not familiar with C++ GUI development on Windows, we recommend Microsoft's excellent documentation and guides, particular for beginners. "[Get Started with Win32 and C++](https://learn.microsoft.com/en-us/windows/win32/learnwin32/learn-to-program-for-windows)" is a great introduction. + +## Requirements + +Just like our [general introduction to Native Code and Electron](./native-code-and-electron.md), this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code. Since this tutorial discusses writing native code that interacts with Windows, we recommend that you follow this tutorial on Windows with both Visual Studio and the "Desktop development with C++ workload" installed. For details, see the [Visual Studio Installation instructions](https://learn.microsoft.com/en-us/visualstudio/install/install-visual-studio). + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +my-native-win32-addon/ +├── binding.gyp +├── include/ +│ └── cpp_code.h +├── js/ +│ └── index.js +├── package.json +└── src/ + ├── cpp_addon.cc + └── cpp_code.cc +``` + +Our `package.json` should look like this: + +```json title='package.json' +{ + "name": "cpp-win32", + "version": "1.0.0", + "description": "A demo module that exposes C++ code to Electron", + "main": "js/index.js", + "author": "Your Name", + "scripts": { + "clean": "rm -rf build_swift && rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + } +} +``` + +## 2) Setting Up the Build Configuration + +For a Windows-specific addon, we need to modify our `binding.gyp` file to include Windows libraries and set appropriate compiler flags. In short, we need to do the following three things: + +1. We need to ensure our addon is only compiled on Windows, since we'll be writing platform-specific code. +2. We need to include the Windows-specific libraries. In our tutorial, we'll be targeting `comctl32.lib` and `shcore.lib`. +3. We need to configure the compiler and define C++ macros. + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "cpp_addon", + "conditions": [ + ['OS=="win"', { + "sources": [ + "src/cpp_addon.cc", + "src/cpp_code.cc" + ], + "include_dirs": [ + " +#include + +namespace cpp_code { + +std::string hello_world(const std::string& input); +void hello_gui(); + +// Callback function types +using TodoCallback = std::function; + +// Callback setters +void setTodoAddedCallback(TodoCallback callback); + +} // namespace cpp_code +``` + +This header: + +* Includes the basic `hello_world` function from the general tutorial +* Adds a `hello_gui` function to create a Win32 GUI +* Defines callback types for Todo operations (add). To keep this tutorial somewhat brief, we'll only be implementing one callback. +* Provides setter functions for these callbacks + +## 4) Implementing Win32 GUI Code + +Now, let's implement our Win32 GUI in `src/cpp_code.cc`. This is a larger file, so we'll review it in sections. First, let's include necessary headers and define basic structures. + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(linker, "\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +struct TodoItem +{ + GUID id; + std::wstring text; + int64_t date; + + std::string toJson() const + { + OLECHAR *guidString; + StringFromCLSID(id, &guidString); + std::wstring widGuid(guidString); + CoTaskMemFree(guidString); + + // Convert wide string to narrow for JSON + std::string guidStr(widGuid.begin(), widGuid.end()); + std::string textStr(text.begin(), text.end()); + + return "{" + "\"id\":\"" + guidStr + "\"," + "\"text\":\"" + textStr + "\"," + "\"date\":" + std::to_string(date) + + "}"; + } +}; + +namespace cpp_code +{ + // More code to follow later... +} +``` + +In this section: + +* We include necessary Win32 headers +* We set up pragma comments to link against required libraries +* We define callback variables for Todo operations +* We create a `TodoItem` struct with a method to convert to JSON + +Next, let's implement the basic functions and helper methods: + +```cpp title='src/cpp_code.cc' +namespace cpp_code +{ + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + // Window procedure function that handles window messages + // hwnd: Handle to the window + // uMsg: Message code + // wParam: Additional message-specific information + // lParam: Additional message-specific information + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + // Helper function to scale a value based on DPI + int Scale(int value, UINT dpi) + { + return MulDiv(value, dpi, 96); // 96 is the default DPI + } + + // Helper function to convert SYSTEMTIME to milliseconds since epoch + int64_t SystemTimeToMillis(const SYSTEMTIME &st) + { + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return (uli.QuadPart - 116444736000000000ULL) / 10000; + } + + // More code to follow later... +} +``` + +In this section, we've added a function that allows us to set the callback for an added todo item. We also added two helper functions that we need when working with JavaScript: One to scale our UI elements depending on the display's DPI - and another one to convert a Windows `SYSTEMTIME` to milliseconds since epoch, which is how JavaScript keeps track of time. + +Now, let's get to the part you probably came to this tutorial for - creating a GUI thread and drawing native pixels on screen. We'll do that by adding a `void hello_gui()` function to our `cpp_code` namespace. There are a few considerations we need to make: + +* We need to create a new thread for the GUI to avoid blocking the Node.js event loop. The Windows message loop that processes GUI events runs in an infinite loop, which would prevent Node.js from processing other events if run on the main thread. By running the GUI on a separate thread, we allow both the native Windows interface and Node.js to remain responsive. This separation also helps prevent potential deadlocks that could occur if GUI operations needed to wait for JavaScript callbacks. You don't need to do that for simpler Windows API interactions - but since you need to check the message loop, you do need to setup your own thread for GUI. +* Then, within our thread, we need to run a message loop to handle any Windows messages. +* We need to setup DPI awareness for proper display scaling. +* We need to register a window class, create a window, and add various UI controls. + +In the code below, we haven't added any actual controls yet. We're doing that on purpose to look at our added code in smaller portions here. + +```cpp title='src/cpp_code.cc' +void hello_gui() { + // Launch GUI in a separate thread + std::thread guiThread([]() { + // Enable Per-Monitor DPI awareness + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + // Initialize Common Controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; + InitCommonControlsEx(&icex); + + // Register window class + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = L"TodoApp"; + RegisterClassExW(&wc); + + // Get the DPI for the monitor + UINT dpi = GetDpiForSystem(); + + // Create window + HWND hwnd = CreateWindowExW( + 0, L"TodoApp", L"Todo List", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + Scale(500, dpi), Scale(500, dpi), + nullptr, nullptr, + GetModuleHandle(nullptr), nullptr + ); + + if (hwnd == nullptr) { + return; + } + + // Controls go here! The window is currently empty, + // we'll add controls in the next step. + + ShowWindow(hwnd, SW_SHOW); + + // Message loop + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Clean up + DeleteObject(hFont); + }); + + // Detach the thread so it runs independently + guiThread.detach(); +} +``` + +Now that we have a thread, a window, and a message loop, we can add some controls. Nothing we're doing here is unique to writing Windows C++ for Electron - you can simply copy & paste the code below into the `Controls go here!` section inside our `hello_gui()` function. + +We're specifically adding buttons, a date picker, and a list. + +```cpp title='src/cpp_code.cc' +void hello_gui() { + // ... + // All the code above "Controls go here!" + + // Create the modern font with DPI-aware size + HFONT hFont = CreateFontW( + -Scale(14, dpi), // Height (scaled) + 0, // Width + 0, // Escapement + 0, // Orientation + FW_NORMAL, // Weight + FALSE, // Italic + FALSE, // Underline + FALSE, // StrikeOut + DEFAULT_CHARSET, // CharSet + OUT_DEFAULT_PRECIS, // OutPrecision + CLIP_DEFAULT_PRECIS, // ClipPrecision + CLEARTYPE_QUALITY, // Quality + DEFAULT_PITCH | FF_DONTCARE, // Pitch and Family + L"Segoe UI" // Font face name + ); + + // Create input controls with scaled positions and sizes + HWND hEdit = CreateWindowExW(0, WC_EDITW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + Scale(10, dpi), Scale(10, dpi), + Scale(250, dpi), Scale(25, dpi), + hwnd, (HMENU)1, GetModuleHandle(nullptr), nullptr); + SendMessageW(hEdit, WM_SETFONT, (WPARAM)hFont, TRUE); + + // Create date picker + HWND hDatePicker = CreateWindowExW(0, DATETIMEPICK_CLASSW, L"", + WS_CHILD | WS_VISIBLE | DTS_SHORTDATECENTURYFORMAT, + Scale(270, dpi), Scale(10, dpi), + Scale(100, dpi), Scale(25, dpi), + hwnd, (HMENU)4, GetModuleHandle(nullptr), nullptr); + SendMessageW(hDatePicker, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hButton = CreateWindowExW(0, WC_BUTTONW, L"Add", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + Scale(380, dpi), Scale(10, dpi), + Scale(50, dpi), Scale(25, dpi), + hwnd, (HMENU)2, GetModuleHandle(nullptr), nullptr); + SendMessageW(hButton, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hListBox = CreateWindowExW(0, WC_LISTBOXW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY, + Scale(10, dpi), Scale(45, dpi), + Scale(460, dpi), Scale(400, dpi), + hwnd, (HMENU)3, GetModuleHandle(nullptr), nullptr); + SendMessageW(hListBox, WM_SETFONT, (WPARAM)hFont, TRUE); + + // Store menu handle in window's user data + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)hContextMenu); + + // All the code below "Controls go here!" + // ... +} +``` + +Now that we have a user interface that allows users to add todos, we need to store them - and add a helper function that'll potentially call our JavaScript callback. Right below the `void hello_gui() { ... }` function, we'll add the following: + +```cpp title='src/cpp_code.cc' + // Global vector to store todos + static std::vector g_todos; + + void NotifyCallback(const TodoCallback &callback, const std::string &json) + { + if (callback) + { + callback(json); + // Process pending messages + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +``` + +We'll also need a function that turns a todo into something we can display. We don't need anything fancy - given the name of the todo and a `SYSTEMTIME` timestamp, we'll return a simple string. Add it right below the function above: + +```cpp title='src/cpp_code.cc' + std::wstring FormatTodoDisplay(const std::wstring &text, const SYSTEMTIME &st) + { + wchar_t dateStr[64]; + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, nullptr, dateStr, 64); + return text + L" - " + dateStr; + } +``` + +When a user adds a todo, we want to reset the controls back to an empty state. To do so, add a helper function below the code we just added: + +```cpp title='src/cpp_code.cc' + void ResetControls(HWND hwnd) + { + HWND hEdit = GetDlgItem(hwnd, 1); + HWND hDatePicker = GetDlgItem(hwnd, 4); + HWND hAddButton = GetDlgItem(hwnd, 2); + + // Clear text + SetWindowTextW(hEdit, L""); + + // Reset date to current + SYSTEMTIME currentTime; + GetLocalTime(¤tTime); + DateTime_SetSystemtime(hDatePicker, GDT_VALID, ¤tTime); + } +``` + +Then, we'll need to implement the window procedure to handle Windows messages. Like a lot of our code here, there is very little specific to Electron in this code - so as a Win32 C++ developer, you'll recognize this function. The only thing that is unique is that we want to potentially notify the JavaScript callback about an added todo. We've previously implemented the `NotifyCallback()` function, which we will be using here. Add this code right below the function above: + +```cpp title='src/cpp_code.cc' + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_COMMAND: + { + HWND hListBox = GetDlgItem(hwnd, 3); + int cmd = LOWORD(wParam); + + switch (cmd) + { + case 2: // Add button + { + wchar_t buffer[256]; + GetDlgItemTextW(hwnd, 1, buffer, 256); + + if (wcslen(buffer) > 0) + { + SYSTEMTIME st; + HWND hDatePicker = GetDlgItem(hwnd, 4); + DateTime_GetSystemtime(hDatePicker, &st); + + TodoItem todo; + CoCreateGuid(&todo.id); + todo.text = buffer; + todo.date = SystemTimeToMillis(st); + + g_todos.push_back(todo); + + std::wstring displayText = FormatTodoDisplay(buffer, st); + SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + ResetControls(hwnd); + NotifyCallback(g_todoAddedCallback, todo.toJson()); + } + break; + } + } + break; + } + + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + } + + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } +``` + +We now have successfully implemented the Win32 C++ code. Most of this should look and feel to you like code you'd write with or without Electron. In the next step, we'll be building the bridge between C++ and JavaScript. Here's the complete implementation: + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(linker, "\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; +static TodoCallback g_todoUpdatedCallback; +static TodoCallback g_todoDeletedCallback; + +struct TodoItem +{ + GUID id; + std::wstring text; + int64_t date; + + std::string toJson() const + { + OLECHAR *guidString; + StringFromCLSID(id, &guidString); + std::wstring widGuid(guidString); + CoTaskMemFree(guidString); + + // Convert wide string to narrow for JSON + std::string guidStr(widGuid.begin(), widGuid.end()); + std::string textStr(text.begin(), text.end()); + + return "{" + "\"id\":\"" + guidStr + "\"," + "\"text\":\"" + textStr + "\"," + "\"date\":" + std::to_string(date) + + "}"; + } +}; + +namespace cpp_code +{ + + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + void setTodoUpdatedCallback(TodoCallback callback) + { + g_todoUpdatedCallback = callback; + } + + void setTodoDeletedCallback(TodoCallback callback) + { + g_todoDeletedCallback = callback; + } + + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + // Helper function to scale a value based on DPI + int Scale(int value, UINT dpi) + { + return MulDiv(value, dpi, 96); // 96 is the default DPI + } + + // Helper function to convert SYSTEMTIME to milliseconds since epoch + int64_t SystemTimeToMillis(const SYSTEMTIME &st) + { + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return (uli.QuadPart - 116444736000000000ULL) / 10000; + } + + void ResetControls(HWND hwnd) + { + HWND hEdit = GetDlgItem(hwnd, 1); + HWND hDatePicker = GetDlgItem(hwnd, 4); + HWND hAddButton = GetDlgItem(hwnd, 2); + + // Clear text + SetWindowTextW(hEdit, L""); + + // Reset date to current + SYSTEMTIME currentTime; + GetLocalTime(¤tTime); + DateTime_SetSystemtime(hDatePicker, GDT_VALID, ¤tTime); + } + + void hello_gui() { + // Launch GUI in a separate thread + std::thread guiThread([]() { + // Enable Per-Monitor DPI awareness + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + // Initialize Common Controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; + InitCommonControlsEx(&icex); + + // Register window class + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = L"TodoApp"; + RegisterClassExW(&wc); + + // Get the DPI for the monitor + UINT dpi = GetDpiForSystem(); + + // Create window + HWND hwnd = CreateWindowExW( + 0, L"TodoApp", L"Todo List", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + Scale(500, dpi), Scale(500, dpi), + nullptr, nullptr, + GetModuleHandle(nullptr), nullptr + ); + + if (hwnd == nullptr) { + return; + } + + // Create the modern font with DPI-aware size + HFONT hFont = CreateFontW( + -Scale(14, dpi), // Height (scaled) + 0, // Width + 0, // Escapement + 0, // Orientation + FW_NORMAL, // Weight + FALSE, // Italic + FALSE, // Underline + FALSE, // StrikeOut + DEFAULT_CHARSET, // CharSet + OUT_DEFAULT_PRECIS, // OutPrecision + CLIP_DEFAULT_PRECIS, // ClipPrecision + CLEARTYPE_QUALITY, // Quality + DEFAULT_PITCH | FF_DONTCARE, // Pitch and Family + L"Segoe UI" // Font face name + ); + + // Create input controls with scaled positions and sizes + HWND hEdit = CreateWindowExW(0, WC_EDITW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + Scale(10, dpi), Scale(10, dpi), + Scale(250, dpi), Scale(25, dpi), + hwnd, (HMENU)1, GetModuleHandle(nullptr), nullptr); + SendMessageW(hEdit, WM_SETFONT, (WPARAM)hFont, TRUE); + + // Create date picker + HWND hDatePicker = CreateWindowExW(0, DATETIMEPICK_CLASSW, L"", + WS_CHILD | WS_VISIBLE | DTS_SHORTDATECENTURYFORMAT, + Scale(270, dpi), Scale(10, dpi), + Scale(100, dpi), Scale(25, dpi), + hwnd, (HMENU)4, GetModuleHandle(nullptr), nullptr); + SendMessageW(hDatePicker, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hButton = CreateWindowExW(0, WC_BUTTONW, L"Add", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + Scale(380, dpi), Scale(10, dpi), + Scale(50, dpi), Scale(25, dpi), + hwnd, (HMENU)2, GetModuleHandle(nullptr), nullptr); + SendMessageW(hButton, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hListBox = CreateWindowExW(0, WC_LISTBOXW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY, + Scale(10, dpi), Scale(45, dpi), + Scale(460, dpi), Scale(400, dpi), + hwnd, (HMENU)3, GetModuleHandle(nullptr), nullptr); + SendMessageW(hListBox, WM_SETFONT, (WPARAM)hFont, TRUE); + + ShowWindow(hwnd, SW_SHOW); + + // Message loop + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Clean up + DeleteObject(hFont); + }); + + // Detach the thread so it runs independently + guiThread.detach(); + } + + // Global vector to store todos + static std::vector g_todos; + + void NotifyCallback(const TodoCallback &callback, const std::string &json) + { + if (callback) + { + callback(json); + // Process pending messages + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + std::wstring FormatTodoDisplay(const std::wstring &text, const SYSTEMTIME &st) + { + wchar_t dateStr[64]; + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, nullptr, dateStr, 64); + return text + L" - " + dateStr; + } + + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_COMMAND: + { + HWND hListBox = GetDlgItem(hwnd, 3); + int cmd = LOWORD(wParam); + + switch (cmd) + { + case 2: // Add button + { + wchar_t buffer[256]; + GetDlgItemTextW(hwnd, 1, buffer, 256); + + if (wcslen(buffer) > 0) + { + SYSTEMTIME st; + HWND hDatePicker = GetDlgItem(hwnd, 4); + DateTime_GetSystemtime(hDatePicker, &st); + + TodoItem todo; + CoCreateGuid(&todo.id); + todo.text = buffer; + todo.date = SystemTimeToMillis(st); + + g_todos.push_back(todo); + + std::wstring displayText = FormatTodoDisplay(buffer, st); + SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + ResetControls(hwnd); + NotifyCallback(g_todoAddedCallback, todo.toJson()); + } + break; + } + } + break; + } + + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + } + + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + +} // namespace cpp_code +``` + +## 5) Creating the Node.js Addon Bridge + +Now let's implement the bridge between our C++ code and Node.js in `src/cpp_addon.cc`. Let's start by creating a basic skeleton for our addon: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + // We'll add code here later + return exports; +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +This is the minimal structure required for a Node.js addon using `node-addon-api`. The `Init` function is called when the addon is loaded, and the `NODE_API_MODULE` macro registers our initializer. + +### Create a Class to Wrap Our C++ Code + +Let's create a class that will wrap our C++ code and expose it to JavaScript: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + // We'll add methods here later + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppWin32Addon", func); + return exports; + } + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + // Constructor logic will go here + } + +private: + // Will add private members and methods later +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return CppAddon::Init(env, exports); +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +This creates a class that inherits from `Napi::ObjectWrap`, which allows us to wrap our C++ object for use in JavaScript. The `Init` function sets up the class and exports it to JavaScript. + +### Implement Basic Functionality - HelloWorld + +Now let's add our first method, the `HelloWorld` function: + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + }); + + // ... rest of Init function + } + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + // Constructor logic will go here + } + +private: + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); + } +}; + +// ... rest of the file +``` + +This adds the `HelloWorld` method to our class and registers it with `DefineClass`. The method validates inputs, calls our C++ function, and returns the result to JavaScript. + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + }); + + // ... rest of Init function + } + + // ... constructor + +private: + // ... HelloWorld method + + void HelloGui(const Napi::CallbackInfo& info) { + cpp_code::hello_gui(); + } +}; + +// ... rest of the file +``` + +This simple method calls our `hello_gui` function from the C++ code, which launches the Win32 GUI window in a separate thread. + +### Setting Up the Event System + +Now comes the complex part - setting up the event system so our C++ code can call back to JavaScript. We need to: + +1. Add private members to store callbacks +2. Create a threadsafe function for cross-thread communication +3. Add an `On` method to register JavaScript callbacks +4. Set up C++ callbacks that will trigger the JavaScript callbacks + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + // ... previous public methods + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + // ... existing private methods +}; + +// ... rest of the file +``` + +Now, let's enhance our constructor to initialize these members: + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + // CallbackData struct to pass data between threads + struct CallbackData { + std::string eventType; + std::string payload; + CppAddon* addon; + }; + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + // We'll add threadsafe function setup here in the next step + } + + // Add destructor to clean up + ~CppAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + + // ... rest of the class +}; + +// ... rest of the file +``` + +Now let's add the threadsafe function setup to our constructor: + +```cpp title='src/cpp_addon.cc' +// ... existing constructor code +CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // We'll add callback setup in the next step +} +``` + +This creates a threadsafe function that allows our C++ code to call JavaScript from any thread. When called, it retrieves the appropriate JavaScript callback and invokes it with the provided payload. + +Now let's add the callbacks setup: + +```cpp title='src/cpp_addon.cc' +// ... existing constructor code after threadsafe function setup + +// Set up the callbacks here +auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; +}; + +cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); +``` + +This creates a function that generates callbacks for each event type. The callbacks capture the event type and, when called, create a `CallbackData` object and pass it to our threadsafe function. + +Finally, let's add the `On` method to allow JavaScript to register callback functions: + +```cpp title='src/cpp_addon.cc' +// ... in the class definition, add On to DefineClass +static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + // ... rest of Init function +} + +// ... and add the implementation in the private section +Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); +} +``` + +This allows JavaScript to register callbacks for specific event types. + +### Putting the bridge together + +Now we have all the pieces in place. + +Here's the complete implementation: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppWin32Addon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + CppAddon* addon; + }; + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks here + auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); + } + + ~CppAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); + } + + void HelloGui(const Napi::CallbackInfo& info) { + cpp_code::hello_gui(); + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return CppAddon::Init(env, exports); +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +## 6) Creating a JavaScript Wrapper + +Let's finish things off by adding a JavaScript wrapper in `js/index.js`. As we could all see, C++ requires a lot of boilerplate code that might be easier or faster to write in JavaScript - and you will find that many production applications end up transforming data or requests in JavaScript before invoking native code. We, for instance, turn our timestamp into a proper JavaScript date. + +```cpp title='js/index.js' +const EventEmitter = require('events') + +class CppWin32Addon extends EventEmitter { + constructor() { + super() + + if (process.platform !== 'win32') { + throw new Error('This module is only available on Windows') + } + + const native = require('bindings')('cpp_addon') + this.addon = new native.CppWin32Addon(); + + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.#parse(payload)) + }); + + this.addon.on('todoUpdated', (payload) => { + this.emit('todoUpdated', this.#parse(payload)) + }); + + this.addon.on('todoDeleted', (payload) => { + this.emit('todoDeleted', this.#parse(payload)) + }); + } + + helloWorld(input = "") { + return this.addon.helloWorld(input) + } + + helloGui() { + this.addon.helloGui() + } + + #parse(payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'win32') { + module.exports = new CppWin32Addon() +} else { + module.exports = {} +} +``` + +## 7) Building and Testing the Addon + +With all files in place, you can build the addon: + +```psh +npm run build +``` + +## Conclusion + +You've now built a complete native Node.js addon for Windows using C++ and the Win32 API. Some of things we've done here are: + +1. Creating a native Windows GUI from C++ +2. Implementing a Todo list application with Add, Edit, and Delete functionality +3. Bidirectional communication between C++ and JavaScript +4. Using Win32 controls and Windows-specific features +5. Safely calling back into JavaScript from C++ threads + +This provides a foundation for building more complex Windows-specific features in your Electron apps, giving you the best of both worlds: the ease of web technologies with the power of native code. + +For more information on working with Win32 API, refer to the [Microsoft C++, C, and Assembler documentation](https://learn.microsoft.com/en-us/cpp/?view=msvc-170) and the [Windows API reference](https://learn.microsoft.com/en-us/windows/win32/api/). diff --git a/docs/tutorial/native-code-and-electron-objc-macos.md b/docs/tutorial/native-code-and-electron-objc-macos.md new file mode 100644 index 0000000000000..7074acda10dc0 --- /dev/null +++ b/docs/tutorial/native-code-and-electron-objc-macos.md @@ -0,0 +1,1152 @@ +# Native Code and Electron: Objective-C (macOS) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for macOS using Objective-C, Objective-C++, and Cocoa frameworks. To illustrate how you can embed native macOS code in your Electron app, we'll be building a basic native macOS GUI (using AppKit) that communicates with Electron's JavaScript. + +Specifically, we'll be integrating with two macOS frameworks: + +* AppKit - The primary UI framework for macOS applications that provides components like windows, buttons, text fields, and more. +* Foundation - A framework that provides data management, file system interaction, and other essential services. + +This tutorial will be most useful to those who already have some familiarity with Objective-C and Cocoa development. You should understand basic concepts like delegates, NSObjects, and the target-action pattern commonly used in macOS development. + +> [!NOTE] +> If you're not already familiar with these concepts, Apple's [documentation on Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html) is an excellent starting point. + +## Requirements + +Just like our general introduction to Native Code and Electron, this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code on macOS. You'll need: + +* Xcode installed (available from the Mac App Store) +* Xcode Command Line Tools (can be installed by running `xcode-select --install` in Terminal) + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +my-native-objc-addon/ +├── binding.gyp +├── include/ +│ └── objc_code.h +├── js/ +│ └── index.js +├── package.json +└── src/ + ├── objc_addon.mm + └── objc_code.mm +``` + +Our `package.json` should look like this: + +```json title='package.json' +{ + "name": "objc-macos", + "version": "1.0.0", + "description": "A demo module that exposes Objective-C code to Electron", + "main": "js/index.js", + "author": "Your Name", + "scripts": { + "clean": "rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + } +} +``` + +## 2) Setting Up the Build Configuration + +For a macOS-specific addon using Objective-C, we need to modify our `binding.gyp` file to include the appropriate frameworks and compiler flags. We need to: + +1. Ensure our addon is only compiled on macOS +2. Include the necessary macOS frameworks (Foundation and AppKit) +3. Configure the compiler for Objective-C/C++ support + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "objc_addon", + "conditions": [ + ['OS=="mac"', { + "sources": [ + "src/objc_addon.mm", + "src/objc_code.mm" + ], + "include_dirs": [ + " +#include + +namespace objc_code { + +std::string hello_world(const std::string& input); +void hello_gui(); + +// Callback function types +using TodoCallback = std::function; + +// Callback setters +void setTodoAddedCallback(TodoCallback callback); + +} // namespace objc_code +``` + +This header: + +* Includes a basic hello_world function from the general tutorial +* Adds a `hello_gui` function to create a native macOS GUI +* Defines callback types for Todo operations +* Provides setter functions for these callbacks + +## 4) Implementing the Objective-C Code + +Now, let's implement our Objective-C code in `src/objc_code.mm`. This is where we'll create our native macOS GUI using AppKit. + +We'll always add code to the bottom of our file. To make this tutorial easier to follow, we'll start with the basic structure and add features incrementally - step by step. + +### Setting Up the Basic Structure + +```objc title='src/objc_code.mm' +#import +#import +#import +#import +#import "../include/objc_code.h" + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +// More code to follow later... +``` + +This imports the required frameworks and defines our callback type. The static `g_todoAddedCallback` variable will store our JavaScript callback function. + +### Defining the Window Controller Interface + +At the bottom of `objc_code.mm`, add the following code to define our window controller class interface: + +```objc title='src/objc_code.mm' +// Previous code... + +// Forward declaration of our custom classes +@interface TodoWindowController : NSWindowController +@property (strong) NSTextField *textField; +@property (strong) NSDatePicker *datePicker; +@property (strong) NSButton *addButton; +@property (strong) NSTableView *tableView; +@property (strong) NSMutableArray *todos; +@end + +// More code to follow later... +``` + +This declares our TodoWindowController class which will manage the window and UI components: + +* A text field (`NSTextField`) for entering todo text +* A date picker (`NSDatePicker`) for selecting the date +* An "Add" button (`NSButton`) +* A table view to display the todos (`NSTableView`) +* An array to store the todo items (`NSMutableArray`) + +### Implementing the Window Controller + +At the bottom of `objc_code.mm`, add the following code to start implementing the window controller with an initialization method: + +```objc title='src/objc_code.mm' +// Previous code... + +// Controller for the main window +@implementation TodoWindowController + +- (instancetype)init { + self = [super initWithWindowNibName:@""]; + if (self) { + // Create an array to store todos + _todos = [NSMutableArray array]; + [self setupWindow]; + } + return self; +} + +// More code to follow later... +``` + +This initializes our controller. We're not using a nib file, so we pass an empty string to `initWithWindowNibName`. We create an empty array to store our todos and call the `setupWindow` method, which we'll implement next. + +At this point, our full file looks like this: + +```objc title='src/objc_code.mm' +#import +#import +#import +#import +#import "../include/objc_code.h" + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +// Forward declaration of our custom classes +@interface TodoWindowController : NSWindowController +@property (strong) NSTextField *textField; +@property (strong) NSDatePicker *datePicker; +@property (strong) NSButton *addButton; +@property (strong) NSTableView *tableView; +@property (strong) NSMutableArray *todos; +@end + +// Controller for the main window +@implementation TodoWindowController + +- (instancetype)init { + self = [super initWithWindowNibName:@""]; + if (self) { + // Create an array to store todos + _todos = [NSMutableArray array]; + [self setupWindow]; + } + return self; +} + +// More code to follow later... +``` + +### Creating the Window and Basic UI + +Now, we'll add a `setupWindow()` method. This method will look a little overwhelming on first sight, but it really just instantiates a number of UI controls and then adds them to our window. + +```objc title='src/objc_code.mm' +// Previous code... + +- (void)setupWindow { + // Create a window + NSRect frame = NSMakeRect(0, 0, 400, 300); + NSWindow *window = [[NSWindow alloc] initWithContentRect:frame + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:@"Todo List"]; + [window center]; + self.window = window; + + // Set up the content view with auto layout + NSView *contentView = [window contentView]; + + // Create text field + _textField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 260, 200, 24)]; + [_textField setPlaceholderString:@"Enter a todo..."]; + [contentView addSubview:_textField]; + + // Create date picker + _datePicker = [[NSDatePicker alloc] initWithFrame:NSMakeRect(230, 260, 100, 24)]; + [_datePicker setDatePickerStyle:NSDatePickerStyleTextField]; + [_datePicker setDatePickerElements:NSDatePickerElementFlagYearMonthDay]; + [contentView addSubview:_datePicker]; + + // Create add button + _addButton = [[NSButton alloc] initWithFrame:NSMakeRect(340, 260, 40, 24)]; + [_addButton setTitle:@"Add"]; + [_addButton setBezelStyle:NSBezelStyleRounded]; + [_addButton setTarget:self]; + [_addButton setAction:@selector(addTodo:)]; + [contentView addSubview:_addButton]; + + // More UI elements to follow in the next step... +} + +// More code to follow later... +``` + +This method: + +1. Creates a window with a title and standard window controls +2. Centers the window on the screen +3. Creates a text field for entering todo text +4. Adds a date picker configured to show only date (no time) +5. Adds an "Add" button that will call the `addTodo:` method when clicked + +We're still missing the table view to display our todos. Let's add that to the bottom of our `setupWindow()` method, right where it says `More UI elements to follow in the next step...` in the code above. + +```objc title='src/objc_code.mm' +// Previous code... + +- (void)setupWindow { + // Previous setupWindow() code... + + // Create a scroll view for the table + NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 20, 360, 230)]; + [scrollView setBorderType:NSBezelBorder]; + [scrollView setHasVerticalScroller:YES]; + [contentView addSubview:scrollView]; + + // Create table view + _tableView = [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 360, 230)]; + + // Add a column for the todo text + NSTableColumn *textColumn = [[NSTableColumn alloc] initWithIdentifier:@"text"]; + [textColumn setWidth:240]; + [textColumn setTitle:@"Todo"]; + [_tableView addTableColumn:textColumn]; + + // Add a column for the date + NSTableColumn *dateColumn = [[NSTableColumn alloc] initWithIdentifier:@"date"]; + [dateColumn setWidth:100]; + [dateColumn setTitle:@"Date"]; + [_tableView addTableColumn:dateColumn]; + + // Set the table's delegate and data source + [_tableView setDataSource:self]; + [_tableView setDelegate:self]; + + // Add the table to the scroll view + [scrollView setDocumentView:_tableView]; +} + +// More code to follow later... +``` + +This extends our `setupWindow` method to: + +1. Create a scroll view to contain the table +2. Create a table view with two columns: one for the todo text and one for the date +3. Set up the data source and delegate to this class +4. Add the table to the scroll view + +This concludes the UI elements in `setupWindow()`, so we can now move on to business logic. + +### Implementing the "Add Todo" Functionality + +Next, let's implement the `addTodo:` method to handle adding new todos. We'll need to do two sets of operations here: First, we need to handle our native UI and perform operations like getting the data out of our UI elements or resetting them. Then, we also need notify our JavaScript world about the newly added todo. + +In the interest of keeping this tutorial easy to follow, we'll do this in two steps. + +```objc title='src/objc_code.mm' +// Previous code... + +// Action method for the Add button +- (void)addTodo:(id)sender { + NSString *text = [_textField stringValue]; + if ([text length] > 0) { + NSDate *date = [_datePicker dateValue]; + + // Create a unique ID + NSUUID *uuid = [NSUUID UUID]; + + // Create a dictionary to store the todo + NSDictionary *todo = @{ + @"id": [uuid UUIDString], + @"text": text, + @"date": date + }; + + // Add to our array + [_todos addObject:todo]; + + // Reload the table + [_tableView reloadData]; + + // Reset the text field + [_textField setStringValue:@""]; + + // Next, we'll notify our JavaScript world here... + } +} + +// More code to follow later... +``` + +This method: + +1. Gets the text from the text field +2. If the text is not empty, creates a new todo with a unique ID, the entered text, and the selected date +3. Adds the todo to our array +4. Reloads the table to show the new todo +5. Clears the text field for the next entry + +Now, let's extend the `addTodo:` method to notify JavaScript when a todo is added. We'll do that at the bottom of the method, where it currently reads "Next, we'll notify our JavaScript world here...". + +```objc title='src/objc_code.mm' +// Previous code... + +// Action method for the Add button +- (void)addTodo:(id)sender { + NSString *text = [_textField stringValue]; + if ([text length] > 0) { + // Previous addTodo() code... + + // Call the callback if it exists + if (g_todoAddedCallback) { + // Convert the todo to JSON + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{ + @"id": [uuid UUIDString], + @"text": text, + @"date": @((NSTimeInterval)[date timeIntervalSince1970] * 1000) + } options:0 error:&error]; + + if (!error) { + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + std::string cppJsonString = [jsonString UTF8String]; + g_todoAddedCallback(cppJsonString); + } + } + } +} + +// More code to follow later... +``` + +This adds code to do a whole bunch of conversions (so that N-API can eventually turn this data into structures ready for V8 and the JavaScript world) - and then calls our JavaScript callback. Specifically, it does the following: + +1. Check if a callback function has been registered +2. Convert the todo to JSON format +3. Convert the date to milliseconds since epoch (JavaScript date format) +4. Convert the JSON to a C++ string +5. Call the callback function with the JSON string + +We're now done with our `addTodo:` method and can move on to the next step: The data source for the Table View. + +### Implementing the Table View Data Source + +Let's implement the table view data source methods to display our todos: + +```objc title='src/objc_code.mm' +// Previous code... + +// NSTableViewDataSource methods +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return [_todos count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSDictionary *todo = _todos[row]; + NSString *identifier = [tableColumn identifier]; + + if ([identifier isEqualToString:@"text"]) { + return todo[@"text"]; + } else if ([identifier isEqualToString:@"date"]) { + NSDate *date = todo[@"date"]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setDateStyle:NSDateFormatterShortStyle]; + return [formatter stringFromDate:date]; + } + + return nil; +} + +@end + +// More code to follow later... +``` + +These methods: + +* Return the number of todos for the table view +* Provide the text or formatted date for each cell in the table + +### Implementing the C++ Functions + +Lastly, we need to implement the C++ namespace functions that were declared in our header file: + +```objc title='src/objc_code.mm' +// Previous code... + +namespace objc_code { + +std::string hello_world(const std::string& input) { + return "Hello from Objective-C! You said: " + input; +} + +void setTodoAddedCallback(TodoCallback callback) { + g_todoAddedCallback = callback; +} + +void hello_gui() { + // Create and run the GUI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // Create our window controller + TodoWindowController *windowController = [[TodoWindowController alloc] init]; + + // Show the window + [windowController showWindow:nil]; + + // Keep a reference to prevent it from being deallocated + // Note: in a real app, you'd store this reference more carefully + static TodoWindowController *staticController = nil; + staticController = windowController; + }); +} + +} // namespace objc_code +``` + +These functions: + +1. Implement the `hello_world` function that returns a greeting string +2. Provide a way to set the callback function for todo additions +3. Implement the `hello_gui` function that creates and shows our native UI +4. Lastly, we also keep a static reference to prevent the window controller from being deallocated + +Note that we're using GCD (Grand Central Dispatch) to dispatch to the main thread, which is required for UI operations. We're not dedicating more time to thread safety in this tutorial, but here's a quick reminder: In macOS/iOS, all UI updates must happen on the main thread. The main thread is the primary execution path where the application runs its event loop and processes user interface events. In our code, when JavaScript calls the `hello_gui()` function, the call might be coming from a Node.js worker thread, not the main thread. Using GCD, we safely redirect the window creation code to the main thread, ensuring proper UI behavior. + +This is a common pattern in macOS/iOS development - any code that touches the UI needs to be executed on the main thread, and GCD provides a clean way to ensure this happens. + +The final version of `objc_code.mm` looks like this: + +```objc title='src/objc_code.mm' +#import +#import +#import +#import +#import "../include/objc_code.h" + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +// Forward declaration of our custom classes +@interface TodoWindowController : NSWindowController +@property (strong) NSTextField *textField; +@property (strong) NSDatePicker *datePicker; +@property (strong) NSButton *addButton; +@property (strong) NSTableView *tableView; +@property (strong) NSMutableArray *todos; +@end + +// Controller for the main window +@implementation TodoWindowController + +- (instancetype)init { + self = [super initWithWindowNibName:@""]; + if (self) { + // Create an array to store todos + _todos = [NSMutableArray array]; + [self setupWindow]; + } + return self; +} + +- (void)setupWindow { + // Create a window + NSRect frame = NSMakeRect(0, 0, 400, 300); + NSWindow *window = [[NSWindow alloc] initWithContentRect:frame + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:@"Todo List"]; + [window center]; + self.window = window; + + // Set up the content view with auto layout + NSView *contentView = [window contentView]; + + // Create text field + _textField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 260, 200, 24)]; + [_textField setPlaceholderString:@"Enter a todo..."]; + [contentView addSubview:_textField]; + + // Create date picker + _datePicker = [[NSDatePicker alloc] initWithFrame:NSMakeRect(230, 260, 100, 24)]; + [_datePicker setDatePickerStyle:NSDatePickerStyleTextField]; + [_datePicker setDatePickerElements:NSDatePickerElementFlagYearMonthDay]; + [contentView addSubview:_datePicker]; + + // Create add button + _addButton = [[NSButton alloc] initWithFrame:NSMakeRect(340, 260, 40, 24)]; + [_addButton setTitle:@"Add"]; + [_addButton setBezelStyle:NSBezelStyleRounded]; + [_addButton setTarget:self]; + [_addButton setAction:@selector(addTodo:)]; + [contentView addSubview:_addButton]; + + // Create a scroll view for the table + NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 20, 360, 230)]; + [scrollView setBorderType:NSBezelBorder]; + [scrollView setHasVerticalScroller:YES]; + [contentView addSubview:scrollView]; + + // Create table view + _tableView = [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 360, 230)]; + + // Add a column for the todo text + NSTableColumn *textColumn = [[NSTableColumn alloc] initWithIdentifier:@"text"]; + [textColumn setWidth:240]; + [textColumn setTitle:@"Todo"]; + [_tableView addTableColumn:textColumn]; + + // Add a column for the date + NSTableColumn *dateColumn = [[NSTableColumn alloc] initWithIdentifier:@"date"]; + [dateColumn setWidth:100]; + [dateColumn setTitle:@"Date"]; + [_tableView addTableColumn:dateColumn]; + + // Set the table's delegate and data source + [_tableView setDataSource:self]; + [_tableView setDelegate:self]; + + // Add the table to the scroll view + [scrollView setDocumentView:_tableView]; +} + +// Action method for the Add button +- (void)addTodo:(id)sender { + NSString *text = [_textField stringValue]; + if ([text length] > 0) { + NSDate *date = [_datePicker dateValue]; + + // Create a unique ID + NSUUID *uuid = [NSUUID UUID]; + + // Create a dictionary to store the todo + NSDictionary *todo = @{ + @"id": [uuid UUIDString], + @"text": text, + @"date": date + }; + + // Add to our array + [_todos addObject:todo]; + + // Reload the table + [_tableView reloadData]; + + // Reset the text field + [_textField setStringValue:@""]; + + // Call the callback if it exists + if (g_todoAddedCallback) { + // Convert the todo to JSON + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{ + @"id": [uuid UUIDString], + @"text": text, + @"date": @((NSTimeInterval)[date timeIntervalSince1970] * 1000) + } options:0 error:&error]; + + if (!error) { + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + std::string cppJsonString = [jsonString UTF8String]; + g_todoAddedCallback(cppJsonString); + } + } + } +} + +// NSTableViewDataSource methods +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return [_todos count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSDictionary *todo = _todos[row]; + NSString *identifier = [tableColumn identifier]; + + if ([identifier isEqualToString:@"text"]) { + return todo[@"text"]; + } else if ([identifier isEqualToString:@"date"]) { + NSDate *date = todo[@"date"]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setDateStyle:NSDateFormatterShortStyle]; + return [formatter stringFromDate:date]; + } + + return nil; +} + +@end + +namespace objc_code { + +std::string hello_world(const std::string& input) { + return "Hello from Objective-C! You said: " + input; +} + +void setTodoAddedCallback(TodoCallback callback) { + g_todoAddedCallback = callback; +} + +void hello_gui() { + // Create and run the GUI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // Create our window controller + TodoWindowController *windowController = [[TodoWindowController alloc] init]; + + // Show the window + [windowController showWindow:nil]; + + // Keep a reference to prevent it from being deallocated + // Note: in a real app, you'd store this reference more carefully + static TodoWindowController *staticController = nil; + staticController = windowController; + }); +} + +} // namespace objc_code +``` + +## 5) Creating the Node.js Addon Bridge + +We now have working Objective-C code. To make sure it can be safely and properly called from the JavaScript world, we need to build a bridge between Objective-C and C++, which we can do with Objective-C++. We'll do that in `src/objc_addon.mm`. + +Bear with us: This bridge code always ends up being pretty verbose and might seem difficult to follow. As far as modern desktop development goes, it's fairly low-level, so be patient with yourself - it might take a little bit before the bridging really "clicks". + +### Basic Class Definition + +```objc title='src/objc_addon.mm' +#include +#include +#include "../include/objc_code.h" + +class ObjcAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "ObjcMacosAddon", { + InstanceMethod("helloWorld", &ObjcAddon::HelloWorld), + InstanceMethod("helloGui", &ObjcAddon::HelloGui), + InstanceMethod("on", &ObjcAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("ObjcMacosAddon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + ObjcAddon* addon; + }; + + // More code to follow later... + // Specifically, we'll add ObjcAddon here in the next step +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return ObjcAddon::Init(env, exports); +} + +NODE_API_MODULE(objc_addon, Init) +``` + +This code: + +1. Defines an ObjcAddon class that inherits from Napi::ObjectWrap +2. Creates a static Init method that registers our JavaScript methods +3. Defines a CallbackData structure for passing data between threads +4. Sets up the Node API module initialization + +### Constructor and Threadsafe Function Setup + +Next, let's implement the constructor that sets up our threadsafe callback mechanism: + +```objc title='src/objc_addon.mm' +ObjcAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "ObjcCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks + auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + objc_code::setTodoAddedCallback(makeCallback("todoAdded")); +} + +~ObjcAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } +} + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; +``` + +This code: + +* Sets up the constructor with member initialization +* Creates a threadsafe function using N-API, which allows safe callbacks from any thread +* Defines a lambda to create callback functions for different event types +* Registers the "todoAdded" callback with our Objective-C code +* Implements a destructor to clean up resources when the addon is destroyed + +The threadsafe function is important because UI events in Objective-C might happen on a different thread than the JavaScript event loop. This mechanism safely bridges those thread boundaries. + +### Implementing JavaScript Methods + +Finally, let's implement the methods that JavaScript will call: + +```objc title='src/objc_addon.mm' +Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = objc_code::hello_world(input); + + return Napi::String::New(env, result); +} + +void HelloGui(const Napi::CallbackInfo& info) { + objc_code::hello_gui(); +} + +Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); +} +``` + +Let's take a look at what we've added in this step: + +* `HelloWorld()`: Takes a string input, calls our Objective-C function, and returns the result +* `HelloGui()`: A simple wrapper around the Objective-C `hello_gui` function +* `On`: Allows JavaScript to register event listeners that will be called when native events occur + +The `On` method is particularly important as it creates the event system that our JavaScript code will use to receive notifications from the native UI. + +Together, these three components form a complete bridge between our Objective-C code and the JavaScript world, allowing bidirectional communication. Here's what the finished file should look like: + +```objc title='src/objc_addon.mm' +#include +#include +#include "../include/objc_code.h" + +class ObjcAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "ObjcMacosAddon", { + InstanceMethod("helloWorld", &ObjcAddon::HelloWorld), + InstanceMethod("helloGui", &ObjcAddon::HelloGui), + InstanceMethod("on", &ObjcAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("ObjcMacosAddon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + ObjcAddon* addon; + }; + + ObjcAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "ObjcCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks + auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + objc_code::setTodoAddedCallback(makeCallback("todoAdded")); + } + + ~ObjcAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = objc_code::hello_world(input); + + return Napi::String::New(env, result); + } + + void HelloGui(const Napi::CallbackInfo& info) { + objc_code::hello_gui(); + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return ObjcAddon::Init(env, exports); +} + +NODE_API_MODULE(objc_addon, Init) +``` + +## 6) Creating a JavaScript Wrapper + +You're so close! We now have working Objective-C and thread-safe ways to expose methods and events to JavaScript. In this final step, let's create a JavaScript wrapper in `js/index.js` to provide a more friendly API: + +```js title='js/index.js' @ts-expect-error=[10] +const EventEmitter = require('events') + +class ObjcMacosAddon extends EventEmitter { + constructor () { + super() + + if (process.platform !== 'darwin') { + throw new Error('This module is only available on macOS') + } + + const native = require('bindings')('objc_addon') + this.addon = new native.ObjcMacosAddon() + + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.parse(payload)) + }) + } + + helloWorld (input = '') { + return this.addon.helloWorld(input) + } + + helloGui () { + this.addon.helloGui() + } + + parse (payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'darwin') { + module.exports = new ObjcMacosAddon() +} else { + module.exports = {} +} +``` + +This wrapper: + +1. Extends EventEmitter to provide event support +2. Checks if we're running on macOS +3. Loads the native addon +4. Sets up event listeners and forwards them +5. Provides a clean API for our functions +6. Parses JSON payloads and converts timestamps to JavaScript Date objects + +## 7) Building and Testing the Addon + +With all files in place, you can build the addon: + +```sh +npm run build +``` + +Please note that you _cannot_ call this script from Node.js directly, since Node.js doesn't set up an "app" in the eyes of macOS. Electron does though, so you can test your code by requiring and calling it from Electron. + +## Conclusion + +You've now built a complete native Node.js addon for macOS using Objective-C and AppKit. This provides a foundation for building more complex macOS-specific features in your Electron apps, giving you the best of both worlds: the ease of web technologies with the power of native macOS code. + +The approach demonstrated here allows you to: + +* Create native macOS UIs using AppKit +* Implement bidirectional communication between JavaScript and Objective-C +* Leverage macOS-specific features and frameworks +* Integrate with existing Objective-C codebases + +For more information on developing with Objective-C and Cocoa, refer to Apple's developer documentation: + +* [Objective-C Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html) +* [AppKit Framework](https://developer.apple.com/documentation/appkit) +* [macOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/macos) diff --git a/docs/tutorial/native-code-and-electron-swift-macos.md b/docs/tutorial/native-code-and-electron-swift-macos.md new file mode 100644 index 0000000000000..1e11b6ac3d3b1 --- /dev/null +++ b/docs/tutorial/native-code-and-electron-swift-macos.md @@ -0,0 +1,1175 @@ +# Native Code and Electron: Swift (macOS) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for macOS using Swift. + +Swift is a modern, powerful language designed for safety and performance. While you can't use Swift directly with the Node.js N-API as used by Electron, you can create a bridge using Objective-C++ to connect Swift with JavaScript in your Electron application. + +To illustrate how you can embed native macOS code in your Electron app, we'll be building a basic native macOS GUI (using SwiftUI) that communicates with Electron's JavaScript. + +This tutorial will be most useful to those who already have some familiarity with Objective-C, Swift, and SwiftUI development. You should understand basic concepts like Swift syntax, optionals, closures, SwiftUI views, property wrappers, and the Objective-C/Swift interoperability mechanisms such as the `@objc` attribute and bridging headers. + +> [!NOTE] +> If you're not already familiar with these concepts, Apple's [Swift Programming Language Guide](https://docs.swift.org/swift-book/), [SwiftUI Documentation](https://developer.apple.com/documentation/swiftui/), and [Swift and Objective-C Interoperability Guide](https://developer.apple.com/documentation/swift/importing-swift-into-objective-c) are excellent starting points. + +## Requirements + +Just like our [general introduction to Native Code and Electron](./native-code-and-electron.md), this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code on macOS. You'll need: + +* Xcode installed (available from the Mac App Store) +* Xcode Command Line Tools (can be installed by running `xcode-select --install` in Terminal) + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +swift-native-addon/ +├── binding.gyp # Build configuration +├── include/ +│ └── SwiftBridge.h # Objective-C header for the bridge +├── js/ +│ └── index.js # JavaScript interface +├── package.json # Package configuration +└── src/ + ├── SwiftCode.swift # Swift implementation + ├── SwiftBridge.m # Objective-C bridge implementation + └── swift_addon.mm # Node.js addon implementation +``` + +Our `package.json` should look like this: + +```json title='package.json' +{ + "name": "swift-macos", + "version": "1.0.0", + "description": "A demo module that exposes Swift code to Electron", + "main": "js/index.js", + "scripts": { + "clean": "rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + }, + "devDependencies": { + "node-gyp": "^11.1.0" + } +} +``` + +## 2) Setting Up the Build Configuration + +In our other tutorials focusing on other native languages, we could use `node-gyp` to build the entirety of our code. With Swift, things are a bit more tricky: We need to first build and then link our Swift code. This is because Swift has its own compilation model and runtime requirements that don't directly integrate with node-gyp's C/C++ focused build system. + +The process involves: + +1. Compiling Swift code separately into a static library (.a file) +2. Creating an Objective-C bridge that exposes Swift functionality +3. Linking the compiled Swift library with our Node.js addon +4. Managing Swift runtime dependencies + +This two-step compilation process ensures that Swift's advanced language features and runtime are properly handled while still allowing us to expose the functionality to JavaScript through Node.js's native addon system. + +Let's start by adding a basic structure: + +```json title='binding.gyp' +{ + "targets": [{ + "target_name": "swift_addon", + "conditions": [ + ['OS=="mac"', { + "sources": [ + "src/swift_addon.mm", + "src/SwiftBridge.m", + "src/SwiftCode.swift" + ], + "include_dirs": [ + " + +@interface SwiftBridge : NSObject ++ (NSString*)helloWorld:(NSString*)input; ++ (void)helloGui; + ++ (void)setTodoAddedCallback:(void(^)(NSString* todoJson))callback; ++ (void)setTodoUpdatedCallback:(void(^)(NSString* todoJson))callback; ++ (void)setTodoDeletedCallback:(void(^)(NSString* todoId))callback; +@end + +#endif +``` + +This header defines the Objective-C interface that we'll use to bridge between our Swift code and the Node.js addon. It includes: + +* A simple `helloWorld` method that takes a string input and returns a string +* A `helloGui` method that will display a native SwiftUI interface +* Methods to set callbacks for todo operations (add, update, delete) + +## 4) Implementing the Objective-C Bridge + +Now, let's create the Objective-C bridge itself in `src/SwiftBridge.m`: + +```objc title='src/SwiftBridge.m' +#import "SwiftBridge.h" +#import "swift_addon-Swift.h" +#import + +@implementation SwiftBridge + +static void (^todoAddedCallback)(NSString*); +static void (^todoUpdatedCallback)(NSString*); +static void (^todoDeletedCallback)(NSString*); + ++ (NSString*)helloWorld:(NSString*)input { + return [SwiftCode helloWorld:input]; +} + ++ (void)helloGui { + [SwiftCode helloGui]; +} + ++ (void)setTodoAddedCallback:(void(^)(NSString*))callback { + todoAddedCallback = callback; + [SwiftCode setTodoAddedCallback:callback]; +} + ++ (void)setTodoUpdatedCallback:(void(^)(NSString*))callback { + todoUpdatedCallback = callback; + [SwiftCode setTodoUpdatedCallback:callback]; +} + ++ (void)setTodoDeletedCallback:(void(^)(NSString*))callback { + todoDeletedCallback = callback; + [SwiftCode setTodoDeletedCallback:callback]; +} + +@end +``` + +This bridge: + +* Imports the Swift-generated header (`swift_addon-Swift.h`) +* Implements the methods defined in our header +* Simply forwards calls to the Swift code +* Stores the callbacks for later use in static variables, allowing them to persist throughout the application's lifecycle. This ensures that the JavaScript callbacks can be invoked at any time when todo items are added, updated, or deleted. + +## 5) Implementing the Swift Code + +Now, let's implement our Objective-C code in `src/SwiftCode.swift`. This is where we'll create our native macOS GUI using SwiftUI. + +To make this tutorial easier to follow, we'll start with the basic structure and add features incrementally - step by step. + +### Setting Up the Basic Structure + +Let's start with the basic structure. Here, we're just setting up variables, some basic callback methods, and a simple helper method we'll use later to convert data into formats ready for the JavaScript world. + +```swift title='src/SwiftCode.swift' +import Foundation +import SwiftUI + +@objc +public class SwiftCode: NSObject { + private static var windowController: NSWindowController? + private static var todoAddedCallback: ((String) -> Void)? + private static var todoUpdatedCallback: ((String) -> Void)? + private static var todoDeletedCallback: ((String) -> Void)? + + @objc + public static func helloWorld(_ input: String) -> String { + return "Hello from Swift! You said: \(input)" + } + + @objc + public static func setTodoAddedCallback(_ callback: @escaping (String) -> Void) { + todoAddedCallback = callback + } + + @objc + public static func setTodoUpdatedCallback(_ callback: @escaping (String) -> Void) { + todoUpdatedCallback = callback + } + + @objc + public static func setTodoDeletedCallback(_ callback: @escaping (String) -> Void) { + todoDeletedCallback = callback + } + + private static func encodeToJson(_ item: T) -> String? { + let encoder = JSONEncoder() + + // Encode date as milliseconds since 1970, which is what the JS side expects + encoder.dateEncodingStrategy = .custom { date, encoder in + let milliseconds = Int64(date.timeIntervalSince1970 * 1000) + var container = encoder.singleValueContainer() + try container.encode(milliseconds) + } + + guard let jsonData = try? encoder.encode(item), + let jsonString = String(data: jsonData, encoding: .utf8) else { + return nil + } + return jsonString + } + + // More code to follow... +} +``` + +This first part of our Swift code: + +1. Declares a class with the `@objc` attribute, making it accessible from Objective-C +2. Implements the `helloWorld` method +3. Adds callback setters for todo operations +4. Includes a helper method to encode Swift objects to JSON strings + +### Implementing `helloGui()` + +Let's continue with the `helloGui` method and the SwiftUI implementation. This is where we start adding user interface elements to the screen. + +```swift title='src/SwiftCode.swift' +// Other code... + +@objc +public class SwiftCode: NSObject { + // Other code... + + @objc + public static func helloGui() -> Void { + let contentView = NSHostingView(rootView: ContentView( + onTodoAdded: { todo in + if let jsonString = encodeToJson(todo) { + todoAddedCallback?(jsonString) + } + }, + onTodoUpdated: { todo in + if let jsonString = encodeToJson(todo) { + todoUpdatedCallback?(jsonString) + } + }, + onTodoDeleted: { todoId in + todoDeletedCallback?(todoId.uuidString) + } + )) + let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), + styleMask: [.titled, .closable, .miniaturizable, .resizable], + backing: .buffered, + defer: false + ) + + window.title = "Todo List" + window.contentView = contentView + window.center() + + windowController = NSWindowController(window: window) + windowController?.showWindow(nil) + + NSApp.activate(ignoringOtherApps: true) + } +} +``` + +This helloGui method: + +1. Creates a SwiftUI view hosted in an `NSHostingView`. This is a crucial bridging component that allows SwiftUI views to be used in AppKit applications. The `NSHostingView` acts as a container that wraps our SwiftUI `ContentView` and handles the translation between SwiftUI's declarative UI system and AppKit's imperative UI system. This enables us to leverage SwiftUI's modern UI framework while still integrating with the traditional macOS window management system. +2. Sets up callbacks to notify JavaScript when todo items change. We'll setup the actual callbacks later, for now we'll just call them if one is available. +3. Creates and displays a native macOS window. +4. Activates the app to bring the window to the front. + +### Implementing the Todo Item + +Next, we'll define a `TodoItem` model with an ID, text, and date. + +```swift title='src/SwiftCode.swift' +// Other code... + +@objc +public class SwiftCode: NSObject { + // Other code... + + private struct TodoItem: Identifiable, Codable { + let id: UUID + var text: String + var date: Date + + init(id: UUID = UUID(), text: String, date: Date) { + self.id = id + self.text = text + self.date = date + } + } +} +``` + +### Implementing the View + +Next, we can implement the actual view. Swift is fairly verbose here, so the code below might look scary if you're new to Swift. The many lines of code obfuscate the simplicity in it - we're just setting up some UI elements. Nothing here is specific to Electron. + +```swift title='src/SwiftCode.swift' +// Other code... + +@objc +public class SwiftCode: NSObject { + // Other code... + + private struct ContentView: View { + @State private var todos: [TodoItem] = [] + @State private var newTodo: String = "" + @State private var newTodoDate: Date = Date() + @State private var editingTodo: UUID? + @State private var editedText: String = "" + @State private var editedDate: Date = Date() + + let onTodoAdded: (TodoItem) -> Void + let onTodoUpdated: (TodoItem) -> Void + let onTodoDeleted: (UUID) -> Void + + private func todoTextField(_ text: Binding, placeholder: String, maxWidth: CGFloat? = nil) -> some View { + TextField(placeholder, text: text) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .frame(maxWidth: maxWidth ?? .infinity) + } + + private func todoDatePicker(_ date: Binding) -> some View { + DatePicker("Due date", selection: date, displayedComponents: [.date]) + .datePickerStyle(CompactDatePickerStyle()) + .labelsHidden() + .frame(width: 100) + .textFieldStyle(RoundedBorderTextFieldStyle()) + } + + var body: some View { + VStack(spacing: 16) { + HStack(spacing: 12) { + todoTextField($newTodo, placeholder: "New todo") + todoDatePicker($newTodoDate) + Button(action: { + if !newTodo.isEmpty { + let todo = TodoItem(text: newTodo, date: newTodoDate) + todos.append(todo) + onTodoAdded(todo) + newTodo = "" + newTodoDate = Date() + } + }) { + Text("Add") + .frame(width: 50) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + List { + ForEach(todos) { todo in + if editingTodo == todo.id { + HStack(spacing: 12) { + todoTextField($editedText, placeholder: "Edit todo", maxWidth: 250) + todoDatePicker($editedDate) + Button(action: { + if let index = todos.firstIndex(where: { $0.id == todo.id }) { + let updatedTodo = TodoItem(id: todo.id, text: editedText, date: editedDate) + todos[index] = updatedTodo + onTodoUpdated(updatedTodo) + editingTodo = nil + } + }) { + Text("Save") + .frame(width: 60) + } + } + .padding(.vertical, 4) + } else { + HStack(spacing: 12) { + Text(todo.text) + .lineLimit(1) + .truncationMode(.tail) + Spacer() + Text(todo.date.formatted(date: .abbreviated, time: .shortened)) + .foregroundColor(.gray) + Button(action: { + editingTodo = todo.id + editedText = todo.text + editedDate = todo.date + }) { + Image(systemName: "pencil") + } + .buttonStyle(BorderlessButtonStyle()) + Button(action: { + todos.removeAll(where: { $0.id == todo.id }) + onTodoDeleted(todo.id) + }) { + Image(systemName: "trash") + .foregroundColor(.red) + } + .buttonStyle(BorderlessButtonStyle()) + } + .padding(.vertical, 4) + } + } + } + } + } + } +} +``` + +This part of the code: + +* Creates a SwiftUI view with a form to add new todos, featuring a text field for the todo description, a date picker for setting due dates, and an Add button that validates input, creates a new TodoItem, adds it to the local array, triggers the `onTodoAdded` callback to notify JavaScript, and then resets the input fields for the next entry. +* Implements a list to display todos with edit and delete capabilities +* Calls the appropriate callbacks when todos are added, updated, or deleted + +The final file should look as follows: + +```swift title='src/SwiftCode.swift' +import Foundation +import SwiftUI + +@objc +public class SwiftCode: NSObject { + private static var windowController: NSWindowController? + private static var todoAddedCallback: ((String) -> Void)? + private static var todoUpdatedCallback: ((String) -> Void)? + private static var todoDeletedCallback: ((String) -> Void)? + + @objc + public static func helloWorld(_ input: String) -> String { + return "Hello from Swift! You said: \(input)" + } + + @objc + public static func setTodoAddedCallback(_ callback: @escaping (String) -> Void) { + todoAddedCallback = callback + } + + @objc + public static func setTodoUpdatedCallback(_ callback: @escaping (String) -> Void) { + todoUpdatedCallback = callback + } + + @objc + public static func setTodoDeletedCallback(_ callback: @escaping (String) -> Void) { + todoDeletedCallback = callback + } + + private static func encodeToJson(_ item: T) -> String? { + let encoder = JSONEncoder() + + // Encode date as milliseconds since 1970, which is what the JS side expects + encoder.dateEncodingStrategy = .custom { date, encoder in + let milliseconds = Int64(date.timeIntervalSince1970 * 1000) + var container = encoder.singleValueContainer() + try container.encode(milliseconds) + } + + guard let jsonData = try? encoder.encode(item), + let jsonString = String(data: jsonData, encoding: .utf8) else { + return nil + } + return jsonString + } + + @objc + public static func helloGui() -> Void { + let contentView = NSHostingView(rootView: ContentView( + onTodoAdded: { todo in + if let jsonString = encodeToJson(todo) { + todoAddedCallback?(jsonString) + } + }, + onTodoUpdated: { todo in + if let jsonString = encodeToJson(todo) { + todoUpdatedCallback?(jsonString) + } + }, + onTodoDeleted: { todoId in + todoDeletedCallback?(todoId.uuidString) + } + )) + let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), + styleMask: [.titled, .closable, .miniaturizable, .resizable], + backing: .buffered, + defer: false + ) + + window.title = "Todo List" + window.contentView = contentView + window.center() + + windowController = NSWindowController(window: window) + windowController?.showWindow(nil) + + NSApp.activate(ignoringOtherApps: true) + } + + private struct TodoItem: Identifiable, Codable { + let id: UUID + var text: String + var date: Date + + init(id: UUID = UUID(), text: String, date: Date) { + self.id = id + self.text = text + self.date = date + } + } + + private struct ContentView: View { + @State private var todos: [TodoItem] = [] + @State private var newTodo: String = "" + @State private var newTodoDate: Date = Date() + @State private var editingTodo: UUID? + @State private var editedText: String = "" + @State private var editedDate: Date = Date() + + let onTodoAdded: (TodoItem) -> Void + let onTodoUpdated: (TodoItem) -> Void + let onTodoDeleted: (UUID) -> Void + + private func todoTextField(_ text: Binding, placeholder: String, maxWidth: CGFloat? = nil) -> some View { + TextField(placeholder, text: text) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .frame(maxWidth: maxWidth ?? .infinity) + } + + private func todoDatePicker(_ date: Binding) -> some View { + DatePicker("Due date", selection: date, displayedComponents: [.date]) + .datePickerStyle(CompactDatePickerStyle()) + .labelsHidden() + .frame(width: 100) + .textFieldStyle(RoundedBorderTextFieldStyle()) + } + + var body: some View { + VStack(spacing: 16) { + HStack(spacing: 12) { + todoTextField($newTodo, placeholder: "New todo") + todoDatePicker($newTodoDate) + Button(action: { + if !newTodo.isEmpty { + let todo = TodoItem(text: newTodo, date: newTodoDate) + todos.append(todo) + onTodoAdded(todo) + newTodo = "" + newTodoDate = Date() + } + }) { + Text("Add") + .frame(width: 50) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + List { + ForEach(todos) { todo in + if editingTodo == todo.id { + HStack(spacing: 12) { + todoTextField($editedText, placeholder: "Edit todo", maxWidth: 250) + todoDatePicker($editedDate) + Button(action: { + if let index = todos.firstIndex(where: { $0.id == todo.id }) { + let updatedTodo = TodoItem(id: todo.id, text: editedText, date: editedDate) + todos[index] = updatedTodo + onTodoUpdated(updatedTodo) + editingTodo = nil + } + }) { + Text("Save") + .frame(width: 60) + } + } + .padding(.vertical, 4) + } else { + HStack(spacing: 12) { + Text(todo.text) + .lineLimit(1) + .truncationMode(.tail) + Spacer() + Text(todo.date.formatted(date: .abbreviated, time: .shortened)) + .foregroundColor(.gray) + Button(action: { + editingTodo = todo.id + editedText = todo.text + editedDate = todo.date + }) { + Image(systemName: "pencil") + } + .buttonStyle(BorderlessButtonStyle()) + Button(action: { + todos.removeAll(where: { $0.id == todo.id }) + onTodoDeleted(todo.id) + }) { + Image(systemName: "trash") + .foregroundColor(.red) + } + .buttonStyle(BorderlessButtonStyle()) + } + .padding(.vertical, 4) + } + } + } + } + } + } +} +``` + +## 6) Creating the Node.js Addon Bridge + +We now have working Objective-C code, which in turn is able to call working Swift code. To make sure it can be safely and properly called from the JavaScript world, we need to build a bridge between Objective-C and C++, which we can do with Objective-C++. We'll do that in `src/swift_addon.mm`. + +```objc title='src/swift_addon.mm' +#import +#import "SwiftBridge.h" +#include + +class SwiftAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "SwiftAddon", { + InstanceMethod("helloWorld", &SwiftAddon::HelloWorld), + InstanceMethod("helloGui", &SwiftAddon::HelloGui), + InstanceMethod("on", &SwiftAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("SwiftAddon", func); + return exports; + } + + // More code to follow... +``` + +This first part: + +1. Defines a C++ class that inherits from `Napi::ObjectWrap` +2. Creates a static `Init` method to register our class with Node.js +3. Defines three methods: `helloWorld`, `helloGui`, and `on` + +### Callback Mechanism + +Next, let's implement the callback mechanism: + +```objc title='src/swift_addon.mm' +// Previous code... + + struct CallbackData { + std::string eventType; + std::string payload; + SwiftAddon* addon; + }; + + SwiftAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "SwiftCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } +``` + +This part: + +1. Defines a struct to pass data between threads +2. Sets up a constructor for our addon +3. Creates a threadsafe function to handle callbacks from Swift + +Let's continue with setting up the Swift callbacks: + +```objc title='src/swift_addon.mm' +// Previous code... + + auto makeCallback = [this](const char* eventType) { + return ^(NSString* payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + std::string([payload UTF8String]), + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + [SwiftBridge setTodoAddedCallback:makeCallback("todoAdded")]; + [SwiftBridge setTodoUpdatedCallback:makeCallback("todoUpdated")]; + [SwiftBridge setTodoDeletedCallback:makeCallback("todoDeleted")]; + } + + ~SwiftAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } +``` + +This part: + +1. Creates a helper function to generate Objective-C blocks that will be used as callbacks for Swift events. This lambda function `makeCallback` takes an event type string and returns an Objective-C block that captures the event type and payload. When Swift calls this block, it creates a CallbackData structure with the event information and passes it to the threadsafe function, which safely bridges between Swift's thread and Node.js's event loop. +2. Sets up the carefully constructed callbacks for todo operations +3. Implements a destructor to clean up resources + +### Instance Methods + +Finally, let's implement the instance methods: + +```objc title='src/swift_addon.mm' +// Previous code... + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + NSString* nsInput = [NSString stringWithUTF8String:input.c_str()]; + NSString* result = [SwiftBridge helloWorld:nsInput]; + + return Napi::String::New(env, [result UTF8String]); + } + + void HelloGui(const Napi::CallbackInfo& info) { + [SwiftBridge helloGui]; + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return SwiftAddon::Init(env, exports); +} + +NODE_API_MODULE(swift_addon, Init) +``` + +This final part does multiple things: + +1. The code defines private member variables for the environment, event emitter, callback storage, and thread-safe function that are essential for the addon's operation. +2. The HelloWorld method implementation takes a string input from JavaScript, passes it to the Swift code, and returns the processed result back to the JavaScript environment. +3. The `HelloGui` method implementation provides a simple wrapper that calls the Swift UI creation function to display the native macOS window. +4. The `On` method implementation allows JavaScript code to register callback functions that will be invoked when specific events occur in the native Swift code. +5. The code sets up the module initialization process that registers the addon with Node.js and makes its functionality available to JavaScript. + +The final and full `src/swift_addon.mm` should look like: + +```objc title='src/swift_addon.mm' +#import +#import "SwiftBridge.h" +#include + +class SwiftAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "SwiftAddon", { + InstanceMethod("helloWorld", &SwiftAddon::HelloWorld), + InstanceMethod("helloGui", &SwiftAddon::HelloGui), + InstanceMethod("on", &SwiftAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("SwiftAddon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + SwiftAddon* addon; + }; + + SwiftAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "SwiftCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + auto makeCallback = [this](const char* eventType) { + return ^(NSString* payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + std::string([payload UTF8String]), + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + [SwiftBridge setTodoAddedCallback:makeCallback("todoAdded")]; + [SwiftBridge setTodoUpdatedCallback:makeCallback("todoUpdated")]; + [SwiftBridge setTodoDeletedCallback:makeCallback("todoDeleted")]; + } + + ~SwiftAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + NSString* nsInput = [NSString stringWithUTF8String:input.c_str()]; + NSString* result = [SwiftBridge helloWorld:nsInput]; + + return Napi::String::New(env, [result UTF8String]); + } + + void HelloGui(const Napi::CallbackInfo& info) { + [SwiftBridge helloGui]; + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return SwiftAddon::Init(env, exports); +} + +NODE_API_MODULE(swift_addon, Init) +``` + +## 6) Creating a JavaScript Wrapper + +You're so close! We now have working Objective-C, Swift, and thread-safe ways to expose methods and events to JavaScript. In this final step, let's create a JavaScript wrapper in `js/index.js` to provide a more friendly API: + +```js title='js/index.js' @ts-expect-error=[10] +const EventEmitter = require('events') + +class SwiftAddon extends EventEmitter { + constructor () { + super() + + if (process.platform !== 'darwin') { + throw new Error('This module is only available on macOS') + } + + const native = require('bindings')('swift_addon') + this.addon = new native.SwiftAddon() + + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.parse(payload)) + }) + + this.addon.on('todoUpdated', (payload) => { + this.emit('todoUpdated', this.parse(payload)) + }) + + this.addon.on('todoDeleted', (payload) => { + this.emit('todoDeleted', this.parse(payload)) + }) + } + + helloWorld (input = '') { + return this.addon.helloWorld(input) + } + + helloGui () { + this.addon.helloGui() + } + + parse (payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'darwin') { + module.exports = new SwiftAddon() +} else { + module.exports = {} +} +``` + +This wrapper: + +1. Extends EventEmitter to provide event support +2. Checks if we're running on macOS +3. Loads the native addon +4. Sets up event listeners and forwards them +5. Provides a clean API for our functions +6. Parses JSON payloads and converts timestamps to JavaScript Date objects + +## 7) Building and Testing the Addon + +With all files in place, you can build the addon: + +```sh +npm run build +``` + +Please note that you _cannot_ call this script from Node.js directly, since Node.js doesn't set up an "app" in the eyes of macOS. Electron does though, so you can test your code by requiring and calling it from Electron. + +## Conclusion + +You've now built a complete native Node.js addon for macOS using Swift and SwiftUI. This provides a foundation for building more complex macOS-specific features in your Electron apps, giving you the best of both worlds: the ease of web technologies with the power of native macOS code. + +The approach demonstrated here allows you to: + +* Setting up a project structure that bridges Swift, Objective-C, and JavaScript +* Implementing Swift code with SwiftUI for native UI +* Creating an Objective-C bridge to connect Swift with Node.js +* Setting up bidirectional communication using callbacks and events +* Configuring a custom build process to compile Swift code + +For more information on developing with Swift and Swift, refer to Apple's developer documentation: + +* [Swift Programming Language](https://developer.apple.com/swift/) +* [SwiftUI Framework](https://developer.apple.com/documentation/swiftui) +* [macOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/macos) +* [Swift and Objective-C Interoperability Guide](https://developer.apple.com/documentation/swift/importing-swift-into-objective-c) diff --git a/docs/tutorial/native-code-and-electron.md b/docs/tutorial/native-code-and-electron.md new file mode 100644 index 0000000000000..70ca945db4e77 --- /dev/null +++ b/docs/tutorial/native-code-and-electron.md @@ -0,0 +1,380 @@ +# Native Code and Electron + +One of Electron's most powerful features is the ability to combine web technologies with native code - both for compute-intensive logic as well as for the occasional native user interface, where desired. + +Electron does so by building on top of "Native Node.js Addons". You've probably already come across a few of them - packages like the famous [sqlite](https://www.npmjs.com/package/sqlite3) use native code to combine JavaScript and native technologies. You can use this feature to extend your Electron application with anything a fully native application can do: + +* Access native platform APIs not available in JavaScript. Any macOS, Windows, or Linux operating system API is available to you. +* Create UI components that interact with native desktop frameworks. +* Integrate with existing native libraries. +* Implement performance-critical code that runs faster than JavaScript. + +Native Node.js addons are dynamically-linked shared objects (on Unix-like systems) or DLL files (on Windows) that can be loaded into Node.js or Electron using the `require()` or `import` functions. They behave just like regular JavaScript modules but provide an interface to code written in C++, Rust, or other languages that can compile to native code. + +# Tutorial: Creating a Native Node.js Addon for Electron + +This tutorial will walk you through building a basic Node.js native addon that can be used in Electron applications. We'll focus on concepts common to all platforms, using C++ as the implementation language. Once you complete this tutorial common to all native Node.js addons, you can move on to one of our platform-specific tutorials. + +## Requirements + +This tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling code on your platform (like Visual Studio on Windows, Xcode on macOS, or GCC/Clang on Linux). You can find detailed instructions in the [`node-gyp` readme](https://github.com/nodejs/node-gyp?tab=readme-ov-file). + +### Requirements: macOS + +To build native Node.js addons on macOS, you'll need the Xcode Command Line Tools. These provide the necessary compilers and build tools (namely, `clang`, `clang++`, and `make`). The following command will prompt you to install the Command Line Tools if they aren't already installed. + +```sh +xcode-select --install +``` + +### Requirements: Windows + +The official Node.js installer offers the optional installation of "Tools for Native Modules", which installs everything required for the basic compilation of C++ modules - specifically, Python 3 and the "Visual Studio Desktop development with C++" workload. Alternatively, you can use `chocolatey`, `winget`, or the Windows Store. + +### Requirements: Linux + +* [A supported version of Python](https://devguide.python.org/versions/) +* `make` +* A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org) + +## 1) Creating a package + +First, create a new Node.js package that will contain your native addon: + +```sh +mkdir my-native-addon +cd my-native-addon +npm init -y +``` + +This creates a basic `package.json` file. Next, we'll install the necessary dependencies: + +```sh +npm install node-addon-api bindings +``` + +* `node-addon-api`: This is a C++ wrapper for the low-level Node.js API that makes it easier to build addons. It provides a C++ object-oriented API that's more convenient and safer to use than the raw C-style API. +* `bindings`: A helper module that simplifies the process of loading your compiled native addon. It handles finding your compiled `.node` file automatically. + +Now, let's update our `package.json` to include the appropriate build scripts. We will explain what these specifically do further below. + +```json title='package.json' +{ + "name": "my-native-addon", + "version": "1.0.0", + "description": "A native addon for Electron", + "main": "js/index.js", + "scripts": { + "clean": "node -e \"require('fs').rmSync('build', { recursive: true, force: true })\"", + "build": "node-gyp configure && node-gyp build" + }, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + }, + "devDependencies": { + "node-gyp": "^11.1.0" + } +} +``` + +These scripts will: + +* `clean`: Remove the build directory, allowing for a fresh build +* `build`: Run the standard node-gyp build process to compile your addon + +## 2) Setting up the build system + +Node.js addons use a build system called `node-gyp`, which is a cross-platform command-line tool written in Node.js. It compiles native addon modules for Node.js using platform-specific build tools behind the scenes: + +* On Windows: Visual Studio +* On macOS: Xcode or command-line tools +* On Linux: GCC or similar compilers + +### Configuring `node-gyp` + +The `binding.gyp` file is a JSON-like configuration file that tells node-gyp how to build your native addon. It's similar to a make file or a project file but in a platform-independent format. Let's create a basic `binding.gyp` file: + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "my_addon", + "sources": [ + "src/my_addon.cc", + "src/cpp_code.cc" + ], + "include_dirs": [ + " + +namespace cpp_code { + // A simple function that takes a string input and returns a string + std::string hello_world(const std::string& input); +} // namespace cpp_code +``` + +The `#pragma once` directive is a header guard that prevents the file from being included multiple times in the same compilation unit. The actual function declaration is inside a namespace to avoid potential name conflicts. + +Next, let's implement the function in `src/cpp_code.cc`: + +```cpp title='src/cpp_code.cc' +#include +#include "../include/cpp_code.h" + +namespace cpp_code { + std::string hello_world(const std::string& input) { + // Simply concatenate strings and return + return "Hello from C++! You said: " + input; + } +} // namespace cpp_code +``` + +This is a simple implementation that just adds some text to the input string and returns it. + +Now, let's create the addon code that bridges our C++ code with the Node.js/JavaScript world. Create `src/my_addon.cc`: + +```cpp title='src/my_addon.cc' +#include +#include +#include "../include/cpp_code.h" + +// Create a class that will be exposed to JavaScript +class MyAddon : public Napi::ObjectWrap { +public: + // This static method defines the class for JavaScript + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + // Define the JavaScript class with method(s) + Napi::Function func = DefineClass(env, "MyAddon", { + InstanceMethod("helloWorld", &MyAddon::HelloWorld) + }); + + // Create a persistent reference to the constructor + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + // Set the constructor on the exports object + exports.Set("MyAddon", func); + return exports; + } + + // Constructor + MyAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) {} + +private: + // Method that will be exposed to JavaScript + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + // Validate arguments (expecting one string) + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + // Convert JavaScript string to C++ string + std::string input = info[0].As(); + + // Call our C++ function + std::string result = cpp_code::hello_world(input); + + // Convert C++ string back to JavaScript string and return + return Napi::String::New(env, result); + } +}; + +// Initialize the addon +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return MyAddon::Init(env, exports); +} + +// Register the initialization function +NODE_API_MODULE(my_addon, Init) +``` + +Let's break down this code: + +1. We define a `MyAddon` class that inherits from `Napi::ObjectWrap`, which handles wrapping our C++ class for JavaScript. +2. The `Init` static method: + 2.1 Defines a JavaScript class with a method called `helloWorld` + 2.2 Creates a persistent reference to the constructor (to prevent garbage collection) + 2.3 Exports the class constructor +3. The constructor simply passes its arguments to the parent class. +4. The `HelloWorld` method: + 4.1 Gets the Napi environment + 4.2 Validates input arguments (expecting a string) + 4.3 Converts the JavaScript string to a C++ string + 4.4 Calls our C++ function + 4.5 Converts the result back to a JavaScript string and returns it +5. We define an initialization function and register it with NODE_API_MODULE macro, which makes our module loadable by Node.js. + +Now, let's create a JavaScript wrapper to make the addon easier to use. Create `js/index.js`: + +```js title='js/index.js' @ts-expect-error=[5] +const EventEmitter = require('events') + +// Load the native addon using the 'bindings' module +// This will look for the compiled .node file in various places +const bindings = require('bindings') +const native = bindings('my_addon') + +// Create a nice JavaScript wrapper +class MyNativeAddon extends EventEmitter { + constructor () { + super() + + // Create an instance of our C++ class + this.addon = new native.MyAddon() + } + + // Wrap the C++ method with a nicer JavaScript API + helloWorld (input = '') { + if (typeof input !== 'string') { + throw new TypeError('Input must be a string') + } + return this.addon.helloWorld(input) + } +} + +// Export a singleton instance +if (process.platform === 'win32' || process.platform === 'darwin' || process.platform === 'linux') { + module.exports = new MyNativeAddon() +} else { + // Provide a fallback for unsupported platforms + console.warn('Native addon not supported on this platform') + + module.exports = { + helloWorld: (input) => `Hello from JS! You said: ${input}` + } +} +``` + +This JavaScript wrapper: + +1. Uses `bindings` to load our compiled native addon +1. Creates a class that extends EventEmitter (useful for future extensions that might emit events) +1. Instantiates our C++ class and provides a simpler API +1. Adds some input validation on the JavaScript side +1. Exports a singleton instance of our wrapper +1. Handles unsupported platforms gracefully + +### Building and testing the addon + +Now we can build our native addon: + +```sh +npm run build +``` + +This will run `node-gyp configure` and `node-gyp build` to compile our C++ code into a `.node` file. +Let's create a simple test script to verify everything works. Create `test.js` in the project root: + +```js title='test.js' @ts-expect-error=[2] +// Load our addon +const myAddon = require('./js') + +// Try the helloWorld function +const result = myAddon.helloWorld('This is a test') + +// Should print: "Hello from C++! You said: This is a test" +console.log(result) +``` + +Run the test: + +```sh +node test.js +``` + +If everything works correctly, you should see: + +```txt +Hello from C++! You said: This is a test +``` + +### Using the addon in Electron + +To use this addon in an Electron application, you would: + +1. Include it as a dependency in your Electron project +1. Build it targeting your specific Electron version. `electron-forge` handles this step automatically for you - for more details, see [Native Node Modules](./using-native-node-modules.md). +1. Import and use it just like any other module in a process that has Node.js enabled. + +```js @ts-expect-error=[2] +// In your main process +const myAddon = require('my-native-addon') +console.log(myAddon.helloWorld('Electron')) +``` + +## References and further learning + +Native addon development can be written in several languages beyond C++. Rust can be used with crates like [`napi-rs`](https://github.com/napi-rs/napi-rs), [`neon`](https://neon-rs.dev/), or [`node-bindgen`](https://github.com/infinyon/node-bindgen). Objective-C/Swift can be used through Objective-C++ on macOS. + +The specific implementation details differ significantly by platform, especially when accessing platform-specific APIs or UI frameworks, like Windows' Win32 API, COM components, UWP/WinRT - or macOS's Cocoa, AppKit, or ObjectiveC runtime. + +This means that you'll likely use two groups of references for your native code: First, on the Node.js side, use the [N-API documentation](https://nodejs.org/api/n-api.html) to learn about creating and exposing complex structures to JavaScript - like asynchronous thread-safe function calls or creating JavaScript-native objects (`error`, `promise`, etc). Secondly, on the side of the technology you're working with, you'll likely be looking at their lower-level documentation: + +* [Microsoft C++, C, and Assembler documentation](https://learn.microsoft.com/en-us/cpp/?view=msvc-170) +* [C++/WinRT](https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/) +* [MSVC-170 C++ Documentation](https://learn.microsoft.com/en-us/cpp/cpp/?view=msvc-170) +* [Apple Developer Documentation](https://developer.apple.com/documentation) +* [Programming with Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210) diff --git a/docs/tutorial/online-offline-events.md b/docs/tutorial/online-offline-events.md index 8cb40ebe8389d..eb0f6dac25714 100644 --- a/docs/tutorial/online-offline-events.md +++ b/docs/tutorial/online-offline-events.md @@ -83,4 +83,5 @@ After launching the Electron application, you should see the notification: ![Connection status](../images/connection-status.png) -> Note: If you need to communicate the connection status to the main process, use the [IPC renderer](../api/ipc-renderer.md) API. +> [!NOTE] +> If you need to communicate the connection status to the main process, use the [IPC renderer](../api/ipc-renderer.md) API. diff --git a/docs/tutorial/process-model.md b/docs/tutorial/process-model.md index f9a6348e88971..e8dd209ccec32 100644 --- a/docs/tutorial/process-model.md +++ b/docs/tutorial/process-model.md @@ -64,7 +64,8 @@ const contents = win.webContents console.log(contents) ``` -> Note: A renderer process is also created for [web embeds][web-embed] such as the +> [!NOTE] +> A renderer process is also created for [web embeds][web-embed] such as the > `BrowserView` module. The `webContents` object is also accessible for embedded > web content. diff --git a/docs/tutorial/recent-documents.md b/docs/tutorial/recent-documents.md index 8bbe27aad952c..072d8a12e170b 100644 --- a/docs/tutorial/recent-documents.md +++ b/docs/tutorial/recent-documents.md @@ -77,6 +77,11 @@ To clear the list of recent documents, use the In this guide, the list of documents is cleared once all windows have been closed. +#### Accessing the list of recent documents + +To access the list of recent documents, use the +[app.getRecentDocuments][getrecentdocuments] API. + ## Additional information ### Windows Notes @@ -138,5 +143,6 @@ of `app` module will be emitted for it. [dock-menu-image]: https://cloud.githubusercontent.com/assets/639601/5069610/2aa80758-6e97-11e4-8cfb-c1a414a10774.png [addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-macos-windows [clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-macos-windows +[getrecentdocuments]: ../api/app.md#appgetrecentdocuments-macos-windows [app-registration]: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration [menu-item-image]: https://user-images.githubusercontent.com/3168941/33003655-ea601c3a-cd70-11e7-97fa-7c062149cfb1.png diff --git a/docs/tutorial/repl.md b/docs/tutorial/repl.md index 82d9c035e1b3d..fda0c53f077dc 100644 --- a/docs/tutorial/repl.md +++ b/docs/tutorial/repl.md @@ -14,8 +14,9 @@ dependency, you should be able to access the REPL with the following command: ./node_modules/.bin/electron --interactive ``` -**Note:** `electron --interactive` is not available on Windows -(see [electron/electron#5776](https://github.com/electron/electron/pull/5776) for more details). +> [!NOTE] +> `electron --interactive` is not available on Windows +> (see [electron/electron#5776](https://github.com/electron/electron/pull/5776) for more details). ## Renderer process diff --git a/docs/tutorial/snapcraft.md b/docs/tutorial/snapcraft.md index dd4e84495077f..9e6094c28ca9b 100644 --- a/docs/tutorial/snapcraft.md +++ b/docs/tutorial/snapcraft.md @@ -5,8 +5,8 @@ for any Snapcraft environment, including the Ubuntu Software Center. ## Background and Requirements -Together with the broader Linux community, Canonical aims to fix many of the -common software installation problems with the [`snapcraft`](https://snapcraft.io/) +Together with the broader Linux community, Canonical aims to address common +software installation issues through the [`snapcraft`](https://snapcraft.io/) project. Snaps are containerized software packages that include required dependencies, auto-update, and work on all major Linux distributions without system modification. @@ -83,7 +83,14 @@ snap(options) ### Step 1: Create Sample Snapcraft Project -Create your project directory and add the following to `snap/snapcraft.yaml`: +```sh +$ npx create-electron-app@latest my-app +``` + +### Step 2: Create Sample Snapcraft Project + +Create a `snap` directory in your project root and add the following to +`snap/snapcraft.yaml`: ```yaml name: electron-packager-hello-world @@ -97,7 +104,7 @@ grade: stable apps: electron-packager-hello-world: - command: electron-quick-start/electron-quick-start --no-sandbox + command: my-app/my-app --no-sandbox extensions: [gnome] plugs: - browser-support @@ -109,13 +116,13 @@ apps: TMPDIR: $XDG_RUNTIME_DIR parts: - electron-quick-start: + my-app: plugin: nil - source: https://github.com/electron/electron-quick-start.git + source: . override-build: | npm install electron @electron/packager npx electron-packager . --overwrite --platform=linux --output=release-build --prune=true - cp -rv ./electron-quick-start-linux-* $SNAPCRAFT_PART_INSTALL/electron-quick-start + cp -rv ./my-app-linux-* $SNAPCRAFT_PART_INSTALL/my-app build-snaps: - node/14/stable build-packages: @@ -125,12 +132,10 @@ parts: - libnspr4 ``` -If you want to apply this example to an existing project: - -- Replace `source: https://github.com/electron/electron-quick-start.git` with `source: .`. -- Replace all instances of `electron-quick-start` with your project's name. +If you want to apply this example to an existing project, replace all instances +of `my-app` with your project's name. -### Step 2: Build the snap +### Step 3: Build the snap ```sh $ snapcraft @@ -139,13 +144,13 @@ $ snapcraft Snapped electron-packager-hello-world_0.1_amd64.snap ``` -### Step 3: Install the snap +### Step 4: Install the snap ```sh sudo snap install electron-packager-hello-world_0.1_amd64.snap --dangerous ``` -### Step 4: Run the snap +### Step 5: Run the snap ```sh electron-packager-hello-world diff --git a/docs/tutorial/tutorial-1-prerequisites.md b/docs/tutorial/tutorial-1-prerequisites.md index c0990dfbe2c05..e47938bdf81e4 100644 --- a/docs/tutorial/tutorial-1-prerequisites.md +++ b/docs/tutorial/tutorial-1-prerequisites.md @@ -1,6 +1,6 @@ --- title: 'Prerequisites' -description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.' +description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.' slug: tutorial-prerequisites hide_title: false --- diff --git a/docs/tutorial/tutorial-2-first-app.md b/docs/tutorial/tutorial-2-first-app.md index 7364d7faf84ac..465c98c0bf26e 100644 --- a/docs/tutorial/tutorial-2-first-app.md +++ b/docs/tutorial/tutorial-2-first-app.md @@ -1,6 +1,6 @@ --- title: 'Building your First App' -description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.' +description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.' slug: tutorial-first-app hide_title: false --- diff --git a/docs/tutorial/tutorial-3-preload.md b/docs/tutorial/tutorial-3-preload.md index 0854de8305092..377293d3fe06c 100644 --- a/docs/tutorial/tutorial-3-preload.md +++ b/docs/tutorial/tutorial-3-preload.md @@ -1,6 +1,6 @@ --- title: 'Using Preload Scripts' -description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.' +description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.' slug: tutorial-preload hide_title: false --- diff --git a/docs/tutorial/web-embeds.md b/docs/tutorial/web-embeds.md index 462b029c03a55..0f9d5b735b8df 100644 --- a/docs/tutorial/web-embeds.md +++ b/docs/tutorial/web-embeds.md @@ -19,12 +19,12 @@ and only allow the capabilities you want to support. ### WebViews -> Important Note: -[we do not recommend you to use WebViews](../api/webview-tag.md#warning), -as this tag undergoes dramatic architectural changes that may affect stability -of your application. Consider switching to alternatives, like `iframe` and -Electron's [`WebContentsView`](../api/web-contents-view.md), or an architecture -that avoids embedded content by design. +> [!IMPORTANT] +> [We do not recommend you to use WebViews](../api/webview-tag.md#warning), +> as this tag undergoes dramatic architectural changes that may affect stability +> of your application. Consider switching to alternatives, like `iframe` and +> Electron's [`WebContentsView`](../api/web-contents-view.md), or an architecture +> that avoids embedded content by design. [WebViews](../api/webview-tag.md) are based on Chromium's WebViews and are not explicitly supported by Electron. We do not guarantee that the WebView API will diff --git a/electron_unsafe_buffers_paths.txt b/electron_unsafe_buffers_paths.txt index 14389345918d6..62ed395531a0b 100644 --- a/electron_unsafe_buffers_paths.txt +++ b/electron_unsafe_buffers_paths.txt @@ -21,6 +21,7 @@ -base/ -chrome/ -components/ +-content/browser/indexed_db -device/ -extensions/ -google_apis/ diff --git a/filenames.auto.gni b/filenames.auto.gni index a2514d5e99676..ecdc659fd06a4 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -14,6 +14,7 @@ auto_filenames = { "docs/api/content-tracing.md", "docs/api/context-bridge.md", "docs/api/cookies.md", + "docs/api/corner-smoothing-css.md", "docs/api/crash-reporter.md", "docs/api/debugger.md", "docs/api/desktop-capturer.md", @@ -24,6 +25,7 @@ auto_filenames = { "docs/api/extensions-api.md", "docs/api/extensions.md", "docs/api/global-shortcut.md", + "docs/api/image-view.md", "docs/api/in-app-purchase.md", "docs/api/incoming-message.md", "docs/api/ipc-main-service-worker.md", diff --git a/filenames.gni b/filenames.gni index 7bd470eec7420..499e8dc95ce93 100644 --- a/filenames.gni +++ b/filenames.gni @@ -35,6 +35,7 @@ filenames = { "shell/browser/relauncher_linux.cc", "shell/browser/ui/electron_desktop_window_tree_host_linux.cc", "shell/browser/ui/file_dialog_linux.cc", + "shell/browser/ui/file_dialog_linux_portal.cc", "shell/browser/ui/gtk/menu_gtk.cc", "shell/browser/ui/gtk/menu_gtk.h", "shell/browser/ui/gtk/menu_util.cc", @@ -125,6 +126,7 @@ filenames = { "shell/browser/animation_util.h", "shell/browser/animation_util_mac.mm", "shell/browser/api/electron_api_app_mac.mm", + "shell/browser/api/electron_api_base_window_mac.mm", "shell/browser/api/electron_api_menu_mac.h", "shell/browser/api/electron_api_menu_mac.mm", "shell/browser/api/electron_api_native_theme_mac.mm", @@ -453,8 +455,6 @@ filenames = { "shell/browser/net/system_network_context_manager.h", "shell/browser/net/url_loader_network_observer.cc", "shell/browser/net/url_loader_network_observer.h", - "shell/browser/net/url_pipe_loader.cc", - "shell/browser/net/url_pipe_loader.h", "shell/browser/net/web_request_api_interface.h", "shell/browser/network_hints_handler_impl.cc", "shell/browser/network_hints_handler_impl.h", @@ -479,6 +479,7 @@ filenames = { "shell/browser/osr/osr_web_contents_view.h", "shell/browser/plugins/plugin_utils.cc", "shell/browser/plugins/plugin_utils.h", + "shell/browser/preload_script.cc", "shell/browser/preload_script.h", "shell/browser/protocol_registry.cc", "shell/browser/protocol_registry.h", @@ -720,6 +721,8 @@ filenames = { "shell/renderer/electron_renderer_client.h", "shell/renderer/electron_sandboxed_renderer_client.cc", "shell/renderer/electron_sandboxed_renderer_client.h", + "shell/renderer/electron_smooth_round_rect.cc", + "shell/renderer/electron_smooth_round_rect.h", "shell/renderer/preload_realm_context.cc", "shell/renderer/preload_realm_context.h", "shell/renderer/preload_utils.cc", diff --git a/filenames.libcxx.gni b/filenames.libcxx.gni index 914055bfb6820..4f1f700d04ce4 100644 --- a/filenames.libcxx.gni +++ b/filenames.libcxx.gni @@ -1390,6 +1390,8 @@ libcxx_headers = [ "//third_party/libc++/src/include/__flat_map/sorted_equivalent.h", "//third_party/libc++/src/include/__flat_map/sorted_unique.h", "//third_party/libc++/src/include/__flat_map/utils.h", + "//third_party/libc++/src/include/__flat_set/flat_set.h", + "//third_party/libc++/src/include/__flat_set/ra_iterator.h", "//third_party/libc++/src/include/__format/buffer.h", "//third_party/libc++/src/include/__format/concepts.h", "//third_party/libc++/src/include/__format/container_adaptor.h", @@ -1460,12 +1462,14 @@ libcxx_headers = [ "//third_party/libc++/src/include/__fwd/get.h", "//third_party/libc++/src/include/__fwd/ios.h", "//third_party/libc++/src/include/__fwd/istream.h", + "//third_party/libc++/src/include/__fwd/map.h", "//third_party/libc++/src/include/__fwd/mdspan.h", "//third_party/libc++/src/include/__fwd/memory.h", "//third_party/libc++/src/include/__fwd/memory_resource.h", "//third_party/libc++/src/include/__fwd/ostream.h", "//third_party/libc++/src/include/__fwd/pair.h", "//third_party/libc++/src/include/__fwd/queue.h", + "//third_party/libc++/src/include/__fwd/set.h", "//third_party/libc++/src/include/__fwd/span.h", "//third_party/libc++/src/include/__fwd/sstream.h", "//third_party/libc++/src/include/__fwd/stack.h", @@ -1888,6 +1892,8 @@ libcxx_headers = [ "//third_party/libc++/src/include/__type_traits/negation.h", "//third_party/libc++/src/include/__type_traits/promote.h", "//third_party/libc++/src/include/__type_traits/rank.h", + "//third_party/libc++/src/include/__type_traits/reference_constructs_from_temporary.h", + "//third_party/libc++/src/include/__type_traits/reference_converts_from_temporary.h", "//third_party/libc++/src/include/__type_traits/remove_all_extents.h", "//third_party/libc++/src/include/__type_traits/remove_const.h", "//third_party/libc++/src/include/__type_traits/remove_const_ref.h", @@ -2012,6 +2018,7 @@ libcxx_headers = [ "//third_party/libc++/src/include/fenv.h", "//third_party/libc++/src/include/filesystem", "//third_party/libc++/src/include/flat_map", + "//third_party/libc++/src/include/flat_set", "//third_party/libc++/src/include/float.h", "//third_party/libc++/src/include/format", "//third_party/libc++/src/include/forward_list", diff --git a/lib/browser/api/browser-window.ts b/lib/browser/api/browser-window.ts index 46882f151474b..909b442c3d27c 100644 --- a/lib/browser/api/browser-window.ts +++ b/lib/browser/api/browser-window.ts @@ -54,7 +54,7 @@ BrowserWindow.prototype._init = function (this: BWT) { }); this.on('close', (event) => { queueMicrotask(() => { - if (!unresponsiveEvent && !event.defaultPrevented) { + if (!unresponsiveEvent && !event?.defaultPrevented) { unresponsiveEvent = setTimeout(emitUnresponsiveEvent, 5000); } }); diff --git a/lib/browser/api/menu-item-roles.ts b/lib/browser/api/menu-item-roles.ts index fe2b07ed0fee7..1bb48b636e929 100644 --- a/lib/browser/api/menu-item-roles.ts +++ b/lib/browser/api/menu-item-roles.ts @@ -78,7 +78,9 @@ export const roleList: Record = { minimize: { label: 'Minimize', accelerator: 'CommandOrControl+M', - windowMethod: w => w.minimize() + windowMethod: w => { + if (w.minimizable) w.minimize(); + } }, paste: { label: 'Paste', diff --git a/lib/browser/api/menu.ts b/lib/browser/api/menu.ts index c0244cc3c1bf2..8e0a63f764654 100644 --- a/lib/browser/api/menu.ts +++ b/lib/browser/api/menu.ts @@ -93,7 +93,7 @@ Menu.prototype.popup = function (options = {}) { } } - this.popupAt(window as unknown as BaseWindow, x, y, positioningItem, sourceType, callback); + this.popupAt(window as unknown as BaseWindow, options.frame, x, y, positioningItem, sourceType, callback); return { browserWindow: window, x, y, position: positioningItem }; }; diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts index a81dd7d5ae7e4..14879f3fdd7d5 100644 --- a/lib/browser/api/web-contents.ts +++ b/lib/browser/api/web-contents.ts @@ -263,8 +263,17 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri throw new TypeError('webContents.print(): Invalid print settings specified.'); } - const pageSize = options.pageSize ?? 'A4'; - if (typeof pageSize === 'object') { + const { pageSize } = options; + if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) { + const mediaSize = PDFPageSizes[pageSize]; + options.mediaSize = { + ...mediaSize, + imageable_area_left_microns: 0, + imageable_area_bottom_microns: 0, + imageable_area_right_microns: mediaSize.width_microns, + imageable_area_top_microns: mediaSize.height_microns + }; + } else if (typeof pageSize === 'object') { if (!pageSize.height || !pageSize.width) { throw new Error('height and width properties are required for pageSize'); } @@ -286,16 +295,7 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri imageable_area_right_microns: width, imageable_area_top_microns: height }; - } else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) { - const mediaSize = PDFPageSizes[pageSize]; - options.mediaSize = { - ...mediaSize, - imageable_area_left_microns: 0, - imageable_area_bottom_microns: 0, - imageable_area_right_microns: mediaSize.width_microns, - imageable_area_top_microns: mediaSize.height_microns - }; - } else { + } else if (pageSize !== undefined) { throw new Error(`Unsupported pageSize: ${pageSize}`); } diff --git a/lib/browser/init.ts b/lib/browser/init.ts index 3f50212555db4..ddca14dbb9fe8 100644 --- a/lib/browser/init.ts +++ b/lib/browser/init.ts @@ -218,7 +218,6 @@ if (packagePath) { } else { // Call appCodeLoaded before just for safety, it doesn't matter here as _load is synchronous appCodeLoaded!(); - process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false); Module._load(path.join(packagePath, mainStartupScript), Module, true); } } else { diff --git a/lib/browser/parse-features-string.ts b/lib/browser/parse-features-string.ts index 4d54479c04381..be37505c64a06 100644 --- a/lib/browser/parse-features-string.ts +++ b/lib/browser/parse-features-string.ts @@ -26,7 +26,12 @@ const keysOfTypeNumberCompileTimeCheck: { [K in IntegerBrowserWindowOptionKeys] }; // Note `top` / `left` are special cases from the browser which we later convert // to y / x. -const keysOfTypeNumber = new Set(['top', 'left', ...Object.keys(keysOfTypeNumberCompileTimeCheck)]); +// NOTE(@mlaurencin) `innerWidth` / `innerHeight` are also special cases. The spec +// states that `width` and `height` represent the window content size and are equivalent +// to `innerWidth` / `innerHeight`. However, our implementation currently incorrectly maps +// `width` and `height` to `outerWidth` and `outerHeight`, or the size of the window +// with all border and related window chrome. +const keysOfTypeNumber = new Set(['top', 'left', 'innerWidth', 'innerHeight', ...Object.keys(keysOfTypeNumberCompileTimeCheck)]); /** * Note that we only allow "0" and "1" boolean conversion when the type is known diff --git a/lib/common/api/net-client-request.ts b/lib/common/api/net-client-request.ts index 1a087ae476477..a2682da670d22 100644 --- a/lib/common/api/net-client-request.ts +++ b/lib/common/api/net-client-request.ts @@ -288,8 +288,12 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod origin: options.origin, referrerPolicy: options.referrerPolicy, cache: options.cache, - allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols) + allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols), + priority: options.priority }; + if ('priorityIncremental' in options) { + urlLoaderOptions.priorityIncremental = options.priorityIncremental; + } const headers: Record = options.headers || {}; for (const [name, value] of Object.entries(headers)) { validateHeader(name, value); diff --git a/lib/common/init.ts b/lib/common/init.ts index d6ad120eb931d..a8d266d7f3349 100644 --- a/lib/common/init.ts +++ b/lib/common/init.ts @@ -93,20 +93,23 @@ makeElectronModule('electron'); makeElectronModule('electron/common'); if (process.type === 'browser') { makeElectronModule('electron/main'); -} -if (process.type === 'renderer') { +} else if (process.type === 'renderer') { makeElectronModule('electron/renderer'); +} else if (process.type === 'utility') { + makeElectronModule('electron/utility'); } const originalResolveFilename = Module._resolveFilename; -// 'electron/main', 'electron/renderer' and 'electron/common' are module aliases +// 'electron/{common,main,renderer,utility}' are module aliases // of the 'electron' module for TypeScript purposes, i.e., the types for // 'electron/main' consist of only main process modules, etc. It is intentional // that these can be `require()`-ed from both the main process as well as the // renderer process regardless of the names, they're superficial for TypeScript // only. -const electronModuleNames = new Set(['electron', 'electron/main', 'electron/renderer', 'electron/common']); +const electronModuleNames = new Set([ + 'electron', 'electron/main', 'electron/renderer', 'electron/common', 'electron/utility' +]); Module._resolveFilename = function (request, parent, isMain, options) { if (electronModuleNames.has(request)) { return 'electron'; diff --git a/lib/node/asar-fs-wrapper.ts b/lib/node/asar-fs-wrapper.ts index e4650036bfdcc..2457fb0f2dd1f 100644 --- a/lib/node/asar-fs-wrapper.ts +++ b/lib/node/asar-fs-wrapper.ts @@ -54,6 +54,10 @@ const { getDirent } = __non_webpack_require__('internal/fs/utils'); +const { + assignFunctionName +} = __non_webpack_require__('internal/util'); + const { validateBoolean, validateFunction @@ -235,7 +239,10 @@ const overrideAPI = function (module: Record, name: string, pathArg }; if (old[util.promisify.custom]) { - module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex); + module[name][util.promisify.custom] = assignFunctionName( + name, + makePromiseFunction(old[util.promisify.custom], pathArgumentIndex) + ); } if (module.promises && module.promises[name]) { @@ -667,10 +674,10 @@ export const wrapFsWithAsar = (fs: Record) => { return p(pathArgument, options); }; - const { readFileSync } = fs; - fs.readFileSync = function (pathArgument: string, options: any) { - const pathInfo = splitPath(pathArgument); - if (!pathInfo.isAsar) return readFileSync.apply(this, arguments); + function readFileFromArchiveSync ( + pathInfo: { asarPath: string; filePath: string }, + options: any + ): ReturnType { const { asarPath, filePath } = pathInfo; const archive = getOrCreateArchive(asarPath); @@ -704,6 +711,14 @@ export const wrapFsWithAsar = (fs: Record) => { fs.readSync(fd, buffer, 0, info.size, info.offset); validateBufferIntegrity(buffer, info.integrity); return (encoding) ? buffer.toString(encoding) : buffer; + } + + const { readFileSync } = fs; + fs.readFileSync = function (pathArgument: string, options: any) { + const pathInfo = splitPath(pathArgument); + if (!pathInfo.isAsar) return readFileSync.apply(this, arguments); + + return readFileFromArchiveSync(pathInfo, options); }; type ReaddirOptions = { encoding: BufferEncoding | null; withFileTypes?: false, recursive?: false } | undefined | null; @@ -727,7 +742,7 @@ export const wrapFsWithAsar = (fs: Record) => { } const dirent = getDirent(currentPath, result[0][i], type); - const stat = internalBinding('fs').internalModuleStat(binding, resultPath); + const stat = internalBinding('fs').internalModuleStat(resultPath); context.readdirResults.push(dirent); if (dirent.isDirectory() || stat === 1) { @@ -740,7 +755,7 @@ export const wrapFsWithAsar = (fs: Record) => { for (let i = 0; i < result.length; i++) { const resultPath = path.join(currentPath, result[i]); const relativeResultPath = path.relative(context.basePath, resultPath); - const stat = internalBinding('fs').internalModuleStat(binding, resultPath); + const stat = internalBinding('fs').internalModuleStat(resultPath); context.readdirResults.push(relativeResultPath); if (stat === 1) { @@ -810,7 +825,7 @@ export const wrapFsWithAsar = (fs: Record) => { if (context.withFileTypes) { readdirResult = [ [...readdirResult], readdirResult.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(pathArg, p)); + return internalBinding('fs').internalModuleStat(path.join(pathArg, p)); }) ]; } @@ -980,30 +995,24 @@ export const wrapFsWithAsar = (fs: Record) => { }; const modBinding = internalBinding('modules'); - const { readPackageJSON } = modBinding; - internalBinding('modules').readPackageJSON = ( - jsonPath: string, - isESM: boolean, - base: undefined | string, - specifier: undefined | string - ) => { + modBinding.overrideReadFileSync((jsonPath: string): Buffer | false | undefined => { const pathInfo = splitPath(jsonPath); - if (!pathInfo.isAsar) return readPackageJSON(jsonPath, isESM, base, specifier); - const { asarPath, filePath } = pathInfo; - - const archive = getOrCreateArchive(asarPath); - if (!archive) return undefined; - const realPath = archive.copyFileOut(filePath); - if (!realPath) return undefined; + // Fallback to Node.js internal implementation + if (!pathInfo.isAsar) return undefined; - return readPackageJSON(realPath, isESM, base, specifier); - }; + try { + return readFileFromArchiveSync(pathInfo, undefined); + } catch { + // Not found + return false; + } + }); const { internalModuleStat } = binding; - internalBinding('fs').internalModuleStat = (receiver: unknown, pathArgument: string) => { + internalBinding('fs').internalModuleStat = (pathArgument: string) => { const pathInfo = splitPath(pathArgument); - if (!pathInfo.isAsar) return internalModuleStat(receiver, pathArgument); + if (!pathInfo.isAsar) return internalModuleStat(pathArgument); const { asarPath, filePath } = pathInfo; // -ENOENT @@ -1038,7 +1047,7 @@ export const wrapFsWithAsar = (fs: Record) => { if (withFileTypes) { initialItem = [ [...initialItem], initialItem.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(originalPath, p)); + return internalBinding('fs').internalModuleStat(path.join(originalPath, p)); }) ]; } @@ -1071,7 +1080,7 @@ export const wrapFsWithAsar = (fs: Record) => { readdirResult = [ [...files], files.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(direntPath, p)); + return internalBinding('fs').internalModuleStat(path.join(direntPath, p)); }) ]; } else { @@ -1092,7 +1101,7 @@ export const wrapFsWithAsar = (fs: Record) => { const { 0: pathArg, 1: readDir } = queue.pop(); for (const ent of readDir) { const direntPath = path.join(pathArg, ent); - const stat = internalBinding('fs').internalModuleStat(binding, direntPath); + const stat = internalBinding('fs').internalModuleStat(direntPath); result.push(path.relative(originalPath, direntPath)); if (stat === 1) { @@ -1146,7 +1155,7 @@ export const wrapFsWithAsar = (fs: Record) => { if (context.withFileTypes) { readdirResult = [ [...readdirResult], readdirResult.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(pathArg, p)); + return internalBinding('fs').internalModuleStat(path.join(pathArg, p)); }) ]; } @@ -1236,7 +1245,7 @@ export const wrapFsWithAsar = (fs: Record) => { // command as a single path to an archive. const { exec, execSync } = childProcess; childProcess.exec = invokeWithNoAsar(exec); - childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]); + childProcess.exec[util.promisify.custom] = assignFunctionName('exec', invokeWithNoAsar(exec[util.promisify.custom])); childProcess.execSync = invokeWithNoAsar(execSync); overrideAPI(childProcess, 'execFile'); diff --git a/lib/utility/api/net.ts b/lib/utility/api/net.ts index 70228ee0e5f46..8ef93d0e282ff 100644 --- a/lib/utility/api/net.ts +++ b/lib/utility/api/net.ts @@ -1,8 +1,7 @@ import { fetchWithSession } from '@electron/internal/browser/api/net-fetch'; import { ClientRequest } from '@electron/internal/common/api/net-client-request'; -import { IncomingMessage } from 'electron/utility'; -import type { ClientRequestConstructorOptions } from 'electron/utility'; +import type { ClientRequestConstructorOptions, IncomingMessage } from 'electron/utility'; const { isOnline, resolveHost } = process._linkedBinding('electron_common_net'); diff --git a/package.json b/package.json index 12b3f777d26e6..a7fbb1b712097 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "@electron/docs-parser": "^2.0.0", "@electron/fiddle-core": "^1.3.4", "@electron/github-app-auth": "^2.2.1", - "@electron/lint-roller": "^2.4.0", - "@electron/typescript-definitions": "^9.0.0", + "@electron/lint-roller": "^3.1.1", + "@electron/typescript-definitions": "^9.1.2", "@octokit/rest": "^20.0.2", "@primer/octicons": "^10.0.0", "@types/minimist": "^1.2.5", @@ -18,7 +18,6 @@ "@types/semver": "^7.5.8", "@types/stream-json": "^1.7.7", "@types/temp": "^0.9.4", - "@types/webpack": "^5.28.5", "@types/webpack-env": "^1.18.5", "@typescript-eslint/eslint-plugin": "^8.7.0", "@typescript-eslint/parser": "^8.7.0", @@ -28,19 +27,18 @@ "dugite": "^2.7.1", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.30.0", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-mocha": "^10.5.0", "eslint-plugin-n": "^16.6.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.6.0", "eslint-plugin-standard": "^5.0.0", - "eslint-plugin-unicorn": "^55.0.0", "events": "^3.2.0", "folder-hash": "^2.1.1", "got": "^11.8.5", "husky": "^8.0.1", "lint-staged": "^10.2.11", - "markdownlint-cli2": "^0.13.0", + "markdownlint-cli2": "^0.18.0", "minimist": "^1.2.8", "null-loader": "^4.0.1", "pre-flight": "^2.0.0", diff --git a/patches/boringssl/expose_ripemd160.patch b/patches/boringssl/expose_ripemd160.patch index 024a131fab244..ad7ed38e1e075 100644 --- a/patches/boringssl/expose_ripemd160.patch +++ b/patches/boringssl/expose_ripemd160.patch @@ -82,7 +82,7 @@ index e04b80cd6a1a215fc87f8fd8d750c3d258c3974f..8fdf1c624794f568bfc77b7b6b0c510b void EVP_MD_do_all(void (*callback)(const EVP_MD *cipher, const char *name, diff --git a/include/openssl/digest.h b/include/openssl/digest.h -index 5ddc2d3b4cfb8a87eb22fb707230f56dcb7ccb3e..dea3c5b3adf49e1b4aab197e822744c80964afac 100644 +index b36c5885a31ff3242ac3104e6d875a008c7c39e4..8fe32508a00263295313e77992e2c208459ca42c 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h @@ -48,6 +48,9 @@ OPENSSL_EXPORT const EVP_MD *EVP_blake2b256(void); diff --git a/patches/boringssl/feat_expose_several_extra_cipher_functions.patch b/patches/boringssl/feat_expose_several_extra_cipher_functions.patch index 0b37e96bb0bbd..b4fdeb6d33b59 100644 --- a/patches/boringssl/feat_expose_several_extra_cipher_functions.patch +++ b/patches/boringssl/feat_expose_several_extra_cipher_functions.patch @@ -118,7 +118,7 @@ index 8fdf1c624794f568bfc77b7b6b0c510b23905a4d..2e40c031e8c681fe921331b26dbf63f4 callback(EVP_des_ede3_cbc(), "des-ede3-cbc", NULL, arg); callback(EVP_rc2_cbc(), "rc2-cbc", NULL, arg); diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h -index 6bb135801326bc1cbe2b93f02e561e38c90abeca..06bcba4451456c72b168266859482343af76a931 100644 +index 13e68ad20ac08a462bb577d7f99e2c6f167579fa..4960d0eeb8f31bec4347ed2a1b63beba530de700 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h @@ -448,6 +448,7 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_des_ede3_ecb(void); diff --git a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch index e1f1d51156825..4fab9c81ec1d6 100644 --- a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch +++ b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch @@ -20,10 +20,10 @@ index 2cdcbc346175eeee69402ecee7f169e61c655199..f7226fe711e4214b216ea2c5173a0212 case ssl_open_record_error: diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index c859ea612ce2e6be80c6351d85fae230ca5bc2dd..5f4ffedb3b19dee4eef62e2033b0000847167111 100644 +index d228b1c3b517e21fb8022c1927b0bc522f46bf78..4a94410d0ea4daabde1397c78c5e5de113398ddf 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -1196,7 +1196,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { +@@ -1205,7 +1205,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { } if (ret_code == 0) { @@ -32,7 +32,7 @@ index c859ea612ce2e6be80c6351d85fae230ca5bc2dd..5f4ffedb3b19dee4eef62e2033b00008 return SSL_ERROR_ZERO_RETURN; } // An EOF was observed which violates the protocol, and the underlying -@@ -2563,13 +2563,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { +@@ -2572,13 +2572,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { return CRYPTO_get_ex_data(&ctx->ex_data, idx); } diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 6464dce67d2ee..19a987b9e4791 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -80,10 +80,9 @@ introduce_ozoneplatform_electron_can_call_x11_property.patch make_gtk_getlibgtk_public.patch custom_protocols_plzserviceworker.patch feat_filter_out_non-shareable_windows_in_the_current_application_in.patch -disable_freezing_flags_after_init_in_node.patch short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch chore_add_electron_deps_to_gitignores.patch -chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch +chore_modify_chromium_handling_of_mouse_events.patch add_electron_deps_to_license_credits_file.patch fix_crash_loading_non-standard_schemes_in_iframes.patch create_browser_v8_snapshot_file_name_fuse.patch @@ -111,7 +110,6 @@ fix_use_delegated_generic_capturer_when_available.patch expose_webblob_path_to_allow_embedders_to_get_file_paths.patch fix_move_autopipsettingshelper_behind_branding_buildflag.patch revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch -fix_activate_background_material_on_windows.patch feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch chore_remove_check_is_test_on_script_injection_tracker.patch fix_restore_original_resize_performance_on_macos.patch @@ -131,13 +129,23 @@ osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch chore_partial_revert_of.patch fix_software_compositing_infinite_loop.patch -fix_add_method_which_disables_headless_mode_on_native_widget.patch +fix_adjust_headless_mode_handling_in_native_widget.patch refactor_unfilter_unresponsive_events.patch build_disable_thin_lto_mac.patch +feat_corner_smoothing_css_rule_and_blink_painting.patch build_add_public_config_simdutf_config.patch +fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch revert_code_health_clean_up_stale_macwebcontentsocclusion.patch -ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch +ignore_parse_errors_for_resolveshortcutproperties.patch feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch +fix_linter_error.patch +revert_enable_crel_for_arm32_targets.patch +mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch +fix_osr_stutter_fix_backport_for_electron.patch +do_not_check_the_order_of_display_id_order_on_windows.patch +make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch +cherry-pick-f1e6422a355c.patch +fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch diff --git a/patches/chromium/add_didinstallconditionalfeatures.patch b/patches/chromium/add_didinstallconditionalfeatures.patch index 6a808112999a6..3ab0381ec2c43 100644 --- a/patches/chromium/add_didinstallconditionalfeatures.patch +++ b/patches/chromium/add_didinstallconditionalfeatures.patch @@ -23,10 +23,10 @@ index 44da0544b778d6ff4c14b6f4e8463cb8260d2f0d..8ae8939af4141a684b7a6d50a43e1abb int32_t world_id) {} virtual void DidClearWindowObject() {} diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index e995c76b1ecc50502c47862607408d0663e91738..9d675b45688572d3b38d29e7d2074e0fb4b737ac 100644 +index b78c7f64fd5e21a8d89318791bf639ab367d2d48..e8850312da428bcb9ec45b7c8605be1e516b22de 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -4787,6 +4787,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, +@@ -4800,6 +4800,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, observer.DidCreateScriptContext(context, world_id); } @@ -40,10 +40,10 @@ index e995c76b1ecc50502c47862607408d0663e91738..9d675b45688572d3b38d29e7d2074e0f int world_id) { for (auto& observer : observers_) diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h -index 398676cc0642115ff825beb1a593c3d14d4a5531..cc7cd3c73d72e933807b8d6f4e9e4ac43cbea00d 100644 +index a072319de5d16d8004cb33792b2275320627fe6a..f52aa0470ca53c9997961c398d2c79d25967562e 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h -@@ -654,6 +654,8 @@ class CONTENT_EXPORT RenderFrameImpl +@@ -653,6 +653,8 @@ class CONTENT_EXPORT RenderFrameImpl void DidObserveLayoutShift(double score, bool after_input_or_scroll) override; void DidCreateScriptContext(v8::Local context, int world_id) override; @@ -53,10 +53,10 @@ index 398676cc0642115ff825beb1a593c3d14d4a5531..cc7cd3c73d72e933807b8d6f4e9e4ac4 int world_id) override; void DidChangeScrollOffset() override; diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h -index d0d0ccafdd25cbac7fa7daa8e615b8ca1018a68b..6bf6b6b8a2847d4677383b9ee4574ebe6894c287 100644 +index fade7618e12889da5a1c3cbe465dec0869634407..53aaf91fe5e68465c9ec8b181e33b5ee95a5a5aa 100644 --- a/third_party/blink/public/web/web_local_frame_client.h +++ b/third_party/blink/public/web/web_local_frame_client.h -@@ -665,6 +665,9 @@ class BLINK_EXPORT WebLocalFrameClient { +@@ -664,6 +664,9 @@ class BLINK_EXPORT WebLocalFrameClient { virtual void DidCreateScriptContext(v8::Local, int32_t world_id) {} @@ -92,10 +92,10 @@ index 8bb6b0465069529f79aaec21792e8b159535b3e9..f9782531c639d07002dda07732750a00 int32_t world_id) = 0; virtual bool AllowScriptExtensions() = 0; diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc -index cfcabe9a443adae146c816419e0845cb6fc18404..717587c2cfe0b0228ae40adce04139cb54a34d0e 100644 +index 66cc44003157657c68e90d295f877789395eca8d..19cd8460711d5d5b1b76c9e4cb82ed7fc71b410c 100644 --- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc +++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc -@@ -296,6 +296,13 @@ void LocalFrameClientImpl::DidCreateScriptContext( +@@ -295,6 +295,13 @@ void LocalFrameClientImpl::DidCreateScriptContext( web_frame_->Client()->DidCreateScriptContext(context, world_id); } @@ -123,10 +123,10 @@ index 4c7375a27a22f04694e14fecc17d633734d4cccc..ea2a32b151aaf07b5704e463d01714eb int32_t world_id) override; diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h -index 6b967bd0b430732732ef30fb404e615a55c7c364..6bba885bca8f65744bcd03efcb98725649687856 100644 +index 286d95397da7b90ba28b4c4faaae26d6ab71a496..23fd67571152897b3bc93fde8e98e1d554cce275 100644 --- a/third_party/blink/renderer/core/loader/empty_clients.h +++ b/third_party/blink/renderer/core/loader/empty_clients.h -@@ -415,6 +415,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient { +@@ -417,6 +417,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient { void DidCreateScriptContext(v8::Local, int32_t world_id) override {} diff --git a/patches/chromium/add_electron_deps_to_license_credits_file.patch b/patches/chromium/add_electron_deps_to_license_credits_file.patch index 62ae21576df52..82c66e747e0ec 100644 --- a/patches/chromium/add_electron_deps_to_license_credits_file.patch +++ b/patches/chromium/add_electron_deps_to_license_credits_file.patch @@ -7,10 +7,10 @@ Ensure that licenses for the dependencies introduced by Electron are included in `LICENSES.chromium.html` diff --git a/tools/licenses/licenses.py b/tools/licenses/licenses.py -index 1368fae3346175c0fdd652d74f882eca26dd7aa2..7f2ca2366ea188bfb0a6e846a519b9cf790ec56a 100755 +index 7b948007755d306babea73498c17031c437ae6dc..adcfad8931c314f86d26347ccda438af452b8167 100755 --- a/tools/licenses/licenses.py +++ b/tools/licenses/licenses.py -@@ -336,6 +336,31 @@ SPECIAL_CASES = { +@@ -337,6 +337,31 @@ SPECIAL_CASES = { "License": "Apache 2.0", "License File": ["//third_party/dawn/third_party/khronos/LICENSE"], }, diff --git a/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch b/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch index bfb86df138f29..8d721cdf6f968 100644 --- a/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch +++ b/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch @@ -8,10 +8,10 @@ was removed as part of the Raw Clipboard API scrubbing. https://bugs.chromium.org/p/chromium/issues/detail?id=1217643 diff --git a/ui/base/clipboard/scoped_clipboard_writer.cc b/ui/base/clipboard/scoped_clipboard_writer.cc -index fd162a607424bd4d0b324ccc7270522eb3b6a991..df1d5564a52ef67b447bbc222ceb1f864eb3dd81 100644 +index 8064a2c27d747d4862503526496a65987df7dc35..c92596713c6d3884be3f63bf78d9117b7ad3ec90 100644 --- a/ui/base/clipboard/scoped_clipboard_writer.cc +++ b/ui/base/clipboard/scoped_clipboard_writer.cc -@@ -230,6 +230,16 @@ void ScopedClipboardWriter::WriteData(std::u16string_view format, +@@ -237,6 +237,16 @@ void ScopedClipboardWriter::WriteData(std::u16string_view format, } } @@ -29,7 +29,7 @@ index fd162a607424bd4d0b324ccc7270522eb3b6a991..df1d5564a52ef67b447bbc222ceb1f86 objects_.clear(); raw_objects_.clear(); diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h -index d8fd611e88c22f2c1319da769dcd14cc218f9c90..f17304e1e9fd202dc4e1b7724031dc4c333a4baf 100644 +index 939a99b2a086d5373f82fe96da73dabe02f6f9d8..fccc200b1b11076c8fcffde071a53598ffba9a12 100644 --- a/ui/base/clipboard/scoped_clipboard_writer.h +++ b/ui/base/clipboard/scoped_clipboard_writer.h @@ -87,6 +87,10 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { diff --git a/patches/chromium/adjust_accessibility_ui_for_electron.patch b/patches/chromium/adjust_accessibility_ui_for_electron.patch index de7a05bb53129..0eab8c33d29a0 100644 --- a/patches/chromium/adjust_accessibility_ui_for_electron.patch +++ b/patches/chromium/adjust_accessibility_ui_for_electron.patch @@ -10,7 +10,7 @@ usage of BrowserList and Browser as we subclass related methods and use our WindowList. diff --git a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc -index ff25e6eb48deb339ab92152eb09ad856068fd589..4a1f81429f0857e129e31103b026079de13fd826 100644 +index 628fe5557f963c52cbe0c5b24c6e06aaa81f9677..868f5eb60c35dfaa0a4e33e7f175743848e0cc2e 100644 --- a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc +++ b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc @@ -48,6 +48,7 @@ @@ -38,7 +38,7 @@ index ff25e6eb48deb339ab92152eb09ad856068fd589..4a1f81429f0857e129e31103b026079d + PrefService* pref = static_cast(current_context)->prefs(); ui::AXMode mode = content::BrowserAccessibilityState::GetInstance()->GetAccessibilityMode(); - bool is_native_enabled = content::BrowserAccessibilityState::GetInstance() + bool is_a11y_allowed = content::BrowserAccessibilityState::GetInstance() @@ -221,7 +222,7 @@ void HandleAccessibilityRequestCallback( data.Set(kPDFPrinting, pdf_printing ? kOn : kOff); diff --git a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch index 225e8ad5768b9..827f1787f99c7 100644 --- a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch +++ b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch @@ -23,7 +23,7 @@ index 6c679ef877067297ec3bf1a23af6c03a2af8dcf5..1ac93433189580c13b69cd52ce62681a return receiver_.BindNewEndpointAndPassDedicatedRemote(); } diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc -index a387789fe4270739408a9beb32a5ad1a8e51e5f6..43c108bd6b51d293e0f8b1b5393c76c5d167ccef 100644 +index 6c1e46e58d975137eee6c0fd7b01d2ae970a2e10..5ca29616369e76e298eb03188e18ca545c76a162 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -765,6 +765,11 @@ void RenderViewHostImpl::SetBackgroundOpaque(bool opaque) { @@ -39,7 +39,7 @@ index a387789fe4270739408a9beb32a5ad1a8e51e5f6..43c108bd6b51d293e0f8b1b5393c76c5 return is_active(); } diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h -index 5fb8a3dc69dc5fc5bfa08e01d8f03707a23c9274..41774b60b8cb7e0a22cedc597dc07ad15c96988c 100644 +index 56e52f079d1ad7c7a22764b976f0c8b2cc48dff2..1231fe522ad103e94d3c30ad7d5e5d23df9b3554 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -135,6 +135,7 @@ class CONTENT_EXPORT RenderViewHostImpl @@ -51,7 +51,7 @@ index 5fb8a3dc69dc5fc5bfa08e01d8f03707a23c9274..41774b60b8cb7e0a22cedc597dc07ad1 void SendRendererPreferencesToRenderer( const blink::RendererPreferences& preferences); diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 92085aca6bd0c95a73b98e4173c0128d596b77f4..9503beefcbcfe7d99674582ece10a7e551fae96d 100644 +index d719b546b8c3c59003698b26dead065da7d76341..95a52f1cc2024e4a9cd694429d5304a5860a1c1e 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -579,8 +579,8 @@ void RenderWidgetHostViewAura::ShowImpl(PageVisibilityState page_visibility) { @@ -116,10 +116,10 @@ index b1689844282d6917b9750fbc6a875848ddf84b70..f1cc159b7c3448a33a6d9e213f8fbd3b // Visibility ----------------------------------------------------------- diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index c6a4f97c483667eaae354d8b99a01a6a3e765700..e4ab513573c7e327627b1ebd5b263f389098727a 100644 +index 1af1fa035b3da8967526d704e362ddbc5c3194ac..f470ada1bf84938427fb89f4508e5f56aaeebc1f 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -2468,6 +2468,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( +@@ -2466,6 +2466,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( TRACE_EVENT2("navigation", "WebViewImpl::SetPageLifecycleStateInternal", "old_state", old_state, "new_state", new_state); @@ -130,7 +130,7 @@ index c6a4f97c483667eaae354d8b99a01a6a3e765700..e4ab513573c7e327627b1ebd5b263f38 bool storing_in_bfcache = new_state->is_in_back_forward_cache && !old_state->is_in_back_forward_cache; bool restoring_from_bfcache = !new_state->is_in_back_forward_cache && -@@ -3994,10 +3998,23 @@ PageScheduler* WebViewImpl::Scheduler() const { +@@ -3989,10 +3993,23 @@ PageScheduler* WebViewImpl::Scheduler() const { return GetPage()->GetPageScheduler(); } diff --git a/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch b/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch index 1af964c5d7b33..0fa0efdf1160d 100644 --- a/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch +++ b/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch @@ -8,10 +8,10 @@ WebPreferences of in-process child windows, rather than relying on process-level command line switches, as before. diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc -index f17c7ae24086ab0112bd1245291f6f512a74e223..a1a23d263e4842996c0f8dde4dca8d8b35802fda 100644 +index 2612e54a2b0d3c71bd8efe9340bc58c795500140..e590387effc958ba7215e75ea1e2a8173f624d02 100644 --- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc +++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc -@@ -147,6 +147,19 @@ bool StructTraitsv8_cache_options = data.v8_cache_options(); out->record_whole_document = data.record_whole_document(); out->stylus_handwriting_enabled = data.stylus_handwriting_enabled(); @@ -32,7 +32,7 @@ index f17c7ae24086ab0112bd1245291f6f512a74e223..a1a23d263e4842996c0f8dde4dca8d8b out->accelerated_video_decode_enabled = data.accelerated_video_decode_enabled(); diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h -index 95e6b4c64bca410586378c88df288ef2bb0c6992..783c44be399a3bcf251b52c47340c26d6d962747 100644 +index 85f312a9ea1eca0ff7ba4c679fabb3156aabbc0b..a9dc007a93003610d68e3118b4a47a86d4e16f6e 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences.h +++ b/third_party/blink/public/common/web_preferences/web_preferences.h @@ -9,6 +9,7 @@ @@ -43,9 +43,9 @@ index 95e6b4c64bca410586378c88df288ef2bb0c6992..783c44be399a3bcf251b52c47340c26d #include "build/build_config.h" #include "net/nqe/effective_connection_type.h" #include "third_party/blink/public/common/common_export.h" -@@ -442,6 +443,20 @@ struct BLINK_COMMON_EXPORT WebPreferences { - // when feature DynamicSafeAreaInsets is enabled. - bool dynamic_safe_area_insets_enabled = false; +@@ -451,6 +452,20 @@ struct BLINK_COMMON_EXPORT WebPreferences { + // WebView and by `kWebPayments` feature flag everywhere. + bool payment_request_enabled = false; + // Begin Electron-specific WebPreferences. + bool context_isolation = false; @@ -65,18 +65,18 @@ index 95e6b4c64bca410586378c88df288ef2bb0c6992..783c44be399a3bcf251b52c47340c26d // chrome, except for the cases where it would require lots of extra work for // the embedder to use the same default value. diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h -index 68f936bc7103accc4521da0a12e5e0fad05cc86c..24f209d5694cdf7128a8fb58af4957f0905058a0 100644 +index 9b7bc55a0e61de85688b2370012e14f8b47c43ab..63b035046ac3d565e5ee1f3a3c559da88d81f059 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h +++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h -@@ -6,6 +6,7 @@ - #define THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PREFERENCES_WEB_PREFERENCES_MOJOM_TRAITS_H_ +@@ -8,6 +8,7 @@ + #include #include "build/build_config.h" +#include "mojo/public/cpp/base/file_path_mojom_traits.h" #include "mojo/public/cpp/bindings/struct_traits.h" #include "net/nqe/effective_connection_type.h" #include "third_party/blink/public/common/common_export.h" -@@ -434,6 +435,52 @@ struct BLINK_COMMON_EXPORT StructTraitsDetached(type); diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 401679f818e7bba02be454f9aa3424e84d36c4af..4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c 100644 +index 84ce7a810816d8229f15b23a2f8bf36c00989378..f721051f30f517c863a935765d5dcd4760ab461e 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -746,10 +746,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { +@@ -750,10 +750,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { } DCHECK(!view_ || !view_->IsAttached()); @@ -63,7 +63,7 @@ index 401679f818e7bba02be454f9aa3424e84d36c4af..4da759d7ab6d6fb6b6d4d1648da0e62d if (!Client()) return false; -@@ -803,6 +799,11 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { +@@ -807,6 +803,11 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { DCHECK(!view_->IsAttached()); Client()->WillBeDetached(); diff --git a/patches/chromium/boringssl_build_gn.patch b/patches/chromium/boringssl_build_gn.patch index 66ecb2fd08813..995fb72833e68 100644 --- a/patches/chromium/boringssl_build_gn.patch +++ b/patches/chromium/boringssl_build_gn.patch @@ -6,7 +6,7 @@ Subject: boringssl BUILD.gn Build BoringSSL with some extra functions that nodejs needs. diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn -index 12b2fb63dd3ff8c3d29d915a7d0f84a9d26c6e0a..c9fd2c069473b1c21ca8610afb246337c412e0be 100644 +index b962901a7db8e6e50155dabbb1371124b12f9b26..b01e197ae2e411a376ffa28b9058d62a16abf23b 100644 --- a/third_party/boringssl/BUILD.gn +++ b/third_party/boringssl/BUILD.gn @@ -48,6 +48,21 @@ all_sources = bcm_internal_headers + bcm_sources + crypto_internal_headers + diff --git a/patches/chromium/build_add_electron_tracing_category.patch b/patches/chromium/build_add_electron_tracing_category.patch index a1897b9d44249..fe61ffc3df307 100644 --- a/patches/chromium/build_add_electron_tracing_category.patch +++ b/patches/chromium/build_add_electron_tracing_category.patch @@ -8,7 +8,7 @@ categories in use are known / declared. This patch is required for us to introduce a new Electron category for Electron-specific tracing. diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h -index c428a5f8d79e826077ab05fb6c56ae8e0e4ff609..c5fd8782fc1343f04f9e2c2c0414245d20696193 100644 +index 19e3f2fc7b3e08f690eb8c3b56c5b5222f645691..b0b7af16b6719a08ab7908ab4d96a5d2c3ea0340 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h @@ -91,6 +91,7 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS( diff --git a/patches/chromium/build_add_public_config_simdutf_config.patch b/patches/chromium/build_add_public_config_simdutf_config.patch index 11a7174857f61..3417d2da5db21 100644 --- a/patches/chromium/build_add_public_config_simdutf_config.patch +++ b/patches/chromium/build_add_public_config_simdutf_config.patch @@ -11,7 +11,7 @@ To accomplish this, we need to make simdutf's config public here for use by third_party/electron_node. diff --git a/third_party/simdutf/BUILD.gn b/third_party/simdutf/BUILD.gn -index d88fe7e43ac2a8129702e58bd2cd2aea094452e3..3cbeb89587f37b0ebc3622258fea0161ebf1d7b2 100644 +index 5fbce38841f04dad38f202f529ae84c609c6a8de..9f5ef9bceade8e30bbd2be616b9143e9708fefd8 100644 --- a/third_party/simdutf/BUILD.gn +++ b/third_party/simdutf/BUILD.gn @@ -6,9 +6,14 @@ source_set("header") { diff --git a/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch b/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch index 1176ce679765f..c6c5e13bcf57f 100644 --- a/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch +++ b/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch @@ -10,10 +10,10 @@ Needed for: 2) //electron/shell/common:web_contents_utility diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn -index dee8a4413f62ac5535f4bce504ff0dc934db743a..064a62b40679f05feac3c81f9d6e6c2c90972727 100644 +index 55921115094f7437ce10db1cac1debe9adc10ea9..7040d83af340f052f5cab4437433e3dd0a688390 100644 --- a/content/public/common/BUILD.gn +++ b/content/public/common/BUILD.gn -@@ -380,6 +380,8 @@ mojom("interfaces") { +@@ -379,6 +379,8 @@ mojom("interfaces") { "//content/common/*", "//extensions/common:mojom", "//extensions/common:mojom_blink", diff --git a/patches/chromium/build_allow_electron_to_use_exec_script.patch b/patches/chromium/build_allow_electron_to_use_exec_script.patch index 0b9bf55adc86e..e79b8783f03f5 100644 --- a/patches/chromium/build_allow_electron_to_use_exec_script.patch +++ b/patches/chromium/build_allow_electron_to_use_exec_script.patch @@ -6,10 +6,10 @@ Subject: build: allow electron to use exec_script This is similar to the //build usecase so we're OK adding ourselves here diff --git a/.gn b/.gn -index 3f6571828197301361ebde2e19e8e3138597c276..9effa81a564c3d2afae3eb2bb7438635e45f124a 100644 +index 54d2631ec203207f44038a36439613709fec1669..d7e197dc75dd711b1b2eb179a58de9030bde0465 100644 --- a/.gn +++ b/.gn -@@ -172,4 +172,26 @@ exec_script_allowlist = +@@ -173,4 +173,27 @@ exec_script_allowlist = "//tools/grit/grit_rule.gni", "//tools/gritsettings/BUILD.gn", @@ -34,5 +34,6 @@ index 3f6571828197301361ebde2e19e8e3138597c276..9effa81a564c3d2afae3eb2bb7438635 + "//third_party/electron_node/deps/sqlite/unofficial.gni", + "//third_party/electron_node/deps/uv/unofficial.gni", + "//third_party/electron_node/deps/uvwasi/unofficial.gni", ++ "//third_party/electron_node/deps/zstd/unofficial.gni", + "//third_party/electron_node/src/inspector/unofficial.gni", ] diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index 416bd10512c85..b83cb6b76dfb7 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -11,10 +11,10 @@ if we ever align our .pak file generation with Chrome we can remove this patch. diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index bdf6d5865fb0069f4df368613167069d2fb50c86..93a126365406badf911d938dde2dcd8b140b7f6f 100644 +index 95c73dcb082999d0a85d82f1fe330d27c88055ca..64cd976eba849435779b280b2941f9150b5d1d36 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -196,11 +196,16 @@ if (!is_android && !is_mac) { +@@ -200,11 +200,16 @@ if (!is_android && !is_mac) { "common/crash_keys.h", ] @@ -33,10 +33,10 @@ index bdf6d5865fb0069f4df368613167069d2fb50c86..93a126365406badf911d938dde2dcd8b "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 0c9c08fe6c1303bc624464582857eb05b422f14c..adb35ecb5ba8fa69b2f20a6b4fd6a154a792bee3 100644 +index b5783087429ee651d21e04f6f5f11f5cb6f015a8..dae1cca8cee4fccfed3183aca1ba7cc32652ea58 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -4565,7 +4565,7 @@ static_library("browser") { +@@ -4599,7 +4599,7 @@ static_library("browser") { [ "//chrome/browser/ui/webui/signin:profile_impl" ] } @@ -46,10 +46,10 @@ index 0c9c08fe6c1303bc624464582857eb05b422f14c..adb35ecb5ba8fa69b2f20a6b4fd6a154 # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index 77056dcfa38390f5e047a9a9fea2e86357c33237..9c9f2c4bd3de71928e63ee8ea8f33a51c3404302 100644 +index 4dac075ed00691be14546b8bcd8f1b14fdc038b1..593ef717845c2c4c98aacd077d0696de207cff29 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn -@@ -7052,9 +7052,12 @@ test("unit_tests") { +@@ -7096,9 +7096,12 @@ test("unit_tests") { "//chrome/notification_helper", ] @@ -63,7 +63,7 @@ index 77056dcfa38390f5e047a9a9fea2e86357c33237..9c9f2c4bd3de71928e63ee8ea8f33a51 "//chrome//services/util_win:unit_tests", "//chrome/app:chrome_dll_resources", "//chrome/app:win_unit_tests", -@@ -8015,6 +8018,10 @@ test("unit_tests") { +@@ -8067,6 +8070,10 @@ test("unit_tests") { "../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc", ] @@ -74,7 +74,7 @@ index 77056dcfa38390f5e047a9a9fea2e86357c33237..9c9f2c4bd3de71928e63ee8ea8f33a51 sources += [ # The importer code is not used on Android. "../common/importer/firefox_importer_utils_unittest.cc", -@@ -8070,7 +8077,6 @@ test("unit_tests") { +@@ -8122,7 +8129,6 @@ test("unit_tests") { # Non-android deps for "unit_tests" target. deps += [ "../browser/screen_ai:screen_ai_install_state", diff --git a/patches/chromium/build_gn.patch b/patches/chromium/build_gn.patch index ccfcfe1f169bc..1ca25368c1572 100644 --- a/patches/chromium/build_gn.patch +++ b/patches/chromium/build_gn.patch @@ -7,7 +7,7 @@ These are variables we add to the root BUILDCONFIG so that they're available everywhere, without having to import("//electron/.../flags.gni"). diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn -index b5c5cb75e5fe2c58b1ee9051a9fe8cc082b88efa..8bf8d75205e984878219dcc5016b8665b9f65577 100644 +index a9d2caeb1c92e102daa9a8bd5bdc5a55af418cf4..efb5bf91722d4b18ae704ebf7355a13f253bef20 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -123,6 +123,9 @@ if (current_os == "") { diff --git a/patches/chromium/build_libc_as_static_library.patch b/patches/chromium/build_libc_as_static_library.patch index fbcf8c5e08ba0..962cbdea673ab 100644 --- a/patches/chromium/build_libc_as_static_library.patch +++ b/patches/chromium/build_libc_as_static_library.patch @@ -7,23 +7,10 @@ Build libc++ as static library to compile and pass nan tests diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn -index 3f437d2b9c193285bdc200c2781b13de75ba993d..d9493066ddc7d7198acc76f345c726be021d8fce 100644 +index d2e6e482d82782d1004e1f8ea377e69cbbe2677b..2bb9ec60180cf80be6e565081d56ea747820196b 100644 --- a/buildtools/third_party/libc++/BUILD.gn +++ b/buildtools/third_party/libc++/BUILD.gn -@@ -204,7 +204,11 @@ libcxx_modules("std_wctype_h") { - if (libcxx_is_shared) { - _libcxx_target_type = "shared_library" - } else { -- _libcxx_target_type = "source_set" -+ if (is_win) { -+ _libcxx_target_type = "source_set" -+ } else { -+ _libcxx_target_type = "static_library" -+ } - } - - target(_libcxx_target_type, "libc++") { -@@ -213,6 +217,7 @@ target(_libcxx_target_type, "libc++") { +@@ -272,6 +272,7 @@ target(libcxx_target_type, "libc++") { # need to explicitly depend on libc++. visibility = [ "//build/config:common_deps", @@ -31,16 +18,3 @@ index 3f437d2b9c193285bdc200c2781b13de75ba993d..d9493066ddc7d7198acc76f345c726be "//third_party/catapult/devil:devil", ] if (is_linux) { -diff --git a/buildtools/third_party/libc++abi/BUILD.gn b/buildtools/third_party/libc++abi/BUILD.gn -index 331ea447ea15e9f439396d4c7d41832de60adf4a..b96a994c43ac2ed0b0d5ec599f907ea0b501156e 100644 ---- a/buildtools/third_party/libc++abi/BUILD.gn -+++ b/buildtools/third_party/libc++abi/BUILD.gn -@@ -6,7 +6,7 @@ import("//build/config/android/config.gni") - import("//build/config/c++/c++.gni") - import("//build/config/unwind.gni") - --source_set("libc++abi") { -+static_library("libc++abi") { - if (export_libcxxabi_from_executables) { - visibility = [ "//build/config:executable_deps" ] - } else { diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index ea0c4c63c43d0..26866d84281ad 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index d7f1dc68e780f08af438c6ce67a3c856ef31daef..09752e9cda20f0020976015d58f73f320031d11e 100644 +index 775a132ea523c509433a3a9560fe3a5419dfaa3f..2b99d0b584760ab3f51561c030ca199055bacd26 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -9612,6 +9612,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -9652,6 +9652,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -21,10 +21,10 @@ index d7f1dc68e780f08af438c6ce67a3c856ef31daef..09752e9cda20f0020976015d58f73f32 &no_javascript_access); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index c3e88374849c6062195fb78072123f05e1929fe8..79bc1f7a5c44a0dd255b5ca676d95c9fa9c31496 100644 +index 2896b2b15d9c22e27c37719867c20213b9983984..83c5918c1bc0417ed3c744828b9f93e7c00d22c3 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5032,6 +5032,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5101,6 +5101,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( SetPartitionedPopinOpenerOnNewWindowIfNeeded(new_contents_impl, params, opener); @@ -37,7 +37,7 @@ index c3e88374849c6062195fb78072123f05e1929fe8..79bc1f7a5c44a0dd255b5ca676d95c9f // If the new frame has a name, make sure any SiteInstances that can find // this named frame have proxies for it. Must be called after // SetSessionStorageNamespace, since this calls CreateRenderView, which uses -@@ -5073,12 +5079,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5142,12 +5148,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( AddWebContentsDestructionObserver(new_contents_impl); } @@ -51,13 +51,13 @@ index c3e88374849c6062195fb78072123f05e1929fe8..79bc1f7a5c44a0dd255b5ca676d95c9f new_contents_impl, opener, params.target_url, params.referrer.To(), params.disposition, diff --git a/content/common/frame.mojom b/content/common/frame.mojom -index 91dcf6c9c4a2d840fb50cb329fe3ef1bba9103c3..cbc887a3034605a93468e73a310e9ca6838b32d8 100644 +index 55bb4ae3bab4cdf20b3e1dde9450a5c0e4e62b37..fe444c7fa140166a1b65c7a8a2676e2de7c4e0fc 100644 --- a/content/common/frame.mojom +++ b/content/common/frame.mojom -@@ -621,6 +621,10 @@ struct CreateNewWindowParams { - // The navigation initiator's user activation and ad status. - blink.mojom.NavigationInitiatorActivationAndAdStatus - initiator_activation_and_ad_status; +@@ -646,6 +646,10 @@ struct CreateNewWindowParams { + pending_associated_remote widget; + pending_associated_receiver frame_widget_host; + pending_associated_remote frame_widget; + + // Extra fields added by Electron. + string raw_features; @@ -66,10 +66,10 @@ index 91dcf6c9c4a2d840fb50cb329fe3ef1bba9103c3..cbc887a3034605a93468e73a310e9ca6 // Operation result when the renderer asks the browser to create a new window. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index eacc673508fded48288c90920f424e93f99f96cd..7eb3bb261a66b3bf8e5eaaea82962838376f3765 100644 +index e2382e9769cb79d676715187a2b8b00392e5445c..22400e44369b8ffb4b3fd886c981afea7a90da02 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc -@@ -805,6 +805,8 @@ bool ContentBrowserClient::CanCreateWindow( +@@ -820,6 +820,8 @@ bool ContentBrowserClient::CanCreateWindow( const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -79,7 +79,7 @@ index eacc673508fded48288c90920f424e93f99f96cd..7eb3bb261a66b3bf8e5eaaea82962838 bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index cafc62e4b6da45e63cb77d27de9794d4b34cb408..da93d31b658577bd4817b2dcfe366d444dbc2e92 100644 +index 9b75a6c3c222a93319e0f26f351aed3dbe6ead28..8a7ea32eb4ea4a78662b96a2eef5a0de4229d214 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -199,6 +199,7 @@ class NetworkService; @@ -90,7 +90,7 @@ index cafc62e4b6da45e63cb77d27de9794d4b34cb408..da93d31b658577bd4817b2dcfe366d44 } // namespace network namespace sandbox { -@@ -1357,6 +1358,8 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -1376,6 +1377,8 @@ class CONTENT_EXPORT ContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -100,7 +100,7 @@ index cafc62e4b6da45e63cb77d27de9794d4b34cb408..da93d31b658577bd4817b2dcfe366d44 bool opener_suppressed, bool* no_javascript_access); diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc -index ac4deeb6acebf79987591756320e2406d74d7a1e..f688e6d1e6c11f0dd80f498e6361fa822e8c2eb8 100644 +index b390356721fa226c348923f33601c4a1a2d9702d..97a3ea6f292563a41fd41f812ac72526a96d8471 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc @@ -32,6 +32,17 @@ namespace content { @@ -122,7 +122,7 @@ index ac4deeb6acebf79987591756320e2406d74d7a1e..f688e6d1e6c11f0dd80f498e6361fa82 WebContents* source, const OpenURLParams& params, diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h -index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1c6b6a128 100644 +index da319cb20733150366d85bee95609f0f2d9def7f..8a18958035cc1dd26be558349f64f7727570c4ba 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h @@ -18,6 +18,7 @@ @@ -133,7 +133,7 @@ index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1 #include "content/public/browser/eye_dropper.h" #include "content/public/browser/fullscreen_types.h" #include "content/public/browser/invalidate_type.h" -@@ -375,6 +376,13 @@ class CONTENT_EXPORT WebContentsDelegate { +@@ -376,6 +377,13 @@ class CONTENT_EXPORT WebContentsDelegate { const StoragePartitionConfig& partition_config, SessionStorageNamespace* session_storage_namespace); @@ -148,10 +148,10 @@ index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1 // typically happens when popups are created. virtual void WebContentsCreated(WebContents* source_contents, diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index d7f25fd51ce82e20146d2df0978644deb6297fbb..e995c76b1ecc50502c47862607408d0663e91738 100644 +index b46ceca2998e6f3706aff3705b9639948eebf60b..b78c7f64fd5e21a8d89318791bf639ab367d2d48 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -6908,6 +6908,10 @@ WebView* RenderFrameImpl::CreateNewWindow( +@@ -6925,6 +6925,10 @@ WebView* RenderFrameImpl::CreateNewWindow( request.HasUserGesture(), GetWebFrame()->IsAdFrame(), GetWebFrame()->IsAdScriptInStack()); @@ -210,10 +210,10 @@ index 82e9d3dfb5f7da76d89fe15ae61d379fa46e177d..fd035512099a54dff6cc951a2226c23a } // namespace blink diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc -index b44f0db98f89befc33430bcf275bffcfae9e7171..66f92d1e975a74720af12662447ea214ac210248 100644 +index f4a1ed78679c5efaea6494a4aa8b22fff31cba1b..afa637807b0a5bdef126031f219115ecee8d478d 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc -@@ -2270,6 +2270,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate, +@@ -2272,6 +2272,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate, WebWindowFeatures window_features = GetWindowFeaturesFromString(features, entered_window); diff --git a/patches/chromium/cherry-pick-f1e6422a355c.patch b/patches/chromium/cherry-pick-f1e6422a355c.patch new file mode 100644 index 0000000000000..a95ef07b925a8 --- /dev/null +++ b/patches/chromium/cherry-pick-f1e6422a355c.patch @@ -0,0 +1,273 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yoshisato Yanagisawa +Date: Wed, 21 May 2025 23:25:12 -0700 +Subject: Enforce SharedWorker::Terminate() procedure order + +During the investigation of crbug.com/409059706, we observed that +PerformShutdownOnWorkerThread() is called during the status is +running. + +I suppose the root cause is race condition between `Terminate()` +procedure and a child process termination procedure in different +thread. WorkerThread can be terminated if two conditions are met; +`Terminate()` is called and all child worker threads have been +terminated. Both `Terminate()` and the child process termination +procedure may call `PerformShutdownOnWorkerThread()`, and former +is executed regardless of two conditions are met. The latter +is called if `Terminate()` is called and no child processes. +To be clear, "`Terminate()` is called" does not mean +`PrepareForShutdownOnWorkerThread()` is executed. `Terminate()` +queues it after the flag to tell `Terminate()` call. And, when +the issue happen, I am quite sure the flag is set but, +`PrepareForShutdownOnWorkerThread()` won't be executed yet. + +The fix is that: +1. The "Terminate() is called" flag to be multi staged. + The flag is used for two purpose; a. avoid re-enter of + `Terminate()`, and b. `PrepareForShutdownOnWorkerThread()` is + in flight. The CL changed the flag to enum to represent + the stage properly. +2. `PerformShutdownOnWorkerThread()` is queued even if it is + called within the child process termination procedure. + It avoid the execution order flip between + `PrepareForShutdownOnWorkerThread()` and + `PerformShutdownOnWorkerThread()`. + +In addition, this change ensures `PerformShutdownOnWorkerThread()` +is called once. While `PerformShutdownOnWorkerThread()` touches +fields inside, the fields must not be touched at some point within +the function, the function is actually not re-entrant when it reaches +to the end. Upon mikt@ suggestion, I made +`PerformShutdownOnWorkerThread()` is called only when two conditions +are fulfilled. i.e. `Terminate()` is called and the number of child +threads is 0. Also, the CL uses the enum to show +`PerformShutdownOnWorkerThread()` is in-flight to avoid re-entrance +in this level. + +Bug: 409059706 +Change-Id: I81a1c3b1a34e827fa75ec2d1a9b37023965dbe27 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6543412 +Reviewed-by: Hiroki Nakagawa +Commit-Queue: Yoshisato Yanagisawa +Cr-Commit-Position: refs/heads/main@{#1463892} + +diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc +index b6f2e57224c8a50ffa65b70b30cf0eda77691613..68ac835c14901a22cd8ae1b55470e4b0a3d9dea8 100644 +--- a/third_party/blink/common/features.cc ++++ b/third_party/blink/common/features.cc +@@ -2828,6 +2828,13 @@ BASE_FEATURE(kWebviewAccelerateSmallCanvases, + "WebviewAccelerateSmallCanvases", + base::FEATURE_DISABLED_BY_DEFAULT); + ++// WorkerThread termination procedure (prepare and shutdown) runs sequentially ++// in the same task without calling another cross thread post task. ++// Kill switch for crbug.com/409059706. ++BASE_FEATURE(kWorkerThreadSequentialShutdown, ++ "WorkerThreadSequentialShutdown", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + BASE_FEATURE(kNoReferrerForPreloadFromSubresource, + "NoReferrerForPreloadFromSubresource", + base::FEATURE_ENABLED_BY_DEFAULT); +diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h +index 9970347ef0ccb1801bbdd9e3ff904ae7591567c6..1cd31c80405bac33b11749587e5b05c0fd461856 100644 +--- a/third_party/blink/public/common/features.h ++++ b/third_party/blink/public/common/features.h +@@ -1844,6 +1844,8 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebUSBTransferSizeLimit); + + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebviewAccelerateSmallCanvases); + ++BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWorkerThreadSequentialShutdown); ++ + // Kill switch for https://crbug.com/415810136. + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kNoReferrerForPreloadFromSubresource); + +diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc +index 9b2878bc23c78f092816524608776dd32fbde5a1..6d1f954733fbc574bcb1fda229fd30e7303fb9aa 100644 +--- a/third_party/blink/renderer/core/workers/worker_thread.cc ++++ b/third_party/blink/renderer/core/workers/worker_thread.cc +@@ -264,9 +264,10 @@ void WorkerThread::Terminate() { + DCHECK_CALLED_ON_VALID_THREAD(parent_thread_checker_); + { + base::AutoLock locker(lock_); +- if (requested_to_terminate_) ++ if (termination_progress_ != TerminationProgress::kNotRequested) { + return; +- requested_to_terminate_ = true; ++ } ++ termination_progress_ = TerminationProgress::kRequested; + } + + // Schedule a task to forcibly terminate the script execution in case that the +@@ -282,10 +283,33 @@ void WorkerThread::Terminate() { + *task_runner, FROM_HERE, + CrossThreadBindOnce(&WorkerThread::PrepareForShutdownOnWorkerThread, + CrossThreadUnretained(this))); +- PostCrossThreadTask( +- *task_runner, FROM_HERE, +- CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread, +- CrossThreadUnretained(this))); ++ ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ PostCrossThreadTask( ++ *task_runner, FROM_HERE, ++ CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread, ++ CrossThreadUnretained(this))); ++ return; ++ } ++ ++ bool perform_shutdown = false; ++ { ++ base::AutoLock locker(lock_); ++ CHECK_EQ(TerminationProgress::kRequested, termination_progress_); ++ termination_progress_ = TerminationProgress::kPrepared; ++ if (num_child_threads_ == 0) { ++ termination_progress_ = TerminationProgress::kPerforming; ++ perform_shutdown = true; ++ } ++ } ++ ++ if (perform_shutdown) { ++ PostCrossThreadTask( ++ *task_runner, FROM_HERE, ++ CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread, ++ CrossThreadUnretained(this))); ++ } + } + + void WorkerThread::TerminateForTesting() { +@@ -422,20 +446,48 @@ scoped_refptr WorkerThread::GetTaskRunner( + + void WorkerThread::ChildThreadStartedOnWorkerThread(WorkerThread* child) { + DCHECK(IsCurrentThread()); +-#if DCHECK_IS_ON() ++ child_threads_.insert(child); + { + base::AutoLock locker(lock_); + DCHECK_EQ(ThreadState::kRunning, thread_state_); ++ CHECK_EQ(TerminationProgress::kNotRequested, termination_progress_); ++ if (base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ ++num_child_threads_; ++ CHECK_EQ(child_threads_.size(), num_child_threads_); ++ } + } +-#endif +- child_threads_.insert(child); + } + + void WorkerThread::ChildThreadTerminatedOnWorkerThread(WorkerThread* child) { + DCHECK(IsCurrentThread()); + child_threads_.erase(child); +- if (child_threads_.empty() && CheckRequestedToTerminate()) +- PerformShutdownOnWorkerThread(); ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ if (child_threads_.empty() && CheckRequestedToTerminate()) { ++ PerformShutdownOnWorkerThread(); ++ } ++ return; ++ } ++ ++ bool perform_shutdown = false; ++ { ++ base::AutoLock locker(lock_); ++ --num_child_threads_; ++ CHECK_EQ(child_threads_.size(), num_child_threads_); ++ if (num_child_threads_ == 0 && ++ termination_progress_ == TerminationProgress::kPrepared) { ++ termination_progress_ = TerminationProgress::kPerforming; ++ perform_shutdown = true; ++ } ++ } ++ if (perform_shutdown) { ++ scoped_refptr task_runner = ++ GetWorkerBackingThread().BackingThread().GetTaskRunner(); ++ GetWorkerBackingThread().BackingThread().GetTaskRunner()->PostTask( ++ FROM_HERE, WTF::BindOnce(&WorkerThread::PerformShutdownOnWorkerThread, ++ WTF::Unretained(this))); ++ } + } + + WorkerThread::WorkerThread(WorkerReportingProxy& worker_reporting_proxy) +@@ -781,18 +833,32 @@ void WorkerThread::PerformShutdownOnWorkerThread() { + DCHECK(IsCurrentThread()); + { + base::AutoLock locker(lock_); +- DCHECK(requested_to_terminate_); ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ DCHECK_NE(TerminationProgress::kNotRequested, termination_progress_); ++ } else { ++ DCHECK_EQ(TerminationProgress::kPerforming, termination_progress_); ++ } + DCHECK_EQ(ThreadState::kReadyToShutdown, thread_state_); + if (exit_code_ == ExitCode::kNotTerminated) + SetExitCode(ExitCode::kGracefullyTerminated); + } + +- // When child workers are present, wait for them to shutdown before shutting +- // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible +- // for completing shutdown on the worker thread after the last child shuts +- // down. +- if (!child_threads_.empty()) +- return; ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ // When child workers are present, wait for them to shutdown before shutting ++ // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible ++ // for completing shutdown on the worker thread after the last child shuts ++ // down. ++ if (!child_threads_.empty()) { ++ return; ++ } ++ } else { ++ // Child workers must not exist when `PerformShutdownOnWorkerThread()` ++ // is called because it has already been checked before calling the ++ // function. ++ CHECK(child_threads_.empty()); ++ } + + inspector_task_runner_->Dispose(); + if (worker_inspector_controller_) { +@@ -848,7 +914,7 @@ void WorkerThread::SetExitCode(ExitCode exit_code) { + + bool WorkerThread::CheckRequestedToTerminate() { + base::AutoLock locker(lock_); +- return requested_to_terminate_; ++ return termination_progress_ != TerminationProgress::kNotRequested; + } + + void WorkerThread::PauseOrFreeze(mojom::blink::FrameLifecycleState state, +diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h +index 48174fc364fd6d98f90b5a99a4ae403602691a52..e0423486b891d4d8638f455a01d07adc5b10b25b 100644 +--- a/third_party/blink/renderer/core/workers/worker_thread.h ++++ b/third_party/blink/renderer/core/workers/worker_thread.h +@@ -321,6 +321,13 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver { + kTerminationUnnecessary, + }; + ++ enum class TerminationProgress { ++ kNotRequested, ++ kRequested, ++ kPrepared, ++ kPerforming, ++ }; ++ + // Returns true if we should synchronously terminate the script execution so + // that a shutdown task can be handled by the thread event loop. + TerminationState ShouldTerminateScriptExecution() +@@ -422,8 +429,10 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver { + // A unique identifier among all WorkerThreads. + const int worker_thread_id_; + +- // Set on the parent thread. +- bool requested_to_terminate_ GUARDED_BY(lock_) = false; ++ // Represents progress after the Terminate() call. ++ TerminationProgress termination_progress_ GUARDED_BY(lock_) = ++ TerminationProgress::kNotRequested; ++ size_t num_child_threads_ GUARDED_BY(lock_) = 0; + + ThreadState thread_state_ GUARDED_BY(lock_) = ThreadState::kNotStarted; + ExitCode exit_code_ GUARDED_BY(lock_) = ExitCode::kNotTerminated; diff --git a/patches/chromium/chore_add_electron_deps_to_gitignores.patch b/patches/chromium/chore_add_electron_deps_to_gitignores.patch index b45633da1f135..da245d70f50f9 100644 --- a/patches/chromium/chore_add_electron_deps_to_gitignores.patch +++ b/patches/chromium/chore_add_electron_deps_to_gitignores.patch @@ -6,7 +6,7 @@ Subject: chore: add electron deps to gitignores Makes things like "git status" quicker when developing electron locally diff --git a/.gitignore b/.gitignore -index 375db3d02ce2837ba69e3e93c38cde7153ba079f..fc3f4f71c43facf695d326f0bc64e0ad920aa9ac 100644 +index 0a0f0118d5c1a5a2f3ad28b068bebb849eba7246..5ca6d03b709ef119ccd6482b2f305f8a3aeb7438 100644 --- a/.gitignore +++ b/.gitignore @@ -217,6 +217,7 @@ vs-chromium-project.txt diff --git a/patches/chromium/chore_introduce_blocking_api_for_electron.patch b/patches/chromium/chore_introduce_blocking_api_for_electron.patch index d72de4fe61705..b2d277ea348e6 100644 --- a/patches/chromium/chore_introduce_blocking_api_for_electron.patch +++ b/patches/chromium/chore_introduce_blocking_api_for_electron.patch @@ -7,7 +7,7 @@ This patch comes after Chromium removed the ScopedAllowIO API in favor of explicitly adding ScopedAllowBlocking calls as friends. diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h -index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e0f597937 100644 +index d97240a7b0dcf63737808850b6ef83852155b794..4a58b1f3e8c2bee32084be82977239e911598077 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h @@ -132,6 +132,7 @@ class KeyStorageLinux; @@ -18,7 +18,7 @@ index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e class Profile; class ProfileImpl; class ScopedAllowBlockingForProfile; -@@ -278,6 +279,9 @@ class BackendImpl; +@@ -277,6 +278,9 @@ class BackendImpl; class InFlightIO; bool CleanupDirectorySync(const base::FilePath&); } // namespace disk_cache @@ -36,7 +36,7 @@ index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e friend class ::ProfileImpl; friend class ::ScopedAllowBlockingForProfile; friend class ::StartupTabProviderImpl; -@@ -615,6 +620,7 @@ class BASE_EXPORT ScopedAllowBlocking { +@@ -614,6 +619,7 @@ class BASE_EXPORT ScopedAllowBlocking { friend class crypto::ScopedAllowBlockingForNSS; // http://crbug.com/59847 friend class drive::FakeDriveService; friend class extensions::DesktopAndroidExtensionSystem; diff --git a/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch similarity index 50% rename from patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch rename to patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch index 47583a50ac415..537678be5c611 100644 --- a/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch +++ b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch @@ -1,16 +1,43 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Fri, 29 Jul 2022 00:29:35 +0900 -Subject: chore: allow chromium to handle synthetic mouse events for touch +Subject: chore: modify chromium handling of mouse events -With WCO, allow chromium to handle synthetic mouse events generated for touch +This patch does the following: + +1. When Windows Control Overlay is enabled, it allows chromium to handle synthetic mouse events generated for touch actions in the non-client caption area. +2. It calls HandleMouseEvent on the delegate earlier in HandleMouseEventInternal, so that Electron can selectively disable +draggable regions to allow events to propagate to the underlying renderer. +diff --git a/ui/events/event.h b/ui/events/event.h +index 39b5a8fdd165efd74b00256552b51b5413107958..bfc4ef4f50efff4a77f2aef64335bb7e34c69f34 100644 +--- a/ui/events/event.h ++++ b/ui/events/event.h +@@ -587,6 +587,9 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { + + const PointerDetails& pointer_details() const { return pointer_details_; } + ++ bool is_system_menu() const { return is_system_menu_; } ++ void set_is_system_menu(bool is_menu) { is_system_menu_ = is_menu; } ++ + // Event: + std::string ToString() const override; + std::unique_ptr Clone() const override; +@@ -619,6 +622,8 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { + + // Structure for holding pointer details for implementing PointerEvents API. + PointerDetails pointer_details_; ++ ++ bool is_system_menu_ = false; + }; + + class ScrollEvent; diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -index 2d05856687cd9669f72553d33c8033fd9083b4f8..645b7dd2cc20ce64ffa541c74930f541f083f931 100644 +index 1d298f66a2c2a0f02668af797b3a421fe5578d56..4780229a4453ad5ca2c89b786459f64c58bf2272 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -@@ -1357,6 +1357,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( +@@ -1386,6 +1386,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( window()->SetProperty(aura::client::kHeadlessBoundsKey, bounds); } @@ -22,10 +49,10 @@ index 2d05856687cd9669f72553d33c8033fd9083b4f8..645b7dd2cc20ce64ffa541c74930f541 DesktopWindowTreeHostWin::GetSingletonDesktopNativeCursorManager() { return new DesktopNativeCursorManagerWin(); diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h -index 932351e288f37fd09ae1a43f44e8b51fb0caa4b8..4a0616bc210d234e51e564daabdd2ebd5ac9fc16 100644 +index 2ee5e4b4673f4f18880dddecc48118c89823fd3f..37109b8d3d439073b5c9e2ea3597c36f32de5704 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h -@@ -267,6 +267,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, +@@ -268,6 +268,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, void HandleWindowSizeUnchanged() override; void HandleWindowScaleFactorChanged(float window_scale_factor) override; void HandleHeadlessWindowBoundsChanged(const gfx::Rect& bounds) override; @@ -34,10 +61,10 @@ index 932351e288f37fd09ae1a43f44e8b51fb0caa4b8..4a0616bc210d234e51e564daabdd2ebd // Overridden from WidgetObserver. void OnWidgetThemeChanged(Widget* widget) override; diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 2bd015be3178ab8dea012d6b1f71d13dd0548c14..759f00dd4e674d1dfca690b82e6e1820900ebf0c 100644 +index 701f89b053674be1bad0a1a3612340a65c1ccfe7..ac7e189f26da75f6f8b40e69cf4bfee94acddd38 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3144,15 +3144,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3186,15 +3186,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } // We must let Windows handle the caption buttons if it's drawing them, or // they won't work. @@ -59,8 +86,35 @@ index 2bd015be3178ab8dea012d6b1f71d13dd0548c14..759f00dd4e674d1dfca690b82e6e1820 return 0; } } +@@ -3217,6 +3221,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, + // handle alt-space, or in the frame itself. + is_right_mouse_pressed_on_caption_ = false; + ReleaseCapture(); ++ + // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() + // expect screen coordinates. + POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); +@@ -3224,7 +3229,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, + w_param = static_cast(SendMessage( + hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y))); + if (w_param == HTCAPTION || w_param == HTSYSMENU) { +- ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point)); ++ LONG message_time = GetMessageTime(); ++ CHROME_MSG msg = {hwnd(), ++ message, ++ w_param, ++ l_param, ++ static_cast(message_time), ++ {CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param)}}; ++ ui::MouseEvent event(msg); ++ event.set_is_system_menu(true); ++ if (!delegate_->HandleMouseEvent(&event)) ++ ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point)); + return 0; + } + } else if (message == WM_NCLBUTTONDOWN && diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h -index 04dea68d74ea4f559db60f716c919e555db9ec80..2f8bd1a3c156bb6c04663c74b7279bb59926fc3d 100644 +index 83c26bf2b4bc11c0e3d839093eea56eed1bf581b..075e456e851bbde2b0174ca0cc34428cc32b6966 100644 --- a/ui/views/win/hwnd_message_handler_delegate.h +++ b/ui/views/win/hwnd_message_handler_delegate.h @@ -255,6 +255,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { diff --git a/patches/chromium/chore_partial_revert_of.patch b/patches/chromium/chore_partial_revert_of.patch index a9c60128ec457..a2ece673822c0 100644 --- a/patches/chromium/chore_partial_revert_of.patch +++ b/patches/chromium/chore_partial_revert_of.patch @@ -14,10 +14,10 @@ track down the source of this problem & figure out if we can fix it by changing something in Electron. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 86a1a0244a3744d0571c046d3e6fc9413c9fbbf0..1396f371c8ae82a1ce52096c176a135cf02b2d65 100644 +index 5465e9d42a5c4eed3b6f819418a7b0ab92c7eae2..54ff9675fef009c5bb79becb59146436508ad835 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4951,7 +4951,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5020,7 +5020,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( : IsGuest(); // While some guest types do not have a guest SiteInstance, the ones that // don't all override WebContents creation above. diff --git a/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch b/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch index 7070caeb5f22c..ccad2444864a5 100644 --- a/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch +++ b/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch @@ -14,7 +14,7 @@ This change patches it out to prevent the DCHECK. It can be removed once/if we see a better solution to the problem. diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc -index d4dd73e7fa1e550d0d13b4c09da0ce6899894494..679a59db98261eca3fbbd7ddb20516ab4f618cfb 100644 +index adaa1cd426c138972b088d0d0093b0e1653af231..be4684c94ba2214255c5dbe9cdcf1ea316c60c06 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -229,7 +229,7 @@ scoped_refptr SiteInstanceImpl::CreateForGuest( diff --git a/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch b/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch index 46f3ddb405d00..4a93ef5d28865 100644 --- a/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch +++ b/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch @@ -9,7 +9,7 @@ Electron does not support Profiles, so this Profile::FromBrowserContext() call is not needed and will not link. This change patches it out. diff --git a/chrome/browser/pdf/chrome_pdf_stream_delegate.cc b/chrome/browser/pdf/chrome_pdf_stream_delegate.cc -index 9774ed3e55c0d78f21bb0cb0b2607cc07eaf78e0..f58c3946130fa469f2775d8bae773e5bbfc71a96 100644 +index e3b9f14a4cf2167064ce6716053e663adffa1542..65f13a4607c8145858fd47d81cb9960c44272d79 100644 --- a/chrome/browser/pdf/chrome_pdf_stream_delegate.cc +++ b/chrome/browser/pdf/chrome_pdf_stream_delegate.cc @@ -45,6 +45,7 @@ namespace { diff --git a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch index 828ecedd74c09..27a9ad01bd17b 100644 --- a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch +++ b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch @@ -80,10 +80,10 @@ index 4fd8dff1089cd6afa6a66dc185734d7671657281..0a1f4268ea771a3d5d4a2668928c6e5d content::WebContents* source, const content::OpenURLParams& params, diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc -index fafd84d08e33622aee01f1792c043180a13dd48d..4490915e0b5c8e93255c6d526d5f37eb6f6b362d 100644 +index d45f8f1713c875f2d430f9155f0cbe626a5f233f..00c7db5afa636d5d1da4e5d2cb01ebcbec70304b 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc -@@ -2222,12 +2222,11 @@ bool Browser::IsWebContentsCreationOverridden( +@@ -2289,12 +2289,11 @@ bool Browser::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -99,10 +99,10 @@ index fafd84d08e33622aee01f1792c043180a13dd48d..4490915e0b5c8e93255c6d526d5f37eb WebContents* Browser::CreateCustomWebContents( diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h -index 8fda0dfbe8e10a3830bd03a994e7d39817584d36..baf24bbeb9f57fe2609a1af51a6ee4f5abd86ce2 100644 +index ce1e404e45a60e43de9580fe8d60fa72ec90b8f4..9267e93fdd03fad3848a45a1c6d090566cac317c 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h -@@ -980,8 +980,7 @@ class Browser : public TabStripModelObserver, +@@ -1005,8 +1005,7 @@ class Browser : public TabStripModelObserver, content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -141,10 +141,10 @@ index ca72b324bf7c3b81ac94b53f0ff454d2df177950..d60ef3075d126e2bbd50c8469f2bf67c // The profile used for the presentation. raw_ptr otr_profile_; diff --git a/chrome/browser/ui/views/hats/hats_next_web_dialog.cc b/chrome/browser/ui/views/hats/hats_next_web_dialog.cc -index 83a0a538fef0da1d3674293d20dac7b8a252273f..155c8a0af46e20f68f8b028c056092b404062861 100644 +index 1085003ab18d471d5c018ac68041924d458fcec7..4aac70febec9f0abadd1ecb96d9066e6c270efca 100644 --- a/chrome/browser/ui/views/hats/hats_next_web_dialog.cc +++ b/chrome/browser/ui/views/hats/hats_next_web_dialog.cc -@@ -99,8 +99,7 @@ class HatsNextWebDialog::HatsWebView : public views::WebView { +@@ -100,8 +100,7 @@ class HatsNextWebDialog::HatsWebView : public views::WebView { content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -155,10 +155,10 @@ index 83a0a538fef0da1d3674293d20dac7b8a252273f..155c8a0af46e20f68f8b028c056092b4 } content::WebContents* CreateCustomWebContents( diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.cc b/components/embedder_support/android/delegate/web_contents_delegate_android.cc -index fb08ed752db063c5a295c479bc5ad990826104cd..659e642cb660263a1cba9bb485ed3f67c2939c0f 100644 +index e0264aff5b60bb5e76ac7687222403dfba516e66..915abd2475f6be95d973f827522f7ef12052a81c 100644 --- a/components/embedder_support/android/delegate/web_contents_delegate_android.cc +++ b/components/embedder_support/android/delegate/web_contents_delegate_android.cc -@@ -188,14 +188,13 @@ bool WebContentsDelegateAndroid::IsWebContentsCreationOverridden( +@@ -189,14 +189,13 @@ bool WebContentsDelegateAndroid::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -176,7 +176,7 @@ index fb08ed752db063c5a295c479bc5ad990826104cd..659e642cb660263a1cba9bb485ed3f67 java_gurl); } diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.h b/components/embedder_support/android/delegate/web_contents_delegate_android.h -index 45d0fc1b9dbeb1c1b5a28a07fb8cf26d3388bfd9..d311ea8dc016659ee8355bc4e7742b4e3ebc413c 100644 +index 67a01b48ae88c7e25aeb5c5103b26afa037f4f97..0ceb539c9c3051b5521236cf866ccb107727c8a9 100644 --- a/components/embedder_support/android/delegate/web_contents_delegate_android.h +++ b/components/embedder_support/android/delegate/web_contents_delegate_android.h @@ -82,8 +82,7 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate { @@ -218,10 +218,10 @@ index c6838c83ef971b88769b1f3fba8095025ae25464..2da6a4e08340e72ba7de5d03444c2f17 content::WebContents* AddNewContents( content::WebContents* source, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9f18308873306e841bbc15d9ba36c93ba5603a15..9d6ad368980202f74d36785623d27354beef3f03 100644 +index d13c38de7a2e69565020ab92c874bf1289d63833..d7ec62c6761bcc8d4bb5889e873e22c4a27e7f17 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4914,8 +4914,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -4983,8 +4983,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( // TODO(crbug.com/40202416): Support a way for MPArch guests to support this. if (delegate_ && delegate_->IsWebContentsCreationOverridden( source_site_instance, params.window_container_type, @@ -232,7 +232,7 @@ index 9f18308873306e841bbc15d9ba36c93ba5603a15..9d6ad368980202f74d36785623d27354 static_cast(delegate_->CreateCustomWebContents( opener, source_site_instance, is_new_browsing_instance, diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc -index f688e6d1e6c11f0dd80f498e6361fa822e8c2eb8..dc47ebfd61f16901b7f8e0e4c30c79eaf5d0710e 100644 +index 97a3ea6f292563a41fd41f812ac72526a96d8471..b299dd5659100d317a3574e902bf2c29c5defd2c 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc @@ -159,8 +159,7 @@ bool WebContentsDelegate::IsWebContentsCreationOverridden( @@ -246,10 +246,10 @@ index f688e6d1e6c11f0dd80f498e6361fa822e8c2eb8..dc47ebfd61f16901b7f8e0e4c30c79ea } diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h -index 96cb58b1a88499cf8f78d748dc5a1cc1c6b6a128..8ff56b56da764c8af14c40babc529ee4cb089c7e 100644 +index 8a18958035cc1dd26be558349f64f7727570c4ba..7d9c9b06bcc57ef5eb0a2ca74ee20632a1393f9e 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h -@@ -354,8 +354,7 @@ class CONTENT_EXPORT WebContentsDelegate { +@@ -355,8 +355,7 @@ class CONTENT_EXPORT WebContentsDelegate { SiteInstance* source_site_instance, mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -324,7 +324,7 @@ index e39031afd8fff7cb6e278555cc58a48d86407d65..f67f6a5603c1fa9e66ccdde9b601df9a content::RenderFrameHost* opener, content::SiteInstance* source_site_instance, diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc -index 16babdc7affdaef1af24850cf35494b8b4fe1479..1e77b091d94457c892788f7a441a83e7a96a49cc 100644 +index 60de0d74ee40fedcbae96e5049e21dc238bf33bf..c568d4d08f772e1d381820bed826a0b64a631449 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc @@ -452,8 +452,7 @@ bool MimeHandlerViewGuest::IsWebContentsCreationOverridden( @@ -380,7 +380,7 @@ index 1012a909ef1fcae51c218ae519fe7e0db65ab087..127b1ae940bc9313aecb635e2b01bb6f int opener_render_process_id, int opener_render_frame_id, diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc -index 62be493c74423875af45a92c98b2ffceb928189f..ec82d8e47bf0a33f832b8ca242f395fe59a1bbee 100644 +index 7ca1e83ba1fd2dc5ea7c7ce644c3b7c54b9999f9..c1639653714d6973bcb5a0b37cb7028db8406742 100644 --- a/headless/lib/browser/headless_web_contents_impl.cc +++ b/headless/lib/browser/headless_web_contents_impl.cc @@ -206,8 +206,7 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate { diff --git a/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch b/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch index 91d27c1ec9608..ce4957d9e66f9 100644 --- a/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch +++ b/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch @@ -32,7 +32,7 @@ index 7d5d0106a3675b3fa21b0e00a755f5c0ed11c87b..d26c645d70b54b31815c8140954ee6d0 // (netwerk/cookie/src/nsCookieService.cpp) /* ***** BEGIN LICENSE BLOCK ***** diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc -index 813f2f7f274bf02b6679b9321ae83948ab634697..2c61297669ba7d513f8493dfb6f478245f5c7c58 100644 +index 11572f927d97da20627dec498b92e445b5db4589..9b02d35a19d8610ade71b96c11551a1050847b49 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -2,11 +2,6 @@ diff --git a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch index 87e64a51fd108..6bd2adcffa567 100644 --- a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch +++ b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch @@ -7,10 +7,10 @@ By default, chromium sets up one v8 snapshot to be used in all v8 contexts. This to have a dedicated browser process v8 snapshot defined by the file `browser_v8_context_snapshot.bin`. diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc -index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abcfc3c1a61 100644 +index 7550d3eb796059aeb108f25071c8028bddebb100..db59a147bd6b4fb596170d9b86c5e71a20b62abc 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc -@@ -271,8 +271,13 @@ void AsanProcessInfoCB(const char*, bool*) { +@@ -273,8 +273,13 @@ void AsanProcessInfoCB(const char*, bool*) { } #endif // defined(ADDRESS_SANITIZER) @@ -25,7 +25,7 @@ index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abc #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) base::FileDescriptorStore& file_descriptor_store = base::FileDescriptorStore::GetInstance(); -@@ -301,11 +306,12 @@ bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, +@@ -303,11 +308,12 @@ bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, #endif // V8_USE_EXTERNAL_STARTUP_DATA @@ -40,7 +40,7 @@ index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abc #endif // V8_USE_EXTERNAL_STARTUP_DATA } -@@ -978,7 +984,7 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { +@@ -980,7 +986,7 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { return TerminateForFatalInitializationError(); #endif // BUILDFLAG(IS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) @@ -50,19 +50,18 @@ index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abc blink::TrialTokenValidator::SetOriginTrialPolicyGetter( base::BindRepeating([]() -> blink::OriginTrialPolicy* { diff --git a/content/public/app/content_main_delegate.cc b/content/public/app/content_main_delegate.cc -index 8194fc8b036482eedb162ff92bb82165cdf3c8d0..f038620516d7783170bc82b3b14dde4e01f3975d 100644 +index 8b02f553e2fc29da88c3e14c05a7ee82210eab51..14f2e66d5ecda6e860724a3ab946eaaffba33d6d 100644 --- a/content/public/app/content_main_delegate.cc +++ b/content/public/app/content_main_delegate.cc -@@ -4,6 +4,8 @@ - +@@ -5,6 +5,7 @@ #include "content/public/app/content_main_delegate.h" + #include +#include -+ + #include "base/check.h" #include "base/notreached.h" - #include "build/build_config.h" -@@ -72,6 +74,10 @@ std::optional ContentMainDelegate::PostEarlyInitialization( +@@ -74,6 +75,10 @@ std::optional ContentMainDelegate::PostEarlyInitialization( return std::nullopt; } @@ -74,13 +73,13 @@ index 8194fc8b036482eedb162ff92bb82165cdf3c8d0..f038620516d7783170bc82b3b14dde4e return new ContentClient(); } diff --git a/content/public/app/content_main_delegate.h b/content/public/app/content_main_delegate.h -index 801bfd401ea4a8e72417d88efaa718cc6fb60883..663fec68d0c2855cdf83bb259b85c22910a67464 100644 +index db611d99a6c0f18f39967b38791822fda7d175b5..cc150475de655d5ef20a107ae3ef80c08af8c7fb 100644 --- a/content/public/app/content_main_delegate.h +++ b/content/public/app/content_main_delegate.h -@@ -8,6 +8,7 @@ - #include +@@ -9,6 +9,7 @@ #include #include + #include +#include #include @@ -95,10 +94,10 @@ index 801bfd401ea4a8e72417d88efaa718cc6fb60883..663fec68d0c2855cdf83bb259b85c229 friend class ContentClientCreator; friend class ContentClientInitializer; diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index e72331d0821a14507e3c973f07889adcf45d3bdf..3acfc0d49787579d0250703eed3d6e31b79c567a 100644 +index 11cafc3e1588cce52b76cc2f09f66b3e451fb087..e07bdaeccecc8015462e35d5cf4606335e2e962c 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -657,8 +657,7 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** snapshot_data_out, +@@ -660,8 +660,7 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** snapshot_data_out, #if defined(V8_USE_EXTERNAL_STARTUP_DATA) @@ -108,7 +107,7 @@ index e72331d0821a14507e3c973f07889adcf45d3bdf..3acfc0d49787579d0250703eed3d6e31 if (g_mapped_snapshot) { // TODO(crbug.com/40558459): Confirm not loading different type of snapshot // files in a process. -@@ -667,10 +666,17 @@ void V8Initializer::LoadV8Snapshot(V8SnapshotFileType snapshot_file_type) { +@@ -670,10 +669,17 @@ void V8Initializer::LoadV8Snapshot(V8SnapshotFileType snapshot_file_type) { base::MemoryMappedFile::Region file_region; base::File file = diff --git a/patches/chromium/disable_compositor_recycling.patch b/patches/chromium/disable_compositor_recycling.patch index 3974a48b6a1b6..6178e0cd02e5d 100644 --- a/patches/chromium/disable_compositor_recycling.patch +++ b/patches/chromium/disable_compositor_recycling.patch @@ -6,10 +6,10 @@ Subject: fix: disabling compositor recycling Compositor recycling is useful for Chrome because there can be many tabs and spinning up a compositor for each one would be costly. In practice, Chrome uses the parent compositor code path of browser_compositor_view_mac.mm; the NSView of each tab is detached when it's hidden and attached when it's shown. For Electron, there is no parent compositor, so we're forced into the "own compositor" code path, which seems to be non-optimal and pretty ruthless in terms of the release of resources. Electron has no real concept of multiple tabs per window, so it should be okay to disable this ruthless recycling altogether in Electron. diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index 943e3b8875c68178e84ae79c241c7a31e9305c6c..6055992c0f61348a95823b64c73574ca6b24904b 100644 +index 1a726fb4a9e8173dc7da6901eb1632c7f326358e..ae8a4117db2f6bac1d62981e7c0e35cf1eee68da 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm -@@ -560,7 +560,11 @@ +@@ -561,7 +561,11 @@ return; host()->WasHidden(); diff --git a/patches/chromium/disable_freezing_flags_after_init_in_node.patch b/patches/chromium/disable_freezing_flags_after_init_in_node.patch deleted file mode 100644 index fa6e951731d35..0000000000000 --- a/patches/chromium/disable_freezing_flags_after_init_in_node.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jeremy Rose -Date: Mon, 20 Jun 2022 14:53:37 -0700 -Subject: disable freezing flags after init in node - -This was introduced in -https://chromium-review.googlesource.com/c/chromium/src/+/3687671. - -When running node in the renderer, flags are updated after initialization, so -freezing the flags in Blink causes node initialization to fail. - -If possible, it would be ideal to do this without a patch. -https://bugs.chromium.org/p/v8/issues/detail?id=12887 suggests that there may -at some point be an API to "unfreeze" the flags, or we may be able to refactor -node initialization to not update flags after V8 initialization. - -diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc -index eb11068e932b7b94cbf215d6f84ae427ce77fcd5..9744e45974af215bfbe9e5feb2db7274f8efebf0 100644 ---- a/content/renderer/render_process_impl.cc -+++ b/content/renderer/render_process_impl.cc -@@ -208,6 +208,9 @@ RenderProcessImpl::RenderProcessImpl() - v8::V8::SetFlagsFromString(kSABPerContextFlag, sizeof(kSABPerContextFlag)); - } - -+ // Freezing flags after init conflicts with node in the renderer. -+ v8::V8::SetFlagsFromString("--no-freeze-flags-after-init"); -+ - if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) { - content::GetContentClient()->renderer()->SetUpWebAssemblyTrapHandler(); - } diff --git a/patches/chromium/disable_hidden.patch b/patches/chromium/disable_hidden.patch index c1e5fe18c56fe..8f2203169c40f 100644 --- a/patches/chromium/disable_hidden.patch +++ b/patches/chromium/disable_hidden.patch @@ -6,7 +6,7 @@ Subject: disable_hidden.patch Electron uses this to disable background throttling for hidden windows. diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 79a679ac59c3ebe4dc1180389cf7073560e843e6..8aa812aa9bb3de4c7bc58192baf45c90402d6ff4 100644 +index 573269ba54150d5350e5c3589217c5e7f41d560d..20fcda4eb20459b69247003c51c2a3ed37c7b1e8 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -832,6 +832,10 @@ void RenderWidgetHostImpl::WasHidden() { @@ -21,10 +21,10 @@ index 79a679ac59c3ebe4dc1180389cf7073560e843e6..8aa812aa9bb3de4c7bc58192baf45c90 // Prompts should remain open and functional across tab switches. if (!delegate_ || !delegate_->IsWaitingForPointerLockPrompt(this)) { diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h -index 068b8036ad91357a5bb274f377ffb44f939d87be..c636170388f81f5afdcd3e86f85ec9f2ec944bb0 100644 +index 3b3eec99c80a8d3081bb0b1b5cfda76c70d8d23c..d4a2affd94a2aa11299222822485e9df84da18fc 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h -@@ -1018,6 +1018,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl +@@ -1016,6 +1016,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl // Requests a commit and forced redraw in the renderer compositor. void ForceRedrawForTesting(); @@ -35,7 +35,7 @@ index 068b8036ad91357a5bb274f377ffb44f939d87be..c636170388f81f5afdcd3e86f85ec9f2 // |routing_id| must not be MSG_ROUTING_NONE. // If this object outlives |delegate|, DetachDelegate() must be called when diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 1d08f25b91496dbafc3a164e7fb3e66d1a48557b..92085aca6bd0c95a73b98e4173c0128d596b77f4 100644 +index 5867fc3e77326991f30d835d08d3cfafe2b6687c..d719b546b8c3c59003698b26dead065da7d76341 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -643,7 +643,7 @@ void RenderWidgetHostViewAura::HideImpl() { diff --git a/patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch b/patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch new file mode 100644 index 0000000000000..372b667ff6fb3 --- /dev/null +++ b/patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mitsuru Oshima +Date: Thu, 17 Apr 2025 16:49:21 -0700 +Subject: Do not check the order of Display ID order on Windows + +Id is generated by Windows side logic so this rule isn't applicable. + +Bug: 394622418 +Change-Id: I79c7f91103c6b752b6a7a123aacd3573a9ab815f +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6471333 +Reviewed-by: Vincent Chiang +Commit-Queue: Mitsuru Oshima +Cr-Commit-Position: refs/heads/main@{#1448671} + +diff --git a/ui/display/display_layout.cc b/ui/display/display_layout.cc +index 0d3746d8174868b743990b5ab10b3506819ef0ea..85d5c06851148576ab4dd272918215335292b4aa 100644 +--- a/ui/display/display_layout.cc ++++ b/ui/display/display_layout.cc +@@ -500,6 +500,7 @@ void DisplayLayout::ApplyToDisplayList(Displays* display_list, + if (!DisplayLayout::Validate(DisplayListToDisplayIdList(*display_list), + *this)) { + // Prevent invalid and non-relevant display layouts. ++ LOG(ERROR) << "Invalid Display Layout"; + return; + } + +@@ -549,15 +550,19 @@ bool DisplayLayout::Validate(const DisplayIdList& list, + + bool has_primary_as_parent = false; + // The placement list must be sorted by the first 8 bits of the display IDs. ++#if BUILDFLAG(IS_CHROMEOS) + int64_t prev_id = std::numeric_limits::min(); ++#endif // BUILDFLAG(IS_CHROMEOS) + for (const auto& placement : layout.placement_list) { +- // Placements are sorted by display_id. ++#if BUILDFLAG(IS_CHROMEOS) ++ // Placements are sorted by display_id on ChromeOS. + if (prev_id >= (placement.display_id & 0xFF)) { + DISPLAY_LOG(ERROR) << "PlacementList must be sorted by first 8 bits of" + << " display_id "; + return false; + } + prev_id = (placement.display_id & 0xFF); ++#endif // BUILDFLAG(IS_CHROMEOS) + if (placement.display_id == kInvalidDisplayId) { + DISPLAY_LOG(ERROR) << "display_id is not initialized"; + return false; +diff --git a/ui/display/display_layout_unittest.cc b/ui/display/display_layout_unittest.cc +index 68327c8a6b71853a0f1bf3c0351e38865bcbe054..4ea830ef086eb009c692a0b90b100eaaed303fd0 100644 +--- a/ui/display/display_layout_unittest.cc ++++ b/ui/display/display_layout_unittest.cc +@@ -122,6 +122,7 @@ TEST(DisplayLayoutTest, SwapPrimaryDisplayThreeDisplays) { + EXPECT_EQ(Position::RIGHT, layout->placement_list[1].position); + } + ++#if BUILDFLAG(IS_CHROMEOS) + // Makes sure that only the least significant 8 bits of the display IDs in the + // placement lists are used to validate their sort order. + TEST(DisplayLayoutTest, PlacementSortOrder) { +@@ -148,6 +149,8 @@ TEST(DisplayLayoutTest, PlacementSortOrder) { + EXPECT_TRUE(DisplayLayout::Validate({456, 0x0504, 0x0605, 0x0406}, *layout)); + } + ++#endif // BUILDFLAG(IS_CHROMEOS) ++ + namespace { + + class TwoDisplays diff --git a/patches/chromium/enable_reset_aspect_ratio.patch b/patches/chromium/enable_reset_aspect_ratio.patch index 6f2ff870b3609..cb93c6b2261b3 100644 --- a/patches/chromium/enable_reset_aspect_ratio.patch +++ b/patches/chromium/enable_reset_aspect_ratio.patch @@ -6,10 +6,10 @@ Subject: feat: enable setting aspect ratio to 0 Make SetAspectRatio accept 0 as valid input, which would reset to null. diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -index 88d8d9985c6b4c7051f00cba9dfa51b3fcfa524b..2d05856687cd9669f72553d33c8033fd9083b4f8 100644 +index 82e6f7c91bac6e8a973947dace1a33de8078a86b..1d298f66a2c2a0f02668af797b3a421fe5578d56 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -@@ -637,7 +637,7 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) { +@@ -636,7 +636,7 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) { void DesktopWindowTreeHostWin::SetAspectRatio( const gfx::SizeF& aspect_ratio, const gfx::Size& excluded_margin) { @@ -19,10 +19,10 @@ index 88d8d9985c6b4c7051f00cba9dfa51b3fcfa524b..2d05856687cd9669f72553d33c8033fd excluded_margin); } diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index b3dc46c34f2aff45b3bd8ea041f2e55ba61d50f9..a802f4b710b6f8fa154d11846c061720a91e15e4 100644 +index a183cfe07b357e0b17e3781e85b79f7c371cbea8..ea67a60d78f4adec960bb4954486420bce423c14 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -988,8 +988,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, +@@ -1010,8 +1010,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, void HWNDMessageHandler::SetAspectRatio(float aspect_ratio, const gfx::Size& excluded_margin) { diff --git a/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch b/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch index d0e1c7f421fc1..9d478cd3aeddc 100644 --- a/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch +++ b/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch @@ -9,7 +9,7 @@ correctly tagged with MAP_JIT we need to use gins page allocator instead of the default V8 allocator. This probably can't be usptreamed. diff --git a/gin/public/v8_platform.h b/gin/public/v8_platform.h -index c34f34146d0aab681318b64ac33081bdc21ce7d6..11cdf6c34d8f79fcf293f59785e586dc8363b983 100644 +index 9dac402705385087ced2df2db757a07246984a94..cb49b4f085026658e920699ed285d524119d6aad 100644 --- a/gin/public/v8_platform.h +++ b/gin/public/v8_platform.h @@ -32,6 +32,7 @@ class GIN_EXPORT V8Platform : public v8::Platform { @@ -21,10 +21,10 @@ index c34f34146d0aab681318b64ac33081bdc21ce7d6..11cdf6c34d8f79fcf293f59785e586dc ThreadIsolatedAllocator* GetThreadIsolatedAllocator() override; #endif diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc -index fa799a4cc32950ce03c154bc7791341d96a17f67..b8ec81b79b6129a7e26a2ffecf535fdedea8ed6c 100644 +index 698b29e9c31a2695cac30bf85c97a216ff8a6257..dbc38d0d87803496ce122da62e639f2a0334e0f6 100644 --- a/gin/v8_platform.cc +++ b/gin/v8_platform.cc -@@ -204,6 +204,10 @@ ThreadIsolatedAllocator* V8Platform::GetThreadIsolatedAllocator() { +@@ -205,6 +205,10 @@ ThreadIsolatedAllocator* V8Platform::GetThreadIsolatedAllocator() { } #endif // PA_BUILDFLAG(ENABLE_THREAD_ISOLATION) diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index ef2408bbf90e3..29b6dc16658b6 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -33,10 +33,10 @@ index 0ab8187b0db8ae6db46d81738f653a2bc4c566f6..de3d55e85c22317f7f9375eb94d0d5d4 } // namespace net diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index e346cfb127a1fa8656bfbc1eba829907e19bc22f..e18583e520b4ab66110605b67c703a1c48b770a4 100644 +index ee56aab6831be256390b88e65a2a949c591bbd31..60c6977d2ffd0da0286e25e5645c11c19d6ab376 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc -@@ -1810,6 +1810,13 @@ void NetworkContext::SetNetworkConditions( +@@ -1814,6 +1814,13 @@ void NetworkContext::SetNetworkConditions( std::move(network_conditions)); } @@ -51,10 +51,10 @@ index e346cfb127a1fa8656bfbc1eba829907e19bc22f..e18583e520b4ab66110605b67c703a1c // This may only be called on NetworkContexts created with the constructor // that calls MakeURLRequestContext(). diff --git a/services/network/network_context.h b/services/network/network_context.h -index fe04f634b32e503f6e93ae6f20c8c7f7c85d02be..990df479da79b513cf8797aa2fcacba10c4fbbd9 100644 +index 930e0bd987c48d111b2c8d71147c09e4418bda6c..9373a53c5cac879c689fcea77f1dbbb32cf35b9e 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h -@@ -317,6 +317,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -323,6 +323,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void CloseIdleConnections(CloseIdleConnectionsCallback callback) override; void SetNetworkConditions(const base::UnguessableToken& throttling_profile_id, mojom::NetworkConditionsPtr conditions) override; @@ -63,10 +63,10 @@ index fe04f634b32e503f6e93ae6f20c8c7f7c85d02be..990df479da79b513cf8797aa2fcacba1 void SetEnableReferrers(bool enable_referrers) override; #if BUILDFLAG(IS_CT_SUPPORTED) diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index e928d012d77ccffe16263fd0ee7c2769adfe4bac..1be1b691fa240fa3c5e2c572821977a968841211 100644 +index 399d0363749e9b79749185bcc1dcd746b026231e..1abd312f068e9326e4a9f95ed58bdb04e35afea8 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -1253,6 +1253,9 @@ interface NetworkContext { +@@ -1275,6 +1275,9 @@ interface NetworkContext { SetNetworkConditions(mojo_base.mojom.UnguessableToken throttling_profile_id, NetworkConditions? conditions); @@ -77,10 +77,10 @@ index e928d012d77ccffe16263fd0ee7c2769adfe4bac..1be1b691fa240fa3c5e2c572821977a9 SetAcceptLanguage(string new_accept_language); diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h -index 97e8f9f145cecad9b0cd8b43a2de4af29cec7778..b9539a731d2252c8301d51ccb7a1bd7b1a23ca53 100644 +index da8551db16849cf82a6e3a7eaeb451cae0707f2d..6e20b8dd4f134ea7a8808223cd59064d89f5ce09 100644 --- a/services/network/test/test_network_context.h +++ b/services/network/test/test_network_context.h -@@ -156,6 +156,7 @@ class TestNetworkContext : public mojom::NetworkContext { +@@ -158,6 +158,7 @@ class TestNetworkContext : public mojom::NetworkContext { void CloseIdleConnections(CloseIdleConnectionsCallback callback) override {} void SetNetworkConditions(const base::UnguessableToken& throttling_profile_id, mojom::NetworkConditionsPtr conditions) override {} diff --git a/patches/chromium/extend_apply_webpreferences.patch b/patches/chromium/extend_apply_webpreferences.patch index e76d41ad4160d..42e76b532e058 100644 --- a/patches/chromium/extend_apply_webpreferences.patch +++ b/patches/chromium/extend_apply_webpreferences.patch @@ -12,10 +12,10 @@ Ideally we could add an embedder observer pattern here but that can be done in future work. diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index e4ab513573c7e327627b1ebd5b263f389098727a..4d5b74aa60a40e49bd0c9131e06eb399d1af4f22 100644 +index f470ada1bf84938427fb89f4508e5f56aaeebc1f..524928729e34cc6b2ae427181e2ac4f8c1128688 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -169,6 +169,7 @@ +@@ -171,6 +171,7 @@ #include "third_party/blink/renderer/core/view_transition/view_transition_supplement.h" #include "third_party/blink/renderer/platform/fonts/font_cache.h" #include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h" @@ -23,7 +23,7 @@ index e4ab513573c7e327627b1ebd5b263f389098727a..4d5b74aa60a40e49bd0c9131e06eb399 #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h" -@@ -1866,6 +1867,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, +@@ -1861,6 +1862,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, #if BUILDFLAG(IS_MAC) web_view_impl->SetMaximumLegibleScale( prefs.default_maximum_page_scale_factor); diff --git a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch index 9add45122b7d3..5ca5f6536b336 100644 --- a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch +++ b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch @@ -13,7 +13,7 @@ uses internally for things like menus and devtools. We can remove this patch once it has in some shape been upstreamed. diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc -index 95876b4040844b598ea7219732a044b4423f8d97..4e02620b7887d1b70b2a9b9e21b819d99efdca18 100644 +index 3f8801c6042ef2b20635838e4d4c572d89b94601..055b15ff72844cd54c60cc92042a5465ce2870ad 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc @@ -210,6 +210,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors, @@ -26,10 +26,10 @@ index 95876b4040844b598ea7219732a044b4423f8d97..4e02620b7887d1b70b2a9b9e21b819d9 } diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h -index 4e825d649919c88aad26e4c3b2fd80575ca57db0..de1d8fea0113b55065e5229b07f95d691ccb393e 100644 +index 97cceb20da0fccf0603cf2d4b1c8985314b1e850..0632d7fdc975dfdc3cc24bd8e641a2680fabc45d 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h -@@ -458,6 +458,23 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -458,6 +458,23 @@ class COMPONENT_EXPORT(NATIVE_THEME) NativeTheme { scoped_refptr custom_theme, bool use_custom_frame = true) const; @@ -53,7 +53,7 @@ index 4e825d649919c88aad26e4c3b2fd80575ca57db0..de1d8fea0113b55065e5229b07f95d69 // Returns a shared instance of the native theme that should be used for web // rendering. Do not use it in a normal application context (i.e. browser). // The returned object should not be deleted by the caller. This function is -@@ -713,6 +730,7 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -713,6 +730,7 @@ class COMPONENT_EXPORT(NATIVE_THEME) NativeTheme { PreferredContrast preferred_contrast_ = PreferredContrast::kNoPreference; std::optional caret_blink_interval_; bool use_overlay_scrollbars_ = false; @@ -62,10 +62,10 @@ index 4e825d649919c88aad26e4c3b2fd80575ca57db0..de1d8fea0113b55065e5229b07f95d69 SEQUENCE_CHECKER(sequence_checker_); }; diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc -index 2b2a12cd9d1ac17a245f049cf0775b46c0596531..350586d35b29fb8d772ea70615768d0e62dc5474 100644 +index dcf2d14d92f8b5a5dd7bf22f8ce00f030824c989..04d0f5d9416eaba569f49a25e7e8c1a89e50ecfe 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc -@@ -688,6 +688,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { +@@ -696,6 +696,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { if (InForcedColorsMode() && !IsForcedDarkMode()) { return false; } diff --git a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch index 681f039b6343b..7f579fde8b272 100644 --- a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch +++ b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch @@ -10,14 +10,16 @@ This CL adds support for the following features to //shell_dialogs: It also: * Changes XDG Portal implementation behavior to set default path regardless of dialog type. +* XDG Portal implementation calls into //electron to perform version checks on the dbus thread + Refs https://github.com/electron/electron/issues/46652. This may be partially upstreamed to Chromium in the future. diff --git a/ui/gtk/select_file_dialog_linux_gtk.cc b/ui/gtk/select_file_dialog_linux_gtk.cc -index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e747b95a33 100644 +index 4a9118dcabbc0cffeea17dc26a8e1f2a54604766..4ae6001c0376822d41a77949ce05ea0328abcee4 100644 --- a/ui/gtk/select_file_dialog_linux_gtk.cc +++ b/ui/gtk/select_file_dialog_linux_gtk.cc -@@ -259,8 +259,12 @@ void SelectFileDialogLinuxGtk::SelectFileImpl( +@@ -261,8 +261,12 @@ void SelectFileDialogLinuxGtk::SelectFileImpl( case SELECT_EXISTING_FOLDER: dialog = CreateSelectFolderDialog(type, title_string, default_path, owning_window); @@ -32,7 +34,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 break; case SELECT_OPEN_FILE: dialog = CreateFileOpenDialog(title_string, default_path, owning_window); -@@ -407,9 +411,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( +@@ -409,9 +413,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( const std::string& title, const base::FilePath& default_path, gfx::NativeWindow parent) { @@ -45,7 +47,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 SetGtkTransientForAura(dialog, parent); AddFilters(GTK_FILE_CHOOSER(dialog)); -@@ -425,6 +431,7 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( +@@ -427,6 +433,7 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( GtkFileChooserSetCurrentFolder(GTK_FILE_CHOOSER(dialog), *last_opened_path()); } @@ -53,7 +55,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 return dialog; } -@@ -440,11 +447,15 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( +@@ -442,11 +449,15 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( ? l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE) : l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); } @@ -74,7 +76,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 GtkWidget* dialog = GtkFileChooserDialogNew( title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, -@@ -466,7 +477,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( +@@ -468,7 +479,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( gtk_file_filter_add_mime_type(only_folders, "inode/directory"); gtk_file_filter_add_mime_type(only_folders, "text/directory"); gtk_file_chooser_add_filter(chooser, only_folders); @@ -84,7 +86,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 return dialog; } -@@ -503,10 +515,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( +@@ -505,10 +517,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( std::string title_string = !title.empty() ? title : l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); @@ -98,7 +100,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 GTK_RESPONSE_ACCEPT); SetGtkTransientForAura(dialog, parent); -@@ -532,9 +545,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( +@@ -534,9 +547,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); // Overwrite confirmation is always enabled in GTK4. if (!GtkCheckVersion(4)) { @@ -111,7 +113,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 return dialog; } -@@ -589,15 +603,29 @@ void SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse( +@@ -591,15 +605,29 @@ void SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse( void SelectFileDialogLinuxGtk::OnSelectMultiFileDialogResponse( GtkWidget* dialog, int response_id) { @@ -345,83 +347,52 @@ index 9d45ec49a4fb5e12407b65b83c1ba0c13cd0dfd8..400cce91b020ecd5e48566f125515d2c + } // namespace ui diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..d29de35ac813cc68b1faa11e803ace9a78df74ba 100644 +index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..e768afc05b42d4d026c88f1516d4e9db84e8dff6 100644 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc +++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -@@ -12,6 +12,7 @@ - #include - - #include "base/check.h" -+#include "base/command_line.h" - #include "base/files/file_util.h" - #include "base/functional/bind.h" - #include "base/logging.h" -@@ -45,6 +46,8 @@ namespace { - constexpr char kXdgPortalService[] = "org.freedesktop.portal.Desktop"; - constexpr char kXdgPortalObject[] = "/org/freedesktop/portal/desktop"; - -+// Version 4 includes support for current_folder option to the OpenFile method via -+// https://github.com/flatpak/xdg-desktop-portal/commit/71165a5. - constexpr int kXdgPortalRequiredVersion = 3; - - constexpr char kFileChooserInterfaceName[] = -@@ -66,6 +69,8 @@ constexpr uint32_t kFileChooserFilterKindGlob = 0; - - constexpr char kFileUriPrefix[] = "file://"; - -+const char kXdgPortalRequiredVersionFlag[] = "xdg-portal-required-version"; -+ - enum class ServiceAvailability { - kNotStarted, - kInProgress, -@@ -75,6 +80,9 @@ enum class ServiceAvailability { - - ServiceAvailability g_service_availability = ServiceAvailability::kNotStarted; - -+uint32_t g_available_portal_version = 0; -+uint32_t g_required_portal_version = kXdgPortalRequiredVersion; -+ - scoped_refptr& GetMainTaskRunner() { - static base::NoDestructor> - main_task_runner; -@@ -94,9 +102,10 @@ void OnGetPropertyReply(dbus::Response* response) { - return; - } - -- g_service_availability = version >= kXdgPortalRequiredVersion -+ g_service_availability = version >= g_required_portal_version - ? ServiceAvailability::kAvailable +@@ -28,6 +28,7 @@ + #include "dbus/message.h" + #include "dbus/object_path.h" + #include "dbus/object_proxy.h" ++#include "electron/shell/browser/ui/file_dialog.h" + #include "ui/aura/window_tree_host.h" + #include "ui/base/l10n/l10n_util.h" + #include "ui/gfx/native_widget_types.h" +@@ -99,7 +100,7 @@ void OnGetPropertyReply(dbus::Response* response) { : ServiceAvailability::kNotAvailable; -+ g_available_portal_version = version; } - void OnServiceStarted(std::optional service_started) { -@@ -164,6 +173,12 @@ void SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground() { - } - g_service_availability = ServiceAvailability::kInProgress; +-void OnServiceStarted(std::optional service_started) { ++[[maybe_unused]] void OnServiceStarted(std::optional service_started) { + if (!service_started.value_or(false)) { + g_service_availability = ServiceAvailability::kNotAvailable; + return; +@@ -166,18 +167,24 @@ void SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground() { -+ auto* cmd = base::CommandLine::ForCurrentProcess(); -+ if (!base::StringToUint(cmd->GetSwitchValueASCII(kXdgPortalRequiredVersionFlag), -+ &g_required_portal_version)) { -+ g_required_portal_version = kXdgPortalRequiredVersion; -+ } -+ GetMainTaskRunner() = base::SequencedTaskRunner::GetCurrentDefault(); ++#if 0 dbus_utils::CheckForServiceAndStart(dbus_thread_linux::GetSharedSessionBus(), -@@ -180,6 +195,11 @@ bool SelectFileDialogLinuxPortal::IsPortalAvailable() { + kXdgPortalService, + base::BindOnce(&OnServiceStarted)); ++#endif ++ file_dialog::StartPortalAvailabilityTestInBackground(); + } + + // static + bool SelectFileDialogLinuxPortal::IsPortalAvailable() { ++#if 0 + if (g_service_availability == ServiceAvailability::kInProgress) { + LOG(WARNING) << "Portal availability checked before test was complete"; + } + return g_service_availability == ServiceAvailability::kAvailable; ++#endif ++ return file_dialog::IsPortalAvailable(); } -+// static -+uint32_t SelectFileDialogLinuxPortal::GetPortalVersion() { -+ return g_available_portal_version; -+} -+ bool SelectFileDialogLinuxPortal::IsRunning( - gfx::NativeWindow parent_window) const { - return parent_window && host_ && host_.get() == parent_window->GetHost(); -@@ -382,11 +402,14 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( +@@ -382,11 +389,14 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( const PortalFilterSet& filter_set) { DbusDictionary dict; @@ -439,7 +410,7 @@ index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..d29de35ac813cc68b1faa11e803ace9a [[fallthrough]]; case SelectFileDialog::SELECT_FOLDER: case SelectFileDialog::Type::SELECT_EXISTING_FOLDER: -@@ -399,6 +422,10 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( +@@ -399,6 +409,10 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( break; } @@ -450,17 +421,3 @@ index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..d29de35ac813cc68b1faa11e803ace9a if (!default_path.empty()) { if (default_path_exists) { // If this is an existing directory, navigate to that directory, with no -diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.h b/ui/shell_dialogs/select_file_dialog_linux_portal.h -index 651684b1840eaff664f3d73d99bbea40e097c866..9a9d541f1e9586d9d545f8547d3f09ff33dce48d 100644 ---- a/ui/shell_dialogs/select_file_dialog_linux_portal.h -+++ b/ui/shell_dialogs/select_file_dialog_linux_portal.h -@@ -45,6 +45,9 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { - // availability test has not yet completed. - static bool IsPortalAvailable(); - -+ // Get version of portal if available. -+ static uint32_t GetPortalVersion(); -+ - protected: - ~SelectFileDialogLinuxPortal() override; - diff --git a/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch b/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch index 48f7a56290f80..2135811c98dc5 100644 --- a/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch +++ b/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch @@ -9,18 +9,18 @@ embedders to make custom schemes allow V8 code cache. Chromium CL: https://chromium-review.googlesource.com/c/chromium/src/+/5019665 diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc -index bba85bb47e489b73c2341bbeb9aa04099d712889..4bd69c3c5511a4f492f78f203284c8e546c9f067 100644 +index cad3cf44df0c65067d18490ee7694fd0f82153af..0f9b64a81e0c4114bd885b24ff65c458810ceb99 100644 --- a/content/browser/code_cache/generated_code_cache.cc +++ b/content/browser/code_cache/generated_code_cache.cc -@@ -12,6 +12,7 @@ - #include +@@ -8,6 +8,7 @@ #include + #include "base/compiler_specific.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" -@@ -36,6 +37,7 @@ +@@ -32,6 +33,7 @@ #include "net/http/http_cache.h" #include "third_party/blink/public/common/scheme_registry.h" #include "url/gurl.h" @@ -28,7 +28,7 @@ index bba85bb47e489b73c2341bbeb9aa04099d712889..4bd69c3c5511a4f492f78f203284c8e5 using storage::BigIOBuffer; -@@ -48,7 +50,7 @@ constexpr char kSeparator[] = " \n"; +@@ -44,7 +46,7 @@ constexpr char kSeparator[] = " \n"; // We always expect to receive valid URLs that can be used as keys to the code // cache. The relevant checks (for ex: resource_url is valid, origin_lock is @@ -37,7 +37,7 @@ index bba85bb47e489b73c2341bbeb9aa04099d712889..4bd69c3c5511a4f492f78f203284c8e5 // // This function doesn't enforce anything in the production code. It is here // to make the assumptions explicit and to catch any errors when DCHECKs are -@@ -58,33 +60,55 @@ void CheckValidKeys(const GURL& resource_url, +@@ -54,33 +56,55 @@ void CheckValidKeys(const GURL& resource_url, GeneratedCodeCache::CodeCacheType cache_type) { // If the resource url is invalid don't cache the code. DCHECK(resource_url.is_valid()); diff --git a/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch b/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch index 1e95cc47416d0..ae499eab7f3b9 100644 --- a/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch +++ b/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch @@ -7,10 +7,10 @@ Subject: feat: allow embedders to add observers on created hunspell This patch is used by Electron to implement spellchecker events. diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc -index 5140efab98b7d8d357ea0d97ee25814ea2e1ed8f..979630ba7ffe84d78804a02b832ce7787aecf2d9 100644 +index c2dd0fdc774a939dfe489180e019718ba4b192f1..910534aac947890b16b176b10ac8eae9857bd2d7 100644 --- a/chrome/browser/spellchecker/spellcheck_service.cc +++ b/chrome/browser/spellchecker/spellcheck_service.cc -@@ -478,6 +478,8 @@ void SpellcheckService::LoadDictionaries() { +@@ -477,6 +477,8 @@ void SpellcheckService::LoadDictionaries() { std::make_unique( dictionary, platform_spellcheck_language, context_, this)); hunspell_dictionaries_.back()->AddObserver(this); @@ -19,7 +19,7 @@ index 5140efab98b7d8d357ea0d97ee25814ea2e1ed8f..979630ba7ffe84d78804a02b832ce778 hunspell_dictionaries_.back()->Load(); } -@@ -532,6 +534,20 @@ bool SpellcheckService::IsSpellcheckEnabled() const { +@@ -531,6 +533,20 @@ bool SpellcheckService::IsSpellcheckEnabled() const { (!hunspell_dictionaries_.empty() || enable_if_uninitialized); } diff --git a/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch b/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch index 5498107b3194f..af85984794495 100644 --- a/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch +++ b/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch @@ -20,7 +20,7 @@ index afb657b7c9e1ede1273532b16428d37cc5d75c59..16707cf516cd34682c84ea2ccebddaa0 requested_format_.pixel_format = media::PIXEL_FORMAT_NV12; DCHECK_GT(requested_format_.frame_size.GetArea(), 0); diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.cc b/content/browser/media/capture/io_surface_capture_device_base_mac.cc -index e59ed011a70358a4ff61342e4db3e0ce4cad8ab0..e3fa18fbade4c11f5317705bc89a1e2456c924bc 100644 +index 4ec4c895423151d2c907d97de1461cbde8c8a639..0e59797833b81c07299b8c342f591e192183c6f1 100644 --- a/content/browser/media/capture/io_surface_capture_device_base_mac.cc +++ b/content/browser/media/capture/io_surface_capture_device_base_mac.cc @@ -20,7 +20,7 @@ void IOSurfaceCaptureDeviceBase::AllocateAndStart( @@ -33,7 +33,7 @@ index e59ed011a70358a4ff61342e4db3e0ce4cad8ab0..e3fa18fbade4c11f5317705bc89a1e24 void IOSurfaceCaptureDeviceBase::StopAndDeAllocate() { diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.h b/content/browser/media/capture/io_surface_capture_device_base_mac.h -index 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124bf2ee1f1 100644 +index e2771b7b281274cdcb601a5bc78a948ad592087b..48d116823a28213e50775f378e6ce04ce3af5072 100644 --- a/content/browser/media/capture/io_surface_capture_device_base_mac.h +++ b/content/browser/media/capture/io_surface_capture_device_base_mac.h @@ -25,7 +25,7 @@ class CONTENT_EXPORT IOSurfaceCaptureDeviceBase @@ -46,11 +46,11 @@ index 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124 // OnStop is called by StopAndDeAllocate. virtual void OnStop() = 0; diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm -index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378719b14fe 100644 +index 7e17594c30ac3cf8cb484b53563b03fc75bd2e0b..0e4a68f2fd8179640f877cb258b4049610fd49da 100644 --- a/content/browser/media/capture/screen_capture_kit_device_mac.mm +++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm -@@ -26,6 +26,61 @@ - std::optional, +@@ -27,6 +27,61 @@ + std::optional, bool)>; using ErrorCallback = base::RepeatingClosure; +using CancelCallback = base::RepeatingClosure; @@ -111,7 +111,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 namespace { API_AVAILABLE(macos(12.3)) -@@ -120,18 +175,22 @@ @interface ScreenCaptureKitDeviceHelper +@@ -123,18 +178,22 @@ @interface ScreenCaptureKitDeviceHelper : NSObject - (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback @@ -134,7 +134,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 _errorCallback = errorCallback; } return self; -@@ -207,29 +266,53 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize +@@ -211,29 +270,53 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac : public IOSurfaceCaptureDeviceBase, @@ -192,7 +192,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 void OnShareableContentCreated(SCShareableContent* content) { DCHECK(device_task_runner_->RunsTasksInCurrentSequence()); -@@ -297,7 +380,7 @@ void CreateStream(SCContentFilter* filter) { +@@ -301,7 +384,7 @@ void CreateStream(SCContentFilter* filter) { return; } @@ -201,7 +201,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 // Update the content size. This step is neccessary when used together // with SCContentSharingPicker. If the Chrome picker is used, it will // change to retina resolution if applicable. -@@ -306,6 +389,9 @@ void CreateStream(SCContentFilter* filter) { +@@ -310,6 +393,9 @@ void CreateStream(SCContentFilter* filter) { filter.contentRect.size.height * filter.pointPixelScale); } @@ -211,7 +211,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 gfx::RectF dest_rect_in_frame; actual_capture_format_ = capture_params().requested_format; actual_capture_format_.pixel_format = media::PIXEL_FORMAT_NV12; -@@ -319,6 +405,7 @@ void CreateStream(SCContentFilter* filter) { +@@ -323,6 +409,7 @@ void CreateStream(SCContentFilter* filter) { stream_ = [[SCStream alloc] initWithFilter:filter configuration:config delegate:helper_]; @@ -219,7 +219,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 { NSError* error = nil; bool add_stream_output_result = -@@ -472,7 +559,7 @@ void OnStreamError() { +@@ -480,7 +567,7 @@ void OnStreamError() { if (fullscreen_module_) { fullscreen_module_->Reset(); } @@ -228,7 +228,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 } else { client()->OnError(media::VideoCaptureError::kScreenCaptureKitStreamError, FROM_HERE, "Stream delegate called didStopWithError"); -@@ -495,23 +582,41 @@ void OnUpdateConfigurationError() { +@@ -503,23 +590,41 @@ void OnUpdateConfigurationError() { } // IOSurfaceCaptureDeviceBase: @@ -285,7 +285,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 } void OnStop() override { DCHECK(device_task_runner_->RunsTasksInCurrentSequence()); -@@ -569,8 +674,9 @@ void ResetStreamTo(SCWindow* window) override { +@@ -577,8 +682,9 @@ void ResetStreamTo(SCWindow* window) override { } private: @@ -296,7 +296,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 const scoped_refptr device_task_runner_; // The actual format of the video frames that are sent to `client`. -@@ -586,6 +692,10 @@ void ResetStreamTo(SCWindow* window) override { +@@ -594,6 +700,10 @@ void ResetStreamTo(SCWindow* window) override { // Helper class that acts as output and delegate for `stream_`. ScreenCaptureKitDeviceHelper* __strong helper_; @@ -307,7 +307,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 // This is used to detect when a captured presentation enters fullscreen mode. // If this happens, the module will call the ResetStreamTo function. std::unique_ptr fullscreen_module_; -@@ -598,6 +708,8 @@ void ResetStreamTo(SCWindow* window) override { +@@ -606,6 +716,8 @@ void ResetStreamTo(SCWindow* window) override { base::WeakPtrFactory weak_factory_{this}; }; diff --git a/patches/chromium/feat_configure_launch_options_for_service_process.patch b/patches/chromium/feat_configure_launch_options_for_service_process.patch index 2ce4043d9ee56..558d8392f093f 100644 --- a/patches/chromium/feat_configure_launch_options_for_service_process.patch +++ b/patches/chromium/feat_configure_launch_options_for_service_process.patch @@ -19,7 +19,7 @@ to STDOUT_FILENO/STD_OUTPUT_HANDLE and STDERR_FILENO/STD_ERROR_HANDLE allowing t parent process to read from the pipe. diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h -index 6a3645f020d7348f758ebcc93c49ddcbee709937..55994606517a4f52475610db2da26b5560865fab 100644 +index ac6f72c99800d5437ddc4aa203870242fb9220b9..93055bda5478f4b7b401ae06dcddce36a26f5ad7 100644 --- a/content/browser/child_process_launcher.h +++ b/content/browser/child_process_launcher.h @@ -33,6 +33,7 @@ @@ -30,7 +30,7 @@ index 6a3645f020d7348f758ebcc93c49ddcbee709937..55994606517a4f52475610db2da26b55 #endif #if BUILDFLAG(IS_POSIX) -@@ -196,7 +197,10 @@ struct ChildProcessLauncherFileData { +@@ -188,7 +189,10 @@ struct ChildProcessLauncherFileData { delete; ~ChildProcessLauncherFileData(); @@ -42,8 +42,8 @@ index 6a3645f020d7348f758ebcc93c49ddcbee709937..55994606517a4f52475610db2da26b55 // Files opened by the browser and passed as corresponding file descriptors // in the child process. If a FilePath is provided, the file will be opened // and the descriptor cached for future process launches. If a ScopedFD is -@@ -211,6 +215,15 @@ struct ChildProcessLauncherFileData { - std::map> +@@ -203,6 +207,15 @@ struct ChildProcessLauncherFileData { + std::map> files_to_preload; #endif + @@ -187,10 +187,10 @@ index 96c9563aac5847e742de5d9c9236f78bcb6cfd9c..73c9d585579ad5bdc407687b8becd0b7 host->GetChildProcess()->BindServiceInterface(std::move(receiver)); } diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc -index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8f137e7b9 100644 +index 9e01e61bf1fce448a93eaa3d5f363fc835b78538..d210af6fb317c922a8415a67a7ccd1d8a4a88ea1 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc -@@ -189,11 +189,13 @@ const ChildProcessData& UtilityProcessHost::GetData() { +@@ -190,11 +190,13 @@ const ChildProcessData& UtilityProcessHost::GetData() { return process_->GetData(); } @@ -206,7 +206,7 @@ index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8 bool UtilityProcessHost::Start() { return StartProcess(); -@@ -240,6 +242,30 @@ void UtilityProcessHost::SetZygoteForTesting(ZygoteCommunication* handle) { +@@ -241,6 +243,30 @@ void UtilityProcessHost::SetZygoteForTesting(ZygoteCommunication* handle) { } #endif // BUILDFLAG(USE_ZYGOTE) @@ -237,7 +237,7 @@ index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8 mojom::ChildProcess* UtilityProcessHost::GetChildProcess() { return static_cast(process_->GetHost()) ->child_process(); -@@ -454,9 +480,26 @@ bool UtilityProcessHost::StartProcess() { +@@ -456,9 +482,26 @@ bool UtilityProcessHost::StartProcess() { } #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) && !BUILDFLAG(IS_WIN) @@ -266,7 +266,7 @@ index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8 #if BUILDFLAG(IS_WIN) if (!preload_libraries_.empty()) { diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h -index 066939fcbb7bc05e27a6bb4ed122750732629454..ab6b64737245a840a3e2b06d2ab501ef4aaed7a2 100644 +index d13e6db4857242480591bff040709532d16f513d..1164da12ee71a8575c17bf1b84a505e8a32b96b3 100644 --- a/content/browser/service_host/utility_process_host.h +++ b/content/browser/service_host/utility_process_host.h @@ -30,6 +30,10 @@ diff --git a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch new file mode 100644 index 0000000000000..c18b4dfd98002 --- /dev/null +++ b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch @@ -0,0 +1,440 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Calvin Watford +Date: Mon, 9 Dec 2024 16:58:15 -0700 +Subject: feat: Corner Smoothing CSS rule and Blink painting + +This patch implements the `-electron-corner-smoothing` CSS rule by +making three primary changes to Blink: + +1. Adds the `-electron-corner-smoothing` CSS rule: + * Metadata in `blink/renderer/core/css/css_properties.json5` + * Parsing in `blink/renderer/core/css/properties/longhands/longhands_custom.cc` + * Other required definitions for all CSS rules (`css_property_id.mojom`, `css_property_equality.cc`) + +2. Modifies how Blink paints rounded rectangles: + * Augments `blink::ContouredRect` to add smoothness. + * Modifies graphics to handle smooth `ContouredRect`s, delegating to + `//electron/shell/renderer/electron_smooth_round_rect`. + +3. Adds a renderer preference / web setting: + * Controls whether the CSS rule is available. + * Mostly simple "plumbing" for the setting through blink. + +diff --git a/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc b/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc +index 5e233d63c09088d73cd1a54a58b235018c193ac3..4f2dcb339ad79f31ba5e4c347cb91d5639d27ce6 100644 +--- a/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc ++++ b/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc +@@ -128,6 +128,8 @@ bool StructTraitselectron_corner_smoothing_css = data.electron_corner_smoothing_css(); ++ + out->canvas_noise_token = data.canvas_noise_token(); + + return true; +diff --git a/third_party/blink/public/common/renderer_preferences/renderer_preferences.h b/third_party/blink/public/common/renderer_preferences/renderer_preferences.h +index ff84a20511448d4211d0e25dfc12e7eabc34a9e0..886e9d819c3bde7f33eec3497d1cadb76de4237f 100644 +--- a/third_party/blink/public/common/renderer_preferences/renderer_preferences.h ++++ b/third_party/blink/public/common/renderer_preferences/renderer_preferences.h +@@ -91,6 +91,7 @@ struct BLINK_COMMON_EXPORT RendererPreferences { + bool caret_browsing_enabled{false}; + bool uses_platform_autofill{false}; + std::vector explicitly_allowed_network_ports; ++ bool electron_corner_smoothing_css{true}; + uint64_t canvas_noise_token{0}; + + RendererPreferences(); +diff --git a/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h b/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h +index c88ddaf7fd5fc27889bcacac9366330e4013eba3..e4f492a11637886c60ece665371d117f3a34ec8d 100644 +--- a/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h ++++ b/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h +@@ -275,6 +275,11 @@ struct BLINK_COMMON_EXPORT + return data.explicitly_allowed_network_ports; + } + ++ static const bool& electron_corner_smoothing_css( ++ const ::blink::RendererPreferences& data) { ++ return data.electron_corner_smoothing_css; ++ } ++ + static const uint64_t& canvas_noise_token( + const ::blink::RendererPreferences& data) { + return data.canvas_noise_token; +diff --git a/third_party/blink/public/mojom/renderer_preferences.mojom b/third_party/blink/public/mojom/renderer_preferences.mojom +index 65766b955e81bfc332bc2c4e0b9da48389c1bd68..a475e1bfee46f0a77d1cfbdea47e9de6516d1194 100644 +--- a/third_party/blink/public/mojom/renderer_preferences.mojom ++++ b/third_party/blink/public/mojom/renderer_preferences.mojom +@@ -202,6 +202,8 @@ struct RendererPreferences { + + array explicitly_allowed_network_ports; + ++ bool electron_corner_smoothing_css; ++ + // A randomized 64 bit token that is generated per browser session, + // used for canvas noising. + uint64 canvas_noise_token = 0; +diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +index a4c78d85e525224823ce4b2434519cc0a39922ef..8071b283b48ab89226313bb199c0cece67ca5039 100644 +--- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom ++++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +@@ -48,6 +48,7 @@ enum CSSSampleId { + kInternalForcedVisitedColor = 0, + kInternalOverflowBlock = 0, + kInternalOverflowInline = 0, ++ kElectronCornerSmoothing = 0, + + // This CSSSampleId represents page load for CSS histograms. It is recorded once + // per page visit for each CSS histogram being logged on the blink side and the +diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h +index a53b4901dde0dc83dce6c9b56616eef0d02d94a5..b419672af985f673f375fbb63b4d2b2c419e3e03 100644 +--- a/third_party/blink/public/web/web_settings.h ++++ b/third_party/blink/public/web/web_settings.h +@@ -285,6 +285,7 @@ class WebSettings { + virtual void SetRequireTransientActivationAndAuthorizationForSubAppsAPIs( + bool) = 0; + virtual void SetRootScrollbarThemeColor(std::optional) = 0; ++ virtual void SetCornerSmoothingCSS(bool) = 0; + + protected: + ~WebSettings() = default; +diff --git a/third_party/blink/renderer/build/scripts/core/css/css_properties.py b/third_party/blink/renderer/build/scripts/core/css/css_properties.py +index aa3a6b93cceae8c8bfbefdd7a043ae576f921979..17544d7ed4757fb51e2f82fd1b90638131ba05cc 100755 +--- a/third_party/blink/renderer/build/scripts/core/css/css_properties.py ++++ b/third_party/blink/renderer/build/scripts/core/css/css_properties.py +@@ -313,7 +313,7 @@ class CSSProperties(object): + if name_without_leading_dash.startswith('-'): + name_without_leading_dash = name_without_leading_dash[1:] + internal_visited_order = 1 +- if name_without_leading_dash.startswith('internal-visited-'): ++ if name_without_leading_dash.startswith('internal-'): + internal_visited_order = 0 + property_.sorting_key = (-property_.priority, + internal_visited_order, +diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 +index 15ed205144fbb058bcc88953acb8380574cb7044..1795bceb2e7548c6e072c1bd2ce6abd0b3bd8188 100644 +--- a/third_party/blink/renderer/core/css/css_properties.json5 ++++ b/third_party/blink/renderer/core/css/css_properties.json5 +@@ -8796,6 +8796,24 @@ + property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], + }, + ++ { ++ name: "-electron-corner-smoothing", ++ property_methods: ["ParseSingleValue"], ++ field_group: "*", ++ field_template: "external", ++ // To keep this patch small, Length is used instead of a more descriptive ++ // custom type. ++ // - `system-ui` = `Length::Auto()` ++ // - percent = `Length::Percent` ++ type_name: "Length", ++ converter: "ConvertCornerSmoothing", ++ keywords: ["system-ui"], ++ default_value: "Length::None()", ++ typedom_types: ["Keyword", "Percentage"], ++ is_border_radius: true, ++ invalidate: ["paint", "border-radius", "clip"], ++ }, ++ + // Visited properties. + { + name: "-internal-visited-color", +diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc +index 10ab8a458fb3348476a6e904f684af77c55b103a..37339df2a5601b59abc50fe75fb844e1b7bb5368 100644 +--- a/third_party/blink/renderer/core/css/css_property_equality.cc ++++ b/third_party/blink/renderer/core/css/css_property_equality.cc +@@ -346,6 +346,8 @@ bool CSSPropertyEquality::PropertiesEqual(const PropertyHandle& property, + return a.DominantBaseline() == b.DominantBaseline(); + case CSSPropertyID::kDynamicRangeLimit: + return a.GetDynamicRangeLimit() == b.GetDynamicRangeLimit(); ++ case CSSPropertyID::kElectronCornerSmoothing: ++ return a.ElectronCornerSmoothing() == b.ElectronCornerSmoothing(); + case CSSPropertyID::kEmptyCells: + return a.EmptyCells() == b.EmptyCells(); + case CSSPropertyID::kFill: +diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +index 3fb84db022f14819239cd55da79f43cb9b0bcbc7..c4b7ff8ea240054d9394a0691e978e9b10eecc50 100644 +--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc ++++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +@@ -12047,5 +12047,25 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue( + CSSValueID::kNone>(stream); + } + ++const CSSValue* ElectronCornerSmoothing::ParseSingleValue( ++ CSSParserTokenStream& stream, ++ const CSSParserContext& context, ++ const CSSParserLocalContext&) const { ++ // Fail parsing if this rule is disabled by document settings. ++ if (Settings* settings = context.GetDocument()->GetSettings(); ++ settings && !settings->GetElectronCornerSmoothingCSS()) { ++ return nullptr; ++ } ++ ++ // Try to parse `system-ui` keyword first. ++ if (auto* ident = ++ css_parsing_utils::ConsumeIdent(stream)) { ++ return ident; ++ } ++ // Try to parse as percent. ++ return css_parsing_utils::ConsumePercent( ++ stream, context, CSSPrimitiveValue::ValueRange::kNonNegative); ++} ++ + } // namespace css_longhand + } // namespace blink +diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +index f7f49ef23b77c7a3f27ae8ba129e5eea1617d545..0ddf6f59c45d2f6212317dc18f3c5a80eeda5395 100644 +--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc ++++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +@@ -3859,4 +3859,12 @@ PositionArea StyleBuilderConverter::ConvertPositionArea( + return PositionArea(span[0], span[1], span[2], span[3]); + } + ++Length StyleBuilderConverter::ConvertCornerSmoothing(StyleResolverState& state, const CSSValue& value) { ++ auto* ident = DynamicTo(value); ++ if (ident && ident->GetValueID() == CSSValueID::kSystemUi) { ++ return Length::Auto(); ++ } ++ return ConvertLength(state, value); ++} ++ + } // namespace blink +diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h +index b9f8145af13fb9d68e85f166905476c177c3a053..0a9ad304f5039747a0580ab63664d4429a67970d 100644 +--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h ++++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h +@@ -419,6 +419,8 @@ class StyleBuilderConverter { + const CSSValue&); + + static PositionArea ConvertPositionArea(StyleResolverState&, const CSSValue&); ++ ++ static Length ConvertCornerSmoothing(StyleResolverState&, const CSSValue&); + }; + + template +diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc +index 4a29a2200eaab5084078e928a68c862296c6ff91..fcd879deec0e68b3b6988402d19570cf0065daa2 100644 +--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc ++++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc +@@ -816,4 +816,8 @@ void WebSettingsImpl::SetRootScrollbarThemeColor( + settings_->SetRootScrollbarThemeColor(theme_color); + } + ++void WebSettingsImpl::SetCornerSmoothingCSS(bool available) { ++ settings_->SetElectronCornerSmoothingCSS(available); ++} ++ + } // namespace blink +diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h +index 5e8d2bfbccd0625c2598544a9cba3d71373eded2..e68a97ee75754fc7196f11cf5c731550b5a12276 100644 +--- a/third_party/blink/renderer/core/exported/web_settings_impl.h ++++ b/third_party/blink/renderer/core/exported/web_settings_impl.h +@@ -237,6 +237,7 @@ class CORE_EXPORT WebSettingsImpl final : public WebSettings { + void SetRequireTransientActivationAndAuthorizationForSubAppsAPIs( + bool) override; + void SetRootScrollbarThemeColor(std::optional) override; ++ void SetCornerSmoothingCSS(bool) override; + + bool RenderVSyncNotificationEnabled() const { + return render_v_sync_notification_enabled_; +diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc +index 524928729e34cc6b2ae427181e2ac4f8c1128688..9ce184f279b11a90c7d6f9b9ab7b20da09122a40 100644 +--- a/third_party/blink/renderer/core/exported/web_view_impl.cc ++++ b/third_party/blink/renderer/core/exported/web_view_impl.cc +@@ -3576,6 +3576,9 @@ void WebViewImpl::UpdateRendererPreferences( + CanvasNoiseToken::Set(renderer_preferences_.canvas_noise_token); + + MaybePreloadSystemFonts(GetPage()); ++ ++ GetSettings()->SetCornerSmoothingCSS( ++ renderer_preferences_.electron_corner_smoothing_css); + } + + void WebViewImpl::SetHistoryIndexAndLength(int32_t history_index, +diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5 +index f4cdee12ea4352067f5de3e074e43d51ef56d2e5..6377e4b1ea8aa46b0bf69f8420b6c439bea70dba 100644 +--- a/third_party/blink/renderer/core/frame/settings.json5 ++++ b/third_party/blink/renderer/core/frame/settings.json5 +@@ -1261,5 +1261,10 @@ + initial: false, + type: "bool" + }, ++ { ++ name: "electronCornerSmoothingCSS", ++ initial: true, ++ invalidate: ["Style"], ++ }, + ], + } +diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc +index 57f71cbd80d88416693e5b83f695fd02eb7a405b..d638da7a734fa40de118d056c59488b63caff7e2 100644 +--- a/third_party/blink/renderer/core/paint/box_painter_base.cc ++++ b/third_party/blink/renderer/core/paint/box_painter_base.cc +@@ -417,16 +417,20 @@ void BoxPainterBase::PaintInsetBoxShadow(const PaintInfo& info, + ContouredRect inner_rounded_rect( + FloatRoundedRect(inner_rect, bounds.GetRadii())); + ApplySpreadToShadowShape(inner_rounded_rect, -shadow.Spread()); ++ ContouredRect contoured_bounds( ++ bounds, ContouredBorderGeometry::ContouredBorder( ++ style, PhysicalRect::EnclosingRect(bounds.Rect())) ++ .GetCornerCurvature()); + if (inner_rounded_rect.IsEmpty()) { + // |AutoDarkMode::Disabled()| is used because |shadow_color| has already + // been adjusted for dark mode. +- context.FillRoundedRect(bounds, shadow_color, AutoDarkMode::Disabled()); ++ context.FillContouredRect(contoured_bounds, shadow_color, AutoDarkMode::Disabled()); + continue; + } + GraphicsContextStateSaver state_saver(context); + if (bounds.IsRounded()) { + // TODO(crbug.com/397459628) render corner-shape with box-shadow +- context.ClipContouredRect(ContouredRect(bounds)); ++ context.ClipContouredRect(contoured_bounds); + } else { + context.Clip(bounds.Rect()); + } +diff --git a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc +index 2c2f4f405074e5baa4a26f255283404f86b40e21..ebeb7d6988ee9e6a4e78cb82fc01fdad6721eaef 100644 +--- a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc ++++ b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc +@@ -43,6 +43,24 @@ float EffectiveCurvature(Superellipse superellipse, const gfx::SizeF& radius) { + : superellipse.Exponent(); + } + ++float SmoothnessFromLength(const Length& length) { ++ // `none` = 0% ++ if (length.IsNone()) { ++ return 0.0f; ++ } ++ ++ // `system-ui` keyword, represented internally as "auto" length ++ if (length.HasAuto()) { ++#if BUILDFLAG(IS_MAC) ++ return 0.6f; ++#else ++ return 0.0f; ++#endif // BUILDFLAG(IS_MAC) ++ } ++ ++ return length.Percent() / 100.0f; ++} ++ + ContouredRect::CornerCurvature CalcCurvatureFor( + const ComputedStyle& style, + const FloatRoundedRect::Radii& radii) { +@@ -50,7 +68,8 @@ ContouredRect::CornerCurvature CalcCurvatureFor( + EffectiveCurvature(style.CornerTopLeftShape(), radii.TopLeft()), + EffectiveCurvature(style.CornerTopRightShape(), radii.TopRight()), + EffectiveCurvature(style.CornerBottomRightShape(), radii.BottomRight()), +- EffectiveCurvature(style.CornerBottomLeftShape(), radii.BottomLeft())); ++ EffectiveCurvature(style.CornerBottomLeftShape(), radii.BottomLeft()), ++ SmoothnessFromLength(style.ElectronCornerSmoothing())); + } + + ContouredRect PixelSnappedContouredBorderInternal( +diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn +index 291676c6583722a3dfbac4d4aaad1ea2ae3cc079..5cd89e8c6c79ab339ffbc330d9bcdb08cca6e869 100644 +--- a/third_party/blink/renderer/platform/BUILD.gn ++++ b/third_party/blink/renderer/platform/BUILD.gn +@@ -1646,6 +1646,8 @@ component("platform") { + "widget/widget_base.h", + "widget/widget_base_client.h", + "windows_keyboard_codes.h", ++ "//electron/shell/renderer/electron_smooth_round_rect.h", ++ "//electron/shell/renderer/electron_smooth_round_rect.cc", + ] + + sources -= blink_platform_avx_files +diff --git a/third_party/blink/renderer/platform/geometry/contoured_rect.h b/third_party/blink/renderer/platform/geometry/contoured_rect.h +index b147b8d321d865295007516b15d0aaccfc6f7fac..8f54a3a657c660a52fcd4c94865ca2197b0af514 100644 +--- a/third_party/blink/renderer/platform/geometry/contoured_rect.h ++++ b/third_party/blink/renderer/platform/geometry/contoured_rect.h +@@ -47,19 +47,29 @@ class PLATFORM_EXPORT ContouredRect { + constexpr CornerCurvature(float top_left, + float top_right, + float bottom_right, +- float bottom_left) ++ float bottom_left, ++ float smoothness) + : top_left_(top_left), + top_right_(top_right), + bottom_right_(bottom_right), +- bottom_left_(bottom_left) { ++ bottom_left_(bottom_left), ++ smoothness_(smoothness) { + DCHECK_GE(top_left, 0); + DCHECK_GE(top_right, 0); + DCHECK_GE(bottom_right, 0); + DCHECK_GE(bottom_left, 0); ++ DCHECK_GE(smoothness, 0); + } ++ constexpr CornerCurvature(float top_left, ++ float top_right, ++ float bottom_right, ++ float bottom_left) ++ : CornerCurvature(top_left, top_right, bottom_right, bottom_left, 0) {} ++ ++ constexpr bool IsSmooth() const { return smoothness_ > 0.0f; } + + constexpr bool IsRound() const { +- return (top_left_ == kRound) && IsUniform(); ++ return (top_left_ == kRound) && IsUniform() && !IsSmooth(); + } + + constexpr bool IsUniform() const { +@@ -71,6 +81,7 @@ class PLATFORM_EXPORT ContouredRect { + constexpr float TopRight() const { return top_right_; } + constexpr float BottomRight() const { return bottom_right_; } + constexpr float BottomLeft() const { return bottom_left_; } ++ constexpr float Smoothness() const { return smoothness_; } + + constexpr bool operator==(const CornerCurvature&) const = default; + +@@ -81,6 +92,7 @@ class PLATFORM_EXPORT ContouredRect { + float top_right_ = kRound; + float bottom_right_ = kRound; + float bottom_left_ = kRound; ++ float smoothness_ = 0.0f; + }; + + // A Corner is a axis-aligned quad, with the points ordered (start, outer, +diff --git a/third_party/blink/renderer/platform/geometry/path_builder.cc b/third_party/blink/renderer/platform/geometry/path_builder.cc +index 346cfc0b13b31808fbe1381b3785150810f347bb..ebb2be8cc3bb71c4df6526d743a851121e36c138 100644 +--- a/third_party/blink/renderer/platform/geometry/path_builder.cc ++++ b/third_party/blink/renderer/platform/geometry/path_builder.cc +@@ -4,6 +4,7 @@ + + #include "third_party/blink/renderer/platform/geometry/path_builder.h" + ++#include "electron/shell/renderer/electron_smooth_round_rect.h" + #include "third_party/blink/renderer/platform/geometry/contoured_rect.h" + #include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h" + #include "third_party/blink/renderer/platform/geometry/path.h" +@@ -231,6 +232,26 @@ PathBuilder& PathBuilder::AddContouredRect( + AddRoundedRect(target_rect); + return *this; + } ++ ++ // TODO(clavin): decompose `electron::DrawSmoothRoundRect` into corners ++ if (contoured_rect.GetCornerCurvature().IsSmooth()) { ++ const gfx::RectF& box = contoured_rect.Rect(); ++ ++ // Constrain the radii (on a copy) to ensure they do not exceed the box. ++ FloatRoundedRect round_rect_copy = contoured_rect.AsRoundedRect(); ++ round_rect_copy.ConstrainRadii(); ++ const FloatRoundedRect::Radii& radii = round_rect_copy.GetRadii(); ++ float smoothness = std::clamp( ++ contoured_rect.GetCornerCurvature().Smoothness(), 0.0f, 1.0f); ++ ++ builder_.addPath(electron::DrawSmoothRoundRect( ++ box.x(), box.y(), box.width(), box.height(), smoothness, ++ radii.TopLeft().width(), radii.TopRight().width(), ++ radii.BottomRight().width(), radii.BottomLeft().width())); ++ ++ return *this; ++ } ++ + const FloatRoundedRect& origin_rect = contoured_rect.GetOriginRect(); + + if (origin_rect == target_rect) { diff --git a/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch b/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch index 59c949c3587cb..d29ecc8b9f5ae 100644 --- a/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch +++ b/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch @@ -11,10 +11,10 @@ ensure it has minimum contrast required to be accessible. This should be upstreamed to Chromium if possible. diff --git a/ui/views/window/frame_caption_button.cc b/ui/views/window/frame_caption_button.cc -index 9f2f19eca80d420e10e5269fb24d4ca52f234ae9..ec54aaa17054468a21af4a1a9736da9134dfe4e6 100644 +index f70d810e5c316a0dfee0b12a0972679f5f56b22b..5a5b2b3e1a70c7365a4d6deb90c183f8392c8c9f 100644 --- a/ui/views/window/frame_caption_button.cc +++ b/ui/views/window/frame_caption_button.cc -@@ -107,7 +107,7 @@ FrameCaptionButton::FrameCaptionButton(PressedCallback callback, +@@ -108,7 +108,7 @@ FrameCaptionButton::FrameCaptionButton(PressedCallback callback, FrameCaptionButton::~FrameCaptionButton() = default; // static @@ -23,7 +23,7 @@ index 9f2f19eca80d420e10e5269fb24d4ca52f234ae9..ec54aaa17054468a21af4a1a9736da91 // Use IsDark() to change target colors instead of PickContrastingColor(), so // that DefaultFrameHeader::GetTitleColor() (which uses different target // colors) can change between light/dark targets at the same time. It looks -@@ -124,6 +124,22 @@ SkColor FrameCaptionButton::GetButtonColor(SkColor background_color) { +@@ -125,6 +125,22 @@ SkColor FrameCaptionButton::GetButtonColor(SkColor background_color) { .color; } @@ -47,10 +47,10 @@ index 9f2f19eca80d420e10e5269fb24d4ca52f234ae9..ec54aaa17054468a21af4a1a9736da91 float FrameCaptionButton::GetInactiveButtonColorAlphaRatio() { return 0.38f; diff --git a/ui/views/window/frame_caption_button.h b/ui/views/window/frame_caption_button.h -index 0ac923a3ca6052d499ed7c1a4f156b0f19ad4e64..3164f79828218d57843eba823e0f14ff456b2df4 100644 +index e74917399d3306e6557069d85fe90d153cecf585..738965e15ea4a5c3d71a5bf79ea0df78dac9cd73 100644 --- a/ui/views/window/frame_caption_button.h +++ b/ui/views/window/frame_caption_button.h -@@ -44,8 +44,18 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { +@@ -45,8 +45,18 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { FrameCaptionButton& operator=(const FrameCaptionButton&) = delete; ~FrameCaptionButton() override; @@ -70,10 +70,10 @@ index 0ac923a3ca6052d499ed7c1a4f156b0f19ad4e64..3164f79828218d57843eba823e0f14ff // Gets the alpha ratio for the colors of inactive frame caption buttons. static float GetInactiveButtonColorAlphaRatio(); -@@ -134,6 +144,7 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { +@@ -135,6 +145,7 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { // TODO(b/292154873): Store the foreground color instead of the background // color for the SkColor type. - absl::variant color_ = gfx::kPlaceholderColor; + std::variant color_ = gfx::kPlaceholderColor; + SkColor button_color_ = SkColor(); // Whether the button should be painted as active. diff --git a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch index ad6cfd909bc43..4dd2eec78d4cd 100644 --- a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch +++ b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch @@ -90,10 +90,10 @@ index 8af69cac78b7488d28f1f05ccb174793fe5148cd..9f74e511c263d147b5fbe81fe100d217 private: const HWND hwnd_; diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn -index 610ed4c922705697b27af569af55064daeff5d25..9cb52cb768e648235f89f65e870d853d45268900 100644 +index 067b3cc0d6a8b5990dc4bad0f15e47bd4922a26a..4aaf506c9fadb3f4c5a79560bf4558462d2c8705 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn -@@ -168,6 +168,8 @@ viz_component("service") { +@@ -178,6 +178,8 @@ viz_component("service") { "display_embedder/skia_output_surface_impl_on_gpu_debug_capture.h", "display_embedder/skia_render_copy_results.cc", "display_embedder/skia_render_copy_results.h", @@ -509,10 +509,10 @@ index 0000000000000000000000000000000000000000..e1a22ee881c0fd679ac2d2d4d11a3c93 + +#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_ diff --git a/components/viz/service/display_embedder/software_output_device_win.cc b/components/viz/service/display_embedder/software_output_device_win.cc -index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109ccda35248f 100644 +index f0aca972c4a81c3dfb536e14244daafae21ee716..a15afbc1a3519e657121b4952444d2f47d872e42 100644 --- a/components/viz/service/display_embedder/software_output_device_win.cc +++ b/components/viz/service/display_embedder/software_output_device_win.cc -@@ -149,7 +149,7 @@ void SoftwareOutputDeviceWinProxy::EndPaintDelegated( +@@ -156,7 +156,7 @@ void SoftwareOutputDeviceWinProxy::EndPaintDelegated( if (!canvas_) return; @@ -522,10 +522,10 @@ index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109cc waiting_on_draw_ack_ = true; diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -index d119eb1e217cf90c85bf752f1530345b1eec01f2..655ed4ee5525a126309f173775a28e183b4dd29a 100644 +index 2b6ceac536ca667caad8f1aa1d8ab6c5139083d8..869468a0ab7708fc300a098c47d5d490f17190a8 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -@@ -112,7 +112,8 @@ RootCompositorFrameSinkImpl::Create( +@@ -115,7 +115,8 @@ RootCompositorFrameSinkImpl::Create( params->gpu_compositing, params->widget); auto output_surface = output_surface_provider->CreateOutputSurface( params->widget, params->gpu_compositing, display_client.get(), @@ -564,10 +564,10 @@ index 399fba1a3d4e601dc2cdd5f1f4def8b7fd7a3011..8bcbe0d26c80323155d536c0d3a177a1 gpu::SyncPointManager* GetSyncPointManager() override; gpu::Scheduler* GetGpuScheduler() override; diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc -index 378910976c702d81c263973d29e17646c3963494..84553085b75b8cb9296c4af726f9a7bfc0318980 100644 +index 7839f5afb9ac1aef3f809042d14e6bf2f48f84af..7df780d195e3cfcbfc0857133f2c0d21abe04121 100644 --- a/content/browser/compositor/viz_process_transport_factory.cc +++ b/content/browser/compositor/viz_process_transport_factory.cc -@@ -441,8 +441,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( +@@ -431,8 +431,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( mojo::AssociatedRemote display_private; root_params->display_private = display_private.BindNewEndpointAndPassReceiver(); @@ -585,10 +585,10 @@ index 378910976c702d81c263973d29e17646c3963494..84553085b75b8cb9296c4af726f9a7bf compositor_data.display_client->GetBoundRemote(resize_task_runner_); mojo::AssociatedRemote diff --git a/services/viz/privileged/mojom/compositing/display_private.mojom b/services/viz/privileged/mojom/compositing/display_private.mojom -index 7d19b6be8bb0e0269c381cf6efdf79eaeff1e935..b8ec06ade095df99c024396a601dbf1abb97cc00 100644 +index 3046e60995bceb1c9931038be3d309577a830a60..4476f48510f807d03f05a4afa2a648dac8720010 100644 --- a/services/viz/privileged/mojom/compositing/display_private.mojom +++ b/services/viz/privileged/mojom/compositing/display_private.mojom -@@ -117,7 +117,6 @@ interface DisplayClient { +@@ -125,7 +125,6 @@ interface DisplayClient { // Creates a LayeredWindowUpdater implementation to draw into a layered // window. @@ -620,10 +620,10 @@ index 2f462f0deb5fc8a637457243fb5d5849fc214d14..695869b83cefaa24af93a2e11b39de05 + Draw(gfx.mojom.Rect damage_rect) => (); }; diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index 7f71cc6b8812cc112262d3e9153884e428a7e495..8a61b0cc5d45a55ac188babf63b59492cff428e1 100644 +index c802f9aae242c1be364890e0e6968d09be87f946..38b6c47f526b428562f340966352d32ba918f2d5 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h -@@ -92,6 +92,7 @@ class DisplayPrivate; +@@ -88,6 +88,7 @@ class DisplayPrivate; class ExternalBeginFrameController; } // namespace mojom @@ -631,7 +631,7 @@ index 7f71cc6b8812cc112262d3e9153884e428a7e495..8a61b0cc5d45a55ac188babf63b59492 class HostFrameSinkManager; class LocalSurfaceId; class RasterContextProvider; -@@ -154,6 +155,15 @@ class COMPOSITOR_EXPORT ExternalBeginFrameControllerClientFactory { +@@ -146,6 +147,15 @@ class COMPOSITOR_EXPORT ExternalBeginFrameControllerClientFactory { viz::mojom::ExternalBeginFrameControllerClient> CreateExternalBeginFrameControllerClient() = 0; }; @@ -647,7 +647,7 @@ index 7f71cc6b8812cc112262d3e9153884e428a7e495..8a61b0cc5d45a55ac188babf63b59492 // Compositor object to take care of GPU painting. // A Browser compositor object is responsible for generating the final -@@ -198,6 +208,9 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -190,6 +200,9 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // Schedules a redraw of the layer tree associated with this compositor. void ScheduleDraw(); @@ -657,7 +657,7 @@ index 7f71cc6b8812cc112262d3e9153884e428a7e495..8a61b0cc5d45a55ac188babf63b59492 // Sets the root of the layer tree drawn by this Compositor. The root layer // must have no parent. The compositor's root layer is reset if the root layer // is destroyed. NULL can be passed to reset the root layer, in which case the -@@ -620,6 +633,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -629,6 +642,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, simple_begin_frame_observers_; std::unique_ptr host_begin_frame_observer_; diff --git a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch index fb8d5a45e5aa4..de4299dc23aca 100644 --- a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch +++ b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch @@ -80,10 +80,10 @@ index 801db538979ba62facdcf3a472dade56723ca639..7abac9a5b13b393713534ae51664c2e5 private: const std::string service_interface_name_; diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc -index b7dfacb25a09b105e982c013119271b8f137e7b9..8c351bb09e5110a2c6692acc37e7187c43eec8ff 100644 +index d210af6fb317c922a8415a67a7ccd1d8a4a88ea1..0de01879f618555030e87ea79a94d41232811c2c 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc -@@ -539,7 +539,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) { +@@ -541,7 +541,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) { // Take ownership of |client_| so the destructor doesn't notify it of // termination. auto client = std::move(client_); @@ -93,7 +93,7 @@ index b7dfacb25a09b105e982c013119271b8f137e7b9..8c351bb09e5110a2c6692acc37e7187c std::optional UtilityProcessHost::GetServiceName() { diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h -index ab6b64737245a840a3e2b06d2ab501ef4aaed7a2..9cf85b5fc97cf797965642d9bae5b3b0abf56e80 100644 +index 1164da12ee71a8575c17bf1b84a505e8a32b96b3..4cbc30fc4b57440d06a0a0f642cc44c5c755e7f9 100644 --- a/content/browser/service_host/utility_process_host.h +++ b/content/browser/service_host/utility_process_host.h @@ -79,7 +79,7 @@ class CONTENT_EXPORT UtilityProcessHost diff --git a/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch b/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch index 6990e1bb49e7d..d5a070ab577e1 100644 --- a/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch +++ b/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch @@ -7,7 +7,7 @@ This allows embedders to call SetDefersLoading without reaching into Blink inter This might be upstreamable? diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h -index 23b29fe25bc463ff1d36aa502a27c4222595e7c5..c1ac6172c4cee72f64f42ca64d2db9c0f1f48738 100644 +index 0527831e1f8d7923ba0f687a5c0da8573189d867..f72af0e6cfcf06d47bd917def993f081530ab66b 100644 --- a/third_party/blink/public/web/web_document_loader.h +++ b/third_party/blink/public/web/web_document_loader.h @@ -38,6 +38,7 @@ @@ -28,10 +28,10 @@ index 23b29fe25bc463ff1d36aa502a27c4222595e7c5..c1ac6172c4cee72f64f42ca64d2db9c0 // Returns the http referrer of original request which initited this load. diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h -index 1c6da71f9526f1567fe18d40524818f48e62584a..63b9bdae7f6552e047023be3b503bf05154e7ca9 100644 +index e86efe8c9fc2d27fefc5e47afe401b8a6b1419ba..9d1ef55e4e122f30564f35406c8b51335e3d6996 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h -@@ -327,7 +327,7 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected, +@@ -328,7 +328,7 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected, soft_navigation_heuristics_task_id, bool should_skip_screenshot); diff --git a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch index 113ebc72ed50b..8a9d441e780e3 100644 --- a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch +++ b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch @@ -17,10 +17,10 @@ headers, moving forward we should find a way in upstream to provide access to these headers for loader clients created on the browser process. diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc -index babcf42e01be00da8b853a1207e869614765b2bb..1c051a64d77e3ea8909dced73d93764d06ed7152 100644 +index 7d97d0fd5481bead1f655f2f933a3ae89a729834..9b4c41209190086353b9a87833c07aabb47470bd 100644 --- a/services/network/public/cpp/resource_request.cc +++ b/services/network/public/cpp/resource_request.cc -@@ -177,6 +177,7 @@ ResourceRequest::TrustedParams& ResourceRequest::TrustedParams::operator=( +@@ -178,6 +178,7 @@ ResourceRequest::TrustedParams& ResourceRequest::TrustedParams::operator=( allow_cookies_from_browser = other.allow_cookies_from_browser; include_request_cookies_with_response = other.include_request_cookies_with_response; @@ -28,7 +28,7 @@ index babcf42e01be00da8b853a1207e869614765b2bb..1c051a64d77e3ea8909dced73d93764d cookie_observer = Clone(&const_cast&>( other.cookie_observer)); -@@ -211,6 +212,7 @@ bool ResourceRequest::TrustedParams::EqualsForTesting( +@@ -212,6 +213,7 @@ bool ResourceRequest::TrustedParams::EqualsForTesting( const TrustedParams& other) const { return isolation_info.IsEqualForTesting(other.isolation_info) && disable_secure_dns == other.disable_secure_dns && @@ -37,10 +37,10 @@ index babcf42e01be00da8b853a1207e869614765b2bb..1c051a64d77e3ea8909dced73d93764d allow_cookies_from_browser == other.allow_cookies_from_browser && include_request_cookies_with_response == diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h -index 3d1e70bd812c18206846f4188b85321e931964bb..cc3aad56c873bd97adb1179b1626d14ef346378c 100644 +index 538e2a52da142a442342383d223ea0d90b75be8f..19f0defab1d1ba3369423842e4eabbceee781091 100644 --- a/services/network/public/cpp/resource_request.h +++ b/services/network/public/cpp/resource_request.h -@@ -76,6 +76,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { +@@ -77,6 +77,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { bool has_user_activation = false; bool allow_cookies_from_browser = false; bool include_request_cookies_with_response = false; @@ -49,10 +49,10 @@ index 3d1e70bd812c18206846f4188b85321e931964bb..cc3aad56c873bd97adb1179b1626d14e mojo::PendingRemote trust_token_observer; mojo::PendingRemote diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc -index de0780c4f29026d3cc7d2742d8e80acddc6916ad..76c724151ee12fccecd36d2e9a6b7f264a9907d7 100644 +index 2021e4c8f05008dd57e67c14fc7e66bf49bd6e3e..2a5ed7fc54f9efd773ec6dded1b66bb9c9f26580 100644 --- a/services/network/public/cpp/url_request_mojom_traits.cc +++ b/services/network/public/cpp/url_request_mojom_traits.cc -@@ -48,6 +48,7 @@ bool StructTraitsallow_cookies_from_browser = data.allow_cookies_from_browser(); out->include_request_cookies_with_response = data.include_request_cookies_with_response(); @@ -61,10 +61,10 @@ index de0780c4f29026d3cc7d2742d8e80acddc6916ad..76c724151ee12fccecd36d2e9a6b7f26 mojo::PendingRemote>(); out->trust_token_observer = data.TakeTrustTokenObserver< diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h -index ebd70d33c9e7df5abd35ed3683f39d0e7fc8b34d..45dd29445589dec900221edb65f8fe566b81ca6e 100644 +index 30a38e3f82360417b682c516263f62181825c621..db8a479f483a041a6944465deb6f799a2a9bd93a 100644 --- a/services/network/public/cpp/url_request_mojom_traits.h +++ b/services/network/public/cpp/url_request_mojom_traits.h -@@ -70,6 +70,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) +@@ -71,6 +71,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) const network::ResourceRequest::TrustedParams& trusted_params) { return trusted_params.include_request_cookies_with_response; } @@ -76,10 +76,10 @@ index ebd70d33c9e7df5abd35ed3683f39d0e7fc8b34d..45dd29445589dec900221edb65f8fe56 cookie_observer( const network::ResourceRequest::TrustedParams& trusted_params) { diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom -index 3e5dae789846c667f7537b04252a49b28298d53d..a229e56674cc75e8dfb78c26962f108ffd59c5e9 100644 +index 78fa9126457ee380b12e39e3bf35ea4c2ccaa36c..4f39fa5d3bd665e3343882d11f1ab5747c80610e 100644 --- a/services/network/public/mojom/url_request.mojom +++ b/services/network/public/mojom/url_request.mojom -@@ -73,6 +73,9 @@ struct TrustedUrlRequestParams { +@@ -74,6 +74,9 @@ struct TrustedUrlRequestParams { // client which should not be able to see them. bool include_request_cookies_with_response = false; @@ -90,7 +90,7 @@ index 3e5dae789846c667f7537b04252a49b28298d53d..a229e56674cc75e8dfb78c26962f108f // a cookie. If this is set to non-null, the observer passed to // URLLoaderFactory will be ignored. diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom -index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b703960dc 100644 +index 72188dae668f2bd91b8b7f181448f459600a82c2..b3dae05e160160cc8f2decee5af78c21bd76c822 100644 --- a/services/network/public/mojom/url_response_head.mojom +++ b/services/network/public/mojom/url_response_head.mojom @@ -13,6 +13,7 @@ import "services/network/public/mojom/attribution.mojom"; @@ -101,7 +101,7 @@ index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b import "services/network/public/mojom/ip_endpoint.mojom"; import "services/network/public/mojom/load_timing_info.mojom"; import "services/network/public/mojom/network_param.mojom"; -@@ -49,6 +50,9 @@ struct URLResponseHead { +@@ -51,6 +52,9 @@ struct URLResponseHead { // The response headers or NULL if the URL type does not support headers. HttpResponseHeaders headers; @@ -112,10 +112,10 @@ index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b string mime_type; diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc -index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf333f5d799 100644 +index 01ddddb7f8f3723814cca8c38ed7150ee1270d57..9a79c530f3b9bfec8ce7307cf202efba8f14c905 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc -@@ -666,6 +666,9 @@ URLLoader::URLLoader( +@@ -673,6 +673,9 @@ URLLoader::URLLoader( mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunner::GetCurrentDefault()), per_factory_orb_state_(context.GetMutableOrbState()), @@ -125,7 +125,7 @@ index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf3 devtools_request_id_(request.devtools_request_id), options_(PopulateOptions(options, factory_params_->is_orb_enabled, -@@ -963,7 +966,7 @@ void URLLoader::ConfigureRequest( +@@ -982,7 +985,7 @@ void URLLoader::ConfigureRequest( &URLLoader::IsSharedDictionaryReadAllowed, base::Unretained(this))); } @@ -134,7 +134,7 @@ index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf3 url_request_->SetResponseHeadersCallback(base::BindRepeating( &URLLoader::SetRawResponseHeaders, base::Unretained(this))); } -@@ -2049,6 +2052,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { +@@ -2146,6 +2149,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { } response_ = BuildResponseHead(); @@ -155,10 +155,10 @@ index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf3 // Parse and remove the Trust Tokens response headers, if any are expected, diff --git a/services/network/url_loader.h b/services/network/url_loader.h -index 5e7d17112b41157117839c65cc13dbc56311d5f0..b40bafde39101124368dd5e5e221240a6eb7fece 100644 +index e6962f686212ca39d66b5d21bd3ff1f852e83644..fd3eb86e2f0f9b3705029befc54c85690e8ca519 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h -@@ -710,6 +710,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader +@@ -773,6 +773,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader std::unique_ptr resource_scheduler_request_handle_; diff --git a/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch b/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch index 3928f3d7244c7..5da28b3c084f9 100644 --- a/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch +++ b/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch @@ -7,10 +7,10 @@ Subject: feat: filter out non-shareable windows in the current application in This patch ensures that windows protected via win.setContentProtection(true) do not appear in full display captures via desktopCapturer. This patch could be upstreamed but as the check is limited to in-process windows it doesn't make a lot of sense for Chromium itself. This patch currently has a limitation that it only function for windows created / protected BEFORE the stream is started. There is theoretical future work we can do via polling / observers to automatically update the SCContentFilter when new windows are made but for now this will solve 99+% of the problem and folks can re-order their logic a bit to get it working for their use cases. diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm -index 8e7fb47f5f28f40e8e3bd00d2bb1c928112976e4..8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c 100644 +index 42705a5c59fb76ba4a6a17a060e215436307de49..7e17594c30ac3cf8cb484b53563b03fc75bd2e0b 100644 --- a/content/browser/media/capture/screen_capture_kit_device_mac.mm +++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm -@@ -250,8 +250,17 @@ void OnShareableContentCreated(SCShareableContent* content) { +@@ -254,8 +254,17 @@ void OnShareableContentCreated(SCShareableContent* content) { // fallback. See https://crbug.com/325530044. if (source_.id == display.displayID || source_.id == webrtc::kFullDesktopScreenId) { diff --git a/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch b/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch index c82e141f93c57..7ce101dd084ba 100644 --- a/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch +++ b/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch @@ -20,59 +20,59 @@ This patch will be removed when the deprecated sync api support is removed. diff --git a/components/permissions/permission_util.cc b/components/permissions/permission_util.cc -index 20056b1464699cc7d45717bfc710366eb48d85f6..b227ed62991cdf285c3b90f19e09e3718e2800ea 100644 +index 6f4d9651862e90abaa22b93e1dcf97ac07592c5c..d91d81297e7cdb79ee7b7a3979dcedf977e16c92 100644 --- a/components/permissions/permission_util.cc +++ b/components/permissions/permission_util.cc -@@ -384,6 +384,7 @@ ContentSettingsType PermissionUtil::PermissionTypeToContentSettingsTypeSafe( - return ContentSettingsType::AUTOMATIC_FULLSCREEN; - case PermissionType::WEB_APP_INSTALLATION: +@@ -389,6 +389,7 @@ ContentSettingsType PermissionUtil::PermissionTypeToContentSettingsTypeSafe( return ContentSettingsType::WEB_APP_INSTALLATION; + case PermissionType::LOCAL_NETWORK_ACCESS: + return ContentSettingsType::LOCAL_NETWORK_ACCESS; + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: case PermissionType::NUM: break; } diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc -index e991887c103618b35688cf72307ca05fdb202e6e..54894f3412d42264eae80d767be5215e52f08184 100644 +index 0c6b537ddc2afbc4cd22f1c08f1bbae3c3bbffe3..333f557c89bc03ae5b25060359b9af096b188726 100644 --- a/content/browser/permissions/permission_controller_impl.cc +++ b/content/browser/permissions/permission_controller_impl.cc -@@ -86,6 +86,7 @@ PermissionToSchedulingFeature(PermissionType permission_name) { - case PermissionType::POINTER_LOCK: +@@ -87,6 +87,7 @@ PermissionToSchedulingFeature(PermissionType permission_name) { case PermissionType::AUTOMATIC_FULLSCREEN: case PermissionType::WEB_APP_INSTALLATION: + case PermissionType::LOCAL_NETWORK_ACCESS: + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: return std::nullopt; } } diff --git a/third_party/blink/common/permissions/permission_utils.cc b/third_party/blink/common/permissions/permission_utils.cc -index 779b458094edfa866ac70d7e0ac6413cc700c6a5..b42cbc34f1d2e214d8cc6ed2aacfa9ae21c2569a 100644 +index aad897dbc32caa3469a2f04b4f004355888c84d8..875260fc2a884ab5b1e62cd45fc91c2e4dc162ee 100644 --- a/third_party/blink/common/permissions/permission_utils.cc +++ b/third_party/blink/common/permissions/permission_utils.cc -@@ -99,6 +99,8 @@ std::string GetPermissionString(PermissionType permission) { - return "AutomaticFullscreen"; - case PermissionType::WEB_APP_INSTALLATION: +@@ -101,6 +101,8 @@ std::string GetPermissionString(PermissionType permission) { return "WebAppInstallation"; + case PermissionType::LOCAL_NETWORK_ACCESS: + return "LocalNetworkAccess"; + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: + return "DeprecatedSyncClipboardRead"; case PermissionType::NUM: NOTREACHED(); } -@@ -171,6 +173,7 @@ PermissionTypeToPermissionsPolicyFeature(PermissionType permission) { - case PermissionType::NOTIFICATIONS: - case PermissionType::KEYBOARD_LOCK: +@@ -175,6 +177,7 @@ PermissionTypeToPermissionsPolicyFeature(PermissionType permission) { case PermissionType::POINTER_LOCK: + // TODO(crbug.com/394009026): Add permission policy for LNA. + case PermissionType::LOCAL_NETWORK_ACCESS: + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: return std::nullopt; case PermissionType::NUM: diff --git a/third_party/blink/public/common/permissions/permission_utils.h b/third_party/blink/public/common/permissions/permission_utils.h -index f9b1db54dd367d1f0e42cdfcfd04255d452f0e1b..54b4b742298af03d8924f6ad613081f1b5dfae4d 100644 +index 947bd8125bf300a3a82c811810e31996e6147c7c..caeeaf6cecf7da47c39b140dfcc295fba12f6f84 100644 --- a/third_party/blink/public/common/permissions/permission_utils.h +++ b/third_party/blink/public/common/permissions/permission_utils.h -@@ -64,6 +64,7 @@ enum class PermissionType { - AUTOMATIC_FULLSCREEN = 40, +@@ -65,6 +65,7 @@ enum class PermissionType { HAND_TRACKING = 41, WEB_APP_INSTALLATION = 42, -+ DEPRECATED_SYNC_CLIPBOARD_READ = 43, + LOCAL_NETWORK_ACCESS = 43, ++ DEPRECATED_SYNC_CLIPBOARD_READ = 44, // Always keep this at the end. NUM, diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch deleted file mode 100644 index 9131aa33730a5..0000000000000 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: clavin -Date: Mon, 11 Dec 2023 20:43:34 -0300 -Subject: fix: activate background material on windows - -This patch adds a condition to the HWND message handler to allow windows -with translucent background materials to become activated. - -It also ensures the lParam of WM_NCACTIVATE is set to -1 so as to not repaint -the client area, which can lead to a title bar incorrectly being displayed in -frameless windows. - -This patch likely can't be upstreamed as-is, as Chromium doesn't have -this use case in mind currently. - -diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 9888ac709c51cd30228e3bca6915f7d89071cb83..e7b2bc1905958d2ff9c34ed7a22eb53e7ea3b9b8 100644 ---- a/ui/views/win/hwnd_message_handler.cc -+++ b/ui/views/win/hwnd_message_handler.cc -@@ -932,13 +932,13 @@ void HWNDMessageHandler::FrameTypeChanged() { - - void HWNDMessageHandler::PaintAsActiveChanged() { - if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || -- !delegate_->HasFrame() || -+ (!delegate_->HasFrame() && !is_translucent_) || - (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) { - return; - } - - DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(), -- 0); -+ delegate_->HasFrame() ? 0 : -1); - } - - void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, -@@ -2327,17 +2327,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, - delegate_->SchedulePaint(); - } - -- // Calling DefWindowProc is only necessary if there's a system frame being -- // drawn. Otherwise it can draw an incorrect title bar and cause visual -- // corruption. -- if (!delegate_->HasFrame() || -+ // If the window is translucent, it may have the Mica background. -+ // In that case, it's necessary to call |DefWindowProc|, but we can -+ // pass -1 in the lParam to prevent any non-client area elements from -+ // being displayed. -+ if ((!delegate_->HasFrame() && !is_translucent_) || - delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { - SetMsgHandled(TRUE); - return TRUE; - } - - return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active, -- 0); -+ delegate_->HasFrame() ? 0 : -1); - } - - LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { diff --git a/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch b/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch index ea3f64becd912..aed4072d110f6 100644 --- a/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch +++ b/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch @@ -16,7 +16,7 @@ Linux or Windows to un-fullscreen in some circumstances without this change. diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc -index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f25d04780 100644 +index 91db1bbd912807833243c101e126e42ad5579d2b..7c624e0babddbca7c0deef1d805ccbfc21e6e9a9 100644 --- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc +++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc @@ -20,12 +20,16 @@ @@ -50,7 +50,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f // Keep the current state. |SetTabWithExclusiveAccess| may change the return // value of |IsWindowFullscreenForTabOrPending|. -@@ -389,12 +395,14 @@ void FullscreenController::ExitFullscreenModeForTab(WebContents* web_contents) { +@@ -388,12 +394,14 @@ void FullscreenController::ExitFullscreenModeForTab(WebContents* web_contents) { void FullscreenController::FullscreenTabOpeningPopup( content::WebContents* opener, content::WebContents* popup) { @@ -65,7 +65,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f } void FullscreenController::OnTabDeactivated( -@@ -484,10 +492,12 @@ void FullscreenController::FullscreenTransitionCompleted() { +@@ -483,10 +491,12 @@ void FullscreenController::FullscreenTransitionCompleted() { #endif // DCHECK_IS_ON() tab_fullscreen_target_display_id_ = display::kInvalidDisplayId; started_fullscreen_transition_ = false; @@ -78,7 +78,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f } void FullscreenController::RunOrDeferUntilTransitionIsComplete( -@@ -612,18 +622,17 @@ void FullscreenController::EnterFullscreenModeInternal( +@@ -616,18 +626,17 @@ void FullscreenController::EnterFullscreenModeInternal( // Do not enter fullscreen mode if disallowed by pref. This prevents the user // from manually entering fullscreen mode and also disables kiosk mode on // desktop platforms. @@ -97,20 +97,20 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f +#if 0 bool entering_tab_fullscreen = option == TAB && !tab_fullscreen_; +#endif - GURL url; + url::Origin origin; +#if 0 if (option == TAB) { - url = GetRequestingOrigin(); + origin = GetRequestingOrigin(); tab_fullscreen_ = true; -@@ -658,6 +667,7 @@ void FullscreenController::EnterFullscreenModeInternal( - url = extension_caused_fullscreen_; +@@ -662,6 +671,7 @@ void FullscreenController::EnterFullscreenModeInternal( + origin = url::Origin::Create(extension_url_.value()); } } +#endif fullscreen_start_time_ = base::TimeTicks::Now(); if (option == BROWSER) { -@@ -679,6 +689,7 @@ void FullscreenController::ExitFullscreenModeInternal() { +@@ -683,6 +693,7 @@ void FullscreenController::ExitFullscreenModeInternal() { return; } @@ -118,7 +118,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f // `fullscreen_start_time_` is null if a fullscreen tab moves to a new window. if (fullscreen_start_time_ && exclusive_access_tab()) { ukm::SourceId source_id = -@@ -690,15 +701,16 @@ void FullscreenController::ExitFullscreenModeInternal() { +@@ -694,15 +705,16 @@ void FullscreenController::ExitFullscreenModeInternal() { .Record(ukm::UkmRecorder::Get()); fullscreen_start_time_.reset(); } @@ -136,9 +136,9 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f -#endif + exclusive_access_manager()->context()->ExitFullscreen(); - extension_caused_fullscreen_ = GURL(); + extension_url_.reset(); exclusive_access_manager()->UpdateBubble(base::NullCallback()); -@@ -762,8 +774,12 @@ GURL FullscreenController::GetEmbeddingOrigin() const { +@@ -766,8 +778,12 @@ url::Origin FullscreenController::GetEmbeddingOrigin() const { void FullscreenController::RecordMetricsOnFullscreenApiRequested( content::RenderFrameHost* requesting_frame) { history::HistoryService* service = @@ -152,7 +152,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f // Check if the origin has been visited more than a day ago and whether it's // on an allowlist, then record those bits of information in a metric. diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.h b/chrome/browser/ui/exclusive_access/fullscreen_controller.h -index 7fdf883310b13e3334ba007580a1a4287d49d592..d7c2bdaa08cae1645bf52f1d9b9d84b1cdb78c66 100644 +index 5854fd8401f3934cabd6abfe48da332d7a670af3..d714c44a6fd2206f9192dbfe82787a4b06042ed0 100644 --- a/chrome/browser/ui/exclusive_access/fullscreen_controller.h +++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.h @@ -250,10 +250,12 @@ class FullscreenController : public ExclusiveAccessControllerBase { diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch deleted file mode 100644 index eb2207994d0b7..0000000000000 --- a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Cezary Kulakowski -Date: Mon, 22 Jul 2024 16:23:13 +0200 -Subject: fix: add method which disables headless mode on native widget - -We need this method as we create window in headless mode and we -switch it back to normal mode only after inital paint is done in -order to get some events like WebContents.beginFrameSubscription. -If we don't set `is_headless_` to false then some child windows -e.g. autofill popups will be created in headless mode leading to -ui problems (like dissapearing popup during typing in html's -input list. - -diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h -index 842ea6f7eb2415170a59e46ca955165d656ba149..1ea926753f65baa8f9671fdd63f98fb94c28ba6c 100644 ---- a/ui/views/widget/widget.h -+++ b/ui/views/widget/widget.h -@@ -1200,6 +1200,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, - // True if widget was created in headless mode. - bool is_headless() const { return is_headless_; } - -+ void DisableHeadlessMode() { is_headless_ = false; } -+ - // True if the window size will follow the content preferred size. - bool is_autosized() const { return is_autosized_; } - diff --git a/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch new file mode 100644 index 0000000000000..5428d36fdc299 --- /dev/null +++ b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cezary Kulakowski +Date: Mon, 22 Jul 2024 16:23:13 +0200 +Subject: fix: adjust headless mode handling in native widget + +We need this method as we create window in headless mode and we +switch it back to normal mode only after inital paint is done in +order to get some events like WebContents.beginFrameSubscription. +If we don't set `is_headless_` to false then some child windows +e.g. autofill popups will be created in headless mode leading to +ui problems (like dissapearing popup during typing in html's +input list). + +We also need to ensure that an initial paint is scheduled when +the compositor is unsuspended in headles mode. + +diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +index 7bda1853d47034c80a4e416b9839e8d18c5a8e2c..e5df49253103c44f01195fa93988753fb163d13e 100644 +--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm ++++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +@@ -659,9 +659,10 @@ void HandleAccelerator(const ui::Accelerator& accelerator, + // case it will never become visible but we want its compositor to produce + // frames for screenshooting and screencasting. + UpdateCompositorProperties(); +- layer()->SetVisible(is_visible_); ++ layer()->SetVisible(is_visible_ || is_headless_mode_window_); + if (is_visible_ || is_headless_mode_window_) { + compositor_->Unsuspend(); ++ layer()->SchedulePaint(layer()->bounds()); + } + + // Register the CGWindowID (used to identify this window for video capture) +diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h +index 44c9248daecd67ccbc32f9afdbb8e7449d435b6c..e7d43ffc134e76f54aa5a71c668ce725a3f9bf6a 100644 +--- a/ui/views/widget/widget.h ++++ b/ui/views/widget/widget.h +@@ -1223,6 +1223,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, + // True if widget was created in headless mode. + bool is_headless() const { return is_headless_; } + ++ void DisableHeadlessMode() { is_headless_ = false; } ++ + // True if the window size will follow the content preferred size. + bool is_autosized() const { return is_autosized_; } + diff --git a/patches/chromium/fix_aspect_ratio_with_max_size.patch b/patches/chromium/fix_aspect_ratio_with_max_size.patch index 82dc65b890e2e..8068f9cf6aab7 100644 --- a/patches/chromium/fix_aspect_ratio_with_max_size.patch +++ b/patches/chromium/fix_aspect_ratio_with_max_size.patch @@ -11,10 +11,10 @@ enlarge window above dimensions set during creation of the BrowserWindow. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index a802f4b710b6f8fa154d11846c061720a91e15e4..2bd015be3178ab8dea012d6b1f71d13dd0548c14 100644 +index ea67a60d78f4adec960bb4954486420bce423c14..701f89b053674be1bad0a1a3612340a65c1ccfe7 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3747,15 +3747,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, +@@ -3790,15 +3790,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, delegate_->GetMinMaxSize(&min_window_size, &max_window_size); min_window_size = delegate_->DIPToScreenSize(min_window_size); max_window_size = delegate_->DIPToScreenSize(max_window_size); diff --git a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch index 1e72fde2fe54e..35edda432b118 100644 --- a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch +++ b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch @@ -28,10 +28,10 @@ The patch should be removed in favor of either: Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397. diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc -index 73f61aa418db85558a0f8cfa574ee2e2f575ef90..4f376522e87fc8075eca6705b1bb781c86cde947 100644 +index 29740121f137118836c6ace05f302cc8aa671d90..7ae2270bc89c4a77f56055283f77d44c7474031b 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc -@@ -11048,6 +11048,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { +@@ -11099,6 +11099,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { "blob"); } @@ -45,10 +45,10 @@ index 73f61aa418db85558a0f8cfa574ee2e2f575ef90..4f376522e87fc8075eca6705b1bb781c // origin of |common_params.url| and/or |common_params.initiator_origin|. url::Origin resolved_origin = url::Origin::Resolve( diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc -index 0c7b5af098a53a8709cdf62d455520ccef222dbb..1b6a7942c1506abc2fbe7d1efe58c0964a4e3be0 100644 +index ca61af9af555e267228430496118797eff130160..bb7c020ba1672e21a3ffee13ff1d3934bde0f099 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc -@@ -2336,6 +2336,10 @@ Frame* DocumentLoader::CalculateOwnerFrame() { +@@ -2334,6 +2334,10 @@ Frame* DocumentLoader::CalculateOwnerFrame() { scoped_refptr DocumentLoader::CalculateOrigin( Document* owner_document) { scoped_refptr origin; @@ -59,7 +59,7 @@ index 0c7b5af098a53a8709cdf62d455520ccef222dbb..1b6a7942c1506abc2fbe7d1efe58c096 StringBuilder debug_info_builder; // Whether the origin is newly created within this call, instead of copied // from an existing document's origin or from `origin_to_commit_`. If this is -@@ -2389,6 +2393,10 @@ scoped_refptr DocumentLoader::CalculateOrigin( +@@ -2387,6 +2391,10 @@ scoped_refptr DocumentLoader::CalculateOrigin( // the end of this function. origin = origin_to_commit_; debug_info_builder.Append("use_origin_to_commit"); diff --git a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch index e1e6bb6555055..1ead859337555 100644 --- a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch +++ b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch @@ -12,10 +12,10 @@ invisible state of the `viz::DisplayScheduler` owned by the `ui::Compositor`. diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc -index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb594514a3 100644 +index 633e5c77e410404053f46ce92a37f30cf743cac0..2b3688df2b5b89c931a84a0e91048ae80a814ac0 100644 --- a/ui/compositor/compositor.cc +++ b/ui/compositor/compositor.cc -@@ -361,7 +361,8 @@ void Compositor::SetLayerTreeFrameSink( +@@ -357,7 +357,8 @@ void Compositor::SetLayerTreeFrameSink( if (display_private_) { disabled_swap_until_resize_ = false; display_private_->Resize(size()); @@ -25,7 +25,7 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb display_private_->SetDisplayColorSpaces(display_color_spaces_); display_private_->SetDisplayColorMatrix( gfx::SkM44ToTransform(display_color_matrix_)); -@@ -581,7 +582,9 @@ void Compositor::SetVisible(bool visible) { +@@ -582,7 +583,9 @@ void Compositor::SetVisible(bool visible) { // updated then. We need to call this even if the visibility hasn't changed, // for the same reason. if (display_private_) @@ -36,7 +36,7 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb if (changed) { observer_list_.Notify(&CompositorObserver::OnCompositorVisibilityChanged, -@@ -1034,6 +1037,15 @@ void Compositor::MaybeUpdateObserveBeginFrame() { +@@ -1046,6 +1049,15 @@ void Compositor::MaybeUpdateObserveBeginFrame() { host_begin_frame_observer_->GetBoundRemote()); } @@ -53,10 +53,10 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb void Compositor::SetSeamlessRefreshRates( const std::vector& seamless_refresh_rates) { diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index 8a61b0cc5d45a55ac188babf63b59492cff428e1..07200e5320c999f1a067ce785b7ad5c7952586d7 100644 +index 38b6c47f526b428562f340966352d32ba918f2d5..4816d7377b39427c0bed9b4c7bab3b410699f5cf 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h -@@ -523,6 +523,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -515,6 +515,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, const cc::LayerTreeSettings& GetLayerTreeSettings() const; @@ -67,7 +67,7 @@ index 8a61b0cc5d45a55ac188babf63b59492cff428e1..07200e5320c999f1a067ce785b7ad5c7 size_t saved_events_metrics_count_for_testing() const { return host_->saved_events_metrics_count_for_testing(); } -@@ -714,6 +718,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -723,6 +727,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // See go/report-ux-metrics-at-painting for details. bool animation_started_ = false; diff --git a/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch b/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch index f70fb46267ec1..f0002496c6df6 100644 --- a/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch +++ b/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch @@ -40,7 +40,7 @@ accessing uninitialized lower indexes can return garbage values that cannot be n Refer to v8::EmbedderDataSlot::store_aligned_pointer for context. diff --git a/gin/public/gin_embedders.h b/gin/public/gin_embedders.h -index 8d7c5631fd8f1499c67384286f0e3c4037673b32..99b2e2f63be8a46c5546dd53bc9b05e8c54e857c 100644 +index 8d7c5631fd8f1499c67384286f0e3c4037673b32..2b7bdfbac06a42e6bc51eb65e023c3673e6eb885 100644 --- a/gin/public/gin_embedders.h +++ b/gin/public/gin_embedders.h @@ -20,6 +20,8 @@ enum GinEmbedder : uint16_t { @@ -86,7 +86,7 @@ index e4a27a24c83dd1a478b2ada8b6c8220076790791..c76dc818f38a62fff63852dbecbc85e3 // Cut the reference from ScriptState to V8 context. diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h -index b3cc8d819b06108386aed9465cab4f27a28b675f..a1757901e52360a9c2ec3c573adb20d03cd6ecae 100644 +index b3cc8d819b06108386aed9465cab4f27a28b675f..9c8818f10de59fdd2a3fd44d9cd23d40a93b53a7 100644 --- a/third_party/blink/renderer/platform/bindings/script_state.h +++ b/third_party/blink/renderer/platform/bindings/script_state.h @@ -185,7 +185,12 @@ class PLATFORM_EXPORT ScriptState : public GarbageCollected { diff --git a/patches/chromium/fix_linter_error.patch b/patches/chromium/fix_linter_error.patch new file mode 100644 index 0000000000000..d9b2b3bb335b1 --- /dev/null +++ b/patches/chromium/fix_linter_error.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Charles Kerr +Date: Thu, 6 Mar 2025 17:06:49 -0600 +Subject: fix: linter error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +fix: linter error + +This is showing up in an eslint build step in Electron: + +> /__w/electron/electron/src/out/Default/gen/ui/webui/resources/cr_elements/preprocessed/cr_menu_selector/cr_menu_selector.ts +> 77:23 error This assertion is unnecessary since the receiver accepts the original type of the expression @typescript-eslint/no-unnecessary-type-assertion +> +> ✖ 1 problem (1 error, 0 warnings) +> 1 error and 0 warnings potentially fixable with the `--fix` option. + +However, removing the assertion causes a typescript build failure: + +> gen/ui/webui/resources/cr_elements/preprocessed/cr_menu_selector/cr_menu_selector.ts:77:23 - error TS2345: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. +> Type 'null' is not assignable to type 'HTMLElement'. +> +> 77 items.indexOf(this.querySelector(':focus')); +> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So I think the two different steps may be picking up typescript definitions. + +This patch should be removed after the issue is tracked down +andfixed in a followup task. + +diff --git a/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts b/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts +index 0a83b8041b8201c95442e680c77555d4c11bc06a..abdb8e9bfbbfb1fce6fa38e226e50a35477e49a2 100644 +--- a/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts ++++ b/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts +@@ -74,6 +74,7 @@ export class CrMenuSelector extends CrMenuSelectorBase { + const items = this.getAllFocusableItems_(); + assert(items.length >= 1); + const currentFocusedIndex = ++ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + items.indexOf(this.querySelector(':focus')!); + + let newFocusedIndex = currentFocusedIndex; diff --git a/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch b/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch index a79c76006c8d0..cec6714770139 100644 --- a/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch +++ b/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch @@ -9,7 +9,7 @@ to support content settings UI. The support pulls in chrome content settings and UI code which are not valid in the scope of Electron. diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc -index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec4294409950 100644 +index 9f82c7f92f735ed9f9569a8d299d23c7cb83e596..9b7598a26781da8b1e1c7364606574024000921f 100644 --- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc +++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc @@ -6,6 +6,7 @@ @@ -30,7 +30,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 #include "media/base/media_switches.h" #include "net/base/url_util.h" #include "third_party/blink/public/common/features.h" -@@ -49,7 +52,7 @@ constexpr gfx::Size kMinWindowSize(240, 52); +@@ -53,7 +56,7 @@ constexpr gfx::Size kMinWindowSize(240, 52); // not apply to video Picture-in-Picture windows. constexpr double kMaxWindowSizeRatio = 0.8; @@ -39,7 +39,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // Returns true if a document picture-in-picture window should be focused upon // opening it. bool ShouldFocusPictureInPictureWindow(const NavigateParams& params) { -@@ -196,7 +199,7 @@ bool PictureInPictureWindowManager::ExitPictureInPictureViaWindowUi( +@@ -200,7 +203,7 @@ bool PictureInPictureWindowManager::ExitPictureInPictureViaWindowUi( return false; } @@ -48,7 +48,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // The user manually closed the pip window, so let the tab helper know in case // the auto-pip permission dialog was visible. if (auto* tab_helper = AutoPictureInPictureTabHelper::FromWebContents( -@@ -401,7 +404,7 @@ gfx::Size PictureInPictureWindowManager::GetMaximumWindowSize( +@@ -415,7 +418,7 @@ gfx::Size PictureInPictureWindowManager::GetMaximumWindowSize( // static void PictureInPictureWindowManager::SetWindowParams(NavigateParams& params) { @@ -57,7 +57,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // Always show document picture-in-picture in a new window. When this is // not opened via the AutoPictureInPictureTabHelper, focus the window. params.window_action = ShouldFocusPictureInPictureWindow(params) -@@ -493,6 +496,7 @@ PictureInPictureWindowManager::GetOverlayView( +@@ -507,6 +510,7 @@ PictureInPictureWindowManager::GetOverlayView( return nullptr; } @@ -65,7 +65,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // It would be nice to create this in `EnterPictureInPicture*`, but detecting // auto-pip while pip is in the process of opening doesn't work. // -@@ -531,6 +535,8 @@ PictureInPictureWindowManager::GetOverlayView( +@@ -545,6 +549,8 @@ PictureInPictureWindowManager::GetOverlayView( } return overlay_view; @@ -75,7 +75,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 PictureInPictureOcclusionTracker* diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc -index f9e29227fe9396747bed8b1ec970cfdfcf72c03c..d96f85145d1ad2843cf2659f68e2fd1d93c4c1c4 100644 +index 531b183bcc017daeefcf158fd58570a889c4b7df..62fbbaa37d07d24e410778e155a7f013479722b3 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc @@ -430,11 +430,13 @@ std::unique_ptr VideoOverlayWindowViews::Create( diff --git a/patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch b/patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch new file mode 100644 index 0000000000000..6605f24d7b15f --- /dev/null +++ b/patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch @@ -0,0 +1,51 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Calvin Watford +Date: Wed, 5 Mar 2025 15:26:28 -0700 +Subject: fix: multiple ScopedPumpMessagesInPrivateModes instances + +Context: When swapping `Menu.popup` to use `ui::ShowContextMenu`, we +found that its use of `ScopedPumpMessagesInPrivateModes` may potentially +be used multiple times simultaneously. This class was designed to work +with only one global instance. + +This patch adds a global reference count to keep track of +`ScopedPumpMessagesInPrivateModes` instances and gate its +enable/disable behavior on this reference count. + +diff --git a/base/message_loop/message_pump_apple.mm b/base/message_loop/message_pump_apple.mm +index 52ed68ac3150bdeef3c5032f3f5f7df3d5aaac51..1658aece3e8fbcef89944a849e311f7949a68de9 100644 +--- a/base/message_loop/message_pump_apple.mm ++++ b/base/message_loop/message_pump_apple.mm +@@ -760,20 +760,29 @@ explicit OptionalAutoreleasePool(MessagePumpCFRunLoopBase* pump) { + + #else + ++static int g_private_mode_ref_count = 0; ++ + ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() { + DCHECK(g_app_pump); +- DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask()); + // Pumping events in private runloop modes is known to interact badly with + // app modal windows like NSAlert. + if (NSApp.modalWindow) { + return; + } +- g_app_pump->SetModeMask(kAllModesMask); ++ ++ g_private_mode_ref_count += 1; ++ if (g_private_mode_ref_count == 1) { ++ g_app_pump->SetModeMask(kAllModesMask); ++ } + } + + ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() { + DCHECK(g_app_pump); +- g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask); ++ DCHECK(g_private_mode_ref_count > 0); ++ g_private_mode_ref_count -= 1; ++ if (g_private_mode_ref_count == 0) { ++ g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask); ++ } + } + + int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() { diff --git a/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch b/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch index c831b895ff618..7f823c37ff7f5 100644 --- a/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch +++ b/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch @@ -13,10 +13,10 @@ messages in the legacy window handle layer. These conditions are regularly hit with WCO-enabled windows on Windows. diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc -index e63c2f637828d426e1c8fb16774a08722bfc9fcc..bfc5254ec692fc5cfbd93fabae7969adeb644393 100644 +index 72f220bc5ac0669994d2a7517b81ab176526cd0c..67c8f7a78738d45c7721c6dfbb004c4ed3d2c7ad 100644 --- a/content/browser/renderer_host/legacy_render_widget_host_win.cc +++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc -@@ -325,12 +325,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, +@@ -328,12 +328,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param, BOOL& handled) { @@ -31,23 +31,23 @@ index e63c2f637828d426e1c8fb16774a08722bfc9fcc..bfc5254ec692fc5cfbd93fabae7969ad tme.hwndTrack = hwnd(); tme.dwHoverTime = 0; TrackMouseEvent(&tme); -@@ -361,7 +361,10 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, - // out of the picture. - if (!handled && - (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) { -- ret = ::DefWindowProc(GetParent(), message, w_param, l_param); -+ // Send WM_NCMOUSEMOVE messages using the LegacyRenderWidgetHostHWND's -+ // handle so mouse tracking on non-client areas doesn't break. -+ HWND target = message == WM_NCMOUSEMOVE ? hwnd() : GetParent(); -+ ret = ::DefWindowProc(target, message, w_param, l_param); - handled = TRUE; - } +@@ -366,7 +366,10 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, + // the picture. + if (!handled && + (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) { +- ret = ::DefWindowProc(GetParent(), message, w_param, l_param); ++ // Send WM_NCMOUSEMOVE messages using the LegacyRenderWidgetHostHWND's ++ // handle so mouse tracking on non-client areas doesn't break. ++ HWND target = message == WM_NCMOUSEMOVE ? hwnd() : GetParent(); ++ ret = ::DefWindowProc(target, message, w_param, l_param); + handled = TRUE; } + return ret; diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.h b/content/browser/renderer_host/legacy_render_widget_host_win.h -index aad10a91356b8b13e15fac0488d25187af23ce36..f9a3db0c54831ad0a1c9946a98a6d7154776ae51 100644 +index 17952b4b6ab559ba1c9076b100b060a7011edeec..1eda20e67d3b3184bec154f24c4259bb4a49b099 100644 --- a/content/browser/renderer_host/legacy_render_widget_host_win.h +++ b/content/browser/renderer_host/legacy_render_widget_host_win.h -@@ -103,6 +103,7 @@ class CONTENT_EXPORT LegacyRenderWidgetHostHWND +@@ -105,6 +105,7 @@ class CONTENT_EXPORT LegacyRenderWidgetHostHWND MESSAGE_HANDLER_EX(WM_VSCROLL, OnScroll) MESSAGE_HANDLER_EX(WM_NCHITTEST, OnNCHitTest) MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK, OnMouseRange) diff --git a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch index 0b51bde982d26..aab52cd7e99d4 100644 --- a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch +++ b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch @@ -9,10 +9,10 @@ focus node change via TextInputManager. chromium-bug: https://crbug.com/1369605 diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 9503beefcbcfe7d99674582ece10a7e551fae96d..32302e3dae2098319ed2d1f1403649fe8db2887e 100644 +index 95a52f1cc2024e4a9cd694429d5304a5860a1c1e..3e096f196b9514fec5738f29e7c63bcbb9b2f640 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc -@@ -3229,6 +3229,12 @@ void RenderWidgetHostViewAura::OnTextSelectionChanged( +@@ -3241,6 +3241,12 @@ void RenderWidgetHostViewAura::OnTextSelectionChanged( } } @@ -26,10 +26,10 @@ index 9503beefcbcfe7d99674582ece10a7e551fae96d..32302e3dae2098319ed2d1f1403649fe RenderWidgetHostViewAura* popup_child_host_view) { popup_child_host_view_ = popup_child_host_view; diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h -index 06fc7daf2761ae728f3adf9eb2ef3910a14cf827..8d34f9dfd9a8625a3fab397f7b96c12c87cb8a61 100644 +index 6f96b83c36ee026bd37b54de55da72cc802ed699..c54ed7d8f5d4d371626865c6ae63ef2efbef1dba 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h -@@ -653,6 +653,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura +@@ -654,6 +654,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura RenderWidgetHostViewBase* updated_view) override; void OnTextSelectionChanged(TextInputManager* text_input_mangager, RenderWidgetHostViewBase* updated_view) override; @@ -39,7 +39,7 @@ index 06fc7daf2761ae728f3adf9eb2ef3910a14cf827..8d34f9dfd9a8625a3fab397f7b96c12c // Detaches |this| from the input method object. // is_removed flag is true if this is called while the window is diff --git a/content/browser/renderer_host/text_input_manager.cc b/content/browser/renderer_host/text_input_manager.cc -index 9a0aad0d4a3dada0faec031d155c6d346355e8a5..8852c53847bbe2f906aff4bdacc355fc41cf5021 100644 +index 4f1d98e960b58cf417d53c487f7eafa2b15c2c1e..1de7a9b74555e01e17ea89ac457dc4d011001a3c 100644 --- a/content/browser/renderer_host/text_input_manager.cc +++ b/content/browser/renderer_host/text_input_manager.cc @@ -183,6 +183,7 @@ void TextInputManager::UpdateTextInputState( @@ -50,7 +50,7 @@ index 9a0aad0d4a3dada0faec031d155c6d346355e8a5..8852c53847bbe2f906aff4bdacc355fc // We reached here because an IPC is received to reset the TextInputState // for |view|. But |view| != |active_view_|, which suggests that at least // one other view has become active and we have received the corresponding -@@ -485,6 +486,12 @@ void TextInputManager::NotifyObserversAboutInputStateUpdate( +@@ -473,6 +474,12 @@ void TextInputManager::NotifyObserversAboutInputStateUpdate( observer.OnUpdateTextInputStateCalled(this, updated_view, did_update_state); } @@ -64,10 +64,10 @@ index 9a0aad0d4a3dada0faec031d155c6d346355e8a5..8852c53847bbe2f906aff4bdacc355fc TextInputManager::SelectionRegion::SelectionRegion( diff --git a/content/browser/renderer_host/text_input_manager.h b/content/browser/renderer_host/text_input_manager.h -index 51522e60d6dc14f1113cc438558b6b393c3fe73a..153ed02f493a83ef9ca354cc18736f9394fc9a72 100644 +index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac46df71d6a 100644 --- a/content/browser/renderer_host/text_input_manager.h +++ b/content/browser/renderer_host/text_input_manager.h -@@ -72,6 +72,10 @@ class CONTENT_EXPORT TextInputManager { +@@ -69,6 +69,10 @@ class CONTENT_EXPORT TextInputManager { virtual void OnTextSelectionChanged( TextInputManager* text_input_manager, RenderWidgetHostViewBase* updated_view) {} @@ -78,7 +78,7 @@ index 51522e60d6dc14f1113cc438558b6b393c3fe73a..153ed02f493a83ef9ca354cc18736f93 }; // Text selection bounds. -@@ -308,6 +312,7 @@ class CONTENT_EXPORT TextInputManager { +@@ -304,6 +308,7 @@ class CONTENT_EXPORT TextInputManager { void NotifyObserversAboutInputStateUpdate(RenderWidgetHostViewBase* view, bool did_update_state); @@ -87,10 +87,10 @@ index 51522e60d6dc14f1113cc438558b6b393c3fe73a..153ed02f493a83ef9ca354cc18736f93 // The view with active text input state, i.e., a focused element. // It will be nullptr if no such view exists. Note that the active view diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 55840c1cf5323d1796f1c3b056602411f513a220..86a1a0244a3744d0571c046d3e6fc9413c9fbbf0 100644 +index 26f41d34766899e81e32c319aad00172ffdc5fca..5465e9d42a5c4eed3b6f819418a7b0ab92c7eae2 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9739,7 +9739,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( +@@ -9856,7 +9856,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( "WebContentsImpl::OnFocusedElementChangedInFrame", "render_frame_host", frame); RenderWidgetHostViewBase* root_view = diff --git a/patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch b/patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch new file mode 100644 index 0000000000000..941dc93331bb1 --- /dev/null +++ b/patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch @@ -0,0 +1,290 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: reito +Date: Wed, 16 Apr 2025 14:09:50 +0800 +Subject: fix: osr stutter fix backport for electron. + +The animated_content_sampler is used to detect the animation frame rate +and adjust the capture frame rate accordingly. However, the detection +algorithm is buggy and can cause output to stutter. This patch is a +upstream patch to allow opt-out the animated_content_sampler. +https://crrev.org/c/6438681 + +diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc +index 67aeb7222ae490cc62717bd7eb8aace022553e9c..11fe61903855b3ef52733aecc3288946bf572de3 100644 +--- a/components/viz/host/client_frame_sink_video_capturer.cc ++++ b/components/viz/host/client_frame_sink_video_capturer.cc +@@ -39,6 +39,17 @@ void ClientFrameSinkVideoCapturer::SetFormat(media::VideoPixelFormat format) { + capturer_remote_->SetFormat(format); + } + ++void ClientFrameSinkVideoCapturer::SetAnimationFpsLockIn( ++ bool enabled, ++ float majority_damaged_pixel_min_ratio) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ animated_content_sampler_enabled_ = enabled; ++ majority_damaged_pixel_min_ratio_ = majority_damaged_pixel_min_ratio; ++ capturer_remote_->SetAnimationFpsLockIn(enabled, ++ majority_damaged_pixel_min_ratio); ++} ++ + void ClientFrameSinkVideoCapturer::SetMinCapturePeriod( + base::TimeDelta min_capture_period) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +@@ -206,6 +217,10 @@ void ClientFrameSinkVideoCapturer::EstablishConnection() { + capturer_remote_->SetMinCapturePeriod(*min_capture_period_); + if (min_size_change_period_) + capturer_remote_->SetMinSizeChangePeriod(*min_size_change_period_); ++ if (animated_content_sampler_enabled_ && majority_damaged_pixel_min_ratio_) { ++ capturer_remote_->SetAnimationFpsLockIn(*animated_content_sampler_enabled_, ++ *majority_damaged_pixel_min_ratio_); ++ } + if (resolution_constraints_) { + capturer_remote_->SetResolutionConstraints( + resolution_constraints_->min_size, resolution_constraints_->max_size, +diff --git a/components/viz/host/client_frame_sink_video_capturer.h b/components/viz/host/client_frame_sink_video_capturer.h +index 8f0693b37dd0aa931a7fd77ddbdb515f0be5d64a..c0613570552072b3da219a920f902ad39a9f1cbc 100644 +--- a/components/viz/host/client_frame_sink_video_capturer.h ++++ b/components/viz/host/client_frame_sink_video_capturer.h +@@ -88,6 +88,8 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer + const gfx::Size& max_size, + bool use_fixed_aspect_ratio); + void SetAutoThrottlingEnabled(bool enabled); ++ void SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio); + void ChangeTarget(const std::optional& target); + void ChangeTarget(const std::optional& target, + uint32_t sub_capture_target_version); +@@ -158,6 +160,8 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer + std::optional resolution_constraints_; + std::optional auto_throttling_enabled_; + std::optional target_; ++ std::optional animated_content_sampler_enabled_; ++ std::optional majority_damaged_pixel_min_ratio_; + uint32_t sub_capture_target_version_ = 0; + // Overlays are owned by the callers of CreateOverlay(). + std::vector> overlays_; +diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc +index db02d9ad523dd1471b5c88cf6b1eade0b592e24f..e8a329f013f39c49056c7eebc7412e84a1b2e98c 100644 +--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc ++++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc +@@ -364,6 +364,18 @@ void FrameSinkVideoCapturerImpl::SetMinSizeChangePeriod( + oracle_->SetMinSizeChangePeriod(min_period); + } + ++void FrameSinkVideoCapturerImpl::SetAnimationFpsLockIn( ++ bool enabled, ++ float majority_damaged_pixel_min_ratio) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ TRACE_EVENT_INSTANT("gpu.capture", "SetAnimationFpsLockIn", "enabled", ++ enabled, "majority_damaged_pixel_min_ratio", ++ majority_damaged_pixel_min_ratio); ++ ++ oracle_->SetAnimationFpsLockIn(enabled, majority_damaged_pixel_min_ratio); ++} ++ + void FrameSinkVideoCapturerImpl::SetResolutionConstraints( + const gfx::Size& min_size, + const gfx::Size& max_size, +diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h +index 8d9036f835c5ced90872b66c8545c65097fd23cc..b387d22c31ab171cde19ceb4a4b5f2a4ce334d9e 100644 +--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h ++++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h +@@ -119,6 +119,8 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final + const gfx::Size& max_size, + bool use_fixed_aspect_ratio) final; + void SetAutoThrottlingEnabled(bool enabled) final; ++ void SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio) final; + void ChangeTarget(const std::optional& target, + uint32_t sub_capture_target_version) final; + void Start(mojo::PendingRemote consumer, +diff --git a/content/browser/devtools/devtools_video_consumer_unittest.cc b/content/browser/devtools/devtools_video_consumer_unittest.cc +index 3420c52fc9bb85057242d25429b00a7a8ec620cd..83ce6ecf31f1bb38de98ff81960aa1948bb5a4e3 100644 +--- a/content/browser/devtools/devtools_video_consumer_unittest.cc ++++ b/content/browser/devtools/devtools_video_consumer_unittest.cc +@@ -70,6 +70,8 @@ class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + min_period_ = min_period; + MockSetMinSizeChangePeriod(min_period_); + } ++ MOCK_METHOD2(SetAnimationFpsLockIn, ++ void(bool enabled, float majority_damaged_pixel_min_ratio)); + MOCK_METHOD1(MockSetMinSizeChangePeriod, void(base::TimeDelta min_period)); + void SetResolutionConstraints(const gfx::Size& min_frame_size, + const gfx::Size& max_frame_size, +diff --git a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc +index b236a38f7f108f823598ae2bf8dc07e53a190141..46a8e24a3ec9ad2ec42fc50c2ac7ab326508a873 100644 +--- a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc ++++ b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc +@@ -105,6 +105,8 @@ class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + MOCK_METHOD1(SetFormat, void(media::VideoPixelFormat format)); + MOCK_METHOD1(SetMinCapturePeriod, void(base::TimeDelta min_period)); + MOCK_METHOD1(SetMinSizeChangePeriod, void(base::TimeDelta)); ++ MOCK_METHOD2(SetAnimationFpsLockIn, ++ void(bool enabled, float majority_damaged_pixel_min_ratio)); + MOCK_METHOD3(SetResolutionConstraints, + void(const gfx::Size& min_size, + const gfx::Size& max_size, +diff --git a/media/capture/content/animated_content_sampler.cc b/media/capture/content/animated_content_sampler.cc +index 6fe67268c1dcb23188200e49c100e985406a9feb..0fadaa2b768a42d3bad511224ad9d6e3ce337e70 100644 +--- a/media/capture/content/animated_content_sampler.cc ++++ b/media/capture/content/animated_content_sampler.cc +@@ -46,7 +46,10 @@ constexpr auto kDriftCorrection = base::Seconds(2); + + AnimatedContentSampler::AnimatedContentSampler( + base::TimeDelta min_capture_period) +- : min_capture_period_(min_capture_period), sampling_state_(NOT_SAMPLING) { ++ : min_capture_period_(min_capture_period), ++ sampling_state_(NOT_SAMPLING), ++ enabled_(true), ++ majority_damaged_pixel_min_ratio_(2.0f / 3) { + DCHECK_GT(min_capture_period_, base::TimeDelta()); + } + +@@ -64,6 +67,10 @@ void AnimatedContentSampler::SetTargetSamplingPeriod(base::TimeDelta period) { + void AnimatedContentSampler::ConsiderPresentationEvent( + const gfx::Rect& damage_rect, + base::TimeTicks event_time) { ++ if (!enabled_) { ++ return; // The sampler is disabled. ++ } ++ + // Analyze the current event and recent history to determine whether animating + // content is detected. + AddObservation(damage_rect, event_time); +@@ -132,6 +139,10 @@ void AnimatedContentSampler::ConsiderPresentationEvent( + } + + bool AnimatedContentSampler::HasProposal() const { ++ if (!enabled_) { ++ return false; ++ } ++ + return sampling_state_ != NOT_SAMPLING; + } + +@@ -230,8 +241,10 @@ bool AnimatedContentSampler::AnalyzeObservations( + if ((last_event_time - first_event_time) < kMinObservationWindow) { + return false; // Content has not animated for long enough for accuracy. + } +- if (num_pixels_damaged_in_chosen <= (num_pixels_damaged_in_all * 2 / 3)) ++ if (num_pixels_damaged_in_chosen <= ++ (num_pixels_damaged_in_all * majority_damaged_pixel_min_ratio_)) { + return false; // Animation is not damaging a supermajority of pixels. ++ } + + *rect = elected_rect; + DCHECK_GT(count_frame_durations, 0u); +diff --git a/media/capture/content/animated_content_sampler.h b/media/capture/content/animated_content_sampler.h +index 89d11829ac038ffeaafa3d4f0b811c6b2bd5665c..b5a325383813210956be7bcd59d8d41fa8c28d9b 100644 +--- a/media/capture/content/animated_content_sampler.h ++++ b/media/capture/content/animated_content_sampler.h +@@ -26,6 +26,15 @@ class CAPTURE_EXPORT AnimatedContentSampler { + explicit AnimatedContentSampler(base::TimeDelta min_capture_period); + ~AnimatedContentSampler(); + ++ // Set whether the animated content sampler would have proposal. ++ void SetEnabled(bool enabled) { enabled_ = enabled; } ++ ++ // Sets the minimum ratio of pixels in the majority-damaged region to all ++ // damaged region's area. ++ void SetMajorityDamagedRectMinRatio(float ratio) { ++ majority_damaged_pixel_min_ratio_ = ratio; ++ } ++ + // Sets a new minimum capture period. + void SetMinCapturePeriod(base::TimeDelta period); + +@@ -34,6 +43,7 @@ class CAPTURE_EXPORT AnimatedContentSampler { + base::TimeDelta target_sampling_period() const { + return target_sampling_period_; + } ++ + void SetTargetSamplingPeriod(base::TimeDelta period); + + // Examines the given presentation event metadata, along with recent history, +@@ -152,6 +162,13 @@ class CAPTURE_EXPORT AnimatedContentSampler { + + // The rewritten frame timestamp for the latest event. + base::TimeTicks frame_timestamp_; ++ ++ // Whether the animated content sampler is enabled ++ bool enabled_; ++ ++ // The minimum ratio of the majority damaged rect area among all damaged ++ // area's pixels ++ float majority_damaged_pixel_min_ratio_; + }; + + } // namespace media +diff --git a/media/capture/content/video_capture_oracle.h b/media/capture/content/video_capture_oracle.h +index 3bb10527f7850e795cb6608dd7e881dc90920eee..b7ac2d4404ae1fdc8a9983da16dcb2ad921b76bc 100644 +--- a/media/capture/content/video_capture_oracle.h ++++ b/media/capture/content/video_capture_oracle.h +@@ -54,6 +54,7 @@ class CAPTURE_EXPORT VideoCaptureOracle { + base::TimeDelta min_capture_period() const { + return smoothing_sampler_.min_capture_period(); + } ++ + void SetMinCapturePeriod(base::TimeDelta period); + + // Sets the range of acceptable capture sizes and whether a fixed aspect ratio +@@ -70,6 +71,19 @@ class CAPTURE_EXPORT VideoCaptureOracle { + // See: SetMinSizeChangePeriod(). + void SetAutoThrottlingEnabled(bool enabled); + ++ // Specifies whether the oracle should detect animation and try to target ++ // the animation frame rate. If |enabled|, the oracle will try to detect a ++ // majority damaged rect and its animation frame rate, and will respect the ++ // minimum damaged pixel ratio of the majority rect's area among all damaged ++ // rect areas set by |majority_damaged_pixel_min_ratio|. If the threshold not ++ // met, it will not use the animated content frame rate. ++ void SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio) { ++ content_sampler_.SetEnabled(enabled); ++ content_sampler_.SetMajorityDamagedRectMinRatio( ++ majority_damaged_pixel_min_ratio); ++ } ++ + // Get/Update the source content size. Changes may not have an immediate + // effect on the proposed capture size, as the oracle will prevent too- + // frequent changes from occurring. +diff --git a/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc b/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc +index eeb4454bfd9500e4bae7544409ff4413fd0874aa..abe0cfb065a7e313b0ca3babd744fce75074ef45 100644 +--- a/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc ++++ b/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc +@@ -195,6 +195,10 @@ class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + + MOCK_METHOD(void, SetAutoThrottlingEnabled, (bool enabled)); + ++ MOCK_METHOD(void, ++ SetAnimationFpsLockIn, ++ (bool enabled, float majority_damaged_pixel_min_ratio)); ++ + MOCK_METHOD(void, + SetResolutionConstraints, + (const Size& min_size, +diff --git a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom +index f0ca9014a0e6b42f99becb7f1fdb9e214c81a16b..fbb002941ca1d36dc2bb12e9391b1faa799d771e 100644 +--- a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom ++++ b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom +@@ -156,6 +156,18 @@ interface FrameSinkVideoCapturer { + // Default, if never called: true. + SetAutoThrottlingEnabled(bool enabled); + ++ // Determines whether the capturer should detect animations and aim to match ++ // their frame rate. If |enabled| is true, the capturer will attempt to ++ // identify the majority damaged rect and its animation frame rate, ++ // while adhering to the |majority_damaged_pixel_min_ratio| threshold. ++ // This ratio is calculated as the area of the majority damaged rect ++ // divided by the total area of all damaged rects. An animation will only ++ // be considered valid if the ratio meets or exceeds the specified threshold. ++ // ++ // By default, this feature is enabled, with the ratio threshold set to 2/3. ++ SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio); ++ + // Targets a different compositor frame sink. This may be called anytime, + // before or after Start(). + // diff --git a/patches/chromium/fix_properly_honor_printing_page_ranges.patch b/patches/chromium/fix_properly_honor_printing_page_ranges.patch index 08b002943d27a..3ba30eeb0356c 100644 --- a/patches/chromium/fix_properly_honor_printing_page_ranges.patch +++ b/patches/chromium/fix_properly_honor_printing_page_ranges.patch @@ -62,7 +62,7 @@ index f39f8b2dd3aa2b89498ed5331aa9b9ba6a02abf5..c0e155c14b2b4e81cde35cea1db284bc PMPrintSettings print_settings = static_cast([print_info_ PMPrintSettings]); diff --git a/printing/printing_context_system_dialog_win.cc b/printing/printing_context_system_dialog_win.cc -index 858e1bb00390b6097a27ffe20997672914e8f28f..953f203d21b4da559791efe228f27b56c2f85263 100644 +index d5eec01f35bd182d5bb49138fc66446fcd5f8cdf..3c289d7cdf7fdf7717fc2397e3344cbe614e0b70 100644 --- a/printing/printing_context_system_dialog_win.cc +++ b/printing/printing_context_system_dialog_win.cc @@ -73,14 +73,30 @@ void PrintingContextSystemDialogWin::AskUserForSettings( @@ -70,7 +70,7 @@ index 858e1bb00390b6097a27ffe20997672914e8f28f..953f203d21b4da559791efe228f27b56 dialog_options.nStartPage = START_PAGE_GENERAL; if (max_pages) { - // Default initialize to print all the pages. - memset(ranges, 0, sizeof(ranges)); + UNSAFE_TODO(memset(ranges, 0, sizeof(ranges))); - ranges[0].nFromPage = 1; - ranges[0].nToPage = max_pages; - dialog_options.nPageRanges = 1; diff --git a/patches/chromium/fix_remove_caption-removing_style_call.patch b/patches/chromium/fix_remove_caption-removing_style_call.patch index fcf8f6764eb5e..1982b96c4bdae 100644 --- a/patches/chromium/fix_remove_caption-removing_style_call.patch +++ b/patches/chromium/fix_remove_caption-removing_style_call.patch @@ -18,10 +18,10 @@ or resizing, but Electron does not seem to run into that issue for opaque frameless windows even with that block commented out. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 759f00dd4e674d1dfca690b82e6e1820900ebf0c..9888ac709c51cd30228e3bca6915f7d89071cb83 100644 +index ac7e189f26da75f6f8b40e69cf4bfee94acddd38..ecab7d7f95998a055f01d7b5180a8fb80eddb32c 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -1786,7 +1786,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { +@@ -1825,7 +1825,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); diff --git a/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch b/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch index 429054930724c..68178f7d8751c 100644 --- a/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch +++ b/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch @@ -6,13 +6,13 @@ Subject: fix: remove profiles from spellcheck_service Electron doesn't support profiles. diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc -index 979630ba7ffe84d78804a02b832ce7787aecf2d9..3b1cdaab5d3229c802c8da53a4fa17a464978391 100644 +index 910534aac947890b16b176b10ac8eae9857bd2d7..1bb22b89b6ec4d620354bb7747c37cae9dd3a57e 100644 --- a/chrome/browser/spellchecker/spellcheck_service.cc +++ b/chrome/browser/spellchecker/spellcheck_service.cc -@@ -21,8 +21,10 @@ +@@ -20,8 +20,10 @@ + #include "base/synchronization/waitable_event.h" #include "base/values.h" #include "build/build_config.h" - #include "build/chromeos_buildflags.h" +#if 0 #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profiles_state.h" @@ -20,7 +20,7 @@ index 979630ba7ffe84d78804a02b832ce7787aecf2d9..3b1cdaab5d3229c802c8da53a4fa17a4 #include "chrome/browser/spellchecker/spellcheck_factory.h" #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" #include "components/language/core/browser/pref_names.h" -@@ -67,7 +69,10 @@ SpellcheckService::SpellCheckerBinder& GetSpellCheckerBinderOverride() { +@@ -66,7 +68,10 @@ SpellcheckService::SpellCheckerBinder& GetSpellCheckerBinderOverride() { // Only record spelling-configuration metrics for profiles in which the user // can configure spelling. bool RecordSpellingConfigurationMetrics(content::BrowserContext* context) { diff --git a/patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch b/patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch new file mode 100644 index 0000000000000..2800abe2e38ca --- /dev/null +++ b/patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch @@ -0,0 +1,115 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: zoy +Date: Mon, 5 May 2025 23:28:53 +0800 +Subject: fix: resolve dynamic background material update issue on Windows 11 + +This patch addresses issues with background materials on Windows 11, +such as the background turning black when maximizing the window and +dynamic background material settings not taking effect. + +diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +index 4780229a4453ad5ca2c89b786459f64c58bf2272..9074b7883f2fcf1341683c68ab7a398acefb0559 100644 +--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc ++++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +@@ -169,6 +169,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) { + } + } + ++void DesktopWindowTreeHostWin::SetIsTranslucent(bool is_translucent) { ++ message_handler_->set_is_translucent(is_translucent); ++} ++ + //////////////////////////////////////////////////////////////////////////////// + // DesktopWindowTreeHostWin, WidgetObserver implementation: + void DesktopWindowTreeHostWin::OnWidgetThemeChanged(Widget* widget) { +diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +index 37109b8d3d439073b5c9e2ea3597c36f32de5704..888fa89d3641ea08a413c0351262cdc967ec12f8 100644 +--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h ++++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +@@ -93,6 +93,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, + // false. + void FinishTouchDrag(gfx::Point screen_point); + ++ void SetIsTranslucent(bool is_translucent); ++ + protected: + // Overridden from DesktopWindowTreeHost: + void Init(const Widget::InitParams& params) override; +diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc +index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..303c186b0a7fbf6b9f7a40e00fc708fecbf68e8d 100644 +--- a/ui/views/win/hwnd_message_handler.cc ++++ b/ui/views/win/hwnd_message_handler.cc +@@ -954,13 +954,13 @@ void HWNDMessageHandler::FrameTypeChanged() { + + void HWNDMessageHandler::PaintAsActiveChanged() { + if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || +- !delegate_->HasFrame() || ++ (!delegate_->HasFrame() && !is_translucent_) || + (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) { + return; + } + + DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(), +- 0); ++ delegate_->HasFrame() ? 0 : -1); + } + + void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, +@@ -1055,7 +1055,14 @@ void HWNDMessageHandler::SizeConstraintsChanged() { + // allowing ui::GetResizableFrameThickness() to be used consistently when + // removing the visible system frame. + const bool had_caption_on_init = window_style() & WS_CAPTION; +- const bool can_resize = !is_translucent_ && delegate_->CanResize(); ++ ++ // In Chromium, the !is_translucent_ check was introduced for Glic-specific ++ // behavior. Since Electron does not use Glic, this restriction can be safely ++ // removed. Keeping the is_translucent_ check disables maximization for ++ // translucent framed windows. Original code: !is_translucent_ && ++ // delegate_->CanResize() See: ++ // https://chromium-review.googlesource.com/c/chromium/src/+/6372329 ++ const bool can_resize = delegate_->CanResize(); + const bool can_maximize = can_resize && delegate_->CanMaximize(); + + auto set_style_func = [&style](LONG bit, bool should_set) { +@@ -1647,11 +1654,16 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { + // through, but that isn't the case when using Direct3D to draw transparent + // windows. So we route translucent windows throught to the delegate to + // allow for a custom hit mask. +- if (!is_translucent_ && !custom_window_region_.is_valid() && ++ // patch: fix_resolve_dynamic_background_material_update_issue_on_windows_11 ++ // Our translucent windows use the native frame by default, and we should not ++ // set a custom region when the window is maximized; otherwise, it will cause ++ // a white title bar to appear under Windows 11. ++ if (!custom_window_region_.is_valid() && + (IsFrameSystemDrawn() || !delegate_->HasNonClientView())) { + if (force) { + SetWindowRgn(hwnd(), nullptr, redraw); + } ++ + return; + } + +@@ -2365,17 +2377,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, + delegate_->SchedulePaint(); + } + +- // Calling DefWindowProc is only necessary if there's a system frame being +- // drawn. Otherwise it can draw an incorrect title bar and cause visual +- // corruption. +- if (!delegate_->HasFrame() || ++ // If the window is translucent, it may have the Mica background. ++ // In that case, it's necessary to call |DefWindowProc|, but we can ++ // pass -1 in the lParam to prevent any non-client area elements from ++ // being displayed. ++ if ((!delegate_->HasFrame() && !is_translucent_) || + delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { + SetMsgHandled(TRUE); + return TRUE; + } + + return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active, +- 0); ++ delegate_->HasFrame() ? 0 : -1); + } + + LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { diff --git a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch index 6e1b26daf7139..23b8d8723968f 100644 --- a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch +++ b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch @@ -11,10 +11,10 @@ This patch should be upstreamed as a conditional revert of the logic in desktop vs mobile runtimes. i.e. restore the old logic only on desktop platforms diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 574e31bcfeb16cdb9a6c0121ffbc1abe175bb4ab..70b56daa9f59db356755b98014f0fce84980c554 100644 +index 9bcab4e1e8a0fa429488555f4f7bd1c54888d10e..bb2e3afdd2b3c6579e32d7eeba59bfc06e952ea2 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc -@@ -2114,9 +2114,8 @@ RenderWidgetHostImpl::GetWidgetInputHandler() { +@@ -2122,9 +2122,8 @@ RenderWidgetHostImpl::GetWidgetInputHandler() { void RenderWidgetHostImpl::NotifyScreenInfoChanged() { // The resize message (which may not happen immediately) will carry with it // the screen info as well as the new size (if the screen has changed scale diff --git a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch index a109363b5a616..4617953a5c613 100644 --- a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch +++ b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch @@ -20,10 +20,10 @@ index fc9cb7e68bdad4c40fab63f70243e09ad005ab80..199fbceda530da31aab9126d78b4bd21 injector_->ExpectsResults(), injector_->ShouldWaitForPromise()); } diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h -index a23931e7a94da9965e70c9f03767b4decf2ae6ea..20340191c2a1e8f9246d4800e3d790340232142f 100644 +index 3758966f3cdfb8c37f3b23b1a903996b9d1ac5fb..a1c908e3281a03600268332d6cde2051aa225ff6 100644 --- a/third_party/blink/public/web/web_local_frame.h +++ b/third_party/blink/public/web/web_local_frame.h -@@ -458,6 +458,7 @@ class BLINK_EXPORT WebLocalFrame : public WebFrame { +@@ -460,6 +460,7 @@ class BLINK_EXPORT WebLocalFrame : public WebFrame { mojom::EvaluationTiming, mojom::LoadEventBlockingOption, WebScriptExecutionCallback, @@ -59,10 +59,10 @@ index cba373664bec3a32abad6fe0396bd67b53b7e67f..a54f1b3351efd2d8f324436f7f35cd43 #endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_EXECUTION_CALLBACK_H_ diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c..7e537b54b11220f3bc6619b027677182485b201d 100644 +index f721051f30f517c863a935765d5dcd4760ab461e..29270267b7884c51064a4618bbddc01c192ff39d 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -3092,6 +3092,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3101,6 +3101,7 @@ void LocalFrame::RequestExecuteScript( mojom::blink::EvaluationTiming evaluation_timing, mojom::blink::LoadEventBlockingOption blocking_option, WebScriptExecutionCallback callback, @@ -70,7 +70,7 @@ index 4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c..7e537b54b11220f3bc6619b027677182 BackForwardCacheAware back_forward_cache_aware, mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_behavior) { -@@ -3124,7 +3125,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3133,7 +3134,7 @@ void LocalFrame::RequestExecuteScript( PausableScriptExecutor::CreateAndRun( script_state, std::move(script_sources), execute_script_policy, user_gesture, evaluation_timing, blocking_option, want_result_option, @@ -80,10 +80,10 @@ index 4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c..7e537b54b11220f3bc6619b027677182 void LocalFrame::SetEvictCachedSessionStorageOnFreezeOrUnload() { diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h -index b90e58cb52d32c10d5dcabf3de001b510e1a4cc4..c2ba0c05a74fc786684b50093720e509c6f723c3 100644 +index 73d924f3675bc84bf0efad5a06abbf0af62ae336..070af1deb56dcd9399915ac436ecce96da19a36c 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h -@@ -814,6 +814,7 @@ class CORE_EXPORT LocalFrame final +@@ -823,6 +823,7 @@ class CORE_EXPORT LocalFrame final mojom::blink::EvaluationTiming, mojom::blink::LoadEventBlockingOption, WebScriptExecutionCallback, @@ -92,10 +92,10 @@ index b90e58cb52d32c10d5dcabf3de001b510e1a4cc4..c2ba0c05a74fc786684b50093720e509 mojom::blink::WantResultOption, mojom::blink::PromiseResultOption); diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc -index b77ad041a717ef8f317a9d15edd61af3c465b2aa..e3955c64ff894682b91c4cc3c49c672659e3a3bf 100644 +index 58b8f5b22c01d41cca3ec0088341d036917d5838..930d9fa36a616fe27d7b5b5a39436cbe375140bc 100644 --- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc +++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc -@@ -964,6 +964,7 @@ void LocalFrameMojoHandler::JavaScriptExecuteRequestInIsolatedWorld( +@@ -969,6 +969,7 @@ void LocalFrameMojoHandler::JavaScriptExecuteRequestInIsolatedWorld( std::move(callback).Run(value ? std::move(*value) : base::Value()); }, std::move(callback)), @@ -104,10 +104,10 @@ index b77ad041a717ef8f317a9d15edd61af3c465b2aa..e3955c64ff894682b91c4cc3c49c6726 wants_result ? mojom::blink::WantResultOption::kWantResultDateAndRegExpAllowed diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc -index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e0564efb3096 100644 +index 2d674cef2d653ed555cd99848f37c67e29eb00fa..cbc8b8b642376959f366a03bd04eace55f9d02bb 100644 --- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc +++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc -@@ -246,7 +246,7 @@ void PausableScriptExecutor::CreateAndRun( +@@ -243,7 +243,7 @@ void PausableScriptExecutor::CreateAndRun( script_state, mojom::blink::UserActivationOption::kDoNotActivate, mojom::blink::LoadEventBlockingOption::kDoNotBlock, want_result_option, mojom::blink::PromiseResultOption::kDoNotWait, @@ -116,7 +116,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 MakeGarbageCollected( script_state->GetIsolate(), function, receiver, argc, argv)); executor->Run(); -@@ -261,10 +261,11 @@ void PausableScriptExecutor::CreateAndRun( +@@ -258,10 +258,11 @@ void PausableScriptExecutor::CreateAndRun( mojom::blink::LoadEventBlockingOption blocking_option, mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_result_option, @@ -130,7 +130,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 MakeGarbageCollected(std::move(sources), execute_script_policy)); switch (evaluation_timing) { -@@ -286,6 +287,14 @@ void PausableScriptExecutor::ContextDestroyed() { +@@ -283,6 +284,14 @@ void PausableScriptExecutor::ContextDestroyed() { ScriptState::Scope script_scope(script_state_); std::move(callback_).Run({}, {}); } @@ -145,7 +145,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 Dispose(); } -@@ -296,10 +305,12 @@ PausableScriptExecutor::PausableScriptExecutor( +@@ -293,10 +302,12 @@ PausableScriptExecutor::PausableScriptExecutor( mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_result_option, WebScriptExecutionCallback callback, @@ -158,7 +158,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 user_activation_option_(user_activation_option), blocking_option_(blocking_option), want_result_option_(want_result_option), -@@ -423,6 +434,13 @@ void PausableScriptExecutor::HandleResults( +@@ -420,6 +431,13 @@ void PausableScriptExecutor::HandleResults( std::move(callback_).Run(std::move(value), start_time_); } @@ -203,7 +203,7 @@ index fa65331f40b90d812b71a489fd560e9359152d2b..390714d631dc88ef92d59ef9618a5706 const mojom::blink::UserActivationOption user_activation_option_; const mojom::blink::LoadEventBlockingOption blocking_option_; diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc -index 2e2aa78d308157642cf27941fc22a211f6396a0d..c5bcbe1f933f2e79003f7eb9f6368174426a6f0a 100644 +index 5e12b61ba14cd1afb07b71ff15e73e905da0addc..685a2ebb6694c173471d0450149321254da652ec 100644 --- a/third_party/blink/renderer/core/frame/web_frame_test.cc +++ b/third_party/blink/renderer/core/frame/web_frame_test.cc @@ -298,6 +298,7 @@ void ExecuteScriptsInMainWorld( @@ -215,10 +215,10 @@ index 2e2aa78d308157642cf27941fc22a211f6396a0d..c5bcbe1f933f2e79003f7eb9f6368174 mojom::blink::WantResultOption::kWantResult, wait_for_promise); } diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -index cdd397ef9e9cba0b25fde78ddd7642ccf3cbdee9..331905c5b4fe488685fc8c156367dee15c602698 100644 +index 6377ce1f6e3c6e6507d73a4bf151b54b30bf042a..0ceb0046224a901297cdf011d5d444e5dd1362c8 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -@@ -1097,14 +1097,15 @@ void WebLocalFrameImpl::RequestExecuteScript( +@@ -1107,14 +1107,15 @@ void WebLocalFrameImpl::RequestExecuteScript( mojom::blink::EvaluationTiming evaluation_timing, mojom::blink::LoadEventBlockingOption blocking_option, WebScriptExecutionCallback callback, @@ -237,7 +237,7 @@ index cdd397ef9e9cba0b25fde78ddd7642ccf3cbdee9..331905c5b4fe488685fc8c156367dee1 bool WebLocalFrameImpl::IsInspectorConnected() { diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h -index 7bc07b1705207447f7727592049c201b011e6b5c..626d0501c0344d7c171f26c96dc78f5e85fb2a81 100644 +index f52966d82c8e904e213e7b2c77ea6d28e980a8e8..940cf84db8ed28212243fac7c61d90f12d85735a 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h @@ -196,6 +196,7 @@ class CORE_EXPORT WebLocalFrameImpl final diff --git a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch index 42ee1e554fb90..2a41793acedb7 100644 --- a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch +++ b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch @@ -6,14 +6,16 @@ Subject: fix: select the first menu item when opened via keyboard This fixes an accessibility issue where the root view is 'focused' to the screen reader instead of the first menu item as with all other native menus. This patch will be upstreamed. diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc -index 6337d0d70f247b06c7871fb7faa75c2b0b7d541c..2bd399161329b054dd67961f767ccc4480af413c 100644 +index 74dd82258742f9e217966174e3ee438086b946bb..9b8b40fa87f3354f9b5ee7a5c4746aae22f5c8c6 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc -@@ -700,6 +700,14 @@ void MenuController::Run(Widget* parent, +@@ -711,6 +711,16 @@ void MenuController::Run(Widget* parent, SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); } -+ if (source_type == ui::mojom::MenuSourceType::kKeyboard && context_menu && root->HasSubmenu()) { ++ if (source_type == ui::mojom::MenuSourceType::kKeyboard && ++ (menu_type == MenuType::kContextMenu || menu_type == MenuType::kMenuItemContextMenu) && ++ root->HasSubmenu()) { + // For context menus opened via the keyboard we select the first item by default + // to match accessibility expectations + MenuItemView* first_item = FindInitialSelectableMenuItem(root, INCREMENT_SELECTION_DOWN); @@ -24,7 +26,7 @@ index 6337d0d70f247b06c7871fb7faa75c2b0b7d541c..2bd399161329b054dd67961f767ccc44 if (button_controller) { pressed_lock_ = button_controller->TakeLock( false, ui::LocatedEvent::FromIfValid(event)); -@@ -2401,19 +2409,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { +@@ -2427,19 +2437,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { } item->GetSubmenu()->ShowAt(params); diff --git a/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch b/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch index 28efacb95da22..cabe355bafb3c 100644 --- a/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch +++ b/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch @@ -15,10 +15,10 @@ capturer was window or screen-specific, as the IDs remain valid for generic capturer as well. diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc -index 010deff30b050c1de9324f969588385958bcb14e..b4b1e9ee805a8565a04737e0898ad8e46709c4d8 100644 +index 93823d191f63f81f79d370f85991f51747c39b46..715a761e3bea87381ccf43b86dddba9bc03bbe9e 100644 --- a/content/browser/media/capture/desktop_capture_device.cc +++ b/content/browser/media/capture/desktop_capture_device.cc -@@ -902,8 +902,14 @@ std::unique_ptr DesktopCaptureDevice::Create( +@@ -908,8 +908,14 @@ std::unique_ptr DesktopCaptureDevice::Create( switch (source.type) { case DesktopMediaID::TYPE_SCREEN: { @@ -35,7 +35,7 @@ index 010deff30b050c1de9324f969588385958bcb14e..b4b1e9ee805a8565a04737e0898ad8e4 if (screen_capturer && screen_capturer->SelectSource(source.id)) { capturer = std::make_unique( std::move(screen_capturer), options); -@@ -916,8 +922,14 @@ std::unique_ptr DesktopCaptureDevice::Create( +@@ -922,8 +928,14 @@ std::unique_ptr DesktopCaptureDevice::Create( } case DesktopMediaID::TYPE_WINDOW: { diff --git a/patches/chromium/fix_win32_synchronous_spellcheck.patch b/patches/chromium/fix_win32_synchronous_spellcheck.patch index 355c8f8e9a6ae..d704d32f69508 100644 --- a/patches/chromium/fix_win32_synchronous_spellcheck.patch +++ b/patches/chromium/fix_win32_synchronous_spellcheck.patch @@ -12,10 +12,10 @@ This patch can be removed when an asynchronous spellcheck API option is added to Electron. diff --git a/components/spellcheck/browser/windows_spell_checker.cc b/components/spellcheck/browser/windows_spell_checker.cc -index f5a7411037758427eddc088b5426554b4a500d33..04b3edd4d8c58d38e260cc54beb0dab86368fc25 100644 +index 7b78720e8d25a3dee4821c816bd9629b1526e50a..be89b74fd85983e63c983aec0d10036ce1ca63df 100644 --- a/components/spellcheck/browser/windows_spell_checker.cc +++ b/components/spellcheck/browser/windows_spell_checker.cc -@@ -239,6 +239,11 @@ std::vector BackgroundHelper::RequestTextCheckForAllLanguages( +@@ -240,6 +240,11 @@ std::vector BackgroundHelper::RequestTextCheckForAllLanguages( (action == CORRECTIVE_ACTION_GET_SUGGESTIONS || action == CORRECTIVE_ACTION_REPLACE)) { std::vector suggestions; diff --git a/patches/chromium/frame_host_manager.patch b/patches/chromium/frame_host_manager.patch index ba06e6937f1e2..2199c77ff9e7c 100644 --- a/patches/chromium/frame_host_manager.patch +++ b/patches/chromium/frame_host_manager.patch @@ -6,10 +6,10 @@ Subject: frame_host_manager.patch Allows embedder to intercept site instances created by chromium. diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc -index 537b9313ca471e342c7dd894dd82e0ab67e23d2b..5573b22d28d1df7dfaa57b702511b78f9606d8d7 100644 +index 301c950ed4f58cc80a6c51584e54c51dd800f25d..758822cc1685c000e6ecc1894b991eb2ccd9afcc 100644 --- a/content/browser/renderer_host/render_frame_host_manager.cc +++ b/content/browser/renderer_host/render_frame_host_manager.cc -@@ -4729,6 +4729,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( +@@ -4785,6 +4785,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( request->ResetStateForSiteInstanceChange(); } @@ -20,7 +20,7 @@ index 537b9313ca471e342c7dd894dd82e0ab67e23d2b..5573b22d28d1df7dfaa57b702511b78f } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index da93d31b658577bd4817b2dcfe366d444dbc2e92..96f76da6ea600f0b13bb78161a84c9c9dab19234 100644 +index 8a7ea32eb4ea4a78662b96a2eef5a0de4229d214..c7f14050af11356b5a985c6fb27570b96f4104c7 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -340,6 +340,11 @@ class CONTENT_EXPORT ContentBrowserClient { diff --git a/patches/chromium/gin_enable_disable_v8_platform.patch b/patches/chromium/gin_enable_disable_v8_platform.patch index 939c9ea078c92..b0ac9ec5498b0 100644 --- a/patches/chromium/gin_enable_disable_v8_platform.patch +++ b/patches/chromium/gin_enable_disable_v8_platform.patch @@ -41,10 +41,10 @@ index ff42cfbb6a228e902317c7e3ab035d8437d5dd62..e27f177ce27e177abf6cee84cd466e7a // Returns whether `Initialize` has already been invoked in the process. // Initialization is a one-way operation (i.e., this method cannot return diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index 532f0a1b5a7dbe6d8fd8c734e7f6b72dbc1df4ad..e72331d0821a14507e3c973f07889adcf45d3bdf 100644 +index 69ab1ef4d2a386126009036d4517c69dcaf9a33a..11cafc3e1588cce52b76cc2f09f66b3e451fb087 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -541,7 +541,8 @@ void SetFeatureFlags() { +@@ -543,7 +543,8 @@ void SetFeatureFlags() { void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, const std::string& js_command_line_flags, bool disallow_v8_feature_flag_overrides, @@ -54,7 +54,7 @@ index 532f0a1b5a7dbe6d8fd8c734e7f6b72dbc1df4ad..e72331d0821a14507e3c973f07889adc static bool v8_is_initialized = false; if (v8_is_initialized) return; -@@ -555,7 +556,8 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, +@@ -558,7 +559,8 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, } SetFlags(mode, js_command_line_flags); diff --git a/patches/chromium/gritsettings_resource_ids.patch b/patches/chromium/gritsettings_resource_ids.patch index ec7f438c0a510..d5b4842d3089e 100644 --- a/patches/chromium/gritsettings_resource_ids.patch +++ b/patches/chromium/gritsettings_resource_ids.patch @@ -6,10 +6,10 @@ Subject: gritsettings_resource_ids.patch Add electron resources file to the list of resource ids generation. diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec -index 2cd6ca756a745e1387469914eb3d649dff104ccb..45ce48afff4178af11d3cb55b859c9b48aa5ab60 100644 +index 2f5be98233528436a8a2ecf91aaca42ebb045921..43bee48208ac3084bcfc32c53caf76058554d7cf 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec -@@ -1472,6 +1472,11 @@ +@@ -1504,6 +1504,11 @@ "<(SHARED_INTERMEDIATE_DIR)/third_party/blink/public/strings/permission_element_generated_strings.grd": { "META": {"sizes": {"messages": [2000],}}, "messages": [10080], diff --git a/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch b/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch index e4d74dfbb8ba1..86eddbcccf563 100644 --- a/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch +++ b/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch @@ -11,7 +11,7 @@ If removing this patch causes no sync failures, it's safe to delete :+1: Ref https://chromium-review.googlesource.com/c/chromium/src/+/2953903 diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py -index 6602d1f1b2f5e6a4f3a49be2d79264aa1477d3af..26b511e1ec9691a91e13acb8297dc38ecc582313 100755 +index 9cf06fbc79ffdc6cd44cd163e1924bc63c9099f6..944d5622c74e7df32b6ab26807442d80b27cc2bb 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py @@ -304,6 +304,8 @@ def GetDefaultHostOs(): diff --git a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch b/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch deleted file mode 100644 index 1df2ebda76fc8..0000000000000 --- a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?David=20L=C3=B6nnhager?= -Date: Fri, 17 Jan 2025 14:30:48 +0100 -Subject: Ignore parse errors for PKEY_AppUserModel_ToastActivatorCLSID - -Some shortcuts store this as a string UUID as opposed to VT_CLSID, -hitting NOTREACHED() and sometimes breaking parsing in Electron. -Ignore this error instead. - -Bug: N/A -Change-Id: I9fc472212b2d3afac2c8e18a2159bc2d50bbdf98 - -diff --git a/AUTHORS b/AUTHORS -index 66e37c775006eaba80440a48b92cce825efd2944..8d03ca899968f24ef73c8563dfe8f13bf4fc6ee8 100644 ---- a/AUTHORS -+++ b/AUTHORS -@@ -340,6 +340,7 @@ David Futcher - David Jin - David Lechner - David Leen -+David Lönnhager - David Manouchehri - David McAllister - David Michael Barr -diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc -index 967e130e823f41c402411dfadb53b805e8a8c92b..3a9df7f31861ca69168fd24513ee554d0984798d 100644 ---- a/base/win/shortcut.cc -+++ b/base/win/shortcut.cc -@@ -356,8 +356,9 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, - *(pv_toast_activator_clsid.get().puuid)); - break; - default: -- NOTREACHED() << "Unexpected variant type: " -- << pv_toast_activator_clsid.get().vt; -+ // Shortcuts may use strings to represent the CLSID. This case is -+ // ignored. -+ break; - } - } - } diff --git a/patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch b/patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch new file mode 100644 index 0000000000000..e3fe60af6e365 --- /dev/null +++ b/patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?David=20L=C3=B6nnhager?= +Date: Fri, 17 Jan 2025 14:30:48 +0100 +Subject: Ignore parse errors in ResolveShortcutProperties + +Some shortcuts store this as a string UUID as opposed to VT_CLSID, +hitting NOTREACHED() and sometimes breaking parsing in Electron. +Ignore this error instead. + +diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc +index 967e130e823f41c402411dfadb53b805e8a8c92b..c1cc95fa8993cc5bdab422710934fb6217272b96 100644 +--- a/base/win/shortcut.cc ++++ b/base/win/shortcut.cc +@@ -317,7 +317,8 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, + break; + } + default: { +- NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt; ++ LOG(WARNING) << "Unexpected variant type: " << pv_app_id.get().vt; ++ break; + } + } + } +@@ -336,7 +337,8 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, + properties->set_dual_mode(pv_dual_mode.get().boolVal == VARIANT_TRUE); + break; + default: +- NOTREACHED() << "Unexpected variant type: " << pv_dual_mode.get().vt; ++ LOG(WARNING) << "Unexpected variant type: " << pv_dual_mode.get().vt; ++ break; + } + } + +@@ -356,8 +358,9 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, + *(pv_toast_activator_clsid.get().puuid)); + break; + default: +- NOTREACHED() << "Unexpected variant type: " +- << pv_toast_activator_clsid.get().vt; ++ LOG(INFO) << "Unexpected variant type: " ++ << pv_toast_activator_clsid.get().vt; ++ break; + } + } + } diff --git a/patches/chromium/ignore_rc_check.patch b/patches/chromium/ignore_rc_check.patch index 427c2306315c9..0e92b98969025 100644 --- a/patches/chromium/ignore_rc_check.patch +++ b/patches/chromium/ignore_rc_check.patch @@ -7,10 +7,10 @@ Dont compare RC.exe and RC.py output. FIXME: It has to be reverted once the script is fixed. diff --git a/build/toolchain/win/rc/rc.py b/build/toolchain/win/rc/rc.py -index a650506a1e0a19566861b672cb800024965d7ff5..cde05b15eef3decdee0093ca8b84201008e4adb0 100755 +index ca98984ee20cf4f608182c5ee5926673b436b89c..fc8a2af013f45db6e4e30891270a5f071c783d65 100755 --- a/build/toolchain/win/rc/rc.py +++ b/build/toolchain/win/rc/rc.py -@@ -244,7 +244,10 @@ def CompareToMsRcOutput(preprocessed_output, is_utf8, flags): +@@ -251,7 +251,10 @@ def CompareToMsRcOutput(preprocessed_output, is_utf8, flags): # Assert Microsoft rc.exe and rc.py produced identical .res files. if rc_exe_exit_code == 0: import filecmp diff --git a/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch b/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch index 704fededfb255..36b9ec492ee0f 100644 --- a/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch +++ b/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch @@ -21,10 +21,10 @@ index 35023e37a34bbcdcfa09cbdad55648f8e0419b37..9a10db499bd0a48feb1c96f87e71af4f properties->supports_global_application_menus = true; properties->app_modal_dialogs_use_event_blocker = true; diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h -index 2f088c0ee697b2525c9770eb8d02e28ae48a85d2..ed8fe0a40537b598695af8aa42c2f0bdc61ab1bb 100644 +index e35bd769c53a544b8a1387f6c25e1ae376643b3d..be7d09d9003011ef4f609f66b8c51a12aa410d80 100644 --- a/ui/ozone/public/ozone_platform.h +++ b/ui/ozone/public/ozone_platform.h -@@ -128,6 +128,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { +@@ -129,6 +129,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // Linux only: determines if Skia can fall back to the X11 output device. bool skia_can_fall_back_to_x11 = false; diff --git a/patches/chromium/load_v8_snapshot_in_browser_process.patch b/patches/chromium/load_v8_snapshot_in_browser_process.patch index 3df6a60b1c153..a6820dea05d63 100644 --- a/patches/chromium/load_v8_snapshot_in_browser_process.patch +++ b/patches/chromium/load_v8_snapshot_in_browser_process.patch @@ -9,10 +9,10 @@ but due to the nature of electron, we need to load the v8 snapshot in the browser process. diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc -index 82e5123e35509d22493809a37e56be5e9bea4264..054f38d683280638c7ac618d2ff8f7aef1a0def0 100644 +index e81e89be0e190b3400023b3105b7774c19428f72..7550d3eb796059aeb108f25071c8028bddebb100 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc -@@ -292,11 +292,8 @@ void LoadV8SnapshotFile(const base::CommandLine& command_line) { +@@ -294,11 +294,8 @@ void LoadV8SnapshotFile(const base::CommandLine& command_line) { bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, const std::string& process_type) { diff --git a/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch b/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch index f823d122dd8f2..3b90c3847d68c 100644 --- a/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch +++ b/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch @@ -9,10 +9,10 @@ be created for each child process, despite logs being redirected to a file. diff --git a/content/app/content_main.cc b/content/app/content_main.cc -index ae8261b3f62a1eec298c5a361c2966ccd3550cdd..d8ba4151c235b9beaade79d478a89d15ab9ba9f7 100644 +index ffc5c377428008a9cee3969e958e19357b3ecead..892fa6258a77a6ec3ddfa209a9d3fd63a372406c 100644 --- a/content/app/content_main.cc +++ b/content/app/content_main.cc -@@ -331,16 +331,14 @@ NO_STACK_PROTECTOR int RunContentProcess( +@@ -330,16 +330,14 @@ NO_STACK_PROTECTOR int RunContentProcess( #if BUILDFLAG(IS_WIN) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); diff --git a/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch b/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch new file mode 100644 index 0000000000000..6dc377ddac639 --- /dev/null +++ b/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Keren Zhu +Date: Fri, 18 Apr 2025 11:02:46 -0700 +Subject: mac: fix CHECK on IME reconversion due to invalid replacement range +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It appears that Google Japanese IME can call -setMarkedText: with an +invalid replacement range when doing text reconversion (変換, i.e., +conversion between different text with same pronunciation). This range +is a NSRange and NSRange.location is supposed to be NSNotFound (2^31-1) +for invalid range, but the IME can pass in 2^32. Subsequently causing +CHECK error. + +This CL fixes the issue by converting such invalid NSRange to +gfx::InvalidRange using FromPossiblyInvalidNSRange(range). + +Fixed: 409864204 +Change-Id: I08ff426a933ef76aa81e33af59aa32e2ac0b674d +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6470915 +Commit-Queue: Keren Zhu +Reviewed-by: Marijn Kruisselbrink +Cr-Commit-Position: refs/heads/main@{#1448935} + +diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +index 300d90a25cb87019d6d179dac029138fd759b80b..008e1963242bde90d818013494b8d78482d482ce 100644 +--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm ++++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +@@ -2424,9 +2424,10 @@ - (void)setMarkedText:(id)string + if ([self isHandlingKeyDown] && !_isReconversionTriggered) { + _setMarkedTextReplacementRange = gfx::Range(replacementRange); + } else { +- _host->ImeSetComposition(_markedText, _imeTextSpans, +- gfx::Range(replacementRange), newSelRange.location, +- NSMaxRange(newSelRange)); ++ _host->ImeSetComposition( ++ _markedText, _imeTextSpans, ++ gfx::Range::FromPossiblyInvalidNSRange(replacementRange), ++ newSelRange.location, NSMaxRange(newSelRange)); + } + + [[self inputContext] invalidateCharacterCoordinates]; diff --git a/patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch b/patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch new file mode 100644 index 0000000000000..8e53a214b87ae --- /dev/null +++ b/patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Wed, 7 May 2025 05:08:18 -0700 +Subject: Make focus methods in WebContentsViewChildFrame NOTIMPLEMENTED + +Change focus methods in WebContentsViewChildFrame to NOTIMPLEMENTED. +It's possible to for focus to be called on the child frame, e.g. in the +context of chrome.webviewTag, and shouldn't necessarily crash. + +This also fixes an associated crash in Electron, where the NOTREACHED is +hit when PointerLockController::LockPointer calls web_contents->Focus(). + +Change-Id: Ide58aae2187fbdd807be4ec176d13c76e459ba9c +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6508949 +Commit-Queue: Bo Liu +Reviewed-by: Bo Liu +Reviewed-by: Rakina Zata Amni +Cr-Commit-Position: refs/heads/main@{#1456886} + +diff --git a/content/browser/web_contents/web_contents_view_child_frame.cc b/content/browser/web_contents/web_contents_view_child_frame.cc +index b89d4621dc2acc84f7d8c749f34f7f5563543c72..9c206f6ee424fc423d5f772c7559e60ec0df6eef 100644 +--- a/content/browser/web_contents/web_contents_view_child_frame.cc ++++ b/content/browser/web_contents/web_contents_view_child_frame.cc +@@ -6,6 +6,7 @@ + + #include + ++#include "base/notimplemented.h" + #include "build/build_config.h" + #include "content/browser/renderer_host/render_frame_proxy_host.h" + #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" +@@ -160,15 +161,15 @@ void WebContentsViewChildFrame::DestroyBackForwardTransitionAnimationManager() { + } + + void WebContentsViewChildFrame::RestoreFocus() { +- NOTREACHED(); ++ NOTIMPLEMENTED(); + } + + void WebContentsViewChildFrame::Focus() { +- NOTREACHED(); ++ NOTIMPLEMENTED(); + } + + void WebContentsViewChildFrame::StoreFocus() { +- NOTREACHED(); ++ NOTIMPLEMENTED(); + } + + void WebContentsViewChildFrame::FocusThroughTabTraversal(bool reverse) { diff --git a/patches/chromium/make_gtk_getlibgtk_public.patch b/patches/chromium/make_gtk_getlibgtk_public.patch index a63d5783f1edf..9a0c214c6a368 100644 --- a/patches/chromium/make_gtk_getlibgtk_public.patch +++ b/patches/chromium/make_gtk_getlibgtk_public.patch @@ -7,10 +7,10 @@ Allows embedders to get a handle to the gdk_pixbuf library already loaded in the process. diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc -index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..e410998c4197c98947d2e1ad8bebe12c70379358 100644 +index f28cf275db0205186fe6143b7e1550b6c30a4435..346992e202c507eeac454d657507e1bd336175fc 100644 --- a/ui/gtk/gtk_compat.cc +++ b/ui/gtk/gtk_compat.cc -@@ -66,11 +66,6 @@ void* GetLibGio() { +@@ -69,11 +69,6 @@ void* GetLibGio() { return libgio; } @@ -22,7 +22,7 @@ index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..e410998c4197c98947d2e1ad8bebe12c void* GetLibGdk3() { static void* libgdk3 = DlOpen("libgdk-3.so.0"); return libgdk3; -@@ -134,6 +129,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) { +@@ -150,6 +145,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) { } // namespace diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 058f6be906a3a..768aa32004924 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -35,10 +35,10 @@ system font by checking if it's kCTFontPriorityAttribute is set to system priority. diff --git a/base/BUILD.gn b/base/BUILD.gn -index a8684cc2bf48a7df896847ad1b8f34308db3c28f..de55a37e5acaa72dbe8c898b6c909fc3b4b53dbc 100644 +index 57a6af006161508474e0647d22580e872168ef4e..08205acb929894d87fecaee2c155aef5715cf180 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn -@@ -1031,6 +1031,7 @@ component("base") { +@@ -1041,6 +1041,7 @@ component("base") { "//build:ios_buildflags", "//build/config/compiler:compiler_buildflags", "//third_party/modp_b64", @@ -269,11 +269,11 @@ index e9f4e5131238b9fb5f1b4b3e90a0cb84a7fc15b4..8b5f4cae3123ac5480ad73f0c873fca0 } // namespace diff --git a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm -index 3a8b44a2a295119f37ca37d5866dfcfa21121db0..b408e9c73fe97dd8885b5479923481e20955cf8d 100644 +index 92646b4978457724d46eb526d6f34cd308562f0b..c3e669ed8fb0682f7f973bb1c69d069bd6a3aa0c 100644 --- a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm +++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm @@ -9,7 +9,9 @@ - #include "components/remote_cocoa/app_shim/features.h" + #include "base/mac/mac_util.h" #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h" +#include "electron/mas.h" @@ -298,7 +298,7 @@ index 3a8b44a2a295119f37ca37d5866dfcfa21121db0..b408e9c73fe97dd8885b5479923481e2 + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle { // - NSThemeFrame and its subclasses will be nil if it's missing at runtime. if ([BrowserWindowFrame class]) -@@ -163,6 +168,8 @@ - (BOOL)_usesCustomDrawing { +@@ -164,6 +169,8 @@ - (BOOL)_usesCustomDrawing { return NO; } @@ -307,7 +307,7 @@ index 3a8b44a2a295119f37ca37d5866dfcfa21121db0..b408e9c73fe97dd8885b5479923481e2 // Handle "Move focus to the window toolbar" configured in System Preferences -> // Keyboard -> Shortcuts -> Keyboard. Usually Ctrl+F5. The argument (|unknown|) // tends to just be nil. -@@ -173,8 +180,8 @@ - (void)_handleFocusToolbarHotKey:(id)unknown { +@@ -174,8 +181,8 @@ - (void)_handleFocusToolbarHotKey:(id)unknown { } - (void)setAlwaysShowTrafficLights:(BOOL)alwaysShow { @@ -382,7 +382,7 @@ index 127a2829fafa04bfbab0b883304dfb815d7e1c22..61d7946e52862f3586b1e098d7d44a12 // The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that // can only be accomplished by overriding methods. diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm -index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec89873549 100644 +index dcaa590375f7cc42996a95ceb0804733d382f60b..e80557dfa4992823ffca81e77aaeef1afcabae09 100644 --- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm +++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm @@ -26,6 +26,7 @@ @@ -412,7 +412,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec - (BOOL)hasKeyAppearance; - (long long)_resizeDirectionForMouseLocation:(CGPoint)location; - (BOOL)_isConsideredOpenForPersistentState; -@@ -159,6 +164,8 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event { +@@ -169,6 +174,8 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event { } @end @@ -421,7 +421,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec @implementation NativeWidgetMacNSWindowTitledFrame - (void)mouseDown:(NSEvent*)event { if (self.window.isMovable) -@@ -186,6 +193,8 @@ - (BOOL)usesCustomDrawing { +@@ -196,6 +203,8 @@ - (BOOL)usesCustomDrawing { } @end @@ -430,7 +430,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec @implementation NativeWidgetMacNSWindow { @private CommandDispatcher* __strong _commandDispatcher; -@@ -383,6 +392,8 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -393,6 +402,8 @@ - (NSAccessibilityRole)accessibilityRole { // NSWindow overrides. @@ -439,7 +439,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { if (windowStyle & NSWindowStyleMaskTitled) { if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class]) -@@ -394,6 +405,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { +@@ -404,6 +415,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { return [super frameViewClassForStyleMask:windowStyle]; } @@ -449,10 +449,10 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec bool shouldShowWindowTitle = YES; if (_bridge) diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -index bcd3ea598687c5a85cddc862cdb16eab2d8dbd5d..2ed210685453409a2aa330bf718694f70a3d862d 100644 +index 0b964e176e59e77a0743e2d347aa185bc7c80bc5..49a60b9aa0597c3397906303b73a4c1650cc60ce 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -@@ -41,6 +41,7 @@ +@@ -42,6 +42,7 @@ #import "components/remote_cocoa/app_shim/views_nswindow_delegate.h" #import "components/remote_cocoa/app_shim/window_move_loop.h" #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h" @@ -460,7 +460,7 @@ index bcd3ea598687c5a85cddc862cdb16eab2d8dbd5d..2ed210685453409a2aa330bf718694f7 #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/cert/x509_util_apple.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" -@@ -676,10 +677,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { +@@ -681,10 +682,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { // this should be treated as an error and caught early. CHECK(bridged_view_); @@ -474,18 +474,18 @@ index bcd3ea598687c5a85cddc862cdb16eab2d8dbd5d..2ed210685453409a2aa330bf718694f7 // Beware: This view was briefly removed (in favor of a bare CALayer) in // https://crrev.com/c/1236675. The ordering of unassociated layers relative diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn -index b1cb8132b0e8d7bb18848d49634ecaaa939eefd4..610ed4c922705697b27af569af55064daeff5d25 100644 +index 2abb9b96124ad7867204dc5f6c678080472f28d7..067b3cc0d6a8b5990dc4bad0f15e47bd4922a26a 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn -@@ -371,6 +371,7 @@ viz_component("service") { +@@ -383,6 +383,7 @@ viz_component("service") { "frame_sinks/external_begin_frame_source_mac.h", ] } + deps += ["//electron/build/config:generate_mas_config"] } - if (is_android || use_ozone) { -@@ -671,6 +672,7 @@ viz_source_set("unit_tests") { + if (is_ios) { +@@ -695,6 +696,7 @@ viz_source_set("unit_tests") { "display_embedder/software_output_device_mac_unittest.mm", ] frameworks = [ "IOSurface.framework" ] @@ -545,7 +545,7 @@ index dbf334caa3a6d10017b69ad76802e389a011436b..da828823e8195cc9e497866363c9af93 void ForwardKeyboardEvent(const input::NativeWebKeyboardEvent& key_event, diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index 08186f30eaa97caf18ac2fe47655526e7733d9df..e4cd1f436ad1734f357429cb3ad6c85430bf1d76 100644 +index c125b021061b74b8a7e01b757d08f0b1fa89f14c..300d90a25cb87019d6d179dac029138fd759b80b 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -35,6 +35,7 @@ @@ -556,7 +556,7 @@ index 08186f30eaa97caf18ac2fe47655526e7733d9df..e4cd1f436ad1734f357429cb3ad6c854 #include "skia/ext/skia_utils_mac.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/input/input_handler.mojom.h" -@@ -2151,15 +2152,21 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -2145,15 +2146,21 @@ - (NSAccessibilityRole)accessibilityRole { // Since this implementation doesn't have to wait any IPC calls, this doesn't // make any key-typing jank. --hbono 7/23/09 // @@ -579,10 +579,10 @@ index 08186f30eaa97caf18ac2fe47655526e7733d9df..e4cd1f436ad1734f357429cb3ad6c854 return kAttributes; } diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 32ec2d854847d28b8c9ad665a1b8a50a6bad3100..885d487afbaae02c0401b322dd59273116c91ad0 100644 +index 9ed48bb42381690b8a5c1a5d3aef7f82cb59ac65..56118dba5d55a937290a1234bd1c70d4d301de25 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -333,6 +333,7 @@ source_set("browser") { +@@ -340,6 +340,7 @@ source_set("browser") { "//ui/webui/resources", "//v8", "//v8:v8_version", @@ -591,7 +591,7 @@ index 32ec2d854847d28b8c9ad665a1b8a50a6bad3100..885d487afbaae02c0401b322dd592731 public_deps = [ diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h -index b58722615ebd4012816ed3246145cb1a088bfe17..1fde1a7ae8a8885bc3210fe0298476acbb2c915d 100644 +index 1b923f0e0dc6d7dc9e67d278b8da00b35745241e..6f7ee79df9b9e3026663e2a4637007de5b5da902 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -24,6 +24,7 @@ @@ -612,7 +612,7 @@ index b58722615ebd4012816ed3246145cb1a088bfe17..1fde1a7ae8a8885bc3210fe0298476ac @class RenderWidgetHostViewCocoa; namespace content { -@@ -693,9 +696,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac +@@ -692,9 +695,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // EnsureSurfaceSynchronizedForWebTest(). uint32_t latest_capture_sequence_number_ = 0u; @@ -625,7 +625,7 @@ index b58722615ebd4012816ed3246145cb1a088bfe17..1fde1a7ae8a8885bc3210fe0298476ac // Used to force the NSApplication's focused accessibility element to be the // content::BrowserAccessibilityCocoa accessibility tree when the NSView for diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31e9305c6c 100644 +index 1f9fbdbc7d617adb019fb8a207baad817b5a8a0e..1a726fb4a9e8173dc7da6901eb1632c7f326358e 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -49,6 +49,7 @@ @@ -636,7 +636,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 #include "media/base/media_switches.h" #include "skia/ext/platform_canvas.h" #include "skia/ext/skia_utils_mac.h" -@@ -274,8 +275,10 @@ +@@ -275,8 +276,10 @@ void RenderWidgetHostViewMac::MigrateNSViewBridge( remote_cocoa::mojom::Application* remote_cocoa_application, uint64_t parent_ns_view_id) { @@ -647,7 +647,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 // Reset `ns_view_` before resetting `remote_ns_view_` to avoid dangling // pointers. `ns_view_` gets reinitialized later in this method. -@@ -1627,8 +1630,10 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -1633,8 +1636,10 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, gfx::NativeViewAccessible RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessibleForWindow() { @@ -658,7 +658,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 return [GetInProcessNSView() window]; } -@@ -1677,9 +1682,11 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -1683,9 +1688,11 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, } void RenderWidgetHostViewMac::SetAccessibilityWindow(NSWindow* window) { @@ -670,7 +670,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 } bool RenderWidgetHostViewMac::SyncIsWidgetForMainFrame( -@@ -2206,20 +2213,26 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -2212,20 +2219,26 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, void RenderWidgetHostViewMac::GetRenderWidgetAccessibilityToken( GetRenderWidgetAccessibilityTokenCallback callback) { base::ProcessId pid = getpid(); @@ -698,10 +698,10 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 /////////////////////////////////////////////////////////////////////////////// diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn -index 9de0d44a70c4c2587a7e4283783b614174393819..87dfae661a98819118d48cd973862d8fabc7bfa6 100644 +index fb23dfd8f90ad3eeb55e4db373ca67f75d8e5ce1..df902b0b01ed4d092070a92fdf77cf69fde475e7 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn -@@ -285,6 +285,7 @@ source_set("common") { +@@ -283,6 +283,7 @@ source_set("common") { "//ui/shell_dialogs", "//url", "//url/ipc:url_ipc", @@ -792,10 +792,10 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe } // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn -index 23d2ac7768ec651a6542b14eb1b9d3636f90b192..c5d33465dba18c7fd1258e8a0e78f870d5be441c 100644 +index 34297736b07e3f76d1768131f4f0e1db5e68c7fd..2686d23798d4d0dff2608a0c411d154aa91c12be 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn -@@ -658,6 +658,7 @@ static_library("test_support") { +@@ -660,6 +660,7 @@ static_library("test_support") { "//url", "//url/mojom:url_mojom_gurl", "//v8", @@ -803,7 +803,7 @@ index 23d2ac7768ec651a6542b14eb1b9d3636f90b192..c5d33465dba18c7fd1258e8a0e78f870 ] data_deps = [ -@@ -1111,6 +1112,7 @@ static_library("browsertest_support") { +@@ -1113,6 +1114,7 @@ static_library("browsertest_support") { } configs += [ "//v8:external_startup_data" ] @@ -811,7 +811,7 @@ index 23d2ac7768ec651a6542b14eb1b9d3636f90b192..c5d33465dba18c7fd1258e8a0e78f870 } mojom("content_test_mojo_bindings") { -@@ -1946,6 +1948,7 @@ test("content_browsertests") { +@@ -1953,6 +1955,7 @@ test("content_browsertests") { "//ui/shell_dialogs", "//ui/snapshot", "//ui/webui:test_support", @@ -819,10 +819,10 @@ index 23d2ac7768ec651a6542b14eb1b9d3636f90b192..c5d33465dba18c7fd1258e8a0e78f870 ] if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) { -@@ -3251,6 +3254,7 @@ test("content_unittests") { - "//ui/latency:test_support", +@@ -3273,6 +3276,7 @@ test("content_unittests") { "//ui/shell_dialogs:shell_dialogs", "//ui/webui:test_support", + "//url", + "//electron/build/config:generate_mas_config" ] @@ -840,10 +840,10 @@ index 8779cf7cbbe2a583fda51867eaebc627f00bd4cd..98da1587fdc8cc8358a69db2b51d3982 # TODO(crbug.com/40139469): Blink test plugin must be migrated from PPAPI. diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn -index d27d7140cc55a35e6ffb65028265bcb2ae5932d1..1ea92b29c730aba1c667de83bb98ca85f2046470 100644 +index 16407ea4758addb6f6b56dc044d58cde0922897a..98cf8359b412a61b48aa87b607740bbeda4ca159 100644 --- a/device/bluetooth/BUILD.gn +++ b/device/bluetooth/BUILD.gn -@@ -253,6 +253,7 @@ component("bluetooth") { +@@ -257,6 +257,7 @@ component("bluetooth") { "IOKit.framework", "Foundation.framework", ] @@ -910,7 +910,7 @@ index 973cd337f2781271b4ca3e29db07939ec6917327..90658a336138e4e50f93c38f1c1f465c if (is_ios) { sources += [ "image_transport_surface_ios.mm" ] diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.h b/gpu/ipc/service/image_transport_surface_overlay_mac.h -index 93665e7ec19efa8d94fe0bb4f195642f43432c24..23c25ce53e4e630e173777943770a09ee736cc3d 100644 +index ae040bbac8755b677dc6e19383a2390df407e5a6..e95ca30f49506c66a37d6d5269929f437005f863 100644 --- a/gpu/ipc/service/image_transport_surface_overlay_mac.h +++ b/gpu/ipc/service/image_transport_surface_overlay_mac.h @@ -8,6 +8,7 @@ @@ -932,10 +932,10 @@ index 93665e7ec19efa8d94fe0bb4f195642f43432c24..23c25ce53e4e630e173777943770a09e namespace ui { diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn -index 87126a36725849cbaf478e2dc24dc3a628a30846..a3a88b07af91b86191d9e5727a1d021ebbbb22ce 100644 +index 8bcbb663710445e977a3080209cd518d3fc2c6e0..0680d3af1f219235e20c0e83b80c0ea9cbf7b3a1 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn -@@ -196,6 +196,7 @@ source_set("audio") { +@@ -198,6 +198,7 @@ source_set("audio") { "CoreMedia.framework", ] weak_frameworks = [ "ScreenCaptureKit.framework" ] # macOS 13.0 @@ -1051,10 +1051,10 @@ index 453e2185fc85fcb29fa7af3f94cce5bda8118b0c..1c383675bb9113b5b1df9280b8ee9941 source_set("sandbox_unittests") { diff --git a/sandbox/mac/sandbox_logging.cc b/sandbox/mac/sandbox_logging.cc -index 095c639b9893e885d8937e29ed7d47a7c28bc6b6..7e0cf9b9f94b16741358bdb45122f8b2bd68c0f9 100644 +index 950cf7cfee4e11766dccf5c0bf3f15a8562f0f1e..a5adaaabdbbd91fedbc4cb679c865bc342536090 100644 --- a/sandbox/mac/sandbox_logging.cc +++ b/sandbox/mac/sandbox_logging.cc -@@ -16,6 +16,7 @@ +@@ -21,6 +21,7 @@ #include #include "build/build_config.h" @@ -1062,7 +1062,7 @@ index 095c639b9893e885d8937e29ed7d47a7c28bc6b6..7e0cf9b9f94b16741358bdb45122f8b2 #include "sandbox/mac/sandbox_crash_message.h" #if defined(ARCH_CPU_X86_64) -@@ -33,9 +34,11 @@ +@@ -38,9 +39,11 @@ } #endif @@ -1074,7 +1074,7 @@ index 095c639b9893e885d8937e29ed7d47a7c28bc6b6..7e0cf9b9f94b16741358bdb45122f8b2 namespace sandbox::logging { -@@ -76,9 +79,11 @@ void SendOsLog(Level level, const char* message) { +@@ -81,9 +84,11 @@ void SendOsLog(Level level, const char* message) { sandbox::crash_message::SetCrashMessage(message); } @@ -1431,7 +1431,7 @@ index eb81a70e4d5d5cd3e6ae9b45f8cd1c795ea76c51..9921ccb10d3455600eddd85f77f10228 } // namespace sandbox diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn -index cffd2db61ae3e2e7fdd2e0faeb18b742249197f5..c4ceda3eb634ac5e35219196105e59f4a35ca3fd 100644 +index 3ff395b3ff3b646a64c43503d2c172776bf68c84..e40030307e8d9df7e00a402e241e00664b236c7d 100644 --- a/third_party/blink/renderer/core/BUILD.gn +++ b/third_party/blink/renderer/core/BUILD.gn @@ -414,6 +414,7 @@ component("core") { @@ -1463,10 +1463,10 @@ index d38e951cdbf1ab9b367273ad0fb8f962155a7f3b..e39ee0414cb54004096dbfffc4f768e8 blink_core_sources_editing += [ "kill_ring_none.cc" ] } diff --git a/ui/accelerated_widget_mac/BUILD.gn b/ui/accelerated_widget_mac/BUILD.gn -index 47e88da13d22b60e27b458f2fd33f10829b31fc0..b32ad8cd5af69c835ecfdbf50329347fb700f9b3 100644 +index ce3dc22f38b9d3f2f12e469b23f5dfe06ecbb98a..a69af46e05365028379dbf6abafaacd9b19e4569 100644 --- a/ui/accelerated_widget_mac/BUILD.gn +++ b/ui/accelerated_widget_mac/BUILD.gn -@@ -68,6 +68,7 @@ component("accelerated_widget_mac") { +@@ -77,6 +77,7 @@ component("accelerated_widget_mac") { "//ui/gfx", "//ui/gfx/geometry", "//ui/gl", @@ -1579,10 +1579,10 @@ index dcf493d62990018040a3f84b6f875af737bd2214..3d1c4dcc9ee0bbfdac15f40d9c74e9f3 void DisplayCALayerTree::GotIOSurfaceFrame( diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn -index 2f7f2a8f81ec0e033cb83f82daac4835a2863cbc..21388d7de99fbb97c79eecbab658934d5cac5650 100644 +index 6207f4941839fdc5a44dc5fe61f50281503623d4..d2d635c1bd2e0497b7fbab331b48e753fae1013e 100644 --- a/ui/accessibility/platform/BUILD.gn +++ b/ui/accessibility/platform/BUILD.gn -@@ -283,6 +283,7 @@ component("platform") { +@@ -295,6 +295,7 @@ component("platform") { "AppKit.framework", "Foundation.framework", ] @@ -1591,7 +1591,7 @@ index 2f7f2a8f81ec0e033cb83f82daac4835a2863cbc..21388d7de99fbb97c79eecbab658934d if (is_ios) { diff --git a/ui/accessibility/platform/browser_accessibility_manager_mac.mm b/ui/accessibility/platform/browser_accessibility_manager_mac.mm -index 444f315852deabbac971165a7de9751a1bb367e3..7aba9f251f869c64d7722558084f209bd5d27075 100644 +index 71c78a4d3350b6a01cd1f8321f0c1faebc829b0f..79c54d9d5c1b335b3877199a64c6d3f940c5a85b 100644 --- a/ui/accessibility/platform/browser_accessibility_manager_mac.mm +++ b/ui/accessibility/platform/browser_accessibility_manager_mac.mm @@ -13,6 +13,7 @@ @@ -1673,10 +1673,10 @@ index c8171f0527fe5194f0ea73b57c4444d4c630fbc4..c2ac4da580e3e7f749a0a4de1e859af6 // Accessible object if (AXElementWrapper::IsValidElement(value)) { diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn -index 04702a2c6fec9f84a279e587a59c27c42ae20655..fdda175688baaeabf4991173b4a68ee8fce60d73 100644 +index a89628087cf9e38a95314972a4d2c38e39176005..2e05b5a472f36d3bff8769cde514820a3e622f4b 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn -@@ -363,6 +363,13 @@ component("base") { +@@ -365,6 +365,13 @@ component("base") { ] } @@ -1690,7 +1690,7 @@ index 04702a2c6fec9f84a279e587a59c27c42ae20655..fdda175688baaeabf4991173b4a68ee8 if (is_ios) { sources += [ "device_form_factor_ios.mm", -@@ -514,6 +521,12 @@ component("base") { +@@ -518,6 +525,12 @@ component("base") { "//url", ] @@ -1796,10 +1796,10 @@ index fc25ba79d2b0e1acdb7ba54b89e7d6e16f94771b..de771ef414b9a69e331261524f08e9a1 } // namespace diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn -index eea9eb92fadfcb72adfffffb0e43082a79bcf3a7..dd2110c1e9393f7ad33a0e64c09ef732b3292a54 100644 +index e598004da50257f4ee37a6431222c2b9e9f24b62..3f2ea7a469d6580d25fe51b657555d77a9ce9b80 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn -@@ -125,6 +125,12 @@ component("display") { +@@ -127,6 +127,12 @@ component("display") { "//ui/gfx/geometry", ] @@ -1813,7 +1813,7 @@ index eea9eb92fadfcb72adfffffb0e43082a79bcf3a7..dd2110c1e9393f7ad33a0e64c09ef732 deps += [ "//build:ios_buildflags" ] } diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm -index 29ae2da6a8a2c2a612dfb92f7f9c03ca5fa306b1..440c139a32a0c205e77b657d4aab64684661a01d 100644 +index 874ac9d572931fe175ccab8beb7738fe0a7b3c1b..b70e2a8a7be9e00a379f47c77589dde6b8b1f081 100644 --- a/ui/display/mac/screen_mac.mm +++ b/ui/display/mac/screen_mac.mm @@ -30,6 +30,7 @@ @@ -1824,7 +1824,7 @@ index 29ae2da6a8a2c2a612dfb92f7f9c03ca5fa306b1..440c139a32a0c205e77b657d4aab6468 #include "ui/display/display.h" #include "ui/display/display_change_notifier.h" #include "ui/display/util/display_util.h" -@@ -176,7 +177,17 @@ DisplayMac BuildDisplayForScreen(NSScreen* screen) { +@@ -177,7 +178,17 @@ DisplayMac BuildDisplayForScreen(NSScreen* screen) { display.set_color_depth(Display::kDefaultBitsPerPixel); display.set_depth_per_component(Display::kDefaultBitsPerComponent); } @@ -1843,10 +1843,10 @@ index 29ae2da6a8a2c2a612dfb92f7f9c03ca5fa306b1..440c139a32a0c205e77b657d4aab6468 // Query the display's refresh rate. if (@available(macos 12.0, *)) { diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn -index cb507971bef962e91cd8fca696692194696d1142..d2aee3962e3377980940e6d3538487a566a609b4 100644 +index 428ba0136f09ace2652e96b0928f84b59048e4ba..9ab318c4f56728eb675d8faa3a17a29f7079905c 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn -@@ -334,6 +334,12 @@ component("gfx") { +@@ -335,6 +335,12 @@ component("gfx") { "//ui/base:ui_data_pack", ] @@ -1898,10 +1898,10 @@ index fe3f85073e31de487a08e57d7f9b07aa4eccf8f3..cf5b07203c8bd559a404600cc98cc8ec // enough. return PlatformFontMac::SystemFontType::kGeneral; diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn -index 0f9f25edf65eaee5191ac1f6b70bd43a25091578..e6cce5c607e437c8a9d0fe43eb472a125fdae0eb 100644 +index f24cdf1484910c6fdc706aefc22c97669e245e57..efcd1779c0f105510c298ac9778792cfc8108847 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn -@@ -720,6 +720,8 @@ component("views") { +@@ -721,6 +721,8 @@ component("views") { "IOSurface.framework", "QuartzCore.framework", ] @@ -1910,7 +1910,7 @@ index 0f9f25edf65eaee5191ac1f6b70bd43a25091578..e6cce5c607e437c8a9d0fe43eb472a12 } if (is_win) { -@@ -1142,6 +1144,8 @@ source_set("test_support") { +@@ -1148,6 +1150,8 @@ source_set("test_support") { "//ui/base/mojom:ui_base_types", ] @@ -1920,7 +1920,7 @@ index 0f9f25edf65eaee5191ac1f6b70bd43a25091578..e6cce5c607e437c8a9d0fe43eb472a12 sources += [ "test/desktop_window_tree_host_win_test_api.cc", diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h -index 6a18c94e8c9d531ab3a59faf6027848caed8de57..63aa6b3ba824a3fa71c5a3db94b9ac5e8d0ea7c0 100644 +index cbd0b7708a7afd39852a7f75a21173d5b8a71a5e..1e8d5b3f66992a6bcbecd7149c45982319076d0c 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.h +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h @@ -18,6 +18,7 @@ @@ -1941,7 +1941,7 @@ index 6a18c94e8c9d531ab3a59faf6027848caed8de57..63aa6b3ba824a3fa71c5a3db94b9ac5e @class NSView; namespace remote_cocoa { -@@ -487,10 +490,12 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost +@@ -489,10 +492,12 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost mojo::AssociatedRemote remote_ns_window_remote_; @@ -1955,7 +1955,7 @@ index 6a18c94e8c9d531ab3a59faf6027848caed8de57..63aa6b3ba824a3fa71c5a3db94b9ac5e // Used to force the NSApplication's focused accessibility element to be the // views::Views accessibility tree when the NSView for this is focused. diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm -index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb490c46a081 100644 +index edf13478844a9686cfbfbd4d96214044c03134b4..7bda1853d47034c80a4e416b9839e8d18c5a8e2c 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm @@ -21,6 +21,7 @@ @@ -1966,7 +1966,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #include "ui/accessibility/accessibility_features.h" -@@ -352,7 +353,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -357,7 +358,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, if (in_process_ns_window_bridge_) { return in_process_ns_window_bridge_->ns_view(); } @@ -1978,7 +1978,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 } gfx::NativeViewAccessible -@@ -367,7 +372,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -372,7 +377,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, return [in_process_ns_window_bridge_->ns_view() window]; } @@ -1990,7 +1990,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 } remote_cocoa::mojom::NativeWidgetNSWindow* -@@ -1371,9 +1380,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -1384,9 +1393,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, // for PWAs. However this breaks accessibility on in-process windows, // so set it back to NO when a local window gains focus. See // https://crbug.com/41485830. @@ -2002,7 +2002,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 // Explicitly set the keyboard accessibility state on regaining key // window status. if (is_key && is_content_first_responder) { -@@ -1514,17 +1525,20 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -1527,17 +1538,20 @@ void HandleAccelerator(const ui::Accelerator& accelerator, void NativeWidgetMacNSWindowHost::SetRemoteAccessibilityTokens( const std::vector& window_token, const std::vector& view_token) { @@ -2023,7 +2023,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 *pid = getpid(); id element_id = GetNativeViewAccessible(); -@@ -1537,6 +1551,7 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -1550,6 +1564,7 @@ void HandleAccelerator(const ui::Accelerator& accelerator, } *token = ui::RemoteAccessibility::GetTokenForLocalElement(element_id); diff --git a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch index d0463e32239fd..0db6c1d159029 100644 --- a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch +++ b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch @@ -7,7 +7,7 @@ This adds a callback from the network service that's used to implement session.setCertificateVerifyCallback. diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907e19bc22f 100644 +index 8014bb16d8108115f901346fa4ed4d84220d86c5..ee56aab6831be256390b88e65a2a949c591bbd31 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -159,6 +159,11 @@ @@ -22,7 +22,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 #if BUILDFLAG(IS_CT_SUPPORTED) // gn check does not account for BUILDFLAG(). So, for iOS builds, it will // complain about a missing dependency on the target exposing this header. Add a -@@ -602,6 +607,99 @@ void RecordHSTSPreconnectUpgradeReason(HSTSRedirectUpgradeReason reason) { +@@ -598,6 +603,99 @@ void RecordHSTSPreconnectUpgradeReason(HSTSRedirectUpgradeReason reason) { } // namespace @@ -122,7 +122,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess; NetworkContext::NetworkContextHttpAuthPreferences:: -@@ -998,6 +1096,13 @@ void NetworkContext::SetClient( +@@ -994,6 +1092,13 @@ void NetworkContext::SetClient( client_.Bind(std::move(client)); } @@ -136,7 +136,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 void NetworkContext::CreateURLLoaderFactory( mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) { -@@ -2583,6 +2688,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( +@@ -2618,6 +2723,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( cert_verifier = std::make_unique( std::make_unique( std::move(cert_verifier))); @@ -148,7 +148,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( diff --git a/services/network/network_context.h b/services/network/network_context.h -index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7c85d02be 100644 +index f2dcec57a22d95892a08f1fa43696d6eea46a820..930e0bd987c48d111b2c8d71147c09e4418bda6c 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h @@ -113,6 +113,7 @@ class URLMatcher; @@ -159,7 +159,7 @@ index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7 class CookieManager; class HostResolver; class MdnsResponderManager; -@@ -245,6 +246,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -249,6 +250,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void CreateURLLoaderFactory( mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) override; @@ -168,7 +168,7 @@ index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7 void ResetURLLoaderFactories() override; void GetViaObliviousHttp( mojom::ObliviousHttpRequestPtr request, -@@ -932,6 +935,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -962,6 +965,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext std::vector dismount_closures_; #endif // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED) @@ -178,10 +178,10 @@ index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7 std::unique_ptr internal_host_resolver_; std::set, base::UniquePtrComparator> diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index d2cc00a218d5cf01562fc9b1dc55cc2d2ca275db..e928d012d77ccffe16263fd0ee7c2769adfe4bac 100644 +index feed273c8c77d6c1e21cd82547860f945b651ae9..399d0363749e9b79749185bcc1dcd746b026231e 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -307,6 +307,17 @@ struct SocketBrokerRemotes { +@@ -308,6 +308,17 @@ struct SocketBrokerRemotes { pending_remote server; }; @@ -199,7 +199,7 @@ index d2cc00a218d5cf01562fc9b1dc55cc2d2ca275db..e928d012d77ccffe16263fd0ee7c2769 // Parameters for constructing a network context. struct NetworkContextParams { // The user agent string. -@@ -935,6 +946,9 @@ interface NetworkContext { +@@ -952,6 +963,9 @@ interface NetworkContext { // Sets a client for this network context. SetClient(pending_remote client); @@ -210,7 +210,7 @@ index d2cc00a218d5cf01562fc9b1dc55cc2d2ca275db..e928d012d77ccffe16263fd0ee7c2769 CreateURLLoaderFactory( pending_receiver url_loader_factory, diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h -index fc22fd2d7fa864dc565c99b0d36b2d31a8e13eaf..97e8f9f145cecad9b0cd8b43a2de4af29cec7778 100644 +index e9579ae8911ebf4a89404564cad13de200cce9ea..da8551db16849cf82a6e3a7eaeb451cae0707f2d 100644 --- a/services/network/test/test_network_context.h +++ b/services/network/test/test_network_context.h @@ -63,6 +63,8 @@ class TestNetworkContext : public mojom::NetworkContext { diff --git a/patches/chromium/notification_provenance.patch b/patches/chromium/notification_provenance.patch index c15d5e3dbbd71..d85e492ebc66c 100644 --- a/patches/chromium/notification_provenance.patch +++ b/patches/chromium/notification_provenance.patch @@ -7,10 +7,10 @@ Pass RenderFrameHost through to PlatformNotificationService so Electron can identify which renderer a notification came from. diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc -index fcc5767027f6b9f2675d954f532779fafb94fd3a..dae6c3a8a8e5f61852896e661e1b169a87e05cdf 100644 +index eedafc323a9e4f736aa637d76034f4cb5239eee8..c562a59998901aaedc05be694a725150feeb4b0a 100644 --- a/chrome/browser/notifications/platform_notification_service_impl.cc +++ b/chrome/browser/notifications/platform_notification_service_impl.cc -@@ -243,6 +243,7 @@ bool PlatformNotificationServiceImpl::WasClosedProgrammatically( +@@ -244,6 +244,7 @@ bool PlatformNotificationServiceImpl::WasClosedProgrammatically( // TODO(awdf): Rename to DisplayNonPersistentNotification (Similar for Close) void PlatformNotificationServiceImpl::DisplayNotification( @@ -133,10 +133,10 @@ index 05d3a12dd84c7005d46cc73b312f97ef418d96f5..4765de982802541b3efc7211d106acc7 const GURL& document_url, const WeakDocumentPtr& weak_document_ptr, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 2bc557c1d9673dc30230a69e75e9c3baf69ae36f..1b82a6df5ada9c9c8536225b8bc7a9dd4aa7e8dd 100644 +index 58a495e02e9298771fdb0542a40dbf99184a70d6..797d90b03331fc0de38b597ec1c29b5e8e58a05f 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -2146,7 +2146,7 @@ void RenderProcessHostImpl::CreateNotificationService( +@@ -2207,7 +2207,7 @@ void RenderProcessHostImpl::CreateNotificationService( case RenderProcessHost::NotificationServiceCreatorType::kSharedWorker: case RenderProcessHost::NotificationServiceCreatorType::kDedicatedWorker: { storage_partition_impl_->GetPlatformNotificationContext()->CreateService( @@ -145,7 +145,7 @@ index 2bc557c1d9673dc30230a69e75e9c3baf69ae36f..1b82a6df5ada9c9c8536225b8bc7a9dd creator_type, std::move(receiver)); break; } -@@ -2154,7 +2154,7 @@ void RenderProcessHostImpl::CreateNotificationService( +@@ -2215,7 +2215,7 @@ void RenderProcessHostImpl::CreateNotificationService( CHECK(rfh); storage_partition_impl_->GetPlatformNotificationContext()->CreateService( diff --git a/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch b/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch index 93df0398cfba7..f3aafed22d11f 100644 --- a/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch +++ b/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch @@ -36,10 +36,10 @@ index f51591d21a0ce44028a3711cba72ceebb55b3567..31e01429e26f2856587b98a0213f1a5f Microsoft::WRL::ComPtr d3d11_texture; diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc -index 4f216eb88e51c929468abf52049524177c3f0bb3..e216283028864b48516f690209b0e6b03f02d11c 100644 +index 7c90eca71d39521ec1968a946b6c68c9a564d225..9c40397be51f1ce30cbe73d8c6fc0bb279c95d7a 100644 --- a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc +++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc -@@ -194,7 +194,7 @@ gfx::Size GetBufferSizeInPixelsForVideoPixelFormat( +@@ -196,7 +196,7 @@ gfx::Size GetBufferSizeInPixelsForVideoPixelFormat( bool FrameResources::Initialize() { auto* context = pool_->GetContext(); @@ -48,7 +48,7 @@ index 4f216eb88e51c929468abf52049524177c3f0bb3..e216283028864b48516f690209b0e6b0 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) gfx::BufferUsage::SCANOUT_VEA_CPU_READ #else -@@ -208,6 +208,23 @@ bool FrameResources::Initialize() { +@@ -210,6 +210,23 @@ bool FrameResources::Initialize() { const gfx::Size buffer_size_in_pixels = GetBufferSizeInPixelsForVideoPixelFormat(format_, coded_size_); @@ -72,7 +72,7 @@ index 4f216eb88e51c929468abf52049524177c3f0bb3..e216283028864b48516f690209b0e6b0 gpu::SharedImageUsageSet usage = #if BUILDFLAG(IS_MAC) gpu::SHARED_IMAGE_USAGE_MACOS_VIDEO_TOOLBOX | -@@ -248,7 +265,7 @@ bool FrameResources::Initialize() { +@@ -250,7 +267,7 @@ bool FrameResources::Initialize() { viz::GetSharedImageFormat(buffer_format); shared_image_ = diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 671ea90564c5d..415cf0d435d46 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,10 +10,10 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index b7a1be346c77fcef6c288789bc11f9849d6a126d..2b6d2a4c068757fa6dd16c7a0ffb7d478993d7d8 100644 +index 9d7b8758cb8f115e5c83f3913c7172f28a4d1fa2..82be480aeec14edb4bdeb761c42652021da741c6 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -802,8 +802,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( +@@ -809,8 +809,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( // TODO(crbug.com/40092527): Consider adding a separate boolean that // tracks this instead of piggybacking `origin_calculation_debug_info`. if (renderer_side_origin.opaque() && diff --git a/patches/chromium/picture-in-picture.patch b/patches/chromium/picture-in-picture.patch index c39dc3bb41374..2d9c53d3e2f24 100644 --- a/patches/chromium/picture-in-picture.patch +++ b/patches/chromium/picture-in-picture.patch @@ -38,7 +38,7 @@ index 8168b4cfbafd42fa93a5aa9a3691c2552fabfb86..ba49212bd76d209f99c1cee649fc1466 ui::ImageModel::FromVectorIcon(*icon, kColorPipWindowForeground, kCloseButtonIconSize)); diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc -index 6671f304fc15221d18fa1be2c4f20368db518e59..f9e29227fe9396747bed8b1ec970cfdfcf72c03c 100644 +index 3b53115eb59e81b690cdef96a2e303c0c8407a8d..531b183bcc017daeefcf158fd58570a889c4b7df 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc @@ -18,9 +18,11 @@ diff --git a/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch b/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch index 3c8b864170a3e..d25f6fc465f9d 100644 --- a/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch +++ b/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch @@ -8,10 +8,10 @@ needed in chromium but our autofill implementation uses them. This patch can be our autofill implementation to work like Chromium's. diff --git a/ui/color/color_id.h b/ui/color/color_id.h -index 4521cef41d441fcf6bd7889243598cab40d3d642..3dd279e6c35387e1f40c389d7dfccfa5497155e8 100644 +index 8b7cc48c3fad34d91f8732c760228b546ce24a89..cf61dcb09d859729c9f1239b8278babbc057a74e 100644 --- a/ui/color/color_id.h +++ b/ui/color/color_id.h -@@ -405,6 +405,10 @@ +@@ -407,6 +407,10 @@ E_CPONLY(kColorRadioButtonForegroundUnchecked) \ E_CPONLY(kColorRadioButtonForegroundDisabled) \ E_CPONLY(kColorRadioButtonForegroundChecked) \ @@ -22,7 +22,7 @@ index 4521cef41d441fcf6bd7889243598cab40d3d642..3dd279e6c35387e1f40c389d7dfccfa5 E_CPONLY(kColorSegmentedButtonBorder) \ E_CPONLY(kColorSegmentedButtonFocus) \ E_CPONLY(kColorSegmentedButtonForegroundChecked) \ -@@ -513,6 +517,7 @@ +@@ -515,6 +519,7 @@ E_CPONLY(kColorTreeNodeForeground) \ E_CPONLY(kColorTreeNodeForegroundSelectedFocused) \ E_CPONLY(kColorTreeNodeForegroundSelectedUnfocused) \ diff --git a/patches/chromium/preconnect_manager.patch b/patches/chromium/preconnect_manager.patch index 323ba2ba432e1..fcc53324fc503 100644 --- a/patches/chromium/preconnect_manager.patch +++ b/patches/chromium/preconnect_manager.patch @@ -10,11 +10,11 @@ in favor of defining PreconnectRequest in this file since we don't build the header. diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc -index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e0890bd75 100644 +index 4c90ccfdc5f1357989f7cd86f572915ea05c80da..cba3346fd84a2f75c40e453aeab1602b7c146dd4 100644 --- a/chrome/browser/predictors/preconnect_manager.cc +++ b/chrome/browser/predictors/preconnect_manager.cc -@@ -14,9 +14,11 @@ - #include "base/trace_event/trace_event.h" +@@ -15,9 +15,11 @@ + #include "base/types/optional_util.h" #include "chrome/browser/predictors/predictors_features.h" #include "chrome/browser/predictors/predictors_traffic_annotations.h" +#if 0 @@ -25,7 +25,7 @@ index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -@@ -27,6 +29,20 @@ namespace predictors { +@@ -28,6 +30,20 @@ namespace predictors { const bool kAllowCredentialsOnPreconnectByDefault = true; @@ -46,7 +46,7 @@ index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e PreconnectedRequestStats::PreconnectedRequestStats(const url::Origin& origin, bool was_preconnected) : origin(origin), was_preconnected(was_preconnected) {} -@@ -90,12 +106,15 @@ PreconnectManager::PreconnectManager(base::WeakPtr delegate, +@@ -94,12 +110,15 @@ PreconnectManager::PreconnectManager(base::WeakPtr delegate, PreconnectManager::~PreconnectManager() = default; bool PreconnectManager::IsEnabled() { @@ -63,7 +63,7 @@ index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e void PreconnectManager::Start(const GURL& url, diff --git a/chrome/browser/predictors/preconnect_manager.h b/chrome/browser/predictors/preconnect_manager.h -index 05963f9d2bff9dd438845c09143039fabe818328..0ab1a08e84b0cd360fd4690f8dd683b7e1697e52 100644 +index 107c0f79ccf10eb5d16181213e12c93f1abc62d6..9babefff03899de91519474956ba1297fbd1c95d 100644 --- a/chrome/browser/predictors/preconnect_manager.h +++ b/chrome/browser/predictors/preconnect_manager.h @@ -17,7 +17,9 @@ @@ -73,10 +73,10 @@ index 05963f9d2bff9dd438845c09143039fabe818328..0ab1a08e84b0cd360fd4690f8dd683b7 +#if 0 #include "chrome/browser/predictors/resource_prefetch_predictor.h" +#endif + #include "content/public/browser/storage_partition_config.h" #include "net/base/network_anonymization_key.h" #include "url/gurl.h" - -@@ -33,7 +35,28 @@ class NetworkContext; +@@ -34,7 +36,28 @@ class NetworkContext; namespace predictors { diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index 3dd47d2f18ed4..0f7bfd5c1b2dc 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -577,10 +577,10 @@ index 1917f8b94962d7a4c83f139623a5f5d352011627..47ef610c43c4dcfee0cf528eb2e6075b // Indication that the job is getting canceled. bool canceling_job_ = false; diff --git a/chrome/browser/printing/printer_query.cc b/chrome/browser/printing/printer_query.cc -index 402be34ab888cdf834d0fb65de0832e9a8021ced..82ddc92a35d824926c30279e660cc4e86e6f0b09 100644 +index 4f2e70c243550b471d0a6b2be743a29004c75599..9a3580d7c2a01290e7aaff1200e7f17c9add2c70 100644 --- a/chrome/browser/printing/printer_query.cc +++ b/chrome/browser/printing/printer_query.cc -@@ -355,17 +355,19 @@ void PrinterQuery::UpdatePrintSettings(base::Value::Dict new_settings, +@@ -356,17 +356,19 @@ void PrinterQuery::UpdatePrintSettings(base::Value::Dict new_settings, #endif // BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS) } @@ -666,7 +666,7 @@ index 6809c4576c71bc1e1a6ad4e0a37707272a9a10f4..3aad10424a6a31dab2ca393d00149ec6 PrintingFailed(int32 cookie, PrintFailureReason reason); diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc -index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3b3ffe41c 100644 +index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..bac316baaf4340848666085b3de69c99ee522abe 100644 --- a/components/printing/renderer/print_render_frame_helper.cc +++ b/components/printing/renderer/print_render_frame_helper.cc @@ -52,6 +52,7 @@ @@ -744,7 +744,7 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 print_preview_context_.OnPrintPreview(); #if BUILDFLAG(IS_CHROMEOS) -@@ -2062,17 +2068,19 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { +@@ -2062,17 +2068,25 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, const blink::WebNode& node, @@ -759,6 +759,12 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 FrameReference frame_ref(frame); - if (!InitPrintSettings(frame, node)) { ++ // If we're silently printing a PDF, we bypass settings logic ++ // that sets modifiability to false so ensure it's set here. ++ if (silent && IsPrintingPdfFrame(frame, node)) { ++ settings.Set(kSettingPreviewModifiable, false); ++ } ++ + if (!InitPrintSettings(frame, node, std::move(settings))) { // Browser triggered this code path. It already knows about the failure. notify_browser_of_print_failure_ = false; @@ -767,7 +773,7 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 DidFinishPrinting(PrintingResult::kFailPrintInit); return; } -@@ -2093,8 +2101,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, +@@ -2093,8 +2107,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, print_pages_params_->params->print_scaling_option; auto self = weak_ptr_factory_.GetWeakPtr(); @@ -784,7 +790,7 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 // Check if `this` is still valid. if (!self) return; -@@ -2362,29 +2377,43 @@ void PrintRenderFrameHelper::IPCProcessed() { +@@ -2362,29 +2383,43 @@ void PrintRenderFrameHelper::IPCProcessed() { } bool PrintRenderFrameHelper::InitPrintSettings(blink::WebLocalFrame* frame, @@ -881,10 +887,10 @@ index 97cb6458bc9eec767db89b56abfc5f4b4136ff7b..d9a0b343158b8464b5c9aa8e0e655c0b ScriptingThrottler scripting_throttler_; diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 885d487afbaae02c0401b322dd59273116c91ad0..d8bb1152a1f713e58da0f29324a1eb877a693bfd 100644 +index 56118dba5d55a937290a1234bd1c70d4d301de25..2086718aba634ffed079c108b04cdd7993133961 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -3109,8 +3109,9 @@ source_set("browser") { +@@ -3139,8 +3139,9 @@ source_set("browser") { "//ppapi/shared_impl", ] @@ -897,10 +903,10 @@ index 885d487afbaae02c0401b322dd59273116c91ad0..d8bb1152a1f713e58da0f29324a1eb87 if (is_chromeos) { sources += [ diff --git a/printing/printing_context.cc b/printing/printing_context.cc -index 0bb34c4715224a0cef10465778db946f299ac513..c70803a5737676eec498dd18074a47ffabac62c0 100644 +index b959c1428784388f514d33bf54550908f60f3ff1..c37bc40570b1e2b29af6e677684705556b15560e 100644 --- a/printing/printing_context.cc +++ b/printing/printing_context.cc -@@ -154,7 +154,6 @@ void PrintingContext::UsePdfSettings() { +@@ -156,7 +156,6 @@ void PrintingContext::UsePdfSettings() { mojom::ResultCode PrintingContext::UpdatePrintSettings( base::Value::Dict job_settings) { @@ -932,3 +938,19 @@ index 63f170c95050416c595e62f4c460c4cd6b7dbd1c..157e3d046889f9c63fdf0fd5d503890f // Does bookkeeping when an error occurs. virtual mojom::ResultCode OnError(); +diff --git a/printing/printing_context_linux.cc b/printing/printing_context_linux.cc +index fa55d2a1c5f4207cb8eda9305e8bf2af29352c91..13ca65c2c7c3c24882061d8e224bf93899d9a7ab 100644 +--- a/printing/printing_context_linux.cc ++++ b/printing/printing_context_linux.cc +@@ -52,9 +52,8 @@ void PrintingContextLinux::AskUserForSettings(int max_pages, + bool is_scripted, + PrintSettingsCallback callback) { + if (!print_dialog_) { +- // Can only get here if the renderer is sending bad messages. +- // http://crbug.com/341777 +- NOTREACHED(); ++ print_dialog_ = ui::LinuxUi::instance()->CreatePrintDialog(this); ++ print_dialog_->UseDefaultSettings(); + } + + print_dialog_->ShowDialog(delegate_->GetParentView(), has_selection, diff --git a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch index 95718e32852ad..fe633c675f5b1 100644 --- a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch +++ b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch @@ -8,7 +8,7 @@ Chrome moved the SetCursor IPC message to mojo, which we use to tell OSR about ` Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2172779 diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h -index 8ad5a5042355ce918ab13784fbc0d633b6f0efa9..7f7b86abf3e18501025a854000f0d9adc9ec9abc 100644 +index a54a0dedf8ef1cfffa4e80a4707debed0e83d277..e66e71fdbabb40a5307b12cd8965e773e76c04fd 100644 --- a/content/browser/renderer_host/render_widget_host_delegate.h +++ b/content/browser/renderer_host/render_widget_host_delegate.h @@ -28,6 +28,7 @@ @@ -30,10 +30,10 @@ index 8ad5a5042355ce918ab13784fbc0d633b6f0efa9..7f7b86abf3e18501025a854000f0d9ad // RenderWidgetHost on the primary main frame, and false otherwise. virtual bool IsWidgetForPrimaryMainFrame(RenderWidgetHostImpl*); diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 8aa812aa9bb3de4c7bc58192baf45c90402d6ff4..574e31bcfeb16cdb9a6c0121ffbc1abe175bb4ab 100644 +index 20fcda4eb20459b69247003c51c2a3ed37c7b1e8..9bcab4e1e8a0fa429488555f4f7bd1c54888d10e 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc -@@ -2048,6 +2048,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) { +@@ -2056,6 +2056,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) { if (view_) { view_->UpdateCursor(cursor); } @@ -44,10 +44,10 @@ index 8aa812aa9bb3de4c7bc58192baf45c90402d6ff4..574e31bcfeb16cdb9a6c0121ffbc1abe void RenderWidgetHostImpl::ShowContextMenuAtPoint( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 79bc1f7a5c44a0dd255b5ca676d95c9fa9c31496..9f18308873306e841bbc15d9ba36c93ba5603a15 100644 +index 83c5918c1bc0417ed3c744828b9f93e7c00d22c3..d13c38de7a2e69565020ab92c874bf1289d63833 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5781,6 +5781,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { +@@ -5853,6 +5853,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { return text_input_manager_.get(); } @@ -60,10 +60,10 @@ index 79bc1f7a5c44a0dd255b5ca676d95c9fa9c31496..9f18308873306e841bbc15d9ba36c93b RenderWidgetHostImpl* render_widget_host) { return render_widget_host == GetPrimaryMainFrame()->GetRenderWidgetHost(); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h -index ea5598aafb86078ed34b18a05e6709c3df0c0f24..adfe5bb3bac589c2ad67cbbad01ce39c2b222936 100644 +index 8fa21e99ab8624817fead79c1b032af9b499c573..17d7fc9c29b8a9f57145fcf920c8bbd913da124b 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h -@@ -1173,6 +1173,7 @@ class CONTENT_EXPORT WebContentsImpl +@@ -1186,6 +1186,7 @@ class CONTENT_EXPORT WebContentsImpl void SendScreenRects() override; void SendActiveState(bool active) override; TextInputManager* GetTextInputManager() override; @@ -72,7 +72,7 @@ index ea5598aafb86078ed34b18a05e6709c3df0c0f24..adfe5bb3bac589c2ad67cbbad01ce39c RenderWidgetHostImpl* render_widget_host) override; bool IsShowingContextMenuOnPage() const override; diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h -index c6f536682a1faa17c2b5b57890d576405ccf5c9f..a6f1ee80ef29aedb919dda7bc84cc5520e43c84f 100644 +index e06a02965a79f4bd0047d33262e2e9511e62fcce..9cf1f30f59d4ab49441ca633d6fa1b784268ca32 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h @@ -37,6 +37,7 @@ @@ -83,7 +83,7 @@ index c6f536682a1faa17c2b5b57890d576405ccf5c9f..a6f1ee80ef29aedb919dda7bc84cc552 #include "ui/base/page_transition_types.h" #include "ui/base/window_open_disposition.h" -@@ -636,6 +637,9 @@ class CONTENT_EXPORT WebContentsObserver : public base::CheckedObserver { +@@ -646,6 +647,9 @@ class CONTENT_EXPORT WebContentsObserver : public base::CheckedObserver { // Invoked when the primary main frame changes size. virtual void PrimaryMainFrameWasResized(bool width_changed) {} diff --git a/patches/chromium/refactor_expose_file_system_access_blocklist.patch b/patches/chromium/refactor_expose_file_system_access_blocklist.patch index e56a6c907c914..6a6e599c4dba1 100644 --- a/patches/chromium/refactor_expose_file_system_access_blocklist.patch +++ b/patches/chromium/refactor_expose_file_system_access_blocklist.patch @@ -8,7 +8,7 @@ it in Electron and prevent drift from Chrome's blocklist. We should look for a w to upstream this change to Chrome. diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc -index 7d5813ad07169f2f779846c4541101eac376aa4c..21e17ef542a89a9829ca4583da760fb6b1c59836 100644 +index 6720c3f12d4ff86bd9c5262d3b0b5e42065ce506..c5081166876717cac04159855b690a24e2424ea8 100644 --- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc +++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc @@ -45,7 +45,6 @@ @@ -153,7 +153,7 @@ index 7d5813ad07169f2f779846c4541101eac376aa4c..21e17ef542a89a9829ca4583da760fb6 // Describes a rule for blocking a directory, which can be constructed // dynamically (based on state) or statically (from kBlockedPaths). diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h -index 68a712cb272224d7a75ff3f512e45ed82eab5a86..d46119d9a7a32c9dc0c4844da73c63b42af9f7a8 100644 +index ce16f32c9ec1280c1d68942dcaf392c21f9e7963..1e0f372a25b49babb33acc05ee86f475684b0441 100644 --- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h +++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h @@ -21,7 +21,7 @@ diff --git a/patches/chromium/refactor_unfilter_unresponsive_events.patch b/patches/chromium/refactor_unfilter_unresponsive_events.patch index d92a90e211ccf..60d9345a1b810 100644 --- a/patches/chromium/refactor_unfilter_unresponsive_events.patch +++ b/patches/chromium/refactor_unfilter_unresponsive_events.patch @@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be accessed from our JS event. The filtering is moved into Electron's code. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 1396f371c8ae82a1ce52096c176a135cf02b2d65..b371464c15a5ab821d8dbea96b33475c9ed92803 100644 +index 54ff9675fef009c5bb79becb59146436508ad835..aebd3b4fd597db7b02214dd61708711dc4ad75f3 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9876,25 +9876,13 @@ void WebContentsImpl::RendererUnresponsive( +@@ -9993,25 +9993,13 @@ void WebContentsImpl::RendererUnresponsive( base::RepeatingClosure hang_monitor_restarter) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", "render_widget_host", render_widget_host); diff --git a/patches/chromium/render_widget_host_view_base.patch b/patches/chromium/render_widget_host_view_base.patch index e909ee12fd3ec..d0485ef878047 100644 --- a/patches/chromium/render_widget_host_view_base.patch +++ b/patches/chromium/render_widget_host_view_base.patch @@ -6,7 +6,7 @@ Subject: render_widget_host_view_base.patch ... something to do with OSR? and maybe as well? terrifying. diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc -index 5a91c313ed757c04810f03bdc2be0e455913921e..0cf7de41a9958fe9c7b9b60e3edc7402f3a74c67 100644 +index ce00b0540a7ac7f7c7b4c65f1a1343f72ae21c42..cc3b694431f14b166a305a446a48c25d5099b152 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc @@ -654,6 +654,13 @@ void RenderWidgetHostViewBase::OnFrameTokenChangedForView( @@ -24,7 +24,7 @@ index 5a91c313ed757c04810f03bdc2be0e455913921e..0cf7de41a9958fe9c7b9b60e3edc7402 const blink::WebMouseEvent& event, const ui::LatencyInfo& latency) { diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h -index e1fb0dc61160a402b7748aac7d048e0ffc9c1b32..c2222c345ca01af92b4f42142a6e0c9851129e85 100644 +index d451701b81bf2c89474c7d1cd22e781d870128a4..959fd197e65bac94b3f3c55059dccb147001943e 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h @@ -30,6 +30,8 @@ @@ -61,8 +61,8 @@ index e1fb0dc61160a402b7748aac7d048e0ffc9c1b32..c2222c345ca01af92b4f42142a6e0c98 void OnAutoscrollStart() override; const viz::DisplayHitTestQueryMap& GetDisplayHitTestQuery() const override; -@@ -195,6 +203,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase - const gfx::Rect& keyboard_rect) override {} +@@ -196,6 +204,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase + void NotifyContextMenuInsetsObservers(const gfx::Rect&) override {} bool IsHTMLFormPopup() const override; + virtual void InitAsGuest(RenderWidgetHostView* parent_host_view, diff --git a/patches/chromium/render_widget_host_view_mac.patch b/patches/chromium/render_widget_host_view_mac.patch index d93baba808224..ed4b423297ced 100644 --- a/patches/chromium/render_widget_host_view_mac.patch +++ b/patches/chromium/render_widget_host_view_mac.patch @@ -8,10 +8,10 @@ respond to the first mouse click in their window, which is desirable for some kinds of utility windows. Similarly for `disableAutoHideCursor`. diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e7733d9df 100644 +index b0a49de7b4ed6402d0a849c9c971712f8bd2a126..c125b021061b74b8a7e01b757d08f0b1fa89f14c 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -@@ -169,6 +169,15 @@ void ExtractUnderlines(NSAttributedString* string, +@@ -170,6 +170,15 @@ void ExtractUnderlines(NSAttributedString* string, } // namespace @@ -27,7 +27,7 @@ index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e // RenderWidgetHostViewCocoa --------------------------------------------------- // Private methods: -@@ -789,6 +798,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { +@@ -783,6 +792,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { } - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { @@ -37,7 +37,7 @@ index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e // Enable "click-through" if mouse clicks are accepted in inactive windows return [self acceptsMouseEventsOption] > kAcceptMouseEventsInActiveWindow; } -@@ -934,6 +946,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { +@@ -928,6 +940,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { // its parent view. BOOL hitSelf = NO; while (view) { @@ -48,7 +48,7 @@ index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e if (view == self) hitSelf = YES; if ([view isKindOfClass:[self class]] && ![view isEqual:self] && -@@ -1268,6 +1284,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { +@@ -1262,6 +1278,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { eventType == NSEventTypeKeyDown && !(modifierFlags & NSEventModifierFlagCommand); diff --git a/patches/chromium/resource_file_conflict.patch b/patches/chromium/resource_file_conflict.patch index 10866376bba26..e5d394a5c6e66 100644 --- a/patches/chromium/resource_file_conflict.patch +++ b/patches/chromium/resource_file_conflict.patch @@ -52,10 +52,10 @@ Some alternatives to this patch: None of these options seems like a substantial maintainability win over this patch to me (@nornagon). diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index 7bad6d2fe242bc71014d3fbd6be4481280297366..bdf6d5865fb0069f4df368613167069d2fb50c86 100644 +index 26ec2b8abc6783a48518c1b15a12eb2212b0db7a..95c73dcb082999d0a85d82f1fe330d27c88055ca 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -1546,7 +1546,7 @@ if (is_chrome_branded && !is_android) { +@@ -1550,7 +1550,7 @@ if (is_chrome_branded && !is_android) { } } @@ -64,7 +64,7 @@ index 7bad6d2fe242bc71014d3fbd6be4481280297366..bdf6d5865fb0069f4df368613167069d chrome_paks("packed_resources") { if (is_mac) { output_dir = "$root_gen_dir/repack" -@@ -1592,6 +1592,12 @@ repack("browser_tests_pak") { +@@ -1596,6 +1596,12 @@ repack("browser_tests_pak") { deps = [ "//chrome/test/data/webui:resources" ] } diff --git a/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch b/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch index c4ce0436f5d13..a68eb28410936 100644 --- a/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch +++ b/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch @@ -233,10 +233,10 @@ index 2991489fae8a4eecad97b1ecb2271f096d9a9229..93b7aa620ad1da250ac06e3383ca6897 } diff --git a/content/common/features.cc b/content/common/features.cc -index b55390ed12152c1d5ab01415ac2b64422b59ad8b..51d76c126025f422def2a46ebb5d6c427f5ae3cb 100644 +index 136290cb51f7b44cc6f8754c49203a7bad1634e8..d11fd0742b10e6318a0d3740759f474b41675ce6 100644 --- a/content/common/features.cc +++ b/content/common/features.cc -@@ -262,6 +262,14 @@ BASE_FEATURE(kIOSurfaceCapturer, +@@ -261,6 +261,14 @@ BASE_FEATURE(kIOSurfaceCapturer, base::FEATURE_ENABLED_BY_DEFAULT); #endif @@ -252,7 +252,7 @@ index b55390ed12152c1d5ab01415ac2b64422b59ad8b..51d76c126025f422def2a46ebb5d6c42 // invalidated upon notifications sent by base::SystemMonitor. If disabled, the // cache is considered invalid on every enumeration request. diff --git a/content/common/features.h b/content/common/features.h -index 7a34bc2fafd421a3c63cb11706e1dac84ef02454..09f4a60267ea2ecb426edf3314274d6806a25bec 100644 +index dc481abb8ef01c8e5a23d0f683a59c2ee6420826..7f61079290ba740cdad7b74feeb94143bdcc6bd4 100644 --- a/content/common/features.h +++ b/content/common/features.h @@ -68,6 +68,9 @@ CONTENT_EXPORT BASE_DECLARE_FEATURE(kInterestGroupUpdateIfOlderThan); diff --git a/patches/chromium/revert_enable_crel_for_arm32_targets.patch b/patches/chromium/revert_enable_crel_for_arm32_targets.patch new file mode 100644 index 0000000000000..79b7b6b8545f7 --- /dev/null +++ b/patches/chromium/revert_enable_crel_for_arm32_targets.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Maddock +Date: Fri, 28 Mar 2025 20:22:26 -0400 +Subject: revert: Enable CREL for arm32 targets + +Enabling CREL on Linux ARM64 seems to cause it to segfault. Disable for Electron +as its one of our supported platforms. +https://chromium-review.googlesource.com/q/I3a62f02f564f07be63173b0773b4ecaffbe939b9 + +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index e7a60d56b5425eae1e1a121189dc49390327cf8a..d63b933b095524e0e4b75b890eda95824c580c0d 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -617,7 +617,8 @@ config("compiler") { + + # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF + # (excluding toolchains that use an older version of LLVM). +- if (is_linux && !llvm_android_mainline && ++ # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds. ++ if (is_linux && !llvm_android_mainline && current_cpu != "arm" && + default_toolchain != "//build/toolchain/cros:target") { + cflags += [ "-Wa,--crel,--allow-experimental-crel" ] + } diff --git a/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch b/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch index bff6322d11a2d..5c78fa8c6b9f1 100644 --- a/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch +++ b/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch @@ -6,10 +6,10 @@ Subject: Revert "Remove the AllowAggressiveThrottlingWithWebSocket feature." This reverts commit 615c1810a187840ffeb04096087efff86edb37de. diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc -index 7a885de263f1c84fc676c62caa6c787983e47994..854d57812e99bb4668aa3bbf3f63bdb0219e658d 100644 +index d7f5c10b6f82d2dcebc7a1ee11ef05757c3fa7ad..b37b02aaddf524b427086aa20c8140a22097a738 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc -@@ -104,6 +104,17 @@ enum WebSocketOpCode { +@@ -99,6 +99,17 @@ enum WebSocketOpCode { kOpCodeBinary = 0x2, }; @@ -27,7 +27,7 @@ index 7a885de263f1c84fc676c62caa6c787983e47994..854d57812e99bb4668aa3bbf3f63bdb0 } // namespace WebSocketChannelImpl::MessageDataDeleter::MessageDataDeleter( -@@ -297,7 +308,10 @@ bool WebSocketChannelImpl::Connect(const KURL& url, const String& protocol) { +@@ -293,7 +304,10 @@ bool WebSocketChannelImpl::Connect(const KURL& url, const String& protocol) { // even if the `WebSocketChannel` is closed. feature_handle_for_scheduler_ = scheduler->RegisterFeature( SchedulingPolicy::Feature::kWebSocket, diff --git a/patches/chromium/scroll_bounce_flag.patch b/patches/chromium/scroll_bounce_flag.patch index e4b923927d98a..bd18c208f5d1f 100644 --- a/patches/chromium/scroll_bounce_flag.patch +++ b/patches/chromium/scroll_bounce_flag.patch @@ -6,10 +6,10 @@ Subject: scroll_bounce_flag.patch Patch to make scrollBounce option work. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index d7eb65e86ca60744a6d919b2899208978a761e22..e80404d2d0aed8096731895580cc879b8827b3be 100644 +index 0a5556cb6eac8be8af3a1691687205e683157794..a88f184db56cc3aab5d67cd1a0f73dff0f002a34 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc -@@ -1306,7 +1306,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { +@@ -1324,7 +1324,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { } bool RenderThreadImpl::IsElasticOverscrollEnabled() { @@ -17,4 +17,4 @@ index d7eb65e86ca60744a6d919b2899208978a761e22..e80404d2d0aed8096731895580cc879b + return base::CommandLine::ForCurrentProcess()->HasSwitch("scroll-bounce"); } - gpu::GpuMemoryBufferManager* RenderThreadImpl::GetGpuMemoryBufferManager() { + blink::scheduler::WebThreadScheduler* diff --git a/patches/chromium/support_mixed_sandbox_with_zygote.patch b/patches/chromium/support_mixed_sandbox_with_zygote.patch index 9326140c56ca3..bcb11e528c7ed 100644 --- a/patches/chromium/support_mixed_sandbox_with_zygote.patch +++ b/patches/chromium/support_mixed_sandbox_with_zygote.patch @@ -22,10 +22,10 @@ However, the patch would need to be reviewed by the security team, as it does touch a security-sensitive class. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 1b82a6df5ada9c9c8536225b8bc7a9dd4aa7e8dd..e45de1723b133d529e8eb0c5b019c1b98e994173 100644 +index 797d90b03331fc0de38b597ec1c29b5e8e58a05f..d771acfb686270b232e2705e9ca4a7ea03d3bcc2 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -1754,6 +1754,10 @@ bool RenderProcessHostImpl::Init() { +@@ -1815,6 +1815,10 @@ bool RenderProcessHostImpl::Init() { std::unique_ptr sandbox_delegate = std::make_unique( *cmd_line, IsPdf(), IsJitDisabled()); diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index ea87df62c9411..f138ae5d41c96 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -9,10 +9,10 @@ is needed for OSR. Originally landed in https://github.com/electron/libchromiumcontent/pull/226. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9d6ad368980202f74d36785623d27354beef3f03..9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff 100644 +index d7ec62c6761bcc8d4bb5889e873e22c4a27e7f17..ac8aa81e213689a612d322fa75e81798ae1010e6 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -3853,6 +3853,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3922,6 +3922,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, params.main_frame_name, GetOpener(), primary_main_frame_policy, base::UnguessableToken::Create()); @@ -26,7 +26,7 @@ index 9d6ad368980202f74d36785623d27354beef3f03..9fb2ac59a8b6a4e6f1ca388cd053c8f4 std::unique_ptr delegate = GetContentClient()->browser()->GetWebContentsViewDelegate(this); -@@ -3863,6 +3870,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3932,6 +3939,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, view_ = CreateWebContentsView(this, std::move(delegate), &render_view_host_delegate_view_); } @@ -35,10 +35,10 @@ index 9d6ad368980202f74d36785623d27354beef3f03..9fb2ac59a8b6a4e6f1ca388cd053c8f4 CHECK(view_.get()); diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h -index 4f3914d6936cbf0119b34da82ab6d5d67bb526f7..49291ee02aa20a34cab44838121e1cff0c380508 100644 +index 4cf7eef3b54bb4d3770786f8eb03d1678516fa54..772b029c67cc74b2922fe7a034edc14bcfda6c31 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h -@@ -116,10 +116,13 @@ class BrowserPluginGuestDelegate; +@@ -119,10 +119,13 @@ class BrowserPluginGuestDelegate; class GuestPageHolder; class RenderFrameHost; class RenderViewHost; @@ -52,7 +52,7 @@ index 4f3914d6936cbf0119b34da82ab6d5d67bb526f7..49291ee02aa20a34cab44838121e1cff class WebUI; struct DropData; struct MHTMLGenerationParams; -@@ -265,6 +268,10 @@ class WebContents : public PageNavigator, public base::SupportsUserData { +@@ -268,6 +271,10 @@ class WebContents : public PageNavigator, public base::SupportsUserData { network::mojom::WebSandboxFlags starting_sandbox_flags = network::mojom::WebSandboxFlags::kNone; diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index 38f2ca15ba7a2..9c32aa1dc63b6 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,10 +15,10 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 09752e9cda20f0020976015d58f73f320031d11e..b7a1be346c77fcef6c288789bc11f9849d6a126d 100644 +index 2b99d0b584760ab3f51561c030ca199055bacd26..9d7b8758cb8f115e5c83f3913c7172f28a4d1fa2 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -8722,6 +8722,17 @@ void RenderFrameHostImpl::EnterFullscreen( +@@ -8762,6 +8762,17 @@ void RenderFrameHostImpl::EnterFullscreen( } } @@ -37,10 +37,10 @@ index 09752e9cda20f0020976015d58f73f320031d11e..b7a1be346c77fcef6c288789bc11f984 if (had_fullscreen_token && !GetView()->HasFocus()) GetView()->Focus(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff..55840c1cf5323d1796f1c3b056602411f513a220 100644 +index ac8aa81e213689a612d322fa75e81798ae1010e6..26f41d34766899e81e32c319aad00172ffdc5fca 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4128,21 +4128,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( +@@ -4197,21 +4197,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( const input::NativeWebKeyboardEvent& event) { OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"), "WebContentsImpl::PreHandleKeyboardEvent"); @@ -78,7 +78,7 @@ index 9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff..55840c1cf5323d1796f1c3b056602411 } bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) { -@@ -4301,7 +4305,7 @@ void WebContentsImpl::EnterFullscreenMode( +@@ -4370,7 +4374,7 @@ void WebContentsImpl::EnterFullscreenMode( OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode"); DCHECK(CanEnterFullscreenMode(requesting_frame)); DCHECK(requesting_frame->IsActive()); @@ -88,7 +88,7 @@ index 9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff..55840c1cf5323d1796f1c3b056602411 features::kAutomaticFullscreenContentSetting)) { // Ensure the window is made active to take input focus. The user may have diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc -index 983f8a795725a097adf4e13f7d9c24b7c16d3467..75f1b9b60460713da6d832a803cfc03d7c90c9ca 100644 +index 0a37c1f1eb49023a4a209e9664e0fadadb97e297..76c52a0528a100787b83ccd76f552f98216a5aaa 100644 --- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc +++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc @@ -110,7 +110,7 @@ void FullscreenElementChanged(Document& document, diff --git a/patches/chromium/worker_context_will_destroy.patch b/patches/chromium/worker_context_will_destroy.patch index 212b2e7b385c0..c8bf328c6faea 100644 --- a/patches/chromium/worker_context_will_destroy.patch +++ b/patches/chromium/worker_context_will_destroy.patch @@ -26,10 +26,10 @@ index 7a2d251ba2d13d0a34df176111e6524a27b87f55..cbbe0fbdd25a0f7859b113fdb3dcd9ce // An empty URL is returned if the URL is not overriden. virtual GURL OverrideFlashEmbedWithHTML(const GURL& url); diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index 30c79bff1b9665ea0186ec6437cda0804b1168d4..b933264fd21f689de436a832476ebbfef89b927e 100644 +index c1d1c8a094a9161856d12945945c31bb187fe5a2..45bd68c7c54c4465831a146ed5d4e72414c18770 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -894,6 +894,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { +@@ -895,6 +895,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); } @@ -43,10 +43,10 @@ index 30c79bff1b9665ea0186ec6437cda0804b1168d4..b933264fd21f689de436a832476ebbfe const v8::Local& worker) { GetContentClient()->renderer()->DidInitializeWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index f726c03e34578032f5fc6c9a914ba72858efce89..9c5eeba6e24bb53fc112082c198a103256b06e32 100644 +index ce5aee7746ba05cdf185899f0b30e1f11f2ba45f..1b8af76a25eb06626ed7f211552eb5658d03429d 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -196,6 +196,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -198,6 +198,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -55,10 +55,10 @@ index f726c03e34578032f5fc6c9a914ba72858efce89..9c5eeba6e24bb53fc112082c198a1032 const blink::WebSecurityOrigin& script_origin) override; blink::ProtocolHandlerSecurityLevel GetProtocolHandlerSecurityLevel( diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index 1e84e382f1f94607384e0a800f4a96daf5e8ba75..d19743d2d2a512a984d831f437adced81ae912dd 100644 +index 617b7a582f1331796129f78c12558ebc81850bf9..ae2de15efaae470ec8b137323c1ad543c465c5ad 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -676,6 +676,7 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -672,6 +672,7 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} @@ -67,10 +67,10 @@ index 1e84e382f1f94607384e0a800f4a96daf5e8ba75..d19743d2d2a512a984d831f437adced8 const WebSecurityOrigin& script_origin) { return false; diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc -index 6e0a669c741753e784fe8a967b1deb125b02d0bf..2449348f956f81845bf314558fa5b7268500adeb 100644 +index 00c8757098f9959f78a0a7383cf1197f88ae14aa..9b2878bc23c78f092816524608776dd32fbde5a1 100644 --- a/third_party/blink/renderer/core/workers/worker_thread.cc +++ b/third_party/blink/renderer/core/workers/worker_thread.cc -@@ -762,6 +762,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() { +@@ -751,6 +751,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() { } pause_handle_.reset(); diff --git a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch index f0c2cbf2f2557..d1ea746900d19 100644 --- a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch +++ b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch @@ -35,10 +35,10 @@ index cbbe0fbdd25a0f7859b113fdb3dcd9ce57e597d6..1345bb5008e1b4fc3a450f7e353d52ec // from the worker thread. virtual void WillDestroyWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index b933264fd21f689de436a832476ebbfef89b927e..e9a9cf8df3318331f69929437b91b11f9273aea0 100644 +index 45bd68c7c54c4465831a146ed5d4e72414c18770..e6c7f23da99c63e44d14c4eb16f6a647ad9f750e 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -906,6 +906,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( +@@ -907,6 +907,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( worker); } @@ -52,10 +52,10 @@ index b933264fd21f689de436a832476ebbfef89b927e..e9a9cf8df3318331f69929437b91b11f const blink::WebSecurityOrigin& script_origin) { return GetContentClient()->renderer()->AllowScriptExtensionForServiceWorker( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index 9c5eeba6e24bb53fc112082c198a103256b06e32..fad9cf104d62d4e19a2b27eff54d17f908357b41 100644 +index 1b8af76a25eb06626ed7f211552eb5658d03429d..cbaf3450fc2f56cc66413c48e178879074ea072d 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -196,6 +196,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -198,6 +198,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -65,10 +65,10 @@ index 9c5eeba6e24bb53fc112082c198a103256b06e32..fad9cf104d62d4e19a2b27eff54d17f9 bool AllowScriptExtensionForServiceWorker( const blink::WebSecurityOrigin& script_origin) override; diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index d19743d2d2a512a984d831f437adced81ae912dd..78267456cf2bccc4bc1120ea8107c950ead24c89 100644 +index ae2de15efaae470ec8b137323c1ad543c465c5ad..852e76537ebcabd37c22c17def182295e7e61bdd 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -676,6 +676,8 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -672,6 +672,8 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} diff --git a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch index 664ff5ff075f4..e4a94ceeffcff 100644 --- a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch +++ b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch @@ -10,10 +10,10 @@ to handle this without patching, but this is fairly clean for now and no longer patching legacy devtools code. diff --git a/front_end/entrypoints/main/MainImpl.ts b/front_end/entrypoints/main/MainImpl.ts -index 469a7440eb5a0306a48e33668453a998ca5fe093..8cf1b93049a16d7bad1c64269b180fdba6d08ef3 100644 +index ab07d9b54f6bc8183a151feabf094eaaf1c64306..b3300b1d49b816013f3ca82b084977e5644ca5b7 100644 --- a/front_end/entrypoints/main/MainImpl.ts +++ b/front_end/entrypoints/main/MainImpl.ts -@@ -787,6 +787,8 @@ export class MainImpl { +@@ -734,6 +734,8 @@ export class MainImpl { globalThis.Main = globalThis.Main || {}; // @ts-expect-error Exported for Tests.js globalThis.Main.Main = MainImpl; diff --git a/patches/node/.patches b/patches/node/.patches index 58552a60de6f8..17e363ed7928b 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -34,13 +34,14 @@ fix_remove_harmony-import-assertions_from_node_cc.patch chore_disable_deprecation_ftbfs_in_simdjson_header.patch build_allow_unbundling_of_node_js_dependencies.patch test_use_static_method_names_in_call_stacks.patch -build_use_third_party_simdutf.patch fix_remove_fastapitypedarray_usage.patch test_handle_explicit_resource_management_globals.patch -linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch -build_remove_explicit_linker_call_to_libm_on_macos.patch build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch -test_make_eval_snapshot_tests_more_flexible.patch -build_option_to_use_custom_inspector_protocol_path.patch fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch chore_add_createexternalizabletwobytestring_to_globals.patch +refactor_attach_cppgc_heap_on_v8_isolate_creation.patch +fix_ensure_traverseparent_bails_on_resource_path_exit.patch +cli_move_--trace-atomics-wait_to_eol.patch +fix_cppgc_initializing_twice.patch +fix_task_starvation_in_inspector_context_test.patch +fix_expose_readfilesync_override_for_modules.patch diff --git a/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch b/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch index 43b5a271bedff..e1497804ce738 100644 --- a/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch +++ b/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch @@ -8,10 +8,10 @@ naturally upstream, and we will be able to remove this patch in a future Node.js upgrade. diff --git a/src/node_platform.cc b/src/node_platform.cc -index 65a9b79ae6ac8b7589e8f8109a709acb41d12b97..743ac069ad579a208a632ef5096ae46c8a0dfd74 100644 +index b438b3774d0aa7680fdbc6c6bf39a87893d221b2..ec355061825fb861c17fa2e6cc967b4c7b8d4586 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc -@@ -556,8 +556,8 @@ bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { +@@ -687,8 +687,8 @@ bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { return ForIsolate(isolate)->IdleTasksEnabled(); } @@ -23,10 +23,10 @@ index 65a9b79ae6ac8b7589e8f8109a709acb41d12b97..743ac069ad579a208a632ef5096ae46c } diff --git a/src/node_platform.h b/src/node_platform.h -index dde2d1b5687a5b52a4f09183bb4ff88d7d3e4d01..0a99f5b4b5eeb221ef3a34db7a50955c32d3c163 100644 +index a0222b4a1b074c6708e390d58d04221717069ac1..8015ca1801573c3a7c4a5db6d0f10b4016a9267c 100644 --- a/src/node_platform.h +++ b/src/node_platform.h -@@ -177,7 +177,7 @@ class NodePlatform : public MultiIsolatePlatform { +@@ -213,7 +213,7 @@ class NodePlatform : public MultiIsolatePlatform { void (*callback)(void*), void* data) override; std::shared_ptr GetForegroundTaskRunner( diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index a5dcecf25f499..5e0111ef8ca4d 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -10,8 +10,21 @@ however those files were cherry-picked from main branch and do not really in 20/21. We have to wait until 22 is released to be able to build with upstream GN files. +diff --git a/configure.py b/configure.py +index 2415940835036226799a7ea14c6687cc0d56c523..0feb07afbccad97a92cee00954443407eb20ac67 100755 +--- a/configure.py ++++ b/configure.py +@@ -1722,7 +1722,7 @@ def configure_v8(o, configs): + # Until we manage to get rid of all those, v8_enable_sandbox cannot be used. + # Note that enabling pointer compression without enabling sandbox is unsupported by V8, + # so this can be broken at any time. +- o['variables']['v8_enable_sandbox'] = 0 ++ o['variables']['v8_enable_sandbox'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_pointer_compression_shared_cage'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_external_code_space'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 diff --git a/node.gni b/node.gni -index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3fd219be7d 100644 +index b049f0692980c3e26771c3209c3bdd2e9a4d637b..e2407027ab05e59b2f0f1c213b98ea469db7a91b 100644 --- a/node.gni +++ b/node.gni @@ -5,10 +5,10 @@ @@ -27,7 +40,15 @@ index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3f # The location of OpenSSL - use the one from node's deps by default. node_openssl_path = "$node_path/deps/openssl" -@@ -44,7 +44,7 @@ declare_args() { +@@ -42,12 +42,15 @@ declare_args() { + # The variable is called "openssl" for parity with node's GYP build. + node_use_openssl = true + ++ # Build node with SQLite support. ++ node_use_sqlite = true ++ + # Use the specified path to system CA (PEM format) in addition to + # the BoringSSL supplied CA store or compiled-in Mozilla CA copy. node_openssl_system_ca_path = "" # Initialize v8 platform during node.js startup. @@ -36,7 +57,7 @@ index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3f # Custom build tag. node_tag = "" -@@ -64,10 +64,16 @@ declare_args() { +@@ -67,10 +70,16 @@ declare_args() { # TODO(zcbenz): There are few broken things for now: # 1. cross-os compilation is not supported. # 2. node_mksnapshot crashes when cross-compiling for x64 from arm64. @@ -54,71 +75,11 @@ index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3f } assert(!node_enable_inspector || node_use_openssl, -diff --git a/src/inspector/unofficial.gni b/src/inspector/unofficial.gni -index 5d87f3c901ab509e534598ed1eb0796a96355b5e..3d7aa148678b2646b88fa7c32abec91791b02b82 100644 ---- a/src/inspector/unofficial.gni -+++ b/src/inspector/unofficial.gni -@@ -13,7 +13,7 @@ template("inspector_gn_build") { - } - - node_gen_dir = get_label_info("../..", "target_gen_dir") -- protocol_tool_path = "../../tools/inspector_protocol" -+ protocol_tool_path = "../../deps/inspector_protocol" - - gypi_values = exec_script( - "../../tools/gypi_to_gn.py", -@@ -35,6 +35,8 @@ template("inspector_gn_build") { - ] - - args = [ -+ "--inspector_protocol_dir", -+ rebase_path(protocol_tool_path, root_build_dir), - "--jinja_dir", - # jinja is in third_party. - rebase_path("//third_party/", root_build_dir), -@@ -72,4 +74,37 @@ template("inspector_gn_build") { - outputs = [ "$node_gen_dir/src/{{source_name_part}}.json" ] - args = [ "{{source}}" ] + rebase_path(outputs, root_build_dir) - } -+ -+ config("crdtp_config") { -+ include_dirs = [ protocol_tool_path ] -+ } -+ -+ static_library("crdtp") { -+ public_configs = [ ":crdtp_config" ] -+ sources = [ -+ "$protocol_tool_path/crdtp/cbor.cc", -+ "$protocol_tool_path/crdtp/cbor.h", -+ "$protocol_tool_path/crdtp/dispatch.cc", -+ "$protocol_tool_path/crdtp/dispatch.h", -+ "$protocol_tool_path/crdtp/error_support.cc", -+ "$protocol_tool_path/crdtp/error_support.h", -+ "$protocol_tool_path/crdtp/export.h", -+ "$protocol_tool_path/crdtp/find_by_first.h", -+ "$protocol_tool_path/crdtp/frontend_channel.h", -+ "$protocol_tool_path/crdtp/glue.h", -+ "$protocol_tool_path/crdtp/json.cc", -+ "$protocol_tool_path/crdtp/json.h", -+ "$protocol_tool_path/crdtp/parser_handler.h", -+ "$protocol_tool_path/crdtp/protocol_core.cc", -+ "$protocol_tool_path/crdtp/protocol_core.h", -+ "$protocol_tool_path/crdtp/serializable.cc", -+ "$protocol_tool_path/crdtp/serializable.h", -+ "$protocol_tool_path/crdtp/span.cc", -+ "$protocol_tool_path/crdtp/span.h", -+ "$protocol_tool_path/crdtp/status.cc", -+ "$protocol_tool_path/crdtp/status.h", -+ "$protocol_tool_path/crdtp/json_platform.cc", -+ "$protocol_tool_path/crdtp/json_platform.h", -+ ] -+ } - } diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index 894fd515202cc3a1f933c2bbc618dd09869ad904..4f1ed661e9c432f3b50f2e7e348ad9794ff773d0 100644 +index abf1583cdac9f139056cf4809f14e28e62f6d24c..8b104e175ccf8de90c138337f83f8f6ce1348ac7 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -781,6 +781,7 @@ void BuiltinLoader::RegisterExternalReferences( +@@ -789,6 +789,7 @@ void BuiltinLoader::RegisterExternalReferences( registry->Register(GetNatives); RegisterExternalReferencesForInternalizedBuiltinCode(registry); @@ -127,7 +88,7 @@ index 894fd515202cc3a1f933c2bbc618dd09869ad904..4f1ed661e9c432f3b50f2e7e348ad979 } // namespace builtins diff --git a/src/node_builtins.h b/src/node_builtins.h -index a73de23a1debfdac66873e0baccf882e383bfc36..7ac5291be093773ee7efd39e77e01bf5d5ce5247 100644 +index f9426599f2d5dc6ad061407f0c4eb2c9203a4433..302030f610965f07dd6998d282275c1bdf738009 100644 --- a/src/node_builtins.h +++ b/src/node_builtins.h @@ -74,6 +74,8 @@ using BuiltinCodeCacheMap = @@ -140,10 +101,18 @@ index a73de23a1debfdac66873e0baccf882e383bfc36..7ac5291be093773ee7efd39e77e01bf5 // Handles compilation and caching of built-in JavaScript modules and // bootstrap scripts, whose source are bundled into the binary as static data. diff --git a/tools/install.py b/tools/install.py -index 17515720ba9c85d533465365188021074a8d30f4..7232be863d517c8445059a57c5938c2cbdeaf81b 100755 +index 8797b59e59c85a8877b977fa3281e50165e6f6b2..0af01e075616195f38fb242626dcab770ec1eb57 100755 --- a/tools/install.py +++ b/tools/install.py -@@ -291,6 +291,7 @@ def headers(options, action): +@@ -222,6 +222,7 @@ def headers(options, action): + 'include/cppgc/internal/caged-heap-local-data.h', + 'include/cppgc/internal/caged-heap.h', + 'include/cppgc/internal/compiler-specific.h', ++ 'include/cppgc/internal/conditional-stack-allocated.h', + 'include/cppgc/internal/finalizer-trait.h', + 'include/cppgc/internal/gc-info.h', + 'include/cppgc/internal/logging.h', +@@ -301,6 +302,7 @@ def headers(options, action): 'include/v8-promise.h', 'include/v8-proxy.h', 'include/v8-regexp.h', @@ -298,7 +267,7 @@ index 21992cbe894a880e3223c379326b62db22f2f12d..1296a5457422099035ba34f2b02624f2 } // namespace js2c } // namespace node diff --git a/tools/search_files.py b/tools/search_files.py -index 65d0e1be42f0a85418491ebb548278cf431aa6a0..d4a31342f1c6107b029394c6e1d00a1d1e877e03 100755 +index 856878c33681a73d41016729dabe48b0a6a80589..91a11852d206b65485fe90fd037a0bd17a16c20b 100755 --- a/tools/search_files.py +++ b/tools/search_files.py @@ -14,6 +14,7 @@ if __name__ == '__main__': @@ -306,14 +275,31 @@ index 65d0e1be42f0a85418491ebb548278cf431aa6a0..d4a31342f1c6107b029394c6e1d00a1d files = SearchFiles(*sys.argv[2:]) files = [ os.path.relpath(x, sys.argv[1]) for x in files ] + files = [os.path.normpath(x).replace(os.sep, '/') for x in files] - print('\n'.join(files)) - except Exception as e: - print(str(e)) + # Apply the same transform in SearchFiles after relpath + if sys.platform == 'win32': + files = [ x.replace('\\', '/') for x in files ] diff --git a/unofficial.gni b/unofficial.gni -index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5aeab1340d 100644 +index da565473f1ae96b4d009935f7733e6ab15ea9de2..26ebc811272ef2990f8d090c54e7f5294aab9d37 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -145,6 +145,7 @@ template("node_gn_build") { +@@ -22,6 +22,11 @@ template("node_gn_build") { + } else { + defines += [ "HAVE_OPENSSL=0" ] + } ++ if (node_use_sqlite) { ++ defines += [ "HAVE_SQLITE=1" ] ++ } else { ++ defines += [ "HAVE_SQLITE=0" ] ++ } + if (node_use_amaro) { + defines += [ "HAVE_AMARO=1" ] + } else { +@@ -142,32 +147,41 @@ template("node_gn_build") { + public_configs = [ + ":node_external_config", + "deps/googletest:googletest_config", ++ ":zstd_include_config" + ] public_deps = [ "deps/ada", "deps/uv", @@ -321,19 +307,32 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a "deps/simdjson", "$node_v8_path", ] -@@ -156,7 +157,6 @@ template("node_gn_build") { + deps = [ + ":run_node_js2c", +- "deps/brotli", + "deps/cares", + "deps/histogram", "deps/llhttp", "deps/nbytes", "deps/nghttp2", - "deps/ngtcp2", "deps/postject", - "deps/sqlite", +- "deps/sqlite", "deps/uvwasi", -@@ -165,7 +165,11 @@ template("node_gn_build") { +- "deps/zstd", + "//third_party/zlib", ++ "//third_party/brotli:dec", ++ "//third_party/brotli:enc", ++ "//third_party/zstd:decompress", ++ "//third_party/zstd:headers", + "$node_simdutf_path", "$node_v8_path:v8_libplatform", ] -+ cflags_cc = [ "-Wno-unguarded-availability-new" ] ++ cflags_cc = [ ++ "-Wno-unguarded-availability-new", ++ "-Wno-return-stack-address" ++ ] + sources = [ + "src/node_snapshot_stub.cc", @@ -341,7 +340,7 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a "$target_gen_dir/node_javascript.cc", ] + gypi_values.node_sources -@@ -185,11 +189,12 @@ template("node_gn_build") { +@@ -190,9 +204,13 @@ template("node_gn_build") { } if (node_use_openssl) { deps += [ "deps/ncrypto" ] @@ -349,13 +348,25 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a + public_deps += [ "//third_party/boringssl" ] sources += gypi_values.node_crypto_sources } ++ if (node_use_sqlite) { ++ deps += [ "deps/sqlite" ] ++ sources += gypi_values.node_sqlite_sources ++ } if (node_enable_inspector) { deps += [ -+ "src/inspector:crdtp", - "src/inspector:node_protocol_generated_sources", - "src/inspector:v8_inspector_compress_protocol_json", - ] -@@ -282,6 +287,7 @@ template("node_gn_build") { + "$node_inspector_protocol_path:crdtp", +@@ -215,6 +233,10 @@ template("node_gn_build") { + } + } + ++ config("zstd_include_config") { ++ include_dirs = [ "//third_party/zstd/src/lib" ] ++ } ++ + executable(target_name) { + forward_variables_from(invoker, "*") + +@@ -289,6 +311,7 @@ template("node_gn_build") { } executable("node_js2c") { @@ -363,7 +374,7 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a deps = [ "deps/uv", "$node_simdutf_path", -@@ -292,26 +298,75 @@ template("node_gn_build") { +@@ -299,26 +322,75 @@ template("node_gn_build") { "src/embedded_data.cc", "src/embedded_data.h", ] @@ -449,7 +460,7 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a outputs = [ "$target_gen_dir/node_javascript.cc" ] # Get the path to node_js2c executable of the host toolchain. -@@ -325,11 +380,11 @@ template("node_gn_build") { +@@ -332,11 +404,11 @@ template("node_gn_build") { get_label_info(":node_js2c($host_toolchain)", "name") + host_executable_suffix diff --git a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch index 952cc525820fc..48d4362fb6792 100644 --- a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch +++ b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch @@ -14,10 +14,10 @@ We don't need to do this for zlib, as the existing gn workflow uses the same Upstreamed at https://github.com/nodejs/node/pull/55903 diff --git a/unofficial.gni b/unofficial.gni -index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d26c4db206 100644 +index 26ebc811272ef2990f8d090c54e7f5294aab9d37..8886f2a79ae77614789d6ae0defd4f18fc756456 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -153,7 +153,6 @@ template("node_gn_build") { +@@ -160,7 +160,6 @@ template("node_gn_build") { ":run_node_js2c", "deps/cares", "deps/histogram", @@ -25,7 +25,7 @@ index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d2 "deps/nbytes", "deps/nghttp2", "deps/postject", -@@ -184,7 +183,17 @@ template("node_gn_build") { +@@ -198,7 +197,17 @@ template("node_gn_build") { configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] configs += [ "//build/config/gcc:symbol_visibility_default" ] } @@ -44,7 +44,7 @@ index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d2 if (v8_enable_i18n_support) { deps += [ "//third_party/icu" ] } -@@ -212,6 +221,19 @@ template("node_gn_build") { +@@ -231,6 +240,19 @@ template("node_gn_build") { sources += node_inspector.node_inspector_sources + node_inspector.node_inspector_generated_sources } @@ -63,4 +63,4 @@ index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d2 + } } - executable(target_name) { + config("zstd_include_config") { diff --git a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch index 9f5b3c70a4ef8..7f669c5b0bd7d 100644 --- a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch +++ b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shelley Vohr -Date: Wed, 12 Feb 2025 15:31:07 +0100 +Date: Thu, 26 Jun 2025 09:25:24 +0000 Subject: build: change crdtp::ProtocolTypeTraits signatures to avoid conflict After https://github.com/nodejs/node/pull/56649 we see conflicts with the @@ -14,7 +14,7 @@ error: duplicate symbol: crdtp::ProtocolTypeTraitstokenizer()->TokenTag() == cbor::CBORTokenTag::STRING8) { span cbor_span = state->tokenizer()->GetString8(); value->assign(reinterpret_cast(cbor_span.data()), -@@ -24,12 +25,13 @@ bool ProtocolTypeTraits::Deserialize(DeserializerState* state, +@@ -24,7 +25,8 @@ bool ProtocolTypeTraits::Deserialize(DeserializerState* state, } void ProtocolTypeTraits::Serialize(const std::string& value, @@ -36,18 +36,12 @@ index d83c53c81ca7745a31b200d5af3656af59f4f530..b2f6d412415e2988ead3c1e25b9557e1 + void* extra) { cbor::EncodeString8(SpanFrom(value), bytes); } -- - } // namespace crdtp -+ - namespace node { - namespace inspector { - namespace protocol { diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h -index d529d1337be0e2292202920446e841701d16b0b7..08e00f9b94918e3385aed18de80eec5c7ad81095 100644 +index bcdedaa2ae4ab1d3267d7a1347f15e0405261277..ddedca4a5b9b35258050f8b4cb446ceeba956896 100644 --- a/src/inspector/node_string.h +++ b/src/inspector/node_string.h -@@ -15,8 +15,8 @@ namespace crdtp { +@@ -18,8 +18,8 @@ namespace crdtp { template <> struct ProtocolTypeTraits { @@ -57,4 +51,4 @@ index d529d1337be0e2292202920446e841701d16b0b7..08e00f9b94918e3385aed18de80eec5c + static void Serialize(const std::string& value, std::vector* bytes, void* extra = nullptr); }; - } // namespace crdtp + template <> diff --git a/patches/node/build_compile_with_c_20_support.patch b/patches/node/build_compile_with_c_20_support.patch index 046acb7ccd575..52a25cc54cd5c 100644 --- a/patches/node/build_compile_with_c_20_support.patch +++ b/patches/node/build_compile_with_c_20_support.patch @@ -10,10 +10,10 @@ V8 requires C++20 support as of https://chromium-review.googlesource.com/c/v8/v8 This can be removed when Electron upgrades to a version of Node.js containing the required V8 version. diff --git a/common.gypi b/common.gypi -index 755bd203173a69564be203ad58c33eb50680b204..a7a0ffde7209de51ffcbf0db0ed7efcf09ad606d 100644 +index 679633dc6b4ce2a1f5f88e93d1a1c1feb4bbadb4..2caa183213d5632be81b763e894e37c09384391f 100644 --- a/common.gypi +++ b/common.gypi -@@ -518,7 +518,7 @@ +@@ -539,7 +539,7 @@ '-fno-rtti', '-fno-exceptions', '-fno-strict-aliasing', @@ -22,7 +22,7 @@ index 755bd203173a69564be203ad58c33eb50680b204..a7a0ffde7209de51ffcbf0db0ed7efcf ], 'defines': [ '__STDC_FORMAT_MACROS' ], 'ldflags': [ '-rdynamic' ], -@@ -688,7 +688,7 @@ +@@ -709,7 +709,7 @@ ['clang==1', { 'xcode_settings': { 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0', diff --git a/patches/node/build_enable_perfetto.patch b/patches/node/build_enable_perfetto.patch index d0548a2e8e818..1bb292dd46878 100644 --- a/patches/node/build_enable_perfetto.patch +++ b/patches/node/build_enable_perfetto.patch @@ -64,10 +64,10 @@ index 251f51ec454f9cba4023b8b6729241ee753aac13..1de8cac6e3953ce9cab9db03530da327 module.exports = { diff --git a/node.gyp b/node.gyp -index d97004c8e148be6b63634dad5306756d503fb1f8..271fb4f76bc1e69baad0b1fafca128b3bca4a360 100644 +index 442c1e7a6ddafbb7a7ec7a42a97ec04b28ea4d93..3a66c11d39dd2fd129c8f54098a9607e080ecca0 100644 --- a/node.gyp +++ b/node.gyp -@@ -174,7 +174,6 @@ +@@ -176,7 +176,6 @@ 'src/timers.cc', 'src/timer_wrap.cc', 'src/tracing/agent.cc', @@ -75,7 +75,7 @@ index d97004c8e148be6b63634dad5306756d503fb1f8..271fb4f76bc1e69baad0b1fafca128b3 'src/tracing/node_trace_writer.cc', 'src/tracing/trace_event.cc', 'src/tracing/traced_value.cc', -@@ -302,7 +301,6 @@ +@@ -304,7 +303,6 @@ 'src/tcp_wrap.h', 'src/timers.h', 'src/tracing/agent.h', diff --git a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch index af3c0c6911778..3f5e9b39ae14b 100644 --- a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch +++ b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch @@ -7,10 +7,10 @@ Subject: build: ensure native module compilation fails if not using a new This should not be upstreamed, it is a quality-of-life patch for downstream module builders. diff --git a/common.gypi b/common.gypi -index 5d74876ab28f8c10bb9543f7652478514414d8d2..755bd203173a69564be203ad58c33eb50680b204 100644 +index 33af43cd768c24b26d523f3db66eb8b9eb26859a..679633dc6b4ce2a1f5f88e93d1a1c1feb4bbadb4 100644 --- a/common.gypi +++ b/common.gypi -@@ -86,6 +86,8 @@ +@@ -89,6 +89,8 @@ 'v8_use_perfetto': 0, 'tsan%': 0, @@ -19,15 +19,17 @@ index 5d74876ab28f8c10bb9543f7652478514414d8d2..755bd203173a69564be203ad58c33eb5 ##### end V8 defaults ##### # When building native modules using 'npm install' with the system npm, -@@ -291,6 +293,7 @@ - # Defines these mostly for node-gyp to pickup. - 'defines': [ +@@ -298,7 +300,8 @@ '_GLIBCXX_USE_CXX11_ABI=1', + # This help forks when building Node.js on a 32-bit arch as + # libuv is always compiled with _FILE_OFFSET_BITS=64 +- '_FILE_OFFSET_BITS=64' ++ '_FILE_OFFSET_BITS=64', + 'ELECTRON_ENSURE_CONFIG_GYPI', ], # Forcibly disable -Werror. We support a wide range of compilers, it's -@@ -437,6 +440,11 @@ +@@ -455,6 +458,11 @@ }], ], }], @@ -40,10 +42,10 @@ index 5d74876ab28f8c10bb9543f7652478514414d8d2..755bd203173a69564be203ad58c33eb5 # list in v8/BUILD.gn. ['v8_enable_v8_checks == 1', { diff --git a/configure.py b/configure.py -index 712ed40f77e54d52d5b3c52bb68e2b7d48879812..6bcb7450975636b5dbc689470663ee37903874d5 100755 +index 0feb07afbccad97a92cee00954443407eb20ac67..5eccced7cf0212f229db68c76cc824a37e4a29bc 100755 --- a/configure.py +++ b/configure.py -@@ -1644,6 +1644,7 @@ def configure_library(lib, output, pkgname=None): +@@ -1704,6 +1704,7 @@ def configure_library(lib, output, pkgname=None): def configure_v8(o, configs): set_configuration_variable(configs, 'v8_enable_v8_checks', release=1, debug=0) @@ -52,7 +54,7 @@ index 712ed40f77e54d52d5b3c52bb68e2b7d48879812..6bcb7450975636b5dbc689470663ee37 o['variables']['v8_enable_javascript_promise_hooks'] = 1 o['variables']['v8_enable_lite_mode'] = 1 if options.v8_lite_mode else 0 diff --git a/src/node.h b/src/node.h -index 7726d3de1e82689655e8fceb4135eec303498572..120e3a1042e29590cbbf4be258a1cd2d3d4f0043 100644 +index 835c78145956de3d8c52b6cc0581bcfef600f90b..174fd4d1af4c8cd75aec09f4548a674fd5539fb2 100644 --- a/src/node.h +++ b/src/node.h @@ -22,6 +22,12 @@ diff --git a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch index 7b52bcc0fd163..8b7051b7f036c 100644 --- a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch +++ b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch @@ -10,10 +10,10 @@ JS errors and ensures embedder JS is loaded via LoadEmbedderJavaScriptSource. That method is generated by our modifications to js2c.cc in the BUILD.gn patch diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js -index 411eab8136d5957ae8a491bc38ffbdc88e59f5da..63c93b5be09692d0d4b6bfbb214b173b50ccca43 100644 +index 0244a214b187e67e0cb89f26cd019855963ec93a..b65a3be6bcb0e28f7f43367d0fa9da533db9d0d1 100644 --- a/lib/internal/fs/watchers.js +++ b/lib/internal/fs/watchers.js -@@ -292,12 +292,13 @@ function emitCloseNT(self) { +@@ -299,12 +299,13 @@ function emitCloseNT(self) { } // Legacy alias on the C++ wrapper object. This is not public API, so we may @@ -34,10 +34,10 @@ index 411eab8136d5957ae8a491bc38ffbdc88e59f5da..63c93b5be09692d0d4b6bfbb214b173b let kResistStopPropagation; diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index 4f1ed661e9c432f3b50f2e7e348ad9794ff773d0..16c95348ee254061d5c48f405968c1b0ee33bf82 100644 +index 8b104e175ccf8de90c138337f83f8f6ce1348ac7..35cf42a5e533cb799bf129df0c8370bfe8310233 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -34,6 +34,7 @@ using v8::Value; +@@ -35,6 +35,7 @@ using v8::Value; BuiltinLoader::BuiltinLoader() : config_(GetConfig()), code_cache_(std::make_shared()) { LoadJavaScriptSource(); @@ -46,7 +46,7 @@ index 4f1ed661e9c432f3b50f2e7e348ad9794ff773d0..16c95348ee254061d5c48f405968c1b0 AddExternalizedBuiltin( "internal/deps/cjs-module-lexer/lexer", diff --git a/src/node_builtins.h b/src/node_builtins.h -index 7ac5291be093773ee7efd39e77e01bf5d5ce5247..c3c987d535285be84026ad0c633650bd2067d22d 100644 +index 302030f610965f07dd6998d282275c1bdf738009..35cb7766eeccc62dd2f0ce9484a2f1ec7beccc05 100644 --- a/src/node_builtins.h +++ b/src/node_builtins.h @@ -138,6 +138,7 @@ class NODE_EXTERN_PRIVATE BuiltinLoader { diff --git a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch deleted file mode 100644 index 960290f55d9ba..0000000000000 --- a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: deepak1556 -Date: Mon, 17 Feb 2025 20:57:05 +0900 -Subject: build: option to use custom inspector_protocol path - -This allows building against //third_party/inspector_protocol -which would align us when building with chromium shared dependencies. - -The span changes will be auto-removed when Node.js bumps their -protocol deps to contain https://chromium-review.googlesource.com/c/v8/v8/+/5996636 - -Rest of the changes can be upstreamed. - -diff --git a/node.gni b/node.gni -index 203b4abbc44df9e58083c819f61f9025104abdc6..73bf3839866a2652ca660f1117e8f249d33fa46a 100644 ---- a/node.gni -+++ b/node.gni -@@ -16,6 +16,9 @@ declare_args() { - # The location of simdutf - use the one from node's deps by default. - node_simdutf_path = "//third_party/simdutf" - -+ # The location of inspector_protocol - use the one from node's deps by default. -+ node_inspector_protocol_path = "//third_party/inspector_protocol" -+ - # The NODE_MODULE_VERSION defined in node_version.h. - node_module_version = exec_script("$node_path/tools/getmoduleversion.py", [], "value") - -diff --git a/src/inspector/node_json.cc b/src/inspector/node_json.cc -index d8aacbdf1a8fc858c792ad3ce17ca2f46baebe7e..4625008c048532c2c3340130670647d2877430bd 100644 ---- a/src/inspector/node_json.cc -+++ b/src/inspector/node_json.cc -@@ -72,7 +72,7 @@ class ValueParserHandler : public ParserHandler { - - void HandleBinary(span bytes) override { - AddValueToParent( -- BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size()))); -+ BinaryValue::create(Binary::fromSpan(bytes))); - } - - void HandleDouble(double value) override { -diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h -index 08e00f9b94918e3385aed18de80eec5c7ad81095..23e678da55c373400d86e67caec436800531d40d 100644 ---- a/src/inspector/node_string.h -+++ b/src/inspector/node_string.h -@@ -63,7 +63,7 @@ class Binary { - static Binary fromBase64(const std::string_view base64, bool* success) { - UNREACHABLE(); - } -- static Binary fromSpan(const uint8_t* data, size_t size) { UNREACHABLE(); } -+ static Binary fromSpan(crdtp::span data) { UNREACHABLE(); } - }; - - } // namespace protocol -diff --git a/src/inspector/unofficial.gni b/src/inspector/unofficial.gni -index 3d7aa148678b2646b88fa7c32abec91791b02b82..4810d93eb971b253f7dadff7011a632f6dbe6a2b 100644 ---- a/src/inspector/unofficial.gni -+++ b/src/inspector/unofficial.gni -@@ -13,7 +13,7 @@ template("inspector_gn_build") { - } - - node_gen_dir = get_label_info("../..", "target_gen_dir") -- protocol_tool_path = "../../deps/inspector_protocol" -+ protocol_tool_path = "$node_inspector_protocol_path" - - gypi_values = exec_script( - "../../tools/gypi_to_gn.py", -diff --git a/unofficial.gni b/unofficial.gni -index cd0eae52ca9bf244e43643a2034fa9d26c4db206..d61a9bd5f4f0c92682f10b449976735859ecbc55 100644 ---- a/unofficial.gni -+++ b/unofficial.gni -@@ -204,13 +204,14 @@ template("node_gn_build") { - } - if (node_enable_inspector) { - deps += [ -- "src/inspector:crdtp", -+ "$node_inspector_protocol_path:crdtp", - "src/inspector:node_protocol_generated_sources", - "src/inspector:v8_inspector_compress_protocol_json", - ] - include_dirs = [ - "$target_gen_dir/src", - "$target_gen_dir/src/inspector", -+ "$node_inspector_protocol_path", - ] - node_inspector = exec_script( - "./tools/gypi_to_gn.py", diff --git a/patches/node/build_remove_explicit_linker_call_to_libm_on_macos.patch b/patches/node/build_remove_explicit_linker_call_to_libm_on_macos.patch deleted file mode 100644 index aad80028b3a5d..0000000000000 --- a/patches/node/build_remove_explicit_linker_call_to_libm_on_macos.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: deepak1556 -Date: Mon, 3 Feb 2025 21:44:36 +0900 -Subject: build: remove explicit linker call to libm on macOS - -/usr/lib/libm.tbd is available via libSystem.*.dylib and -reexports sanitizer symbols. When building for asan -this becomes an issue as the linker will resolve the symbols -from the system library rather from libclang_rt.* - -For V8 that rely on specific version of these symbols -that get bundled as part of clang, for ex: -https://source.chromium.org/chromium/chromium/src/+/main:v8/src/heap/cppgc/platform.cc;l=93-97 -accepting nullptr for shadow_offset in `asan_get_shadow_mapping`, -linking to system version that doesn't support this will lead to -a crash. - -Clang driver eventually links with `-lSystem` -https://github.com/llvm/llvm-project/blob/e82f93890daefeb38fe2a22ee3db87a89948ec57/clang/lib/Driver/ToolChains/Darwin.cpp#L1628-L1631, -this is done after linking the sanitizer libraries which -ensures right order of resolution for the symbols. - -PR-URL: https://github.com/nodejs/node/pull/56901 -Reviewed-By: Joyee Cheung -Reviewed-By: Chengzhong Wu -Reviewed-By: Luigi Pinca -Reviewed-By: Shelley Vohr - -diff --git a/deps/brotli/unofficial.gni b/deps/brotli/unofficial.gni -index 5e07e106672a04508a77584c109c97a67926c858..91001fa43ea4807d061f296eaeccb7512e34863e 100644 ---- a/deps/brotli/unofficial.gni -+++ b/deps/brotli/unofficial.gni -@@ -25,7 +25,7 @@ template("brotli_gn_build") { - } else if (target_os == "freebsd") { - defines = [ "OS_FREEBSD" ] - } -- if (!is_win) { -+ if (is_linux) { - libs = [ "m" ] - } - if (is_clang || !is_win) { -diff --git a/deps/uv/unofficial.gni b/deps/uv/unofficial.gni -index 348d2f0703e47ca7c5326a4b4c1d6ae31157eeb5..0944d6ddd241b113970ab6aa5804f9534fde882a 100644 ---- a/deps/uv/unofficial.gni -+++ b/deps/uv/unofficial.gni -@@ -87,11 +87,11 @@ template("uv_gn_build") { - ] - } - if (is_posix) { -- libs = [ "m" ] - ldflags = [ "-pthread" ] - } - if (is_linux) { -- libs += [ -+ libs = [ -+ "m", - "dl", - "rt", - ] diff --git a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch index be9e0aeffa012..88f7586d58c69 100644 --- a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch +++ b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch @@ -11,10 +11,10 @@ node-gyp will use the result of `process.config` that reflects the environment in which the binary got built. diff --git a/common.gypi b/common.gypi -index a7a0ffde7209de51ffcbf0db0ed7efcf09ad606d..20fd68eeb878b51f361d72070d87338db3d9a8d4 100644 +index 2caa183213d5632be81b763e894e37c09384391f..2cce436c4a9e3d942f957f6c94a4ef9e3db391ce 100644 --- a/common.gypi +++ b/common.gypi -@@ -125,6 +125,7 @@ +@@ -128,6 +128,7 @@ 'v8_base': '<(PRODUCT_DIR)/obj.target/tools/v8_gypfiles/libv8_snapshot.a', }], ['OS=="mac"', { diff --git a/patches/node/build_use_third_party_simdutf.patch b/patches/node/build_use_third_party_simdutf.patch deleted file mode 100644 index 07d50bf3a9e44..0000000000000 --- a/patches/node/build_use_third_party_simdutf.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Charles Kerr -Date: Mon, 9 Dec 2024 11:18:51 -0600 -Subject: build: use third_party/simdutf - -use the Chromium version of simdutf to avoid duplicate symbols - -diff --git a/node.gni b/node.gni -index 56a554175b805c1703f13d62041f8c80d6e94dd9..203b4abbc44df9e58083c819f61f9025104abdc6 100644 ---- a/node.gni -+++ b/node.gni -@@ -14,7 +14,7 @@ declare_args() { - node_openssl_path = "//third_party/boringssl" - - # The location of simdutf - use the one from node's deps by default. -- node_simdutf_path = "$node_path/deps/simdutf" -+ node_simdutf_path = "//third_party/simdutf" - - # The NODE_MODULE_VERSION defined in node_version.h. - node_module_version = exec_script("$node_path/tools/getmoduleversion.py", [], "value") diff --git a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch index db45eab11fe45..a2901455347a2 100644 --- a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch +++ b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch @@ -7,29 +7,11 @@ This allows embedders to tell Node.js what the first "real" file is when they use themselves as the entry point. We should try to upstream some form of this. -diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 9b5772fe9b8babbb892c7a5ec79258472da55a76..3568fd6ea0816f62d97d46f5d497bb1b23bf4e25 100644 ---- a/lib/internal/modules/cjs/loader.js -+++ b/lib/internal/modules/cjs/loader.js -@@ -1555,6 +1555,13 @@ Module.prototype._compile = function(content, filename, format) { - this[kIsExecuting] = true; - if (this[kIsMainSymbol] && getOptionValue('--inspect-brk')) { - const { callAndPauseOnStart } = internalBinding('inspector'); -+ // process._firstFileName is used by Embedders to tell node what -+ // the first "real" file is when they use themselves as the entry -+ // point -+ if (process._firstFileName) { -+ resolvedArgv = process._firstFileName; -+ delete process._firstFileName; -+ } - result = callAndPauseOnStart(compiledWrapper, thisValue, exports, - require, module, filename, dirname, - process, localGlobal, localBuffer); diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js -index d1c05d1717cdc825c4e48885c963c9ed65bcf51c..278665921c5160ff10b3178db27d4df319fab6b3 100644 +index 0cda54fd85e1e0bff13d4718a269eb3e7c60312a..6b165062a5eaa40f6e5614bca50bc33ccbdb85cc 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js -@@ -243,12 +243,14 @@ function patchProcessObject(expandArgv1) { +@@ -245,12 +245,14 @@ function patchProcessObject(expandArgv1) { // the entry point. if (expandArgv1 && process.argv[1] && process.argv[1][0] !== '-') { // Expand process.argv[1] into a full path. diff --git a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch index b07d10c2715ec..b91313c8345e4 100644 --- a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch +++ b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch @@ -11,10 +11,10 @@ Without this patch, building with simdjson fails with This patch can be removed once this is fixed upstream in simdjson. diff --git a/deps/simdjson/simdjson.h b/deps/simdjson/simdjson.h -index f21cd9381eef59ec43502c796fcaddb1b96525f5..e691fd24aa24d225f8c00fa5638be07265bfeeab 100644 +index 8f52a4331d59996786450eec982659da9244cac1..74729673d87b068dff5f24166bbb77d844f15f42 100644 --- a/deps/simdjson/simdjson.h +++ b/deps/simdjson/simdjson.h -@@ -3654,12 +3654,17 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result padded_string::load(std::string_view filen +@@ -4304,6 +4309,9 @@ inline simdjson_result padded_string::load(std::string_view filen } // namespace simdjson +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-literal-operator" + - inline simdjson::padded_string operator "" _padded(const char *str, size_t len) { + inline simdjson::padded_string operator ""_padded(const char *str, size_t len) { return simdjson::padded_string(str, len); } -@@ -4045,6 +4053,8 @@ inline simdjson::padded_string operator "" _padded(const char8_t *str, size_t le +@@ -4312,6 +4320,8 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len return simdjson::padded_string(reinterpret_cast(str), len); } #endif @@ -51,10 +51,3 @@ index f21cd9381eef59ec43502c796fcaddb1b96525f5..e691fd24aa24d225f8c00fa5638be072 #endif // SIMDJSON_PADDED_STRING_INL_H /* end file simdjson/padded_string-inl.h */ /* skipped duplicate #include "simdjson/padded_string_view.h" */ -@@ -118292,4 +118302,4 @@ namespace simdjson { - /* end file simdjson/ondemand.h */ - - #endif // SIMDJSON_H --/* end file simdjson.h */ -+/* end file simdjson.h */ -\ No newline at end of file diff --git a/patches/node/chore_expose_importmoduledynamically_and.patch b/patches/node/chore_expose_importmoduledynamically_and.patch index 2596e1482f10d..3f44ec26449ef 100644 --- a/patches/node/chore_expose_importmoduledynamically_and.patch +++ b/patches/node/chore_expose_importmoduledynamically_and.patch @@ -11,7 +11,7 @@ its own blended handler between Node and Blink. Not upstreamable. diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js -index 99061e62976e7cb24be81e8632b0e21d1e9adf9a..bbc9311c059e8ab0328c5f92b21a6be57620717e 100644 +index 9b41db8b0714b7408f79cbd5b4c460d9bc08f239..35ecfb9bbaf2c8e7351e1c69da84c82a4a7cb049 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -30,7 +30,7 @@ const { @@ -23,7 +23,7 @@ index 99061e62976e7cb24be81e8632b0e21d1e9adf9a..bbc9311c059e8ab0328c5f92b21a6be5 const { loadPreloadModules, initializeFrozenIntrinsics, -@@ -274,12 +274,13 @@ let _forceDefaultLoader = false; +@@ -281,12 +281,13 @@ let _forceDefaultLoader = false; * @param {boolean} [forceDefaultLoader=false] - A boolean indicating disabling custom loaders. */ function initializeESM(forceDefaultLoader = false) { @@ -40,10 +40,10 @@ index 99061e62976e7cb24be81e8632b0e21d1e9adf9a..bbc9311c059e8ab0328c5f92b21a6be5 /** diff --git a/src/module_wrap.cc b/src/module_wrap.cc -index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba430915fd79d8 100644 +index e317a84e55714af0a93719336d02ac26410ad724..e3880111172363feafb53b51deb08c93596cd4f4 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc -@@ -832,7 +832,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( +@@ -895,7 +895,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( return module->module_.Get(isolate); } @@ -52,7 +52,7 @@ index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba4309 Local context, Local host_defined_options, Local resource_name, -@@ -897,12 +897,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( +@@ -967,12 +967,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( Realm* realm = Realm::GetCurrent(args); HandleScope handle_scope(isolate); @@ -68,7 +68,7 @@ index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba4309 } void ModuleWrap::HostInitializeImportMetaObjectCallback( -@@ -944,13 +945,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( +@@ -1014,13 +1015,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( Realm* realm = Realm::GetCurrent(args); Isolate* isolate = realm->isolate(); @@ -87,7 +87,7 @@ index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba4309 MaybeLocal ModuleWrap::SyntheticModuleEvaluationStepsCallback( diff --git a/src/module_wrap.h b/src/module_wrap.h -index 83b5793013cbc453cf92c0a006fc7be3c06ad276..90353954bc497cb4ae413dc134850f8abb4efc7c 100644 +index 9363ce73e51cde3d3a94f9912f072d532d0f8560..c0e972ed293157726efc5fa76dfa62d3da51c22a 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -8,6 +8,7 @@ @@ -114,7 +114,7 @@ index 83b5793013cbc453cf92c0a006fc7be3c06ad276..90353954bc497cb4ae413dc134850f8a public: enum InternalFields { kModuleSlot = BaseObject::kInternalFieldCount, -@@ -91,6 +99,8 @@ class ModuleWrap : public BaseObject { +@@ -92,6 +100,8 @@ class ModuleWrap : public BaseObject { static void CreateRequiredModuleFacade( const v8::FunctionCallbackInfo& args); @@ -123,7 +123,7 @@ index 83b5793013cbc453cf92c0a006fc7be3c06ad276..90353954bc497cb4ae413dc134850f8a private: ModuleWrap(Realm* realm, v8::Local object, -@@ -129,7 +139,6 @@ class ModuleWrap : public BaseObject { +@@ -131,7 +141,6 @@ class ModuleWrap : public BaseObject { v8::Local specifier, v8::Local import_attributes, v8::Local referrer); diff --git a/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch b/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch index ef4d76a3e8049..4890f20896718 100644 --- a/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch +++ b/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch @@ -8,15 +8,18 @@ which causes the `ELECTRON_RUN_AS_NODE` variable to be lost. This patch re-injects it. diff --git a/test/fixtures/test-runner/output/arbitrary-output-colored.js b/test/fixtures/test-runner/output/arbitrary-output-colored.js -index af23e674cb361ed81dafa22670d5633559cd1144..1dd59990cb7cdba8aecf4f499ee6b92e7cd41b30 100644 +index 444531da1df2f9bbbc19bb8a43fb6eb2d1802d1a..81b3b3e37e5664dc53bec987a2ce3a83a8da105f 100644 --- a/test/fixtures/test-runner/output/arbitrary-output-colored.js +++ b/test/fixtures/test-runner/output/arbitrary-output-colored.js -@@ -7,6 +7,6 @@ const fixtures = require('../../../common/fixtures'); +@@ -7,9 +7,9 @@ const fixtures = require('../../../common/fixtures'); (async function run() { const test = fixtures.path('test-runner/output/arbitrary-output-colored-1.js'); const reset = fixtures.path('test-runner/output/reset-color-depth.js'); - await once(spawn(process.execPath, ['-r', reset, '--test', test], { stdio: 'inherit' }), 'exit'); -- await once(spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit' }), 'exit'); + await once(spawn(process.execPath, ['-r', reset, '--test', test], { stdio: 'inherit', env: { ELECTRON_RUN_AS_NODE: 1 }}), 'exit'); -+ await once(spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit', env: { ELECTRON_RUN_AS_NODE: 1 } }), 'exit'); + await once( +- spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit' }), ++ spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit', env: { ELECTRON_RUN_AS_NODE: 1 }}), + 'exit', + ); })().then(common.mustCall()); diff --git a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch new file mode 100644 index 0000000000000..dae7ed917b98d --- /dev/null +++ b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch @@ -0,0 +1,305 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Marco Ippolito +Date: Wed, 1 May 2024 14:24:48 +0200 +Subject: cli: move --trace-atomics-wait to eol +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PR-URL: https://github.com/nodejs/node/pull/52747 +Fixes: https://github.com/nodejs/node/issues/42982 +Reviewed-By: Matteo Collina +Reviewed-By: Rafael Gonzaga +Reviewed-By: Michaël Zasso +Reviewed-By: Benjamin Gruenbaum +Reviewed-By: Yagiz Nizipli + +diff --git a/doc/api/cli.md b/doc/api/cli.md +index 404e87e6d1237b5ee79cafd8a959c1b6d9d23fe5..7deda572c940f7b2e8c6813f1826796a13e4db38 100644 +--- a/doc/api/cli.md ++++ b/doc/api/cli.md +@@ -2709,39 +2709,6 @@ added: v12.0.0 + Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.3'. Use to disable support + for TLSv1.2, which is not as secure as TLSv1.3. + +-### `--trace-atomics-wait` +- +- +- +-> Stability: 0 - Deprecated +- +-Print short summaries of calls to [`Atomics.wait()`][] to stderr. +-The output could look like this: +- +-```text +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) did not wait because the values mismatched +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) timed out +-(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) started +-(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread +-(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) was woken up by another thread +-``` +- +-The fields here correspond to: +- +-* The thread id as given by [`worker_threads.threadId`][] +-* The base address of the `SharedArrayBuffer` in question, as well as the +- byte offset corresponding to the index passed to `Atomics.wait()` +-* The expected value that was passed to `Atomics.wait()` +-* The timeout passed to `Atomics.wait` +- + ### `--trace-deprecation` + +