diff --git a/.circleci/config.yml b/.circleci/config.yml index 4db758a17d93b..33a52f19f8e60 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,6 +57,16 @@ parameters: type: boolean default: false +# Executors +executors: + linux-arm: + resource_class: electronjs/linux-arm + machine: true + + linux-arm64: + resource_class: electronjs/linux-arm64 + machine: true + # The config expects the following environment variables to be set: # - "SLACK_WEBHOOK" Slack hook URL to send notifications. # @@ -69,7 +79,7 @@ parameters: # Build machines configs. docker-image: &docker-image docker: - - image: electron.azurecr.io/build:4cec2c5ab66765caa724e37bae2bffb9b29722a5 + - image: electron.azurecr.io/build:d818f06a9b1540c7fd38f75ad5a2c493dd6843b6 machine-linux-medium: &machine-linux-medium <<: *docker-image @@ -158,10 +168,6 @@ env-mas-apple-silicon: &env-mas-apple-silicon TARGET_ARCH: arm64 USE_PREBUILT_V8_CONTEXT_SNAPSHOT: 1 -# Misc build configuration options. -env-enable-sccache: &env-enable-sccache - USE_SCCACHE: true - env-send-slack-notifications: &env-send-slack-notifications NOTIFY_SLACK: true @@ -229,6 +235,25 @@ step-maybe-notify-slack-success: &step-maybe-notify-slack-success fi when: on_success +step-maybe-cleanup-arm: &step-maybe-cleanup-arm + run: + name: Cleanup after testing + command: | + if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then + killall Electron || echo "No Electron processes left running" + killall Safari || echo "No Safari processes left running" + rm -rf ~/Library/Application\ Support/Electron* + rm -rf ~/Library/Application\ Support/electron* + elif [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then + XVFB=/usr/bin/Xvfb + /sbin/start-stop-daemon --stop --exec $XVFB || echo "Xvfb not running" + pkill electron || echo "electron not running" + rm -rf ~/.config/Electron* + rm -rf ~/.config/electron* + fi + + when: always + step-checkout-electron: &step-checkout-electron checkout: path: src/electron @@ -265,7 +290,7 @@ step-gclient-sync: &step-gclient-sync 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="Electron Bot" GIT_COMMITTER_EMAIL="electron@github.com" git commit -m "update patches" --author="Electron Bot " + 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 @@ -295,22 +320,10 @@ step-setup-env-for-build: &step-setup-env-for-build # To find `gn` executable. echo 'export CHROMIUM_BUILDTOOLS_PATH="'"$PWD"'/src/buildtools"' >> $BASH_ENV - if [ "$USE_SCCACHE" == "true" ]; then - # https://github.com/mozilla/sccache - SCCACHE_PATH="$PWD/src/electron/external_binaries/sccache" - echo 'export SCCACHE_PATH="'"$SCCACHE_PATH"'"' >> $BASH_ENV - if [ "$CIRCLE_PR_NUMBER" != "" ]; then - #if building a fork set readonly access to sccache - echo 'export SCCACHE_BUCKET="electronjs-sccache-ci"' >> $BASH_ENV - echo 'export SCCACHE_TWO_TIER=true' >> $BASH_ENV - fi - fi - step-setup-goma-for-build: &step-setup-goma-for-build run: name: Setup Goma command: | - echo 'export USE_GOMA=true' >> $BASH_ENV echo 'export NUMBER_OF_NINJA_PROCESSES=300' >> $BASH_ENV if [ "`uname`" == "Darwin" ]; then echo 'ulimit -n 10000' >> $BASH_ENV @@ -324,9 +337,11 @@ step-setup-goma-for-build: &step-setup-goma-for-build npm install mkdir third_party node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })" + export GOMA_FALLBACK_ON_AUTH_FAILURE=true third_party/goma/goma_ctl.py ensure_start echo 'export GN_GOMA_FILE='`node -e "console.log(require('./src/utils/goma.js').gnFilePath)"` >> $BASH_ENV echo 'export LOCAL_GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV + echo 'export GOMA_FALLBACK_ON_AUTH_FAILURE=true' >> $BASH_ENV cd .. step-restore-brew-cache: &step-restore-brew-cache @@ -494,11 +509,7 @@ step-gn-gen-default: &step-gn-gen-default name: Default GN gen command: | cd src - if [ "$USE_GOMA" == "true" ]; then - gn gen out/Default --args="import(\"$GN_CONFIG\") import(\"$GN_GOMA_FILE\") $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" - else - gn gen out/Default --args="import(\"$GN_CONFIG\") cc_wrapper=\"$SCCACHE_PATH\" $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" - fi + gn gen out/Default --args="import(\"$GN_CONFIG\") import(\"$GN_GOMA_FILE\") $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" step-gn-check: &step-gn-check run: @@ -536,14 +547,11 @@ step-electron-build: &step-electron-build ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES ninja -C out/Default tools/v8_context_snapshot -j $NUMBER_OF_NINJA_PROCESSES gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args + (cd out/Default; zip mksnapshot.zip mksnapshot_args clang_x64_v8_arm64/gen/v8/embedded.S) rm -rf out/Default/clang_x64_v8_arm64/obj - # Regenerate because we just deleted some ninja files - if [ "$USE_GOMA" == "true" ]; then - gn gen out/Default --args="import(\"$GN_CONFIG\") import(\"$GN_GOMA_FILE\") $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" - else - gn gen out/Default --args="import(\"$GN_CONFIG\") cc_wrapper=\"$SCCACHE_PATH\" $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" - fi + # Regenerate because we just deleted some ninja files + gn gen out/Default --args="import(\"$GN_CONFIG\") import(\"$GN_GOMA_FILE\") $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" fi ninja -C out/Default electron -j $NUMBER_OF_NINJA_PROCESSES node electron/script/check-symlinks.js @@ -619,11 +627,7 @@ step-electron-maybe-chromedriver-gn-gen: &step-electron-maybe-chromedriver-gn-ge command: | cd src if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then - if [ "$USE_GOMA" == "true" ]; then - gn gen out/chromedriver --args="import(\"$GN_CONFIG\") import(\"$GN_GOMA_FILE\") is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" - else - gn gen out/chromedriver --args="import(\"$GN_CONFIG\") cc_wrapper=\"$SCCACHE_PATH\" is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" - fi + gn gen out/chromedriver --args="import(\"$GN_CONFIG\") import(\"$GN_GOMA_FILE\") is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" fi step-electron-chromedriver-build: &step-electron-chromedriver-build @@ -698,6 +702,7 @@ step-persist-data-for-tests: &step-persist-data-for-tests - src/electron - src/third_party/electron_node - src/third_party/nan + - src/cross-arch-snapshots step-electron-dist-unzip: &step-electron-dist-unzip run: @@ -734,11 +739,7 @@ step-ffmpeg-gn-gen: &step-ffmpeg-gn-gen name: ffmpeg GN gen command: | cd src - if [ "$USE_GOMA" == "true" ]; then - gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") import(\"$GN_GOMA_FILE\") $GN_EXTRA_ARGS" - else - gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") cc_wrapper=\"$SCCACHE_PATH\" $GN_EXTRA_ARGS" - fi + gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") import(\"$GN_GOMA_FILE\") $GN_EXTRA_ARGS" step-ffmpeg-build: &step-ffmpeg-build run: @@ -764,7 +765,11 @@ step-verify-mksnapshot: &step-verify-mksnapshot name: Verify mksnapshot command: | cd src - python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default + if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then + python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/cross-arch-snapshots + else + python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default + fi step-verify-chromedriver: &step-verify-chromedriver run: @@ -781,21 +786,16 @@ step-setup-linux-for-headless-testing: &step-setup-linux-for-headless-testing sh -e /etc/init.d/xvfb start fi -step-show-sccache-stats: &step-show-sccache-stats +step-show-goma-stats: &step-show-goma-stats run: shell: /bin/bash - name: Check sccache/goma stats after build - command: | - if [ "$SCCACHE_PATH" != "" ]; then - $SCCACHE_PATH -s - fi - if [ "$USE_GOMA" == "true" ]; then - set +e - set +o pipefail - $LOCAL_GOMA_DIR/goma_ctl.py stat - $LOCAL_GOMA_DIR/diagnose_goma_log.py - true - fi + name: Check goma stats after build + command: | + set +e + set +o pipefail + $LOCAL_GOMA_DIR/goma_ctl.py stat + $LOCAL_GOMA_DIR/diagnose_goma_log.py + true when: always step-mksnapshot-build: &step-mksnapshot-build @@ -896,23 +896,6 @@ step-maybe-cross-arch-snapshot-store: &step-maybe-cross-arch-snapshot-store path: src/cross-arch-snapshots destination: cross-arch-snapshots -step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test - run: - name: Trigger an arm test on VSTS if applicable - command: | - cd src - # Only run for non-fork prs - if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then - #Trigger VSTS job, passing along CircleCI job number and branch to build - if [ "`uname`" == "Darwin" ]; then - echo "Triggering electron-arm2-testing build on Azure DevOps" - node electron/script/release/ci-release-build.js --job=electron-arm2-testing --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH - else - echo "Triggering electron-$TARGET_ARCH-testing build on VSTS" - node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH - fi - fi - step-maybe-generate-typescript-defs: &step-maybe-generate-typescript-defs run: name: Generate type declarations @@ -1203,6 +1186,7 @@ steps-electron-gn-check: &steps-electron-gn-check - *step-maybe-early-exit-doc-only-change - *step-depot-tools-add-to-path - *step-setup-env-for-build + - *step-setup-goma-for-build - *step-gn-gen-default - *step-gn-check @@ -1236,6 +1220,7 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha - *step-depot-tools-add-to-path - *step-setup-env-for-build + - *step-setup-goma-for-build - *step-restore-brew-cache - *step-get-more-space-on-mac - *step-install-npm-deps-on-mac @@ -1245,26 +1230,13 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha #Compile ts/js to verify doc change didn't break anything - *step-ts-compile -steps-chromedriver-build: &steps-chromedriver-build - steps: - - attach_workspace: - at: . - - *step-depot-tools-add-to-path - - *step-setup-env-for-build - - *step-fix-sync-on-mac - - - *step-electron-maybe-chromedriver-gn-gen - - *step-electron-chromedriver-build - - *step-electron-chromedriver-store - - - *step-maybe-notify-slack-failure - steps-native-tests: &steps-native-tests steps: - attach_workspace: at: . - *step-depot-tools-add-to-path - *step-setup-env-for-build + - *step-setup-goma-for-build - *step-gn-gen-default - run: @@ -1272,7 +1244,7 @@ steps-native-tests: &steps-native-tests command: | cd src ninja -C out/Default $BUILD_TARGET - - *step-show-sccache-stats + - *step-show-goma-stats - *step-setup-linux-for-headless-testing - run: @@ -1338,8 +1310,14 @@ steps-tests: &steps-tests ELECTRON_DISABLE_SECURITY_WARNINGS: 1 command: | cd src - (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split)) - (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split)) + if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then + export ELECTRON_SKIP_NATIVE_MODULE_TESTS=true + (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging) + (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging) + else + (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split)) + (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split)) + fi - run: name: Check test results existence command: | @@ -1360,6 +1338,8 @@ steps-tests: &steps-tests - *step-maybe-notify-slack-failure + - *step-maybe-cleanup-arm + steps-test-nan: &steps-test-nan steps: - attach_workspace: @@ -1580,7 +1560,7 @@ commands: - *step-nodejs-headers-build - *step-nodejs-headers-store - - *step-show-sccache-stats + - *step-show-goma-stats # mksnapshot - *step-mksnapshot-build @@ -1635,9 +1615,6 @@ commands: steps: - *step-save-out-cache - # Trigger tests on arm hardware if needed - - *step-maybe-trigger-arm-test - - *step-maybe-notify-slack-failure electron-publish: @@ -1668,13 +1645,14 @@ commands: - *step-gclient-sync - *step-delete-git-directories - *step-minimize-workspace-size-from-checkout - - *step-fix-sync-on-mac + - *step-fix-sync-on-mac - *step-setup-env-for-build + - *step-setup-goma-for-build - *step-gn-gen-default # Electron app - *step-electron-build - - *step-show-sccache-stats + - *step-show-goma-stats - *step-maybe-generate-breakpad-symbols - *step-maybe-electron-dist-strip - *step-electron-dist-build @@ -1858,14 +1836,6 @@ jobs: <<: *env-testing-build <<: *steps-electron-gn-check - linux-x64-chromedriver: - <<: *machine-linux-medium - environment: - <<: *env-linux-medium - <<: *env-release-build - <<: *env-send-slack-notifications - <<: *steps-chromedriver-build - linux-x64-release: <<: *machine-linux-2xlarge environment: @@ -1885,8 +1855,8 @@ jobs: <<: *env-linux-2xlarge-release GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True' <<: *env-release-build - <<: *env-enable-sccache UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -1897,8 +1867,8 @@ jobs: environment: <<: *env-linux-2xlarge-release <<: *env-release-build - <<: *env-enable-sccache UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -1918,15 +1888,6 @@ jobs: checkout: true use-out-cache: false - linux-ia32-chromedriver: - <<: *machine-linux-medium - environment: - <<: *env-linux-medium - <<: *env-ia32 - <<: *env-release-build - <<: *env-send-slack-notifications - <<: *steps-chromedriver-build - linux-ia32-release: <<: *machine-linux-2xlarge environment: @@ -1948,9 +1909,9 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True' <<: *env-ia32 <<: *env-release-build - <<: *env-enable-sccache <<: *env-32bit-release UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -1962,9 +1923,9 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-ia32 <<: *env-release-build - <<: *env-enable-sccache <<: *env-32bit-release UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -1981,19 +1942,10 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - electron-build: - persist: false + persist: true checkout: true use-out-cache: false - linux-arm-chromedriver: - <<: *machine-linux-medium - environment: - <<: *env-linux-medium - <<: *env-arm - <<: *env-release-build - <<: *env-send-slack-notifications - <<: *steps-chromedriver-build - linux-arm-release: <<: *machine-linux-2xlarge environment: @@ -2014,10 +1966,10 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-arm <<: *env-release-build - <<: *env-enable-sccache <<: *env-32bit-release GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True' UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -2029,9 +1981,9 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-arm <<: *env-release-build - <<: *env-enable-sccache <<: *env-32bit-release UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -2048,7 +2000,7 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - electron-build: - persist: false + persist: true checkout: true use-out-cache: false @@ -2060,15 +2012,6 @@ jobs: <<: *env-testing-build <<: *steps-electron-gn-check - linux-arm64-chromedriver: - <<: *machine-linux-medium - environment: - <<: *env-linux-medium - <<: *env-arm64 - <<: *env-release-build - <<: *env-send-slack-notifications - <<: *steps-chromedriver-build - linux-arm64-release: <<: *machine-linux-2xlarge environment: @@ -2089,9 +2032,9 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-arm64 <<: *env-release-build - <<: *env-enable-sccache GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True' UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -2103,8 +2046,8 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-arm64 <<: *env-release-build - <<: *env-enable-sccache UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -2151,9 +2094,9 @@ jobs: environment: <<: *env-mac-large-release <<: *env-release-build - <<: *env-enable-sccache GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True' UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -2164,10 +2107,10 @@ jobs: environment: <<: *env-mac-large-release <<: *env-release-build - <<: *env-enable-sccache <<: *env-apple-silicon GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True' UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -2178,8 +2121,8 @@ jobs: environment: <<: *env-mac-large-release <<: *env-release-build - <<: *env-enable-sccache UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -2190,9 +2133,9 @@ jobs: environment: <<: *env-mac-large-release <<: *env-release-build - <<: *env-enable-sccache <<: *env-apple-silicon UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -2259,9 +2202,9 @@ jobs: <<: *env-mac-large-release <<: *env-mas <<: *env-release-build - <<: *env-enable-sccache GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True' UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -2273,9 +2216,9 @@ jobs: <<: *env-mac-large-release <<: *env-mas-apple-silicon <<: *env-release-build - <<: *env-enable-sccache GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True' UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: false @@ -2287,7 +2230,6 @@ jobs: <<: *env-mac-large-release <<: *env-mas <<: *env-release-build - <<: *env-enable-sccache UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> steps: - electron-publish: @@ -2300,8 +2242,8 @@ jobs: <<: *env-mac-large-release <<: *env-mas-apple-silicon <<: *env-release-build - <<: *env-enable-sccache UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> + <<: *env-ninja-status steps: - electron-publish: attach: true @@ -2446,6 +2388,24 @@ jobs: <<: *env-send-slack-notifications <<: *steps-verify-ffmpeg + linux-arm-testing-tests: + executor: linux-arm + environment: + <<: *env-arm + <<: *env-global + <<: *env-headless-testing + <<: *env-stack-dumping + <<: *steps-tests + + linux-arm64-testing-tests: + executor: linux-arm64 + environment: + <<: *env-arm64 + <<: *env-global + <<: *env-headless-testing + <<: *env-stack-dumping + <<: *steps-tests + osx-testing-x64-tests: <<: *machine-mac-large environment: @@ -2626,15 +2586,19 @@ workflows: - osx-publish-x64-skip-checkout: requires: - mac-checkout + context: release-env - mas-publish-x64-skip-checkout: requires: - mac-checkout + context: release-env - osx-publish-arm64-skip-checkout: requires: - mac-checkout + context: release-env - mas-publish-arm64-skip-checkout: requires: - mac-checkout + context: release-env lint: when: << pipeline.parameters.run-lint >> @@ -2674,8 +2638,23 @@ workflows: - linux-ia32-testing - linux-arm-testing + - linux-arm-testing-tests: + filters: + branches: + # Do not run this on forked pull requests + ignore: /pull\/[0-9]+/ + requires: + - linux-arm-testing - linux-arm64-testing + - linux-arm64-testing-tests: + filters: + branches: + # Do not run this on forked pull requests + ignore: /pull\/[0-9]+/ + requires: + - linux-arm64-testing + - linux-arm64-testing-gn-check: requires: - linux-checkout-fast diff --git a/.env.example b/.env.example index eb3df4b6bdf9c..4d218327bd60e 100644 --- a/.env.example +++ b/.env.example @@ -4,4 +4,3 @@ APPVEYOR_CLOUD_TOKEN= CIRCLE_TOKEN= ELECTRON_GITHUB_TOKEN= -VSTS_TOKEN= \ No newline at end of file diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 1709bb98f4bf0..2ff206b07c9a6 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -11.4.1 \ No newline at end of file +11.5.0 \ No newline at end of file diff --git a/README.md b/README.md index 50e3bc1cbe4ef..a5cbcae7f99cd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/4lggi9dpjc1qob7k/branch/master?svg=true)](https://ci.appveyor.com/project/electron-bot/electron-ljo26/branch/master) [![devDependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron?type=dev) -:memo: Available Translations: 🇨🇳 🇹🇼 🇧🇷 🇪🇸 🇰🇷 🇯🇵 🇷🇺 🇫🇷 🇹🇭 🇳🇱 🇹🇷 🇮🇩 🇺🇦 🇨🇿 🇮🇹 🇵🇱. +:memo: Available Translations: 🇨🇳 🇧🇷 🇪🇸 🇯🇵 🇷🇺 🇫🇷 🇺🇸 🇩🇪. View these docs in other languages at [electron/i18n](https://github.com/electron/i18n/tree/master/content/). The Electron framework lets you write cross-platform desktop applications diff --git a/appveyor.yml b/appveyor.yml index 4ce73c87dd432..a220925aa21e1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,6 +36,7 @@ environment: ELECTRON_ENABLE_STACK_DUMPING: 1 MOCHA_REPORTER: mocha-multi-reporters MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap + GOMA_FALLBACK_ON_AUTH_FAILURE: true notifications: - provider: Webhook url: https://electron-mission-control.herokuapp.com/rest/appveyor-hook diff --git a/default_app/default_app.ts b/default_app/default_app.ts index 7c2d8a3b9eecb..08d1c7ce39558 100644 --- a/default_app/default_app.ts +++ b/default_app/default_app.ts @@ -41,14 +41,14 @@ ipcMain.handle('bootstrap', (event) => { return isTrustedSender(event.sender) ? electronPath : null; }); -async function createWindow () { +async function createWindow (backgroundColor?: string) { await app.whenReady(); const options: Electron.BrowserWindowConstructorOptions = { width: 960, height: 620, autoHideMenuBar: true, - backgroundColor: '#2f3241', + backgroundColor, webPreferences: { preload: path.resolve(__dirname, 'preload.js'), contextIsolation: true, @@ -96,7 +96,7 @@ export const loadURL = async (appUrl: string) => { }; export const loadFile = async (appPath: string) => { - mainWindow = await createWindow(); + mainWindow = await createWindow(appPath === 'index.html' ? '#2f3241' : undefined); mainWindow.loadFile(appPath); mainWindow.focus(); }; diff --git a/docs/README.md b/docs/README.md index 2dd0ed683ce71..bd92a37e22d5a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -149,7 +149,6 @@ These individual tutorials expand on topics discussed in the guide above. ### Modules for the Renderer Process (Web Page): * [contextBridge](api/context-bridge.md) -* [desktopCapturer](api/desktop-capturer.md) * [ipcRenderer](api/ipc-renderer.md) * [remote](api/remote.md) * [webFrame](api/web-frame.md) @@ -158,6 +157,7 @@ These individual tutorials expand on topics discussed in the guide above. * [clipboard](api/clipboard.md) * [crashReporter](api/crash-reporter.md) +* [desktopCapturer](api/desktop-capturer.md) * [nativeImage](api/native-image.md) * [shell](api/shell.md) diff --git a/docs/api/app.md b/docs/api/app.md index ac57ba6581a67..4f1c2aa92f296 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -749,7 +749,8 @@ Overrides the current application's name. ### `app.getLocale()` -Returns `String` - The current application locale. Possible return values are documented [here](locales.md). +Returns `String` - The current application locale, fetched using Chromium's `l10n_util` library. +Possible return values are documented [here](https://source.chromium.org/chromium/chromium/src/+/master:ui/base/l10n/l10n_util.cc). To set the locale, you'll want to use a command line switch at app startup, which may be found [here](https://github.com/electron/electron/blob/master/docs/api/command-line-switches.md). @@ -924,6 +925,10 @@ 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. + Here's a very simple example of creating a custom Jump List: ```javascript diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index b5667d95b8ca4..2bcaa37a409cf 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -79,6 +79,12 @@ This switch can not be used in `app.commandLine.appendSwitch` since it is parsed earlier than user's app is loaded, but you can set the `ELECTRON_ENABLE_LOGGING` environment variable to achieve the same effect. +### --force-fieldtrials=`trials` + +Field trials to be forcefully enabled or disabled. + +For example: `WebRTC-Audio-Red-For-Opus/Enabled/` + ### --host-rules=`rules` A comma-separated list of `rules` that control how hostnames are mapped. diff --git a/docs/api/dialog.md b/docs/api/dialog.md index a0ed591d8c415..e4f9fb98b15f9 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -154,7 +154,7 @@ dialog.showOpenDialog(mainWindow, { * `browserWindow` [BrowserWindow](browser-window.md) (optional) * `options` Object - * `title` String (optional) + * `title` String (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments. * `defaultPath` String (optional) - Absolute directory path, absolute file path, or file name to use by default. * `buttonLabel` String (optional) - Custom label for the confirmation button, when @@ -185,7 +185,7 @@ The `filters` specifies an array of file types that can be displayed, see * `browserWindow` [BrowserWindow](browser-window.md) (optional) * `options` Object - * `title` String (optional) + * `title` String (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments. * `defaultPath` String (optional) - Absolute directory path, absolute file path, or file name to use by default. * `buttonLabel` String (optional) - Custom label for the confirmation button, when diff --git a/docs/api/frameless-window.md b/docs/api/frameless-window.md index fcdb1cc5d211c..3a3e5f4fd93b5 100644 --- a/docs/api/frameless-window.md +++ b/docs/api/frameless-window.md @@ -83,8 +83,11 @@ win.show() * The `blur` filter only applies to the web page, so there is no way to apply blur effect to the content below the window (i.e. other applications open on the user's system). -* On Windows operating systems, transparent windows will not work when DWM is +* The window will not be transparent when DevTools is opened. +* On Windows operating systems, + * 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 [this pull request](https://github.com/electron/electron/pull/28207). * On Linux, users have to put `--enable-transparent-visuals --disable-gpu` in the command line to disable GPU and allow ARGB to make transparent window, this is caused by an upstream bug that [alpha channel doesn't work on some diff --git a/docs/api/locales.md b/docs/api/locales.md deleted file mode 100644 index a45fdbcbe5774..0000000000000 --- a/docs/api/locales.md +++ /dev/null @@ -1,142 +0,0 @@ -# Locales - -> Locale values returned by `app.getLocale()`. - -Electron uses Chromium's `l10n_util` library to fetch the locale. Possible -values are listed below: - -| Language Code | Language Name | -|---------------|---------------| -| af | Afrikaans | -| am | Amharic | -| ar | Arabic | -| az | Azerbaijani | -| be | Belarusian | -| bg | Bulgarian | -| bh | Bihari | -| bn | Bengali | -| br | Breton | -| bs | Bosnian | -| ca | Catalan | -| co | Corsican | -| cs | Czech | -| cy | Welsh | -| da | Danish | -| de | German | -| de-AT | German (Austria) | -| de-CH | German (Switzerland) | -| de-DE | German (Germany) | -| el | Greek | -| en | English | -| en-AU | English (Australia) | -| en-CA | English (Canada) | -| en-GB | English (UK) | -| en-NZ | English (New Zealand) | -| en-US | English (US) | -| en-ZA | English (South Africa) | -| eo | Esperanto | -| es | Spanish | -| es-419 | Spanish (Latin America) | -| et | Estonian | -| eu | Basque | -| fa | Persian | -| fi | Finnish | -| fil | Filipino | -| fo | Faroese | -| fr | French | -| fr-CA | French (Canada) | -| fr-CH | French (Switzerland) | -| fr-FR | French (France) | -| fy | Frisian | -| ga | Irish | -| gd | Scots Gaelic | -| gl | Galician | -| gn | Guarani | -| gu | Gujarati | -| ha | Hausa | -| haw | Hawaiian | -| he | Hebrew | -| hi | Hindi | -| hr | Croatian | -| hu | Hungarian | -| hy | Armenian | -| ia | Interlingua | -| id | Indonesian | -| is | Icelandic | -| it | Italian | -| it-CH | Italian (Switzerland) | -| it-IT | Italian (Italy) | -| ja | Japanese | -| jw | Javanese | -| ka | Georgian | -| kk | Kazakh | -| km | Cambodian | -| kn | Kannada | -| ko | Korean | -| ku | Kurdish | -| ky | Kyrgyz | -| la | Latin | -| ln | Lingala | -| lo | Laothian | -| lt | Lithuanian | -| lv | Latvian | -| mk | Macedonian | -| ml | Malayalam | -| mn | Mongolian | -| mo | Moldavian | -| mr | Marathi | -| ms | Malay | -| mt | Maltese | -| nb | Norwegian (Bokmal) | -| ne | Nepali | -| nl | Dutch | -| nn | Norwegian (Nynorsk) | -| no | Norwegian | -| oc | Occitan | -| om | Oromo | -| or | Oriya | -| pa | Punjabi | -| pl | Polish | -| ps | Pashto | -| pt | Portuguese | -| pt-BR | Portuguese (Brazil) | -| pt-PT | Portuguese (Portugal) | -| qu | Quechua | -| rm | Romansh | -| ro | Romanian | -| ru | Russian | -| sd | Sindhi | -| sh | Serbo-Croatian | -| si | Sinhalese | -| sk | Slovak | -| sl | Slovenian | -| sn | Shona | -| so | Somali | -| sq | Albanian | -| sr | Serbian | -| st | Sesotho | -| su | Sundanese | -| sv | Swedish | -| sw | Swahili | -| ta | Tamil | -| te | Telugu | -| tg | Tajik | -| th | Thai | -| ti | Tigrinya | -| tk | Turkmen | -| to | Tonga | -| tr | Turkish | -| tt | Tatar | -| tw | Twi | -| ug | Uighur | -| uk | Ukrainian | -| ur | Urdu | -| uz | Uzbek | -| vi | Vietnamese | -| xh | Xhosa | -| yi | Yiddish | -| yo | Yoruba | -| zh | Chinese | -| zh-CN | Chinese (Simplified) | -| zh-TW | Chinese (Traditional) | -| zu | Zulu | diff --git a/docs/api/structures/jump-list-category.md b/docs/api/structures/jump-list-category.md index 07627e78c98e7..0f27f2085f6eb 100644 --- a/docs/api/structures/jump-list-category.md +++ b/docs/api/structures/jump-list-category.md @@ -19,3 +19,7 @@ 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. diff --git a/docs/api/structures/jump-list-item.md b/docs/api/structures/jump-list-item.md index d75637cb472dc..491abf80d301d 100644 --- a/docs/api/structures/jump-list-item.md +++ b/docs/api/structures/jump-list-item.md @@ -17,7 +17,7 @@ * `title` String (optional) - The text to be displayed for the item in the Jump List. Should only be set if `type` is `task`. * `description` String (optional) - Description of the task (displayed in a tooltip). - Should only be set if `type` is `task`. + Should only be set if `type` is `task`. Maximum length 260 characters. * `iconPath` String (optional) - The absolute path to an icon to be displayed in a Jump List, which can be an arbitrary resource file that contains an icon (e.g. `.ico`, `.exe`, `.dll`). You can usually specify `process.execPath` to diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index 5ba7e7a52e3f3..ed3212fd6594e 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -132,6 +132,8 @@ This is necessary for events such as `NSUserDefaultsDidChangeNotification`. * `userInfo` Record * `object` String +Returns `Number` - The ID of this subscription + Same as `subscribeNotification`, but uses `NSWorkspace.sharedWorkspace.notificationCenter`. This is necessary for events such as `NSWorkspaceDidActivateApplicationNotification`. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 68f72ff2d3f4f..37420bce217f2 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -1233,8 +1233,7 @@ Inserts `text` to the focused element. * `text` String - Content to be searched, must not be empty. * `options` Object (optional) * `forward` Boolean (optional) - Whether to search forward or backward, defaults to `true`. - * `findNext` Boolean (optional) - Whether the operation is first request or a follow up, - defaults to `false`. + * `findNext` Boolean (optional) - Whether to begin a new text finding session with this request. Should be `true` for initial requests, and `false` for follow-up requests. Defaults to `false`. * `matchCase` Boolean (optional) - Whether search should be case-sensitive, defaults to `false`. * `wordStart` Boolean (optional) - Whether to look only at the start of words. diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index 5d60ec38a8e9f..08a6ed1b711be 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -5,7 +5,7 @@ Electron's `webview` tag is based on [Chromium's `webview`][chrome-webview], which is undergoing dramatic architectural changes. This impacts the stability of `webviews`, including rendering, navigation, and event routing. We currently recommend to not -use the `webview` tag and to consider alternatives, like `iframe`, Electron's `BrowserView`, +use the `webview` tag and to consider alternatives, like `iframe`, [Electron's `BrowserView`](browser-view.md), or an architecture that avoids embedded content altogether. ## Enabling @@ -515,8 +515,7 @@ Inserts `text` to the focused element. * `text` String - Content to be searched, must not be empty. * `options` Object (optional) * `forward` Boolean (optional) - Whether to search forward or backward, defaults to `true`. - * `findNext` Boolean (optional) - Whether the operation is first request or a follow up, - defaults to `false`. + * `findNext` Boolean (optional) - Whether to begin a new text finding session with this request. Should be `true` for initial requests, and `false` for follow-up requests. Defaults to `false`. * `matchCase` Boolean (optional) - Whether search should be case-sensitive, defaults to `false`. * `wordStart` Boolean (optional) - Whether to look only at the start of words. diff --git a/filenames.auto.gni b/filenames.auto.gni index 0d88a9969b635..585d0184d4048 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -29,7 +29,6 @@ auto_filenames = { "docs/api/incoming-message.md", "docs/api/ipc-main.md", "docs/api/ipc-renderer.md", - "docs/api/locales.md", "docs/api/menu-item.md", "docs/api/menu.md", "docs/api/message-channel-main.md", diff --git a/lib/browser/api/net.ts b/lib/browser/api/net.ts index c8d1681007373..3339f1c2591a3 100644 --- a/lib/browser/api/net.ts +++ b/lib/browser/api/net.ts @@ -173,6 +173,11 @@ class ChunkedBodyStream extends Writable { this._downstream = pipe; if (this._pendingChunk) { const doneWriting = (maybeError: Error | void) => { + // If the underlying request has been aborted, we honeslty don't care about the error + // all work should cease as soon as we abort anyway, this error is probably a + // "mojo pipe disconnected" error (code=9) + if (this._clientRequest._aborted) return; + const cb = this._pendingCallback!; delete this._pendingCallback; delete this._pendingChunk; diff --git a/lib/browser/api/touch-bar.ts b/lib/browser/api/touch-bar.ts index 1ce8b272f97ef..383bc392b9148 100644 --- a/lib/browser/api/touch-bar.ts +++ b/lib/browser/api/touch-bar.ts @@ -7,8 +7,8 @@ const hiddenProperties = Symbol('hidden touch bar props'); const extendConstructHook = (target: any, hook: Function) => { const existingHook = target._hook; target._hook = function () { - hook.call(this); if (existingHook) existingHook.call(this); + hook.call(this); }; }; @@ -135,7 +135,7 @@ class TouchBarGroup extends TouchBarItem { return crashReporter.getCrashesDirectory(); }); - -ipcMainInternal.handle('ELECTRON_NATIVE_IMAGE_CREATE_THUMBNAIL_FROM_PATH', async (_, path: string, size: Electron.Size) => { - return typeUtils.serialize(await nativeImage.createThumbnailFromPath(path, size)); -}); diff --git a/lib/renderer/api/native-image.ts b/lib/renderer/api/native-image.ts index 1264a900edbef..e68d81a132c42 100644 --- a/lib/renderer/api/native-image.ts +++ b/lib/renderer/api/native-image.ts @@ -1,10 +1,3 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -import { deserialize } from '@electron/internal/common/type-utils'; - const { nativeImage } = process._linkedBinding('electron_common_native_image'); -nativeImage.createThumbnailFromPath = async (path: string, size: Electron.Size) => { - return deserialize(await ipcRendererInternal.invoke('ELECTRON_NATIVE_IMAGE_CREATE_THUMBNAIL_FROM_PATH', path, size)); -}; - export default nativeImage; diff --git a/lib/renderer/security-warnings.ts b/lib/renderer/security-warnings.ts index 4f17200b5ffbb..e8cb7a5b9041d 100644 --- a/lib/renderer/security-warnings.ts +++ b/lib/renderer/security-warnings.ts @@ -270,7 +270,7 @@ const warnAboutAllowedPopups = function () { const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) { if (!webPreferences || isLocalhost()) return; - const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true; + const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false; if (!remoteModuleEnabled) return; if (getIsRemoteProtocol()) { diff --git a/lib/renderer/web-view/guest-view-internal.ts b/lib/renderer/web-view/guest-view-internal.ts index f84f46e4e6d40..ccdc53f9c1060 100644 --- a/lib/renderer/web-view/guest-view-internal.ts +++ b/lib/renderer/web-view/guest-view-internal.ts @@ -1,4 +1,4 @@ -import { webFrame, IpcMessageEvent } from 'electron'; +import { webFrame } from 'electron'; import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; @@ -52,15 +52,15 @@ const dispatchEvent = function ( dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args); } - const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent; + const props: Record = {}; WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => { - (domEvent as any)[prop] = args[index]; + props[prop] = args[index]; }); - webView.dispatchEvent(domEvent); + webView.dispatchEvent(eventName, props); if (eventName === 'load-commit') { - webView.onLoadCommit(domEvent); + webView.onLoadCommit(props); } else if (eventName === 'focus-change') { webView.onFocusChange(); } @@ -70,8 +70,7 @@ export function registerEvents (webView: WebViewImpl, viewInstanceId: number) { ipcRendererInternal.onMessageFromMain(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () { webView.guestInstanceId = undefined; webView.reset(); - const domEvent = new Event('destroyed'); - webView.dispatchEvent(domEvent); + webView.dispatchEvent('destroyed'); }); ipcRendererInternal.onMessageFromMain(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) { @@ -79,11 +78,7 @@ export function registerEvents (webView: WebViewImpl, viewInstanceId: number) { }); ipcRendererInternal.onMessageFromMain(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) { - const domEvent = new Event('ipc-message') as IpcMessageEvent; - domEvent.channel = channel; - domEvent.args = args; - - webView.dispatchEvent(domEvent); + webView.dispatchEvent('ipc-message', { channel, args }); }); } diff --git a/lib/renderer/web-view/web-view-element.ts b/lib/renderer/web-view/web-view-element.ts index e723ebbbe1076..c63748456abad 100644 --- a/lib/renderer/web-view/web-view-element.ts +++ b/lib/renderer/web-view/web-view-element.ts @@ -39,7 +39,13 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof constructor () { super(); - v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this)); + const internal = new WebViewImpl(this); + internal.dispatchEventInMainWorld = (eventName, props) => { + const event = new Event(eventName); + Object.assign(event, props); + return internal.webviewNode.dispatchEvent(event); + }; + v8Util.setHiddenValue(this, 'internal', internal); } connectedCallback () { diff --git a/lib/renderer/web-view/web-view-impl.ts b/lib/renderer/web-view/web-view-impl.ts index 73b5b306c345e..3d296cabf3a29 100644 --- a/lib/renderer/web-view/web-view-impl.ts +++ b/lib/renderer/web-view/web-view-impl.ts @@ -37,6 +37,8 @@ export class WebViewImpl { public attributes = new Map(); public setupWebViewAttributes (): void {} + public dispatchEventInMainWorld?: (eventName: string, props: any) => boolean; + constructor (public webviewNode: HTMLElement) { // Create internal iframe element. this.internalElement = this.createInternalElement(); @@ -107,10 +109,11 @@ export class WebViewImpl { } onElementResize () { - const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent; - resizeEvent.newWidth = this.webviewNode.clientWidth; - resizeEvent.newHeight = this.webviewNode.clientHeight; - this.dispatchEvent(resizeEvent); + const props = { + newWidth: this.webviewNode.clientWidth, + newHeight: this.webviewNode.clientHeight + }; + this.dispatchEvent('resize', props); } createGuest () { @@ -119,8 +122,8 @@ export class WebViewImpl { }); } - dispatchEvent (webViewEvent: Electron.Event) { - this.webviewNode.dispatchEvent(webViewEvent); + dispatchEvent (eventName: string, props: Record = {}) { + this.dispatchEventInMainWorld!(eventName, props); } // Adds an 'on' property on the webview, which can be used to set/unset @@ -145,10 +148,10 @@ export class WebViewImpl { } // Updates state upon loadcommit. - onLoadCommit (webViewEvent: ElectronInternal.WebViewEvent) { + onLoadCommit (props: Record) { const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC); - const newValue = webViewEvent.url; - if (webViewEvent.isMainFrame && (oldValue !== newValue)) { + const newValue = props.url; + if (props.isMainFrame && (oldValue !== newValue)) { // Touching the src attribute triggers a navigation. To avoid // triggering a page reload on every guest-initiated navigation, // we do not handle this mutation. @@ -158,10 +161,10 @@ export class WebViewImpl { // Emits focus/blur events. onFocusChange () { - const hasFocus = document.activeElement === this.webviewNode; + const hasFocus = this.webviewNode.ownerDocument.activeElement === this.webviewNode; if (hasFocus !== this.hasFocus) { this.hasFocus = hasFocus; - this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur')); + this.dispatchEvent(hasFocus ? 'focus' : 'blur'); } } diff --git a/package.json b/package.json index 41fe317809fdc..ab430c9046b0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "11.4.1", + "version": "11.5.0", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/patches/angle/.patches b/patches/angle/.patches new file mode 100644 index 0000000000000..b291c2cc2b545 --- /dev/null +++ b/patches/angle/.patches @@ -0,0 +1,2 @@ +d3d11_skip_blits_if_there_is_no_intersection_of_dest_areas.patch +cherry-pick-3d4f87ab5b9b.patch diff --git a/patches/angle/cherry-pick-3d4f87ab5b9b.patch b/patches/angle/cherry-pick-3d4f87ab5b9b.patch new file mode 100644 index 0000000000000..811d0a8867ebc --- /dev/null +++ b/patches/angle/cherry-pick-3d4f87ab5b9b.patch @@ -0,0 +1,59 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jamie Madill +Date: Thu, 20 May 2021 12:22:46 -0400 +Subject: D3D11: Fix respecifying 3D textures. + +The missing check for the "Depth" dimension could lead to a bug +where we would not recreate a texture when the dimension changed. + +Bug: chromium:1210414 +Change-Id: Id59097ad14ae77ff80d27081f61786dad17a77ea +Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2911032 +Reviewed-by: Geoff Lang +Commit-Queue: Jamie Madill +(cherry picked from commit 2697358464cf20576701987f60300b6c4086c11e) +Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2937026 +Reviewed-by: Jamie Madill + +diff --git a/src/libANGLE/renderer/d3d/d3d11/Image11.cpp b/src/libANGLE/renderer/d3d/d3d11/Image11.cpp +index c502d00fac032ea708015bbbf4f51db2dc2b3c59..daa5c3abc3ab4f4460ec48d0aba9649cf66897ac 100644 +--- a/src/libANGLE/renderer/d3d/d3d11/Image11.cpp ++++ b/src/libANGLE/renderer/d3d/d3d11/Image11.cpp +@@ -223,8 +223,8 @@ bool Image11::redefine(gl::TextureType type, + const gl::Extents &size, + bool forceRelease) + { +- if (mWidth != size.width || mHeight != size.height || mInternalFormat != internalformat || +- forceRelease) ++ if (mWidth != size.width || mHeight != size.height || mDepth != size.depth || ++ mInternalFormat != internalformat || forceRelease) + { + // End the association with the TextureStorage, since that data will be out of date. + // Also reset mRecoveredFromStorageCount since this Image is getting completely redefined. +diff --git a/src/tests/gl_tests/MipmapTest.cpp b/src/tests/gl_tests/MipmapTest.cpp +index 888088b5e38191e38e07bf50a0d1b6fa0a09db95..372c6645a587563c616a961ed3ac46dc667e9660 100644 +--- a/src/tests/gl_tests/MipmapTest.cpp ++++ b/src/tests/gl_tests/MipmapTest.cpp +@@ -1991,6 +1991,22 @@ void main() + EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green); + } + ++// Tests respecifying 3D mipmaps. ++TEST_P(MipmapTestES3, Generate3DMipmapRespecification) ++{ ++ std::vector pixels(256 * 256 * 100, GLColor::black); ++ ++ GLTexture texture; ++ glBindTexture(GL_TEXTURE_3D, texture); ++ glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 256, 256, 100, 0, GL_RGBA, GL_UNSIGNED_BYTE, ++ pixels.data()); ++ glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA, 128, 128, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, ++ pixels.data()); ++ glGenerateMipmap(GL_TEXTURE_3D); ++ ++ ASSERT_GL_NO_ERROR(); ++} ++ + // Use this to select which configurations (e.g. which renderer, which GLES major version) these + // tests should be run against. + ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(MipmapTest); diff --git a/patches/angle/d3d11_skip_blits_if_there_is_no_intersection_of_dest_areas.patch b/patches/angle/d3d11_skip_blits_if_there_is_no_intersection_of_dest_areas.patch new file mode 100644 index 0000000000000..a33a90fdd6001 --- /dev/null +++ b/patches/angle/d3d11_skip_blits_if_there_is_no_intersection_of_dest_areas.patch @@ -0,0 +1,137 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Geoff Lang +Date: Mon, 19 Apr 2021 12:47:05 -0400 +Subject: D3D11: Skip blits if there is no intersection of dest areas + +Blit11 would clip the destination rectangle with the destination size +but ignore the result. gl::ClipRectangle returns false when the +rectangles do not intersect at all, indicating the blit can be skipped. + +This could lead to an out-of-bounds write to the GPU memory for the +destination texture. + +Mark ClipRectangle as nodiscard to prevent future issues. + +Bug: chromium:1199402 +Change-Id: I260e82d0917b8aa7e7887f2c9f7ed4b1a03ba785 +Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2836786 +Reviewed-by: Jamie Madill +Commit-Queue: Geoff Lang +(cherry picked from commit b574643ef28c92fcea5122dd7a72acb42a514eed) +Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2846982 +Reviewed-by: Geoff Lang + +diff --git a/src/libANGLE/angletypes.h b/src/libANGLE/angletypes.h +index b92e5b439f0678e76404c292d9008099378129be..2ef2aa19dd50a6efac4ac2d4bd0f81fc7ea96d69 100644 +--- a/src/libANGLE/angletypes.h ++++ b/src/libANGLE/angletypes.h +@@ -74,7 +74,7 @@ struct Rectangle + bool operator==(const Rectangle &a, const Rectangle &b); + bool operator!=(const Rectangle &a, const Rectangle &b); + +-bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection); ++ANGLE_NO_DISCARD bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection); + + struct Offset + { +diff --git a/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp b/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp +index 55f8f8f4d38f30990d7061de6ebaa0595f0447da..6d9365af8db5fa9835127650162d5aeb6ce46b77 100644 +--- a/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp ++++ b/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp +@@ -141,7 +141,10 @@ void StretchedBlitNearest(const gl::Box &sourceArea, + uint8_t *destData) + { + gl::Rectangle clippedDestArea(destArea.x, destArea.y, destArea.width, destArea.height); +- gl::ClipRectangle(clippedDestArea, clipRect, &clippedDestArea); ++ if (!gl::ClipRectangle(clippedDestArea, clipRect, &clippedDestArea)) ++ { ++ return; ++ } + + // Determine if entire rows can be copied at once instead of each individual pixel. There + // must be no out of bounds lookups, whole rows copies, and no scale. +diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp +index 833d4fe9bb1b430c3180f2d663eed1a6fb24e84c..bb56048779bd2dfe5d7d19304686b0ef3585c02e 100644 +--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp ++++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp +@@ -1117,7 +1117,10 @@ angle::Result FramebufferGL::clipSrcRegion(const gl::Context *context, + // If pixels lying outside the read framebuffer, adjust src region + // and dst region to appropriate in-bounds regions respectively. + gl::Rectangle realSourceRegion; +- ClipRectangle(bounds.sourceRegion, bounds.sourceBounds, &realSourceRegion); ++ if (!ClipRectangle(bounds.sourceRegion, bounds.sourceBounds, &realSourceRegion)) ++ { ++ return angle::Result::Stop; ++ } + GLuint xOffset = realSourceRegion.x - bounds.sourceRegion.x; + GLuint yOffset = realSourceRegion.y - bounds.sourceRegion.y; + +diff --git a/src/libANGLE/renderer/metal/ContextMtl.mm b/src/libANGLE/renderer/metal/ContextMtl.mm +index 88c4987433e4351540e8c83e5d9128eaab28b318..b069da6e504cb15150b2ae34b519a3fc9e37731f 100644 +--- a/src/libANGLE/renderer/metal/ContextMtl.mm ++++ b/src/libANGLE/renderer/metal/ContextMtl.mm +@@ -1362,7 +1362,10 @@ bool NeedToInvertDepthRange(float near, float far) + + // Clip the render area to the viewport. + gl::Rectangle viewportClippedRenderArea; +- gl::ClipRectangle(renderArea, glState.getViewport(), &viewportClippedRenderArea); ++ if (!gl::ClipRectangle(renderArea, glState.getViewport(), &viewportClippedRenderArea)) ++ { ++ viewportClippedRenderArea = gl::Rectangle(); ++ } + + gl::Rectangle scissoredArea = ClipRectToScissor(getState(), viewportClippedRenderArea, false); + if (framebufferMtl->flipY()) +diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp +index 8cdf049cd30a15a30dac06b39e24b39add9a2c5a..433a034b82e7f141ba7f867ad7325ce245b936f7 100644 +--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp ++++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp +@@ -2824,8 +2824,11 @@ angle::Result ContextVk::updateScissorImpl(const gl::State &glState, bool should + + // Clip the render area to the viewport. + gl::Rectangle viewportClippedRenderArea; +- gl::ClipRectangle(renderArea, getCorrectedViewport(glState.getViewport()), +- &viewportClippedRenderArea); ++ if (!gl::ClipRectangle(renderArea, getCorrectedViewport(glState.getViewport()), ++ &viewportClippedRenderArea)) ++ { ++ viewportClippedRenderArea = gl::Rectangle(); ++ } + + gl::Rectangle scissoredArea = ClipRectToScissor(getState(), viewportClippedRenderArea, false); + gl::Rectangle rotatedScissoredArea; +diff --git a/src/tests/gl_tests/BlitFramebufferANGLETest.cpp b/src/tests/gl_tests/BlitFramebufferANGLETest.cpp +index dc5a324684e209e6b50f8d67149c48ad845ae58f..6c4258f36ab64c4ce9d634179b38791a00b4f814 100644 +--- a/src/tests/gl_tests/BlitFramebufferANGLETest.cpp ++++ b/src/tests/gl_tests/BlitFramebufferANGLETest.cpp +@@ -2360,6 +2360,30 @@ TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow2) + EXPECT_GL_ERROR(GL_INVALID_VALUE); + } + ++// Test an edge case in D3D11 stencil blitting on the CPU that does not properly clip the ++// destination regions ++TEST_P(BlitFramebufferTest, BlitFramebufferStencilClipNoIntersection) ++{ ++ GLFramebuffer framebuffers[2]; ++ glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]); ++ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]); ++ ++ GLRenderbuffer renderbuffers[2]; ++ glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]); ++ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 4, 4); ++ glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, ++ renderbuffers[0]); ++ ++ glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[1]); ++ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 4, 4); ++ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, ++ renderbuffers[1]); ++ ++ glBlitFramebuffer(0, 0, 4, 4, 1 << 24, 1 << 24, 1 << 25, 1 << 25, GL_STENCIL_BUFFER_BIT, ++ GL_NEAREST); ++ EXPECT_GL_NO_ERROR(); ++} ++ + // Use this to select which configurations (e.g. which renderer, which GLES major version) these + // tests should be run against. + ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest, diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 56b467278b0af..e1f759deef89b 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -127,6 +127,7 @@ fix_heap_overflow_in_videoframeyuvconverter.patch merge_to_m88_avoid_spinning_a_nested_message_loop_for_x11_clipboard.patch merge_to_m88_xproto_switch_event_queue_from_a_std_list_to_a.patch websocket_don_t_clear_event_queue_on_destruction.patch +cherry-pick-162efe98330e.patch cherry-pick-7e0e52df283c.patch cherry-pick-6ed1c0c425e0.patch cherry-pick-aeb6bc551b60.patch @@ -140,3 +141,47 @@ cherry-pick-93ce5606cd9a.patch cherry-pick-c6d6f7aee733.patch cherry-pick-e1505713dc31.patch cherry-pick-a66dbdcf6493.patch +blink_wasm_eval_csp.patch +cherry-pick-3c80bb2a594f.patch +cherry-pick-8c3eb9d1c409.patch +cherry-pick-012e9baf46c9.patch +cherry-pick-6a6361c9f31c.patch +use_idtype_for_permission_change_subscriptions.patch +m90_devtools_expect_pagehandler_may_be_destroyed_during.patch +m86-lts_add_null_pointer_check_in_renderwidgethostinputeventrouter.patch +m86-lts_add_weak_pointer_to_rwhier_framesinkidownermap_and.patch +cherry-pick-7dd3b1c86795.patch +cherry-pick-fe85e04a1797.patch +cherry-pick-6b84dc72351b.patch +cherry-pick-1028ffc9bd83.patch +privacy_budget_remove_unnecessary_kcanvasreadback_metrics.patch +cherry-pick-406ae3e8a9a8.patch +cherry-pick-668cf831e912.patch +cherry-pick-02f5ef8c88d7.patch +cherry-pick-f37149c4434f.patch +replace_std_vector_with_base_observerlist_to_support_container.patch +guard_webcontents_downloadimage_against_malformed_renderer.patch +fileapi_terminate_filereaderloader_before_dispatching_onabort_event.patch +notifications_crash_if_improper_action_icons_sent_from_renderer.patch +reland_views_handle_deletion_when_toggling_fullscreen.patch +media_feeds_disable_media_feeds_and_related_features.patch +remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch +autofill_fixed_refill_of_changed_form.patch +x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch +cherry-pick-0e36d324d6ef.patch +m86-lts_longtaskdetector_remove_container_mutation_during.patch +m86-lts_reduce_memory_consumption_on.patch +cherry-pick-b77b38a3380c.patch +cherry-pick-d9556a80a790.patch +cherry-pick-910e9e40d376.patch +cherry-pick-ff0d013f60fa.patch +cherry-pick-e60cc80ff744.patch +fix_use-after-free_with_xslt_strip-space.patch +cherry-pick-3feda0244490.patch +cherry-pick-cd98d7c0dae9.patch +replace_first_of_two_waitableevents_in_creditcardaccessmanager.patch +cherry-pick-ac9dc1235e28.patch +cherry-pick-1227933.patch +cherry-pick-1231134.patch +cherry-pick-1233564.patch +cherry-pick-1234009.patch diff --git a/patches/chromium/autofill_fixed_refill_of_changed_form.patch b/patches/chromium/autofill_fixed_refill_of_changed_form.patch new file mode 100644 index 0000000000000..7d732afd22575 --- /dev/null +++ b/patches/chromium/autofill_fixed_refill_of_changed_form.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 17:22:52 +0200 +Subject: Fixed refill of changed form. + +(cherry picked from commit 533bb3adcfe3499f90e2646fc60312f303b963ac) + +Bug: 1203667 +Change-Id: I2693a024531775e0e60cc330107d77d10558f466 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2867655 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2874611 + +diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc +index 0b72ac3b8a0d3bb3294be995d2b9a4bc52b81a8f..5f92b20734d06249fc119432f45def087214709c 100644 +--- a/components/autofill/core/browser/autofill_manager.cc ++++ b/components/autofill/core/browser/autofill_manager.cc +@@ -1743,7 +1743,10 @@ void AutofillManager::FillOrPreviewDataModelForm( + form_structure->RationalizePhoneNumbersInSection(autofill_field->section); + + FormData result = form; +- DCHECK_EQ(form_structure->field_count(), form.fields.size()); ++ // TODO(crbug/1203667#c9): Skip if the form has changed in the meantime, which ++ // may happen with refills. ++ if (form_structure->field_count() != form.fields.size()) ++ return; + + if (action == AutofillDriver::FORM_DATA_ACTION_FILL && !is_refill) { + filling_contexts_map_[form_structure->GetIdentifierForRefill()] = +@@ -1787,8 +1790,10 @@ void AutofillManager::FillOrPreviewDataModelForm( + continue; + } + +- // The field order should be the same in |form_structure| and |result|. +- DCHECK(form_structure->field(i)->SameFieldAs(result.fields[i])); ++ // TODO(crbug/1203667#c9): Skip if the form has changed in the meantime, ++ // which may happen with refills. ++ if (!form_structure->field(i)->SameFieldAs(result.fields[i])) ++ continue; + + AutofillField* cached_field = form_structure->field(i); + FieldTypeGroup field_group_type = cached_field->Type().group(); diff --git a/patches/chromium/blink_wasm_eval_csp.patch b/patches/chromium/blink_wasm_eval_csp.patch new file mode 100644 index 0000000000000..ab951e6cf1628 --- /dev/null +++ b/patches/chromium/blink_wasm_eval_csp.patch @@ -0,0 +1,101 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 4 Oct 2018 14:57:02 -0700 +Subject: feat: support wasm-eval csp behind WebAssemblyCSP flag + +This is a minimal backport of +https://chromium.googlesource.com/chromium/src/+/83913676803db53648b6a47d159102a7cf1dac36 + +The tracking issue in Chromium is +https://bugs.chromium.org/p/chromium/issues/detail?id=948834 + +diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc +index 43c72ec32aef73b71f25aa3672f01ac098810432..869ce99f8800566c3ec46a0885933090608cdb95 100644 +--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc ++++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc +@@ -315,7 +315,8 @@ void ContentSecurityPolicy::CopyPluginTypesFrom( + + void ContentSecurityPolicy::DidReceiveHeaders( + const ContentSecurityPolicyResponseHeaders& headers) { +- if (headers.ShouldParseWasmEval()) ++ if (RuntimeEnabledFeatures::WebAssemblyCSPEnabled() || ++ headers.ShouldParseWasmEval()) + supports_wasm_eval_ = true; + if (!headers.ContentSecurityPolicy().IsEmpty()) { + AddAndReportPolicyFromHeaderValue(headers.ContentSecurityPolicy(), +diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc +index 5aec2cab35a7a615e2689b298f18487183c047c7..e76b7a2d99feaf0d7d0992ce79f322ab6b00fbc4 100644 +--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc ++++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc +@@ -272,8 +272,13 @@ bool CSPDirectiveList::CheckEval(SourceListDirective* directive) const { + return !directive || directive->AllowEval(); + } + ++bool SupportsWasmEval(const ContentSecurityPolicy* policy) { ++ return RuntimeEnabledFeatures::WebAssemblyCSPEnabled() || ++ policy->SupportsWasmEval(); ++} ++ + bool CSPDirectiveList::CheckWasmEval(SourceListDirective* directive) const { +- return !directive || directive->AllowWasmEval(); ++ return !directive || (SupportsWasmEval(policy_.Get()) && directive->AllowWasmEval()); + } + + bool CSPDirectiveList::IsMatchingNoncePresent(SourceListDirective* directive, +@@ -661,11 +666,15 @@ bool CSPDirectiveList::AllowWasmEval( + ContentSecurityPolicy::ExceptionStatus exception_status, + const String& content) const { + if (reporting_disposition == ReportingDisposition::kReport) { ++ String infix = SupportsWasmEval(policy_.Get()) ++ ? "neither 'wasm-eval' nor 'unsafe-eval' is" ++ : "'unsafe-eval' is not"; + return CheckWasmEvalAndReportViolation( + OperativeDirective(ContentSecurityPolicy::DirectiveType::kScriptSrc), +- "Refused to compile or instantiate WebAssembly module because " +- "'wasm-eval' is not an allowed source of script in the following " +- "Content Security Policy directive: ", ++ "Refused to compile or instantiate WebAssembly module because " + ++ infix + ++ " an allowed source of script in the following " ++ "Content Security Policy directive: ", + exception_status, content); + } + return IsReportOnly() || +diff --git a/third_party/blink/renderer/core/frame/csp/source_list_directive.cc b/third_party/blink/renderer/core/frame/csp/source_list_directive.cc +index 063158759fbfdff4be9821aa4da30c6c6a094c57..68d159c98a3ad4de11fab330a190824f06209bb4 100644 +--- a/third_party/blink/renderer/core/frame/csp/source_list_directive.cc ++++ b/third_party/blink/renderer/core/frame/csp/source_list_directive.cc +@@ -233,10 +233,15 @@ bool SourceListDirective::ParseSource( + return true; + } + +- if (policy_->SupportsWasmEval() && +- EqualIgnoringASCIICase("'wasm-eval'", token)) { +- AddSourceWasmEval(); +- return true; ++ // Temporarily behind a runtime feature ++ if (EqualIgnoringASCIICase("'wasm-eval'", token)) { ++ if (RuntimeEnabledFeatures::WebAssemblyCSPEnabled() || ++ policy_->SupportsWasmEval()) { ++ AddSourceWasmEval(); ++ return true; ++ } else { ++ return false; ++ } + } + + if (EqualIgnoringASCIICase("'strict-dynamic'", token) || +diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 +index 4bd783c23496c9b499a5f809e9a00c141bb465b2..1ee6d8863c8c226e60dc2b733fd660cf32c190d6 100644 +--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 ++++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5 +@@ -1989,6 +1989,9 @@ + { + name: "WebAppManifestDisplayOverride", + }, ++ { ++ name: "WebAssemblyCSP", ++ }, + { + name: "WebAssemblySimd", + origin_trial_feature_name: "WebAssemblySimd", diff --git a/patches/chromium/cherry-pick-012e9baf46c9.patch b/patches/chromium/cherry-pick-012e9baf46c9.patch new file mode 100644 index 0000000000000..3f66f08fbed9f --- /dev/null +++ b/patches/chromium/cherry-pick-012e9baf46c9.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jana Grill +Date: Thu, 15 Apr 2021 20:49:42 +0000 +Subject: Mojo: Remove some inappropriate DCHECKs + +There are a few places where we DCHECK conditions that cannot be +reliably asserted since they depend on untrusted inputs. These are +replaced with logic to conditionally terminate the connection to the +offending peer process. + +(cherry picked from commit a32b061fc92cc3864d036ffb8c22c12b05202589) + +Fixed: 1195333 +Change-Id: I0c6873bf55d6b0b1d0cbb3c2e5b256e1a57ff696 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2808893 +Reviewed-by: Robert Sesek +Commit-Queue: Ken Rockot +Cr-Original-Commit-Position: refs/heads/master@{#870007} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821958 +Reviewed-by: Achuith Bhandarkar +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Achuith Bhandarkar +Owners-Override: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1608} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc +index 823a4619efa0886401c0a46283a5782992b0c8a0..a297c500215a58aa5b86157e765a64671ca91188 100644 +--- a/mojo/core/node_controller.cc ++++ b/mojo/core/node_controller.cc +@@ -943,7 +943,11 @@ void NodeController::OnBrokerClientAdded(const ports::NodeName& from_node, + void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, + const ports::NodeName& broker_name, + PlatformHandle broker_channel) { +- DCHECK(!GetConfiguration().is_broker_process); ++ if (GetConfiguration().is_broker_process) { ++ // The broker should never receive this message from anyone. ++ DropPeer(from_node, nullptr); ++ return; ++ } + + // This node should already have an inviter in bootstrap mode. + ports::NodeName inviter_name; +@@ -954,8 +958,13 @@ void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, + inviter = bootstrap_inviter_channel_; + bootstrap_inviter_channel_ = nullptr; + } +- DCHECK(inviter_name == from_node); +- DCHECK(inviter); ++ ++ if (inviter_name != from_node || !inviter || ++ broker_name == ports::kInvalidNodeName) { ++ // We are not expecting this message. Assume the source is hostile. ++ DropPeer(from_node, nullptr); ++ return; ++ } + + base::queue pending_broker_clients; + std::unordered_map +@@ -966,22 +975,22 @@ void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, + std::swap(pending_broker_clients, pending_broker_clients_); + std::swap(pending_relay_messages, pending_relay_messages_); + } +- DCHECK(broker_name != ports::kInvalidNodeName); + + // It's now possible to add both the broker and the inviter as peers. + // Note that the broker and inviter may be the same node. + scoped_refptr broker; + if (broker_name == inviter_name) { +- DCHECK(!broker_channel.is_valid()); + broker = inviter; +- } else { +- DCHECK(broker_channel.is_valid()); ++ } else if (broker_channel.is_valid()) { + broker = NodeChannel::Create( + this, + ConnectionParams(PlatformChannelEndpoint(std::move(broker_channel))), + Channel::HandlePolicy::kAcceptHandles, io_task_runner_, + ProcessErrorCallback()); + AddPeer(broker_name, broker, true /* start_channel */); ++ } else { ++ DropPeer(from_node, nullptr); ++ return; + } + + AddPeer(inviter_name, inviter, false /* start_channel */); diff --git a/patches/chromium/cherry-pick-02f5ef8c88d7.patch b/patches/chromium/cherry-pick-02f5ef8c88d7.patch new file mode 100644 index 0000000000000..118a82c47e780 --- /dev/null +++ b/patches/chromium/cherry-pick-02f5ef8c88d7.patch @@ -0,0 +1,106 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Josh Karlin +Date: Wed, 14 Apr 2021 09:21:00 +0000 +Subject: Fix removal of observers in NetworkStateNotifier + +The NetworkStateNotifier has a per-thread list of observer pointers. If +one is deleted mid-iteration, what we do is replace the pointer in the +list with a 0, and add the index to the zeroed list of observers to +remove after iteration completes. Well, the removal step was broken +for cases where there were multiple elements to remove. It didn't adjust +for the fact that the indexes shifted after each removal. + +(cherry picked from commit 5d34987de6cffb8d747c5ed16e82614e9146cc0a) + +Bug: 1170148 +Change-Id: I446acaae5f8a805a58142848634a0ee8c5f90882 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2727306 +Reviewed-by: Kentaro Hara +Commit-Queue: Josh Karlin +Cr-Original-Commit-Position: refs/heads/master@{#858853} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821797 +Reviewed-by: Achuith Bhandarkar +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Jana Grill +Cr-Commit-Position: refs/branch-heads/4240@{#1602} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/third_party/blink/renderer/platform/network/network_state_notifier.cc b/third_party/blink/renderer/platform/network/network_state_notifier.cc +index 32328e881876a7445e3284fc47978daaa7407fca..c7227f634838ec8deab60cde878311162cbb5472 100644 +--- a/third_party/blink/renderer/platform/network/network_state_notifier.cc ++++ b/third_party/blink/renderer/platform/network/network_state_notifier.cc +@@ -395,8 +395,14 @@ void NetworkStateNotifier::CollectZeroedObservers( + + // If any observers were removed during the iteration they will have + // 0 values, clean them up. +- for (wtf_size_t i = 0; i < list->zeroed_observers.size(); ++i) +- list->observers.EraseAt(list->zeroed_observers[i]); ++ std::sort(list->zeroed_observers.begin(), list->zeroed_observers.end()); ++ int removed = 0; ++ for (wtf_size_t i = 0; i < list->zeroed_observers.size(); ++i) { ++ int index_to_remove = list->zeroed_observers[i] - removed; ++ DCHECK_EQ(nullptr, list->observers[index_to_remove]); ++ list->observers.EraseAt(index_to_remove); ++ removed += 1; ++ } + + list->zeroed_observers.clear(); + +diff --git a/third_party/blink/renderer/platform/network/network_state_notifier_test.cc b/third_party/blink/renderer/platform/network/network_state_notifier_test.cc +index eb2bd791529df4339ebd8159700769fcd06d795f..f7c359235a87adc231c00eb252dc24a7d95065f8 100644 +--- a/third_party/blink/renderer/platform/network/network_state_notifier_test.cc ++++ b/third_party/blink/renderer/platform/network/network_state_notifier_test.cc +@@ -528,6 +528,53 @@ TEST_F(NetworkStateNotifierTest, RemoveFutureObserverWhileNotifying) { + kUnknownThroughputMbps, SaveData::kOff)); + } + ++// It should be safe to remove multiple observers in one iteration. ++TEST_F(NetworkStateNotifierTest, RemoveMultipleObserversWhileNotifying) { ++ StateObserver observer1, observer2, observer3; ++ std::unique_ptr handle1 = ++ notifier_.AddConnectionObserver(&observer1, GetTaskRunner()); ++ std::unique_ptr handle2 = ++ notifier_.AddConnectionObserver(&observer2, GetTaskRunner()); ++ std::unique_ptr handle3 = ++ notifier_.AddConnectionObserver(&observer3, GetTaskRunner()); ++ observer1.RemoveObserverOnNotification(std::move(handle1)); ++ observer3.RemoveObserverOnNotification(std::move(handle3)); ++ ++ // Running the first time should delete observers 1 and 3. ++ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, ++ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff); ++ EXPECT_TRUE(VerifyObservations( ++ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt, ++ kUnknownThroughputMbps, SaveData::kOff)); ++ EXPECT_TRUE(VerifyObservations( ++ observer2, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt, ++ kUnknownThroughputMbps, SaveData::kOff)); ++ EXPECT_TRUE(VerifyObservations( ++ observer3, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt, ++ kUnknownThroughputMbps, SaveData::kOff)); ++ ++ // Run again and only observer 2 should have been updated. ++ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, ++ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff); ++ EXPECT_TRUE(VerifyObservations( ++ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt, ++ kUnknownThroughputMbps, SaveData::kOff)); ++ EXPECT_TRUE(VerifyObservations( ++ observer2, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt, ++ kUnknownThroughputMbps, SaveData::kOff)); ++ EXPECT_TRUE(VerifyObservations( ++ observer3, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps, ++ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt, ++ kUnknownThroughputMbps, SaveData::kOff)); ++} ++ + TEST_F(NetworkStateNotifierTest, MultipleContextsAddObserver) { + StateObserver observer1, observer2; + std::unique_ptr handle1 = diff --git a/patches/chromium/cherry-pick-0e36d324d6ef.patch b/patches/chromium/cherry-pick-0e36d324d6ef.patch new file mode 100644 index 0000000000000..800e88803009a --- /dev/null +++ b/patches/chromium/cherry-pick-0e36d324d6ef.patch @@ -0,0 +1,101 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Asami Doi +Date: Thu, 10 Jun 2021 07:07:44 +0000 +Subject: BFCache: remove a controllee stored in `bfcached_controllee_map_` + +This CL fixes the UAF that happens with the following case: +Let's assume we have 2 service workers (sw1.js and sw2.js) are +registered in the same page. When the second service worker (sw2.js) is +registered, ServiceWorkerContainerHost::UpdateController() is called +and the previous SWVersion (sw1.js) removes a controllee from +`controllee_map_`. If BackForwardCache is enabled, a controllee is +stored in `bfcached_controllee_map_` instead and the controllee will +not be removed in ServiceWorkerContainerHost::UpdateController(). +When ServiceWorkerContainerHost::UpdateController() is called and +keep a controllee in `bfcached_controllee_map_`, and a page navigates to +a different page (evicts BFCache), use-after-free (UAF) happens. + +This CL updates ServiceWorkerContainerHost::UpdateController() +to remove a controllee from `bfcached_controllee_map_` if it exists. + +(cherry picked from commit a2414a05a486ca0ad18ba4caf78e883a668a0555) + +(cherry picked from commit 7cd7f6741fc4491c2f7ef21052a370ee23887e37) + +Bug: 1212618 +Change-Id: I13e023e6d273268a08ea9276a056f7f5acba39cd +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2919020 +Commit-Queue: Asami Doi +Reviewed-by: Matt Falkenhagen +Cr-Original-Original-Commit-Position: refs/heads/master@{#887109} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2929401 +Reviewed-by: Krishna Govind +Reviewed-by: Ben Mason +Reviewed-by: Prudhvi Kumar Bommana +Commit-Queue: Krishna Govind +Owners-Override: Krishna Govind +Cr-Original-Commit-Position: refs/branch-heads/4472@{#1375} +Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2948660 +Owners-Override: Victor-Gabriel Savu +Reviewed-by: Artem Sumaneev +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/4240@{#1663} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc +index 5cb7b2da2c9a4c857c756589ba2c9eaf0dc2b67f..b91a0ec16483a38574ed7d63fdeb9133877d17ae 100644 +--- a/content/browser/service_worker/service_worker_container_host.cc ++++ b/content/browser/service_worker/service_worker_container_host.cc +@@ -149,7 +149,7 @@ ServiceWorkerContainerHost::~ServiceWorkerContainerHost() { + FrameTreeNodeIdRegistry::GetInstance()->Remove(fetch_request_window_id_); + + if (IsContainerForClient() && controller_) +- controller_->OnControlleeDestroyed(client_uuid()); ++ controller_->Uncontrol(client_uuid()); + + // Remove |this| as an observer of ServiceWorkerRegistrations. + // TODO(falken): Use ScopedObserver instead of this explicit call. +@@ -1236,7 +1236,7 @@ void ServiceWorkerContainerHost::UpdateController( + } + } + if (previous_version) +- previous_version->RemoveControllee(client_uuid()); ++ previous_version->Uncontrol(client_uuid()); + + // SetController message should be sent only for clients. + DCHECK(IsContainerForClient()); +diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc +index d360afea47c40f8c533ff52f46417371bd3a1ad3..7d057af1b760b67f061cbcb7436c1bfb651ded68 100644 +--- a/content/browser/service_worker/service_worker_version.cc ++++ b/content/browser/service_worker/service_worker_version.cc +@@ -880,8 +880,7 @@ void ServiceWorkerVersion::RemoveControlleeFromBackForwardCacheMap( + bfcached_controllee_map_.erase(client_uuid); + } + +-void ServiceWorkerVersion::OnControlleeDestroyed( +- const std::string& client_uuid) { ++void ServiceWorkerVersion::Uncontrol(const std::string& client_uuid) { + if (!IsBackForwardCacheEnabled()) { + RemoveControllee(client_uuid); + } else { +diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h +index b7c3b5cc809c42111b0bf5bddff574197c9a77a8..90431fe256dc67d51ed1c01ec8202a335af78032 100644 +--- a/content/browser/service_worker/service_worker_version.h ++++ b/content/browser/service_worker/service_worker_version.h +@@ -390,9 +390,12 @@ class CONTENT_EXPORT ServiceWorkerVersion + void RestoreControlleeFromBackForwardCacheMap(const std::string& client_uuid); + // Called when a back-forward cached controllee is evicted or destroyed. + void RemoveControlleeFromBackForwardCacheMap(const std::string& client_uuid); +- // Called when a controllee is destroyed. Remove controllee from whichever +- // map it belongs to, or do nothing when it is already removed. +- void OnControlleeDestroyed(const std::string& client_uuid); ++ // Called when this version should no longer be the controller of this client. ++ // Called when the controllee is destroyed or it changes controller. Removes ++ // controllee from whichever map it belongs to, or do nothing when it is ++ // already removed. This function is different from RemoveController(), which ++ // can only be called if the controllee is not in the back-forward cache map. ++ void Uncontrol(const std::string& client_uuid); + + // Returns true if this version has a controllee. + // Note regarding BackForwardCache: diff --git a/patches/chromium/cherry-pick-1028ffc9bd83.patch b/patches/chromium/cherry-pick-1028ffc9bd83.patch new file mode 100644 index 0000000000000..52d63a7cf32b8 --- /dev/null +++ b/patches/chromium/cherry-pick-1028ffc9bd83.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bill Budge +Date: Tue, 20 Apr 2021 15:22:33 +0000 +Subject: M86-LTS: [GeneratedCodeCache] Copy large data before hashing and + writing + +- Makes a copy before hashing and writing large code entries. + +(cherry picked from commit cea0cb8eee9900308d9b43661e9faca449086940) + +Bug: chromium:1194046 +Change-Id: Id5a6e6d3a04c83cfed2f18db53587d654d642fc0 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2807255 +Reviewed-by: Nasko Oskov +Reviewed-by: Mythri Alle +Commit-Queue: Bill Budge +Cr-Original-Commit-Position: refs/heads/master@{#870064} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2838077 +Reviewed-by: Bill Budge +Commit-Queue: Achuith Bhandarkar +Owners-Override: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1612} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc +index 69d1f926f3e8b1d7eb23565ebebbebaad4fc392d..f4d084481a3f3faec5906fa27c16feab014f8cff 100644 +--- a/content/browser/code_cache/generated_code_cache.cc ++++ b/content/browser/code_cache/generated_code_cache.cc +@@ -384,9 +384,18 @@ void GeneratedCodeCache::WriteEntry(const GURL& url, + // [stream1] + // [stream0 (checksum key entry)] + // [stream1 (checksum key entry)] data ++ ++ // Make a copy of the data before hashing. A compromised renderer could ++ // change shared memory before we can compute the hash and write the data. ++ // TODO(1135729) Eliminate this copy when the shared memory can't be written ++ // by the sender. ++ mojo_base::BigBuffer copy({data.data(), data.size()}); ++ if (copy.size() != data.size()) ++ return; ++ data = mojo_base::BigBuffer(); // Release the old buffer. + uint8_t result[crypto::kSHA256Length]; + crypto::SHA256HashString( +- base::StringPiece(reinterpret_cast(data.data()), data.size()), ++ base::StringPiece(reinterpret_cast(copy.data()), copy.size()), + result, base::size(result)); + std::string checksum_key = base::HexEncode(result, base::size(result)); + small_buffer = base::MakeRefCounted( +@@ -401,7 +410,7 @@ void GeneratedCodeCache::WriteEntry(const GURL& url, + // Issue another write operation for the code, with the checksum as the key + // and nothing in the header. + auto small_buffer2 = base::MakeRefCounted(0); +- auto large_buffer2 = base::MakeRefCounted(std::move(data)); ++ auto large_buffer2 = base::MakeRefCounted(std::move(copy)); + auto op2 = std::make_unique(Operation::kWriteWithSHAKey, + checksum_key, small_buffer2, + large_buffer2); diff --git a/patches/chromium/cherry-pick-1227933.patch b/patches/chromium/cherry-pick-1227933.patch new file mode 100644 index 0000000000000..abbdf26d4e598 --- /dev/null +++ b/patches/chromium/cherry-pick-1227933.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Koji Ishii +Date: Mon, 26 Jul 2021 07:09:18 +0000 +Subject: Fix nested inline box fragmentation + +This patch fixes when nested inline boxes are fragmented in a +line due to bidi reordering. + +Before this change, the fragmented boxes are appended to the +end of |box_data_list_|. Then when |NGInlineLayoutStateStack:: +CreateBoxFragments| creates inline boxes in the ascending +order of |box_data_list_|, it failed to add the fragmented +boxes into their parent inline boxes. + +This is critical for out-of-flow positioned objects whose +containing block is an inline box, because they expect to be +propagated through all ancestor inline boxes. + +|UpdateBoxDataFragmentRange| is a little tricky by appending +to a vector it is iterating. Changing it to insert to the +correct position makes the function even trickier. This patch +changes it to add fragmented boxes to a separate vector, and +let later process |UpdateFragmentedBoxDataEdges| to merge the +vector to |box_data_list_|. + +(cherry picked from commit 9c8a39c14a9c80556468593cddf436f5047a16ce) + +Bug: 1227933, 1229999 +Change-Id: I7edcd209e1fdac06bab01b16d660383e7e9c37bd +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3038308 +Commit-Queue: Koji Ishii +Reviewed-by: Yoshifumi Inoue +Cr-Original-Commit-Position: refs/heads/master@{#903356} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3053212 +Commit-Queue: Rubber Stamper +Bot-Commit: Rubber Stamper +Auto-Submit: Koji Ishii +Cr-Commit-Position: refs/branch-heads/4577@{#145} +Cr-Branched-From: 761ddde228655e313424edec06497d0c56b0f3c4-refs/heads/master@{#902210} + +diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc +index c61298842eaa308b7ae863d6e1cf5457a8091dd2..f6fb0f03c3ec63c0c2de2b9501534108dea85422 100644 +--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc ++++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc +@@ -4,6 +4,7 @@ + + #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" + ++#include "base/containers/adapters.h" + #include "third_party/blink/renderer/core/layout/geometry/logical_offset.h" + #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" + #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" +@@ -389,13 +390,14 @@ void NGInlineLayoutStateStack::UpdateAfterReorder( + box_data.fragment_start = box_data.fragment_end = 0; + + // Scan children and update start/end from their box_data_index. +- unsigned box_count = box_data_list_.size(); ++ Vector fragmented_boxes; + for (unsigned index = 0; index < line_box->size();) +- index = UpdateBoxDataFragmentRange(line_box, index); ++ index = UpdateBoxDataFragmentRange(line_box, index, &fragmented_boxes); + +- // If any inline fragmentation due to BiDi reorder, adjust box edges. +- if (box_count != box_data_list_.size()) +- UpdateFragmentedBoxDataEdges(); ++ // If any inline fragmentation occurred due to BiDi reorder, append them and ++ // adjust box edges. ++ if (UNLIKELY(!fragmented_boxes.IsEmpty())) ++ UpdateFragmentedBoxDataEdges(&fragmented_boxes); + + #if DCHECK_IS_ON() + // Check all BoxData have ranges. +@@ -412,7 +414,8 @@ void NGInlineLayoutStateStack::UpdateAfterReorder( + + unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange( + NGLogicalLineItems* line_box, +- unsigned index) { ++ unsigned index, ++ Vector* fragmented_boxes) { + // Find the first line box item that should create a box fragment. + for (; index < line_box->size(); index++) { + NGLogicalLineItem* start = &(*line_box)[index]; +@@ -440,7 +443,7 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange( + // It also changes other BoxData, but not the one we're dealing with here + // because the update is limited only when its |box_data_index| is lower. + while (end->box_data_index && end->box_data_index < box_data_index) { +- UpdateBoxDataFragmentRange(line_box, index); ++ UpdateBoxDataFragmentRange(line_box, index, fragmented_boxes); + } + + if (box_data_index != end->box_data_index) +@@ -455,14 +458,9 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange( + } else { + // This box is fragmented by BiDi reordering. Add a new BoxData for the + // fragmented range. +- box_data_list_[box_data_index - 1].fragmented_box_data_index = +- box_data_list_.size(); +- // Do not use `emplace_back()` here because adding to |box_data_list_| may +- // reallocate the buffer, but the `BoxData` ctor must run before the +- // reallocation. Create a new instance and |push_back()| instead. +- BoxData fragmented_box_data(box_data_list_[box_data_index - 1], +- start_index, index); +- box_data_list_.push_back(fragmented_box_data); ++ BoxData& fragmented_box = fragmented_boxes->emplace_back( ++ box_data_list_[box_data_index - 1], start_index, index); ++ fragmented_box.fragmented_box_data_index = box_data_index; + } + // If this box has parent boxes, we need to process it again. + if (box_data_list_[box_data_index - 1].parent_box_data_index) +@@ -472,7 +470,43 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange( + return index; + } + +-void NGInlineLayoutStateStack::UpdateFragmentedBoxDataEdges() { ++void NGInlineLayoutStateStack::UpdateFragmentedBoxDataEdges( ++ Vector* fragmented_boxes) { ++ DCHECK(!fragmented_boxes->IsEmpty()); ++ // Append in the descending order of |fragmented_box_data_index| because the ++ // indices will change as boxes are inserted into |box_data_list_|. ++ std::sort(fragmented_boxes->begin(), fragmented_boxes->end(), ++ [](const BoxData& a, const BoxData& b) { ++ if (a.fragmented_box_data_index != b.fragmented_box_data_index) { ++ return a.fragmented_box_data_index < ++ b.fragmented_box_data_index; ++ } ++ DCHECK_NE(a.fragment_start, b.fragment_start); ++ return a.fragment_start < b.fragment_start; ++ }); ++ for (BoxData& fragmented_box : base::Reversed(*fragmented_boxes)) { ++ // Insert the fragmented box to right after the box it was fragmented from. ++ // The order in the |box_data_list_| is critical when propagating child ++ // fragment data such as OOF to ancestors. ++ const unsigned insert_at = fragmented_box.fragmented_box_data_index; ++ DCHECK_GT(insert_at, 0u); ++ fragmented_box.fragmented_box_data_index = 0; ++ box_data_list_.insert(insert_at, fragmented_box); ++ ++ // Adjust box data indices by the insertion. ++ for (BoxData& box_data : box_data_list_) { ++ if (box_data.fragmented_box_data_index >= insert_at) ++ ++box_data.fragmented_box_data_index; ++ } ++ ++ // Set the index of the last fragment to the original box. This is needed to ++ // update fragment edges. ++ const unsigned fragmented_from = insert_at - 1; ++ if (!box_data_list_[fragmented_from].fragmented_box_data_index) ++ box_data_list_[fragmented_from].fragmented_box_data_index = insert_at; ++ } ++ ++ // Move the line-right edge to the last fragment. + for (BoxData& box_data : box_data_list_) { + if (box_data.fragmented_box_data_index) + box_data.UpdateFragmentEdges(box_data_list_); +diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h +index 36aa27914a6598671c3f9266f323ab03847db5a6..7fb13383a21116ce1dd17a327ad2cc911c3f7c01 100644 +--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h ++++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h +@@ -157,17 +157,6 @@ class CORE_EXPORT NGInlineLayoutStateStack { + // reordering. + void UpdateAfterReorder(NGLogicalLineItems*); + +- // Update start/end of the first BoxData found at |index|. +- // +- // If inline fragmentation is found, a new BoxData is added. +- // +- // Returns the index to process next. It should be given to the next call to +- // this function. +- unsigned UpdateBoxDataFragmentRange(NGLogicalLineItems*, unsigned index); +- +- // Update edges of inline fragmented boxes. +- void UpdateFragmentedBoxDataEdges(); +- + // Compute inline positions of fragments and boxes. + LayoutUnit ComputeInlinePositions(NGLogicalLineItems*, LayoutUnit position); + +@@ -260,6 +249,19 @@ class CORE_EXPORT NGInlineLayoutStateStack { + scoped_refptr CreateBoxFragment(NGLogicalLineItems*); + }; + ++ // Update start/end of the first BoxData found at |index|. ++ // ++ // If inline fragmentation is found, a new BoxData is added. ++ // ++ // Returns the index to process next. It should be given to the next call to ++ // this function. ++ unsigned UpdateBoxDataFragmentRange(NGLogicalLineItems*, ++ unsigned index, ++ Vector* fragmented_boxes); ++ ++ // Update edges of inline fragmented boxes. ++ void UpdateFragmentedBoxDataEdges(Vector* fragmented_boxes); ++ + Vector stack_; + Vector box_data_list_; + +diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/text/crashtests/bidi-inline-fragment-oof-crash.html b/third_party/blink/web_tests/external/wpt/css/CSS2/text/crashtests/bidi-inline-fragment-oof-crash.html +new file mode 100644 +index 0000000000000000000000000000000000000000..b701d2b5688ace54aa99530c12fa8143f1e6a508 +--- /dev/null ++++ b/third_party/blink/web_tests/external/wpt/css/CSS2/text/crashtests/bidi-inline-fragment-oof-crash.html +@@ -0,0 +1,13 @@ ++ ++ ++ ++
++ ++ ++
++ ++
++
++
++
++
diff --git a/patches/chromium/cherry-pick-1231134.patch b/patches/chromium/cherry-pick-1231134.patch new file mode 100644 index 0000000000000..2c95e166f67c4 --- /dev/null +++ b/patches/chromium/cherry-pick-1231134.patch @@ -0,0 +1,163 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Lei Zhang +Date: Tue, 10 Aug 2021 21:38:36 +0000 +Subject: Do more class validity checks in PrintViewManagerBase. + +PrintViewManagerBase runs a nested loop. In some situations, +PrintViewManagerBase and related classes like PrintViewManager and +PrintPreviewHandler can get deleted while the nested loop is running. +When this happens, the nested loop exists to a PrintViewManagerBase +that is no longer valid. + +Use base::WeakPtrs liberally to check for this condition and exit +safely. + +(cherry picked from commit a2cb1fb333d2faacb2fe1380f8d2621b5ee6af7e) + +Bug: 1231134 +Change-Id: I21ec131574331ce973d22594c11e70088147e149 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3057880 +Reviewed-by: Alan Screen +Commit-Queue: Lei Zhang +Cr-Original-Commit-Position: refs/heads/master@{#906269} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3086110 +Bot-Commit: Rubber Stamper +Cr-Commit-Position: refs/branch-heads/4515@{#2024} +Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287} + +diff --git a/chrome/browser/printing/print_view_manager.cc b/chrome/browser/printing/print_view_manager.cc +index b7a2fa5a10b2461e83d78fca63e3165d5eb1350f..9f11d5053ff19e4ccfdbe54e7f59e311da3c6e0a 100644 +--- a/chrome/browser/printing/print_view_manager.cc ++++ b/chrome/browser/printing/print_view_manager.cc +@@ -91,7 +91,11 @@ bool PrintViewManager::PrintForSystemDialogNow( + DCHECK(!on_print_dialog_shown_callback_); + on_print_dialog_shown_callback_ = std::move(dialog_shown_callback); + is_switching_to_system_dialog_ = true; ++ ++ auto weak_this = weak_factory_.GetWeakPtr(); + DisconnectFromCurrentPrintJob(); ++ if (!weak_this) ++ return false; + + // Don't print / print preview crashed tabs. + if (IsCrashed()) +diff --git a/chrome/browser/printing/print_view_manager.h b/chrome/browser/printing/print_view_manager.h +index 8770b7bc60cb28d70cde3af8303939bf7ac27892..16c13724c397fade0eb64f9b5ec26c7bf51570ac 100644 +--- a/chrome/browser/printing/print_view_manager.h ++++ b/chrome/browser/printing/print_view_manager.h +@@ -130,6 +130,11 @@ class PrintViewManager : public PrintViewManagerBase, + + WEB_CONTENTS_USER_DATA_KEY_DECL(); + ++ // Keep this last so that all weak pointers will be invalidated at the ++ // beginning of destruction. Note that PrintViewManagerBase has its own ++ // base::WeakPtrFactory as well, but PrintViewManager should use this one. ++ base::WeakPtrFactory weak_factory_{this}; ++ + DISALLOW_COPY_AND_ASSIGN(PrintViewManager); + }; + +diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc +index 75908fc9978db020d752e7fc7211d1a075c11ffb..b896f69f3ec272001c5c6c0071bc23a3c9134d70 100644 +--- a/chrome/browser/printing/print_view_manager_base.cc ++++ b/chrome/browser/printing/print_view_manager_base.cc +@@ -192,7 +192,10 @@ bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh, + bool silent, + base::Value settings, + CompletionCallback callback) { ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); + DisconnectFromCurrentPrintJob(); ++ if (!weak_this) ++ return false; + + // Don't print / print preview crashed tabs. + if (IsCrashed()) +@@ -625,6 +628,8 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() { + // or in DidPrintDocument(). The check is done in + // ShouldQuitFromInnerMessageLoop(). + // BLOCKS until all the pages are received. (Need to enable recursive task) ++ // WARNING: Do not do any work after RunInnerMessageLoop() returns, as `this` ++ // may have gone away. + if (!RunInnerMessageLoop()) { + // This function is always called from DisconnectFromCurrentPrintJob() so we + // know that the job will be stopped/canceled in any case. +@@ -651,8 +656,11 @@ bool PrintViewManagerBase::CreateNewPrintJob( + DCHECK(query); + + if (callback_.is_null()) { ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); + // Disconnect the current |print_job_| only when calling window.print() + DisconnectFromCurrentPrintJob(); ++ if (!weak_this) ++ return false; + } + + // We can't print if there is no renderer. +@@ -681,7 +689,10 @@ bool PrintViewManagerBase::CreateNewPrintJob( + void PrintViewManagerBase::DisconnectFromCurrentPrintJob() { + // Make sure all the necessary rendered page are done. Don't bother with the + // return value. ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); + bool result = RenderAllMissingPagesNow(); ++ if (!weak_this) ++ return; + + // Verify that assertion. + if (print_job_ && print_job_->document() && +@@ -763,7 +774,10 @@ bool PrintViewManagerBase::RunInnerMessageLoop() { + + quit_inner_loop_ = run_loop.QuitClosure(); + ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); + run_loop.Run(); ++ if (!weak_this) ++ return false; + + // If the inner-loop quit closure is still set then we timed out. + bool success = !quit_inner_loop_; +diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h +index 095d9dfcc3ee14b646b63c29e406434c1623704a..ed4c0ec98bb84753828d0649799077edc9796317 100644 +--- a/chrome/browser/printing/print_view_manager_base.h ++++ b/chrome/browser/printing/print_view_manager_base.h +@@ -115,6 +115,8 @@ class PrintViewManagerBase : public content::NotificationObserver, + + // Makes sure the current print_job_ has all its data before continuing, and + // disconnect from it. ++ // WARNING: `this` may not be alive after DisconnectFromCurrentPrintJob() ++ // returns. + void DisconnectFromCurrentPrintJob(); + + // Manages the low-level talk to the printer. +@@ -168,6 +170,7 @@ class PrintViewManagerBase : public content::NotificationObserver, + // Requests the RenderView to render all the missing pages for the print job. + // No-op if no print job is pending. Returns true if at least one page has + // been requested to the renderer. ++ // WARNING: `this` may not be alive after RenderAllMissingPagesNow() returns. + bool RenderAllMissingPagesNow(); + + // Checks that synchronization is correct with |print_job_| based on |cookie|. +@@ -201,6 +204,7 @@ class PrintViewManagerBase : public content::NotificationObserver, + // while the blocking inner message loop is running. This is useful in cases + // where the RenderView is about to be destroyed while a printing job isn't + // finished. ++ // WARNING: `this` may not be alive after RunInnerMessageLoop() returns. + bool RunInnerMessageLoop(); + + // In the case of Scripted Printing, where the renderer is controlling the +diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +index 2cea700c82790f696775ece3080662232326aabc..ab89b180e6f9586d6280d884f126fb46b278be04 100644 +--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc ++++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +@@ -864,9 +864,12 @@ void PrintPreviewHandler::HandleShowSystemDialog( + if (!initiator) + return; + ++ auto weak_this = weak_factory_.GetWeakPtr(); + auto* print_view_manager = PrintViewManager::FromWebContents(initiator); + print_view_manager->PrintForSystemDialogNow(base::BindOnce( + &PrintPreviewHandler::ClosePreviewDialog, weak_factory_.GetWeakPtr())); ++ if (!weak_this) ++ return; + + // Cancel the pending preview request if exists. + print_preview_ui()->OnCancelPendingPreviewRequest(); diff --git a/patches/chromium/cherry-pick-1233564.patch b/patches/chromium/cherry-pick-1233564.patch new file mode 100644 index 0000000000000..9803f407050ea --- /dev/null +++ b/patches/chromium/cherry-pick-1233564.patch @@ -0,0 +1,77 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Hongchan Choi +Date: Mon, 9 Aug 2021 18:43:22 +0000 +Subject: Protect HRTF database loader thread from access by different threads + +This patch add a new mutex locker around the HRTF database loader +thread to ensure the safe exclusive access of the loader thread +and the HRTF database. + +(cherry picked from commit 6811e850ee10847da16c4d5fdc0f845494586b65) + +Bug: 1233564 +Change-Id: Ie12b99ffe520d3747e34af387a37637a10aab38a +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3068260 +Auto-Submit: Hongchan Choi +Commit-Queue: Kentaro Hara +Reviewed-by: Kentaro Hara +Cr-Original-Commit-Position: refs/heads/master@{#908269} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3082114 +Reviewed-by: Chris Mumford +Commit-Queue: Hongchan Choi +Cr-Commit-Position: refs/branch-heads/4577@{#601} +Cr-Branched-From: 761ddde228655e313424edec06497d0c56b0f3c4-refs/heads/master@{#902210} + +diff --git a/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc b/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc +index 034ded03d11fa42f0d0f62c6a91f6e20ee5f93e1..01cb98a1116fe1eb6a13ff6345b6bdf4e136badc 100644 +--- a/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc ++++ b/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc +@@ -86,6 +86,8 @@ void HRTFDatabaseLoader::LoadTask() { + void HRTFDatabaseLoader::LoadAsynchronously() { + DCHECK(IsMainThread()); + ++ MutexLocker locker(lock_); ++ + // m_hrtfDatabase and m_thread should both be unset because this should be a + // new HRTFDatabaseLoader object that was just created by + // createAndLoadAsynchronouslyIfNecessary and because we haven't started +@@ -122,6 +124,10 @@ void HRTFDatabaseLoader::CleanupTask(base::WaitableEvent* sync) { + } + + void HRTFDatabaseLoader::WaitForLoaderThreadCompletion() { ++ // We can lock this because this is called from either the main thread or ++ // the offline audio rendering thread. ++ MutexLocker locker(lock_); ++ + if (!thread_) + return; + +diff --git a/third_party/blink/renderer/platform/audio/hrtf_database_loader.h b/third_party/blink/renderer/platform/audio/hrtf_database_loader.h +index 3ce476fa68e066d6faf40011e94203f0fb778e71..a94997b4f7e06f96018187967faa524d4acfd5f6 100644 +--- a/third_party/blink/renderer/platform/audio/hrtf_database_loader.h ++++ b/third_party/blink/renderer/platform/audio/hrtf_database_loader.h +@@ -64,8 +64,8 @@ class PLATFORM_EXPORT HRTFDatabaseLoader final + // must be called from the audio thread. + bool IsLoaded() { return Database(); } + +- // waitForLoaderThreadCompletion() may be called more than once and is +- // thread-safe. ++ // May be called from both main and audio thread, and also can be called more ++ // than once. + void WaitForLoaderThreadCompletion(); + + // Returns the database or nullptr if the database doesn't yet exist. Must +@@ -87,11 +87,10 @@ class PLATFORM_EXPORT HRTFDatabaseLoader final + void LoadTask(); + void CleanupTask(base::WaitableEvent*); + +- // Holding a m_lock is required when accessing m_hrtfDatabase since we access +- // it from multiple threads. ++ // |lock_| MUST be held when accessing |hrtf_database_| or |thread_| because ++ // it can be accessed by multiple threads (e.g multiple AudioContexts). + Mutex lock_; + std::unique_ptr hrtf_database_; +- + std::unique_ptr thread_; + + float database_sample_rate_; diff --git a/patches/chromium/cherry-pick-1234009.patch b/patches/chromium/cherry-pick-1234009.patch new file mode 100644 index 0000000000000..e475f309c142a --- /dev/null +++ b/patches/chromium/cherry-pick-1234009.patch @@ -0,0 +1,138 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sam McNally +Date: Tue, 10 Aug 2021 02:14:43 +0000 +Subject: Defer looking up the WebContents for the directory confirmation + dialog. + +Look up the WebContents to use for the sensitive directory confirmation +dialog immediately before it's used instead of before performing some +blocking file access to determine whether it's necessary. + +(cherry picked from commit 18236a0db8341302120c60781ae3129e94fbaf1c) + +Bug: 1234009 +Change-Id: I5e00c7fa199b3da522e1fdb73242891d7f5f7423 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3063743 +Reviewed-by: Alex Danilo +Reviewed-by: Ben Wells +Commit-Queue: Sam McNally +Cr-Original-Commit-Position: refs/heads/master@{#907467} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3083204 +Bot-Commit: Rubber Stamper +Cr-Commit-Position: refs/branch-heads/4577@{#648} +Cr-Branched-From: 761ddde228655e313424edec06497d0c56b0f3c4-refs/heads/master@{#902210} + +diff --git a/extensions/browser/api/file_system/file_system_api.cc b/extensions/browser/api/file_system/file_system_api.cc +index a128893387beac06fb1256416ae234af251378db..870298116be17a2bb0874f8b32c8926ec19ed0d4 100644 +--- a/extensions/browser/api/file_system/file_system_api.cc ++++ b/extensions/browser/api/file_system/file_system_api.cc +@@ -196,6 +196,9 @@ void PassFileInfoToUIThread(FileInfoOptCallback callback, + content::WebContents* GetWebContentsForRenderFrameHost( + content::BrowserContext* browser_context, + content::RenderFrameHost* render_frame_host) { ++ if (!render_frame_host) ++ return nullptr; ++ + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + // Check if there is an app window associated with the web contents; if not, +@@ -508,15 +511,6 @@ void FileSystemChooseEntryFunction::FilesSelected( + } + + if (is_directory_) { +- // Get the WebContents for the app window to be the parent window of the +- // confirmation dialog if necessary. +- content::WebContents* const web_contents = GetWebContentsForRenderFrameHost( +- browser_context(), render_frame_host()); +- if (!web_contents) { +- Respond(Error(kInvalidCallingPage)); +- return; +- } +- + DCHECK_EQ(paths.size(), 1u); + bool non_native_path = false; + #if defined(OS_CHROMEOS) +@@ -530,7 +524,7 @@ void FileSystemChooseEntryFunction::FilesSelected( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, + base::BindOnce( + &FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync, this, +- non_native_path, paths, web_contents)); ++ non_native_path, paths)); + return; + } + +@@ -543,8 +537,7 @@ void FileSystemChooseEntryFunction::FileSelectionCanceled() { + + void FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync( + bool non_native_path, +- const std::vector& paths, +- content::WebContents* web_contents) { ++ const std::vector& paths) { + const base::FilePath check_path = + non_native_path ? paths[0] : base::MakeAbsoluteFilePath(paths[0]); + if (check_path.empty()) { +@@ -576,7 +569,7 @@ void FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync( + FROM_HERE, + base::BindOnce( + &FileSystemChooseEntryFunction::ConfirmSensitiveDirectoryAccess, +- this, paths, web_contents)); ++ this, paths)); + return; + } + +@@ -587,8 +580,7 @@ void FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync( + } + + void FileSystemChooseEntryFunction::ConfirmSensitiveDirectoryAccess( +- const std::vector& paths, +- content::WebContents* web_contents) { ++ const std::vector& paths) { + if (ExtensionsBrowserClient::Get()->IsShuttingDown()) { + FileSelectionCanceled(); + return; +@@ -601,6 +593,13 @@ void FileSystemChooseEntryFunction::ConfirmSensitiveDirectoryAccess( + return; + } + ++ content::WebContents* const web_contents = ++ GetWebContentsForRenderFrameHost(browser_context(), render_frame_host()); ++ if (!web_contents) { ++ Respond(Error(kInvalidCallingPage)); ++ return; ++ } ++ + delegate->ConfirmSensitiveDirectoryAccess( + app_file_handler_util::HasFileSystemWritePermission(extension_.get()), + base::UTF8ToUTF16(extension_->name()), web_contents, +diff --git a/extensions/browser/api/file_system/file_system_api.h b/extensions/browser/api/file_system/file_system_api.h +index 2a95c4d89fd2746aec0792f231bd20eac1b82d63..95ae48b3338ca90c25c098cd23655a84236aa6e6 100644 +--- a/extensions/browser/api/file_system/file_system_api.h ++++ b/extensions/browser/api/file_system/file_system_api.h +@@ -18,10 +18,6 @@ + #include "extensions/common/api/file_system.h" + #include "ui/shell_dialogs/select_file_dialog.h" + +-namespace content { +-class WebContents; +-} // namespace content +- + namespace extensions { + class ExtensionPrefs; + +@@ -167,13 +163,12 @@ class FileSystemChooseEntryFunction : public FileSystemEntryFunction { + // directory. If so, calls ConfirmSensitiveDirectoryAccess. Otherwise, calls + // OnDirectoryAccessConfirmed. + void ConfirmDirectoryAccessAsync(bool non_native_path, +- const std::vector& paths, +- content::WebContents* web_contents); ++ const std::vector& paths); + + // Shows a dialog to confirm whether the user wants to open the directory. + // Calls OnDirectoryAccessConfirmed or FileSelectionCanceled. +- void ConfirmSensitiveDirectoryAccess(const std::vector& paths, +- content::WebContents* web_contents); ++ void ConfirmSensitiveDirectoryAccess( ++ const std::vector& paths); + + void OnDirectoryAccessConfirmed(const std::vector& paths); + diff --git a/patches/chromium/cherry-pick-162efe98330e.patch b/patches/chromium/cherry-pick-162efe98330e.patch new file mode 100644 index 0000000000000..40105c8d8d16b --- /dev/null +++ b/patches/chromium/cherry-pick-162efe98330e.patch @@ -0,0 +1,177 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 4 Oct 2018 14:57:02 -0700 +Subject: fix: backport 162efe98330e to fix crash on m1 + +Backport of +https://chromium.googlesource.com/chromium/src/+/162efe98330e8d243d472889db81dd78b691156f + +diff --git a/base/message_loop/message_pump_kqueue.cc b/base/message_loop/message_pump_kqueue.cc +index 9a0be64d50a8460bd80615f83e6d41d71c65e586..4a01b36db138a43c39196206b8dd015c9700c8c3 100644 +--- a/base/message_loop/message_pump_kqueue.cc ++++ b/base/message_loop/message_pump_kqueue.cc +@@ -12,6 +12,7 @@ + #include "base/mac/mach_logging.h" + #include "base/mac/scoped_nsautorelease_pool.h" + #include "base/posix/eintr_wrapper.h" ++#include "base/time/time_override.h" + + namespace base { + +@@ -21,10 +22,22 @@ namespace { + // port sets. MessagePumpKqueue will directly use Mach ports in the kqueue if + // it is possible. + bool KqueueNeedsPortSet() { +- static bool kqueue_needs_port_set = mac::IsAtMostOS10_11(); ++ static const bool kqueue_needs_port_set = mac::IsAtMostOS10_11(); + return kqueue_needs_port_set; + } + ++#if DCHECK_IS_ON() ++// Prior to macOS 10.14, kqueue timers may spuriously wake up, because earlier ++// wake ups race with timer resets in the kernel. As of macOS 10.14, updating a ++// timer from the thread that reads the kqueue does not cause spurious wakeups. ++// Note that updating a kqueue timer from one thread while another thread is ++// waiting in a kevent64 invocation is still (inherently) racy. ++bool KqueueTimersSpuriouslyWakeUp() { ++ static const bool kqueue_timers_spuriously_wakeup = mac::IsAtMostOS10_13(); ++ return kqueue_timers_spuriously_wakeup; ++} ++#endif ++ + int ChangeOneEvent(const ScopedFD& kqueue, kevent64_s* event) { + return HANDLE_EINTR(kevent64(kqueue.get(), event, 1, nullptr, 0, 0, nullptr)); + } +@@ -364,25 +377,13 @@ bool MessagePumpKqueue::DoInternalWork(Delegate::NextWorkInfo* next_work_info) { + + bool poll = next_work_info == nullptr; + int flags = poll ? KEVENT_FLAG_IMMEDIATE : 0; +- bool indefinite = +- next_work_info != nullptr && next_work_info->delayed_run_time.is_max(); +- +- int rv = 0; +- do { +- timespec timeout{}; +- if (!indefinite && !poll) { +- if (rv != 0) { +- // The wait was interrupted and made |next_work_info|'s view of +- // TimeTicks::Now() stale. Refresh it before doing another wait. +- next_work_info->recent_now = TimeTicks::Now(); +- } +- timeout = next_work_info->remaining_delay().ToTimeSpec(); +- } +- // This does not use HANDLE_EINTR, since retrying the syscall requires +- // adjusting the timeout to account for time already waited. +- rv = kevent64(kqueue_.get(), nullptr, 0, events_.data(), events_.size(), +- flags, indefinite ? nullptr : &timeout); +- } while (rv < 0 && errno == EINTR); ++ if (!poll && scheduled_wakeup_time_ != next_work_info->delayed_run_time) { ++ UpdateWakeupTimer(next_work_info->delayed_run_time); ++ DCHECK_EQ(scheduled_wakeup_time_, next_work_info->delayed_run_time); ++ } ++ ++ int rv = HANDLE_EINTR(kevent64(kqueue_.get(), nullptr, 0, events_.data(), ++ events_.size(), flags, nullptr)); + + PCHECK(rv >= 0) << "kevent64"; + return ProcessEvents(rv); +@@ -445,6 +446,25 @@ bool MessagePumpKqueue::ProcessEvents(int count) { + if (controller) { + controller->watcher()->OnMachMessageReceived(port); + } ++ } else if (event->filter == EVFILT_TIMER) { ++ // The wakeup timer fired. ++#if DCHECK_IS_ON() ++ // On macOS 10.13 and earlier, kqueue timers may spuriously wake up. ++ // When this happens, the timer will be re-scheduled the next time ++ // DoInternalWork is entered, which means this doesn't lead to a ++ // spinning wait. ++ // When clock overrides are active, TimeTicks::Now may be decoupled from ++ // wall-clock time, and can therefore not be used to validate whether the ++ // expected wall-clock time has passed. ++ if (!KqueueTimersSpuriouslyWakeUp() && ++ !subtle::ScopedTimeClockOverrides::overrides_active()) { ++ // Given the caveats above, assert that the timer didn't fire early. ++ DCHECK_LE(scheduled_wakeup_time_, base::TimeTicks::Now()); ++ } ++#endif ++ DCHECK_NE(scheduled_wakeup_time_, base::TimeTicks::Max()); ++ scheduled_wakeup_time_ = base::TimeTicks::Max(); ++ --event_count_; + } else { + NOTREACHED() << "Unexpected event for filter " << event->filter; + } +@@ -453,4 +473,47 @@ bool MessagePumpKqueue::ProcessEvents(int count) { + return did_work; + } + ++void MessagePumpKqueue::UpdateWakeupTimer(const base::TimeTicks& wakeup_time) { ++ DCHECK_NE(wakeup_time, scheduled_wakeup_time_); ++ ++ // The ident of the wakeup timer. There's only the one timer as the pair ++ // (ident, filter) is the identity of the event. ++ constexpr uint64_t kWakeupTimerIdent = 0x0; ++ if (wakeup_time == base::TimeTicks::Max()) { ++ // Clear the timer. ++ kevent64_s timer{}; ++ timer.ident = kWakeupTimerIdent; ++ timer.filter = EVFILT_TIMER; ++ timer.flags = EV_DELETE; ++ ++ int rv = ChangeOneEvent(kqueue_, &timer); ++ PCHECK(rv == 0) << "kevent64, delete timer"; ++ --event_count_; ++ } else { ++ // Set/reset the timer. ++ kevent64_s timer{}; ++ timer.ident = kWakeupTimerIdent; ++ timer.filter = EVFILT_TIMER; ++ // This updates the timer if it already exists in |kqueue_|. ++ timer.flags = EV_ADD | EV_ONESHOT; ++ // Specify the sleep in microseconds to avoid undersleeping due to ++ // numeric problems. The sleep is computed from TimeTicks::Now rather than ++ // NextWorkInfo::recent_now because recent_now is strictly earlier than ++ // current wall-clock. Using an earlier wall clock time to compute the ++ // delta to the next wakeup wall-clock time would guarantee oversleep. ++ // If wakeup_time is in the past, the delta below will be negative and the ++ // timer is set immediately. ++ timer.fflags = NOTE_USECONDS; ++ timer.data = (wakeup_time - base::TimeTicks::Now()).InMicroseconds(); ++ int rv = ChangeOneEvent(kqueue_, &timer); ++ PCHECK(rv == 0) << "kevent64, set timer"; ++ ++ // Bump the event count if we just added the timer. ++ if (scheduled_wakeup_time_ == base::TimeTicks::Max()) ++ ++event_count_; ++ } ++ ++ scheduled_wakeup_time_ = wakeup_time; ++} ++ + } // namespace base +diff --git a/base/message_loop/message_pump_kqueue.h b/base/message_loop/message_pump_kqueue.h +index 9acfcf22c3e0643000938ae1804eaeb4bbf7bd69..824d90fe2ab3083b094bb1a3ab1a0840f98b89f1 100644 +--- a/base/message_loop/message_pump_kqueue.h ++++ b/base/message_loop/message_pump_kqueue.h +@@ -136,6 +136,10 @@ class BASE_EXPORT MessagePumpKqueue : public MessagePump, + // true if work was done, or false if no work was done. + bool ProcessEvents(int count); + ++ // Sets the wakeup timer to |wakeup_time|, or clears it if |wakeup_time| is ++ // base::TimeTicks::Max(). Updates |scheduled_wakeup_time_| to follow. ++ void UpdateWakeupTimer(const base::TimeTicks& wakeup_time); ++ + // Receive right to which an empty Mach message is sent to wake up the pump + // in response to ScheduleWork(). + mac::ScopedMachReceiveRight wakeup_; +@@ -159,6 +163,10 @@ class BASE_EXPORT MessagePumpKqueue : public MessagePump, + // Whether the pump has been Quit() or not. + bool keep_running_ = true; + ++ // The currently scheduled wakeup, if any. If no wakeup is scheduled, ++ // contains base::TimeTicks::Max(). ++ base::TimeTicks scheduled_wakeup_time_{base::TimeTicks::Max()}; ++ + // The number of events scheduled on the |kqueue_|. There is always at least + // 1, for the |wakeup_| port (or |port_set_|). + size_t event_count_ = 1; diff --git a/patches/chromium/cherry-pick-3c80bb2a594f.patch b/patches/chromium/cherry-pick-3c80bb2a594f.patch new file mode 100644 index 0000000000000..3429d6472ff9e --- /dev/null +++ b/patches/chromium/cherry-pick-3c80bb2a594f.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jana Grill +Date: Wed, 14 Apr 2021 08:40:10 +0000 +Subject: Forbid script execution while updating the paint lifecycle. + +(cherry picked from commit 5425d3b100fab533ea9ddc2ed8fbfc4870db0587) + +Bug: 1196781 +Change-Id: Idc8d24792d5c413691977b09ca821de4e13887ad +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2812000 +Commit-Queue: Adrian Taylor +Commit-Queue: Robert Flack +Reviewed-by: Xianzhu Wang +Cr-Original-Commit-Position: refs/heads/master@{#870275} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821879 +Reviewed-by: Robert Flack +Reviewed-by: Achuith Bhandarkar +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Jana Grill +Cr-Commit-Position: refs/branch-heads/4240@{#1601} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc +index 60288427035e864861af48d8066d49da6cfc68df..d470bd94093970b486ce723339a8daa0658433cb 100644 +--- a/third_party/blink/renderer/core/frame/local_frame_view.cc ++++ b/third_party/blink/renderer/core/frame/local_frame_view.cc +@@ -2766,11 +2766,14 @@ void LocalFrameView::RunPaintLifecyclePhase() { + for (PaintLayerScrollableArea* area : *animating_scrollable_areas) + area->UpdateCompositorScrollAnimations(); + } +- frame_view.GetLayoutView() +- ->GetDocument() +- .GetDocumentAnimations() +- .UpdateAnimations(DocumentLifecycle::kPaintClean, +- paint_artifact_compositor_.get()); ++ { ++ ScriptForbiddenScope forbid_script; ++ frame_view.GetLayoutView() ++ ->GetDocument() ++ .GetDocumentAnimations() ++ .UpdateAnimations(DocumentLifecycle::kPaintClean, ++ paint_artifact_compositor_.get()); ++ } + }); + + // Initialize animation properties in the newly created paint property diff --git a/patches/chromium/cherry-pick-3feda0244490.patch b/patches/chromium/cherry-pick-3feda0244490.patch new file mode 100644 index 0000000000000..619fe129e0fb4 --- /dev/null +++ b/patches/chromium/cherry-pick-3feda0244490.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Reilly Grant +Date: Mon, 28 Jun 2021 21:55:24 +0000 +Subject: serial: Fix parent class tracing for SerialPort + +When SerialPort was updated to be ActiveScriptWrappable and an +EventTarget the Trace method was not updated to call the parent class +trace methods. + +(cherry picked from commit 4059ecc3a5352601a4d79196f90c8ca19262afe1) + +Bug: 1220078 +Change-Id: If6967a913268bce86d4488359a9418a814530f84 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2965255 +Auto-Submit: Reilly Grant +Commit-Queue: Tom Sepez +Reviewed-by: Tom Sepez +Cr-Original-Commit-Position: refs/heads/master@{#893039} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2992740 +Bot-Commit: Rubber Stamper +Commit-Queue: Reilly Grant +Cr-Commit-Position: refs/branch-heads/4472@{#1531} +Cr-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} + +diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc +index 989104ef12774896c254812fff804eaafc5b2e6c..ee3c26a0a711751de93e3bc2ed457b54a84d430d 100644 +--- a/third_party/blink/renderer/modules/serial/serial_port.cc ++++ b/third_party/blink/renderer/modules/serial/serial_port.cc +@@ -492,6 +492,7 @@ void SerialPort::Trace(Visitor* visitor) const { + visitor->Trace(signal_resolvers_); + visitor->Trace(close_resolver_); + ScriptWrappable::Trace(visitor); ++ ActiveScriptWrappable::Trace(visitor); + } + + ExecutionContext* SerialPort::GetExecutionContext() const { diff --git a/patches/chromium/cherry-pick-406ae3e8a9a8.patch b/patches/chromium/cherry-pick-406ae3e8a9a8.patch new file mode 100644 index 0000000000000..3f162b698a13d --- /dev/null +++ b/patches/chromium/cherry-pick-406ae3e8a9a8.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ken Rockot +Date: Tue, 20 Apr 2021 15:46:33 +0000 +Subject: M86-LTS: Mojo: Properly validate broadcast events + +This corrects broadcast event deserialization by adding a missing +validation step when decoding the outer message header. + +(cherry picked from commit 6740adb28374ddeee13febfd5e5d20cb8a365979) + +Fixed: 1195308 +Change-Id: Ia67a20e48614e7ef00b1b32f7f4e5f20235be310 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2808678 +Reviewed-by: Daniel Cheng +Commit-Queue: Ken Rockot +Cr-Original-Commit-Position: refs/heads/master@{#870238} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2837712 +Owners-Override: Achuith Bhandarkar +Auto-Submit: Achuith Bhandarkar +Reviewed-by: Artem Sumaneev +Commit-Queue: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1614} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/mojo/core/node_channel.cc b/mojo/core/node_channel.cc +index 5db339ec802497dd46364bba2425aae0af2f9615..901c0f76c638ac01e1b9e2b0d4fa19c8424a2ce0 100644 +--- a/mojo/core/node_channel.cc ++++ b/mojo/core/node_channel.cc +@@ -191,13 +191,16 @@ Channel::MessagePtr NodeChannel::CreateEventMessage(size_t capacity, + } + + // static +-void NodeChannel::GetEventMessageData(Channel::Message* message, ++bool NodeChannel::GetEventMessageData(Channel::Message& message, + void** data, + size_t* num_data_bytes) { +- // NOTE: OnChannelMessage guarantees that we never accept a Channel::Message +- // with a payload of fewer than |sizeof(Header)| bytes. +- *data = reinterpret_cast(message->mutable_payload()) + 1; +- *num_data_bytes = message->payload_size() - sizeof(Header); ++ // NOTE: Callers must guarantee that the payload in `message` must be at least ++ // large enough to hold a Header. ++ if (message.payload_size() < sizeof(Header)) ++ return false; ++ *data = reinterpret_cast(message.mutable_payload()) + 1; ++ *num_data_bytes = message.payload_size() - sizeof(Header); ++ return true; + } + + void NodeChannel::Start() { +diff --git a/mojo/core/node_channel.h b/mojo/core/node_channel.h +index 58ab42bd01fc856856d171985dac50934d4e00b2..7ae08e3e73110667f0eafe0fe4f70242bfeece39 100644 +--- a/mojo/core/node_channel.h ++++ b/mojo/core/node_channel.h +@@ -90,7 +90,9 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeChannel + void** payload, + size_t num_handles); + +- static void GetEventMessageData(Channel::Message* message, ++ // Retrieves address and size of an Event message's underlying message data. ++ // Returns `false` if the message is not a valid Event message. ++ static bool GetEventMessageData(Channel::Message& message, + void** data, + size_t* num_data_bytes); + +diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc +index a297c500215a58aa5b86157e765a64671ca91188..38ebaea2d988c241982824dfae62cccc7c5e28eb 100644 +--- a/mojo/core/node_controller.cc ++++ b/mojo/core/node_controller.cc +@@ -76,7 +76,9 @@ ports::ScopedEvent DeserializeEventMessage( + Channel::MessagePtr channel_message) { + void* data; + size_t size; +- NodeChannel::GetEventMessageData(channel_message.get(), &data, &size); ++ bool valid = NodeChannel::GetEventMessageData(*channel_message, &data, &size); ++ if (!valid) ++ return nullptr; + auto event = ports::Event::Deserialize(data, size); + if (!event) + return nullptr; +diff --git a/mojo/core/user_message_impl.cc b/mojo/core/user_message_impl.cc +index bd3a6766e9dfd1c405209dfb2bdf348ba568af26..e40682b0718b5e1dd9063d2418e82fabc7b322aa 100644 +--- a/mojo/core/user_message_impl.cc ++++ b/mojo/core/user_message_impl.cc +@@ -417,7 +417,14 @@ Channel::MessagePtr UserMessageImpl::FinalizeEventMessage( + if (channel_message) { + void* data; + size_t size; +- NodeChannel::GetEventMessageData(channel_message.get(), &data, &size); ++ // The `channel_message` must either be produced locally or must have ++ // already been validated by the caller, as is done for example by ++ // NodeController::DeserializeEventMessage before ++ // NodeController::OnBroadcast re-serializes each copy of the message it ++ // received. ++ bool result = ++ NodeChannel::GetEventMessageData(*channel_message, &data, &size); ++ DCHECK(result); + message_event->Serialize(data); + } + diff --git a/patches/chromium/cherry-pick-668cf831e912.patch b/patches/chromium/cherry-pick-668cf831e912.patch new file mode 100644 index 0000000000000..e724f55a883e0 --- /dev/null +++ b/patches/chromium/cherry-pick-668cf831e912.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ken Rockot +Date: Tue, 23 Mar 2021 21:13:00 +0000 +Subject: Never fail in ReceiverSet::Add + +Because of how UniqueReceiverSet is implemented and used, it is +dangerous to allow Add() to fail: callers reasonably assume that added +objects are still alive immediately after the Add() call. + +This changes ReceiverId to a uint64 and simply CHECK-fails on +insert collision. + +This fundamentally increases binary size of 32-bit builds, because +a widely used 32-bit data type is expanding to 64 bits for the sake +of security and stability. It is effectively unavoidable for now, and +also just barely above the tolerable threshold. + +A follow-up (but less backwards-mergeable) change should be able to +reduce binary size beyond this increase by consolidating shared +code among ReceiverSet template instantiations. + +Fixed: 1185732 +Change-Id: I9acf6aaaa36e10fdce5aa49a890173caddc13c52 +Binary-Size: Unavoidable (see above) +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2778871 +Commit-Queue: Ken Rockot +Auto-Submit: Ken Rockot +Reviewed-by: Robert Sesek +Cr-Commit-Position: refs/heads/master@{#865815} + +diff --git a/mojo/public/cpp/bindings/receiver_set.h b/mojo/public/cpp/bindings/receiver_set.h +index 8d7d73231543c70b67913fdf735c1a16cc6170b1..56027d1f3e6393f739c3b51330137d54ae3fc0d2 100644 +--- a/mojo/public/cpp/bindings/receiver_set.h ++++ b/mojo/public/cpp/bindings/receiver_set.h +@@ -24,7 +24,7 @@ + + namespace mojo { + +-using ReceiverId = size_t; ++using ReceiverId = uint64_t; + + template + struct ReceiverSetTraits; +@@ -359,11 +359,11 @@ class ReceiverSetBase { + Context context, + scoped_refptr task_runner) { + ReceiverId id = next_receiver_id_++; +- DCHECK_GE(next_receiver_id_, 0u); + auto entry = + std::make_unique(std::move(impl), std::move(receiver), this, id, + std::move(context), std::move(task_runner)); +- receivers_.insert(std::make_pair(id, std::move(entry))); ++ auto result = receivers_.insert(std::make_pair(id, std::move(entry))); ++ CHECK(result.second) << "ReceiverId overflow with collision"; + return id; + } + diff --git a/patches/chromium/cherry-pick-6a6361c9f31c.patch b/patches/chromium/cherry-pick-6a6361c9f31c.patch new file mode 100644 index 0000000000000..75e4a57634291 --- /dev/null +++ b/patches/chromium/cherry-pick-6a6361c9f31c.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Palak Agarwal +Date: Wed, 31 Mar 2021 16:10:26 +0000 +Subject: WebContents bug fix: Device capture only if web contents is valid + +(cherry picked from commit a462be0883486431086c5f07cdafbd3607005a59) + +(cherry picked from commit e6f11cafde08981e47ba77e71abf99a271f7a042) + +Bug: 1181228 +Change-Id: I0a4c9718a3c0ccb52cefa4565b9787e6912554c9 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2752235 +Reviewed-by: Guido Urdaneta +Commit-Queue: Palak Agarwal +Cr-Original-Original-Commit-Position: refs/heads/master@{#863828} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2782122 +Auto-Submit: Guido Urdaneta +Commit-Queue: Rubber Stamper +Bot-Commit: Rubber Stamper +Cr-Original-Commit-Position: refs/branch-heads/4389@{#1586} +Cr-Original-Branched-From: 9251c5db2b6d5a59fe4eac7aafa5fed37c139bb7-refs/heads/master@{#843830} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2795101 +Reviewed-by: Victor-Gabriel Savu +Reviewed-by: Artem Sumaneev +Auto-Submit: Artem Sumaneev +Commit-Queue: Guido Urdaneta +Cr-Commit-Position: refs/branch-heads/4240@{#1585} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc +index e561093253bc0bd49797991a7610393ac91a809e..c3fa03081b2fde6b0e2932107863ddedc7cf49ae 100644 +--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc ++++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc +@@ -249,6 +249,14 @@ void DesktopCaptureAccessHandler::ProcessScreenCaptureAccessRequest( + const bool display_notification = + display_notification_ && ShouldDisplayNotification(extension); + ++ if (!content::WebContents::FromRenderFrameHost( ++ content::RenderFrameHost::FromID(request.render_process_id, ++ request.render_frame_id))) { ++ std::move(callback).Run( ++ devices, blink::mojom::MediaStreamRequestResult::INVALID_STATE, ++ std::move(ui)); ++ return; ++ } + ui = GetDevicesForDesktopCapture( + web_contents, &devices, screen_id, + blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, diff --git a/patches/chromium/cherry-pick-6b84dc72351b.patch b/patches/chromium/cherry-pick-6b84dc72351b.patch new file mode 100644 index 0000000000000..a2b14d5e9deb3 --- /dev/null +++ b/patches/chromium/cherry-pick-6b84dc72351b.patch @@ -0,0 +1,82 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Brendon Tiszka +Date: Tue, 20 Apr 2021 15:45:03 +0000 +Subject: M86-LTS: Ensure that BrowserContext is not used after it has been + freed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previously, it was possible for the BrowserContext to be destroyed +before ReportAnchorElementMetricsOnClick attempted to access it. + +The fix uses the fact that NavigationPredictor extends +WebContentsObserver and checks that web_contents is still alive +before dereferencing BrowserContext. WebContents will always +outlive BrowserContext. + +R=​lukasza@chromium.org, ryansturm@chromium.org + +(cherry picked from commit 7313a810ae0b1361cbe8453bc5496654dee24c76) + +Bug: 1197904 +Change-Id: Iee4f126e92670a84d57c7a4ec7d6f702fb975c7e +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821639 +Reviewed-by: Ryan Sturm +Reviewed-by: Łukasz Anforowicz +Commit-Queue: Łukasz Anforowicz +Cr-Original-Commit-Position: refs/heads/master@{#872021} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2838328 +Owners-Override: Achuith Bhandarkar +Auto-Submit: Achuith Bhandarkar +Reviewed-by: Artem Sumaneev +Commit-Queue: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1613} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/AUTHORS b/AUTHORS +index d51c01045102715691a2e28cd44701a58a11cfa0..ae48471f7360a946447c915621236155e1399f86 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -148,6 +148,7 @@ Bobby Powers + Branden Archer + Brendan Kirby + Brendan Long ++Brendon Tiszka + Brian Clifton + Brian G. Merrell + Brian Konzman, SJ +diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc +index c532ddf44166215ae8eaa3d8433c7ed6fefdef06..f3799eaff3f960b162fbe5fb93e043804a26d121 100644 +--- a/chrome/browser/navigation_predictor/navigation_predictor.cc ++++ b/chrome/browser/navigation_predictor/navigation_predictor.cc +@@ -506,6 +506,9 @@ void NavigationPredictor::ReportAnchorElementMetricsOnClick( + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(base::FeatureList::IsEnabled(blink::features::kNavigationPredictor)); + ++ if (!web_contents()) ++ return; ++ + if (browser_context_->IsOffTheRecord()) + return; + +@@ -652,6 +655,9 @@ void NavigationPredictor::ReportAnchorElementMetricsOnLoad( + // Each document should only report metrics once when page is loaded. + DCHECK(navigation_scores_map_.empty()); + ++ if (!web_contents()) ++ return; ++ + if (browser_context_->IsOffTheRecord()) + return; + +@@ -897,6 +903,9 @@ void NavigationPredictor::MaybeTakeActionOnLoad( + } + + void NavigationPredictor::MaybePrefetch() { ++ if (!web_contents()) ++ return; ++ + // If prefetches aren't allowed here, this URL has already + // been prefetched, or the current tab is hidden, + // we shouldn't prefetch again. diff --git a/patches/chromium/cherry-pick-7dd3b1c86795.patch b/patches/chromium/cherry-pick-7dd3b1c86795.patch new file mode 100644 index 0000000000000..59310d730463e --- /dev/null +++ b/patches/chromium/cherry-pick-7dd3b1c86795.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Wez +Date: Thu, 15 Apr 2021 18:24:27 +0000 +Subject: Handle window deletion during HandleDisplayChange. + +In principle there is no reason why the HWNDMessageHandler shouldn't be +deleted by a HandleDisplayChange() call out to the delegate, e.g. if the +change results in a change in window layout. + +(cherry picked from commit 299155e5e37a77670b7969771e09e9a16b1f5612) + +Bug: 1192552 +Change-Id: I9fca35ff32e7037c6492f4cee7069e272059b920 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2804382 +Auto-Submit: Wez +Commit-Queue: Scott Violet +Reviewed-by: Scott Violet +Cr-Original-Commit-Position: refs/heads/master@{#869603} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2826321 +Cr-Commit-Position: refs/branch-heads/4430@{#1291} +Cr-Branched-From: e5ce7dc4f7518237b3d9bb93cccca35d25216cbe-refs/heads/master@{#857950} + +diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc +index ce3672e4d5cee51651bc6f8c294b27f0a9e01682..bb8b1c4fd7f1e93b6d50978ccb701393df956425 100644 +--- a/ui/views/win/hwnd_message_handler.cc ++++ b/ui/views/win/hwnd_message_handler.cc +@@ -1673,7 +1673,13 @@ void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel, + const gfx::Size& screen_size) { + TRACE_EVENT0("ui", "HWNDMessageHandler::OnDisplayChange"); + ++ base::WeakPtr ref(msg_handler_weak_factory_.GetWeakPtr()); + delegate_->HandleDisplayChange(); ++ ++ // HandleDisplayChange() may result in |this| being deleted. ++ if (!ref) ++ return; ++ + // Force a WM_NCCALCSIZE to occur to ensure that we handle auto hide + // taskbars correctly. + SendFrameChanged(); diff --git a/patches/chromium/cherry-pick-8c3eb9d1c409.patch b/patches/chromium/cherry-pick-8c3eb9d1c409.patch new file mode 100644 index 0000000000000..bafd8cd35215b --- /dev/null +++ b/patches/chromium/cherry-pick-8c3eb9d1c409.patch @@ -0,0 +1,143 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Scott Violet +Date: Wed, 31 Mar 2021 13:28:05 +0000 +Subject: x11/ozone: fix two edge cases + +WindowTreeHost::OnHostMovedInPixels() may trigger a nested message +loop (tab dragging), which when the stack unravels means this may +be deleted. This adds an early out if this happens. + +X11WholeScreenMoveLoop has a similar issue, in so far as notifying +the delegate may delete this. + +BUG=1185482 +TEST=WindowTreeHostPlatform.DeleteHostFromOnHostMovedInPixels + +(cherry picked from commit 5e3a738b1204941aab9f15c0eb3d06e20fefd96e) + +(cherry picked from commit 8ad84a8e7882275fb32f938fd0adc04d1a2a5773) + +Change-Id: Ieca1c90b3e4358da50b332abe2941fdbb50c5c25 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2743555 +Reviewed-by: Thomas Anderson +Commit-Queue: Scott Violet +Cr-Original-Original-Commit-Position: refs/heads/master@{#860852} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2779886 +Cr-Original-Commit-Position: refs/branch-heads/4389@{#1583} +Cr-Original-Branched-From: 9251c5db2b6d5a59fe4eac7aafa5fed37c139bb7-refs/heads/master@{#843830} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2794391 +Reviewed-by: Scott Violet +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Artem Sumaneev +Cr-Commit-Position: refs/branch-heads/4240@{#1583} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/ui/aura/window_tree_host_platform.cc b/ui/aura/window_tree_host_platform.cc +index 01843f47b17164201d406c8821f3d1a4f0c0010a..e59d3c6f3bb7b665cdbb6034ccc13376d9272933 100644 +--- a/ui/aura/window_tree_host_platform.cc ++++ b/ui/aura/window_tree_host_platform.cc +@@ -214,13 +214,21 @@ void WindowTreeHostPlatform::OnBoundsChanged(const gfx::Rect& new_bounds) { + float current_scale = compositor()->device_scale_factor(); + float new_scale = ui::GetScaleFactorForNativeView(window()); + gfx::Rect old_bounds = bounds_in_pixels_; ++ auto weak_ref = GetWeakPtr(); + bounds_in_pixels_ = new_bounds; +- if (bounds_in_pixels_.origin() != old_bounds.origin()) ++ if (bounds_in_pixels_.origin() != old_bounds.origin()) { + OnHostMovedInPixels(bounds_in_pixels_.origin()); ++ // Changing the bounds may destroy this. ++ if (!weak_ref) ++ return; ++ } + if (bounds_in_pixels_.size() != old_bounds.size() || + current_scale != new_scale) { + pending_size_ = gfx::Size(); + OnHostResizedInPixels(bounds_in_pixels_.size()); ++ // Changing the size may destroy this. ++ if (!weak_ref) ++ return; + } + DCHECK_GT(on_bounds_changed_recursion_depth_, 0); + if (--on_bounds_changed_recursion_depth_ == 0) { +diff --git a/ui/aura/window_tree_host_platform_unittest.cc b/ui/aura/window_tree_host_platform_unittest.cc +index eda14e2f0cdf5015f366aa70ea68ae2a2c2b431e..4de039c88af8a6f0ac03df2f772cfea2dfe3514f 100644 +--- a/ui/aura/window_tree_host_platform_unittest.cc ++++ b/ui/aura/window_tree_host_platform_unittest.cc +@@ -34,7 +34,7 @@ class TestWindowTreeHost : public WindowTreeHostPlatform { + // OnHostWill/DidProcessBoundsChange. Additionally, this triggers a bounds + // change from within OnHostResized(). Such a scenario happens in production + // code. +-class TestWindowTreeHostObserver : public aura::WindowTreeHostObserver { ++class TestWindowTreeHostObserver : public WindowTreeHostObserver { + public: + TestWindowTreeHostObserver(WindowTreeHostPlatform* host, + ui::PlatformWindow* platform_window) +@@ -51,7 +51,7 @@ class TestWindowTreeHostObserver : public aura::WindowTreeHostObserver { + return on_host_will_process_bounds_change_count_; + } + +- // aura::WindowTreeHostObserver: ++ // WindowTreeHostObserver: + void OnHostResized(WindowTreeHost* host) override { + if (!should_change_bounds_in_on_resized_) + return; +@@ -92,5 +92,41 @@ TEST_F(WindowTreeHostPlatformTest, HostWillProcessBoundsChangeRecursion) { + EXPECT_EQ(1, observer.on_host_will_process_bounds_change_count()); + } + ++// Deletes WindowTreeHostPlatform from OnHostMovedInPixels(). ++class DeleteHostWindowTreeHostObserver : public WindowTreeHostObserver { ++ public: ++ explicit DeleteHostWindowTreeHostObserver( ++ std::unique_ptr host) ++ : host_(std::move(host)) { ++ host_->AddObserver(this); ++ } ++ ~DeleteHostWindowTreeHostObserver() override = default; ++ ++ TestWindowTreeHost* host() { return host_.get(); } ++ ++ // WindowTreeHostObserver: ++ void OnHostMovedInPixels(WindowTreeHost* host, ++ const gfx::Point& new_origin_in_pixels) override { ++ host_->RemoveObserver(this); ++ host_.reset(); ++ } ++ ++ private: ++ std::unique_ptr host_; ++ ++ DISALLOW_COPY_AND_ASSIGN(DeleteHostWindowTreeHostObserver); ++}; ++ ++// Verifies WindowTreeHostPlatform can be safely deleted when calling ++// OnHostMovedInPixels(). ++// Regression test for https://crbug.com/1185482 ++TEST_F(WindowTreeHostPlatformTest, DeleteHostFromOnHostMovedInPixels) { ++ std::unique_ptr host = ++ std::make_unique(); ++ DeleteHostWindowTreeHostObserver observer(std::move(host)); ++ observer.host()->SetBoundsInPixels(gfx::Rect(1, 2, 3, 4)); ++ EXPECT_EQ(nullptr, observer.host()); ++} ++ + } // namespace + } // namespace aura +diff --git a/ui/base/x/x11_whole_screen_move_loop.cc b/ui/base/x/x11_whole_screen_move_loop.cc +index e26532b8403ea198641b0a09f32083ae945b0082..a168057a52c564bea8cb8b610c77726598036d4d 100644 +--- a/ui/base/x/x11_whole_screen_move_loop.cc ++++ b/ui/base/x/x11_whole_screen_move_loop.cc +@@ -64,9 +64,13 @@ X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() { + void X11WholeScreenMoveLoop::DispatchMouseMovement() { + if (!last_motion_in_screen_) + return; ++ auto weak_ref = weak_factory_.GetWeakPtr(); + delegate_->OnMouseMovement(last_motion_in_screen_->root_location(), + last_motion_in_screen_->flags(), + last_motion_in_screen_->time_stamp()); ++ // The delegate may delete this during dispatch. ++ if (!weak_ref) ++ return; + last_motion_in_screen_.reset(); + } + diff --git a/patches/chromium/cherry-pick-910e9e40d376.patch b/patches/chromium/cherry-pick-910e9e40d376.patch new file mode 100644 index 0000000000000..ce9249722ccf4 --- /dev/null +++ b/patches/chromium/cherry-pick-910e9e40d376.patch @@ -0,0 +1,204 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Matt Menke +Date: Thu, 10 Jun 2021 07:05:07 +0000 +Subject: Fix URLLoader cleanup on CorsURLLoaderFactory destruction. + +Destroying one URLLoader can result in other URLLoaders getting errors, +due to to cache interconnectedness. CorsURLLoaderFactory's destructor +was not taking that into account. + +Also fix a bonus bug: HttpCache::Transaction::response_ wasn't being +cleared in HttpCache::Transaction::DoHeadersPhaseCannotProceed(), which +could result in DCHECKs when calling GetResponseInfo() when a +transaction that was waiting on a cached response from another +transaction ended up failing. + +[M90]: Fixed trivial conflict + +(cherry picked from commit 2f49a3c69a2184c95f43a395e4f33a3959cb8dbc) + +(cherry picked from commit baf23e3c5b1394982cff718a0e055d4f239245ad) + +Bug: 1209769 +Change-Id: I2c18caa488767a29011aca1e1b0bace24c1ba8fc +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2922826 +Reviewed-by: Maksim Orlovich +Commit-Queue: Matt Menke +Cr-Original-Original-Commit-Position: refs/heads/master@{#887522} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2935241 +Auto-Submit: Matt Menke +Cr-Original-Commit-Position: refs/branch-heads/4472@{#1433} +Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2948654 +Owners-Override: Victor-Gabriel Savu +Reviewed-by: Artem Sumaneev +Reviewed-by: Matt Menke +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/4430@{#1513} +Cr-Branched-From: e5ce7dc4f7518237b3d9bb93cccca35d25216cbe-refs/heads/master@{#857950} + +diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc +index 663677c5865f6fee039fcd49a0adcd86a94b03ba..610d65e11b6b268d323247927bf2db3869906c90 100644 +--- a/net/http/http_cache_transaction.cc ++++ b/net/http/http_cache_transaction.cc +@@ -2101,6 +2101,8 @@ int HttpCache::Transaction::DoHeadersPhaseCannotProceed(int result) { + entry_ = nullptr; + new_entry_ = nullptr; + ++ SetResponse(HttpResponseInfo()); ++ + // Bypass the cache for timeout scenario. + if (result == ERR_CACHE_LOCK_TIMEOUT) + effective_load_flags_ |= LOAD_DISABLE_CACHE; +diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc +index 93328dbcc3f219a92b3ebcc5c0a3b6af37ebd4a1..60f73825b80ef4c27cf5926e33c351d505389631 100644 +--- a/services/network/cors/cors_url_loader_factory.cc ++++ b/services/network/cors/cors_url_loader_factory.cc +@@ -229,7 +229,17 @@ CorsURLLoaderFactory::CorsURLLoaderFactory( + &CorsURLLoaderFactory::DeleteIfNeeded, base::Unretained(this))); + } + +-CorsURLLoaderFactory::~CorsURLLoaderFactory() = default; ++CorsURLLoaderFactory::~CorsURLLoaderFactory() { ++ // Delete loaders one at a time, since deleting one loader can cause another ++ // loader waiting on it to fail synchronously, which could result in the other ++ // loader calling DestroyURLLoader(). ++ while (!loaders_.empty()) { ++ // No need to call context_->LoaderDestroyed(), since this method is only ++ // called from the NetworkContext's destructor, or when there are no ++ // remaining URLLoaders. ++ loaders_.erase(loaders_.begin()); ++ } ++} + + void CorsURLLoaderFactory::OnLoaderCreated( + std::unique_ptr loader) { +diff --git a/services/network/cors/cors_url_loader_factory_unittest.cc b/services/network/cors/cors_url_loader_factory_unittest.cc +index 994d6f091e663a2b12d9002fa12526790fdc14e9..292d77116ceef94633e693009ce1b165c8b0af38 100644 +--- a/services/network/cors/cors_url_loader_factory_unittest.cc ++++ b/services/network/cors/cors_url_loader_factory_unittest.cc +@@ -8,7 +8,9 @@ + #include "base/test/scoped_feature_list.h" + #include "base/test/task_environment.h" + #include "mojo/public/cpp/bindings/remote.h" ++#include "net/base/load_flags.h" + #include "net/proxy_resolution/configured_proxy_resolution_service.h" ++#include "net/test/embedded_test_server/embedded_test_server.h" + #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" + #include "net/url_request/url_request_context.h" + #include "net/url_request/url_request_context_builder.h" +@@ -50,6 +52,9 @@ class CorsURLLoaderFactoryTest : public testing::Test { + protected: + // testing::Test implementation. + void SetUp() override { ++ test_server_.AddDefaultHandlers(); ++ ASSERT_TRUE(test_server_.Start()); ++ + network_service_ = NetworkService::CreateForTesting(); + + auto context_params = mojom::NetworkContextParams::New(); +@@ -69,7 +74,7 @@ class CorsURLLoaderFactoryTest : public testing::Test { + auto factory_params = network::mojom::URLLoaderFactoryParams::New(); + factory_params->process_id = kProcessId; + factory_params->request_initiator_origin_lock = +- url::Origin::Create(GURL("http://localhost")); ++ url::Origin::Create(test_server_.base_url()); + auto resource_scheduler_client = + base::MakeRefCounted( + kProcessId, kRouteId, &resource_scheduler_, +@@ -82,15 +87,25 @@ class CorsURLLoaderFactoryTest : public testing::Test { + } + + void CreateLoaderAndStart(const ResourceRequest& request) { ++ url_loaders_.emplace_back(mojo::Remote()); ++ test_cors_loader_clients_.emplace_back( ++ std::make_unique()); + cors_url_loader_factory_->CreateLoaderAndStart( +- url_loader_.BindNewPipeAndPassReceiver(), kRouteId, kRequestId, ++ url_loaders_.back().BindNewPipeAndPassReceiver(), kRouteId, kRequestId, + mojom::kURLLoadOptionNone, request, +- test_cors_loader_client_.CreateRemote(), ++ test_cors_loader_clients_.back()->CreateRemote(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + } + + void ResetFactory() { cors_url_loader_factory_.reset(); } + ++ net::test_server::EmbeddedTestServer* test_server() { return &test_server_; } ++ ++ std::vector>& ++ test_cors_loader_clients() { ++ return test_cors_loader_clients_; ++ } ++ + private: + // Test environment. + base::test::TaskEnvironment task_environment_; +@@ -100,15 +115,17 @@ class CorsURLLoaderFactoryTest : public testing::Test { + std::unique_ptr network_context_; + mojo::Remote network_context_remote_; + ++ net::test_server::EmbeddedTestServer test_server_; ++ + // CorsURLLoaderFactory instance under tests. + std::unique_ptr cors_url_loader_factory_; + mojo::Remote cors_url_loader_factory_remote_; + +- // Holds URLLoader that CreateLoaderAndStart() creates. +- mojo::Remote url_loader_; ++ // Holds the URLLoaders that CreateLoaderAndStart() creates. ++ std::vector> url_loaders_; + +- // TestURLLoaderClient that records callback activities. +- TestURLLoaderClient test_cors_loader_client_; ++ // TestURLLoaderClients that record callback activities. ++ std::vector> test_cors_loader_clients_; + + // Holds for allowed origin access lists. + OriginAccessList origin_access_list_; +@@ -119,7 +136,7 @@ class CorsURLLoaderFactoryTest : public testing::Test { + // Regression test for https://crbug.com/906305. + TEST_F(CorsURLLoaderFactoryTest, DestructionOrder) { + ResourceRequest request; +- GURL url("http://localhost"); ++ GURL url = test_server()->GetURL("/hung"); + request.mode = mojom::RequestMode::kNoCors; + request.credentials_mode = mojom::CredentialsMode::kOmit; + request.method = net::HttpRequestHeaders::kGetMethod; +@@ -142,5 +159,36 @@ TEST_F(CorsURLLoaderFactoryTest, DestructionOrder) { + ResetFactory(); + } + ++TEST_F(CorsURLLoaderFactoryTest, CleanupWithSharedCacheObjectInUse) { ++ // Create a loader for a response that hangs after receiving headers, and run ++ // it until headers are received. ++ ResourceRequest request; ++ GURL url = test_server()->GetURL("/hung-after-headers"); ++ request.mode = mojom::RequestMode::kNoCors; ++ request.credentials_mode = mojom::CredentialsMode::kOmit; ++ request.method = net::HttpRequestHeaders::kGetMethod; ++ request.url = url; ++ request.request_initiator = url::Origin::Create(url); ++ CreateLoaderAndStart(request); ++ test_cors_loader_clients().back()->RunUntilResponseReceived(); ++ ++ // Read only requests will fail synchonously on destruction of the request ++ // they're waiting on if they're in the |done_headers_queue| when the other ++ // request fails. Make a large number of such requests, spin the message loop ++ // so they end up blocked on the hung request, and then destroy all loads. A ++ // large number of loaders is needed because they're stored in a set, indexed ++ // by address, so teardown order is random. ++ request.load_flags = ++ net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION; ++ for (int i = 0; i < 10; ++i) ++ CreateLoaderAndStart(request); ++ base::RunLoop().RunUntilIdle(); ++ ++ // This should result in a crash if tearing down one URLLoaderFactory ++ // resulting in a another one failing causes a crash during teardown. See ++ // https://crbug.com/1209769. ++ ResetFactory(); ++} ++ + } // namespace cors + } // namespace network diff --git a/patches/chromium/cherry-pick-ac9dc1235e28.patch b/patches/chromium/cherry-pick-ac9dc1235e28.patch new file mode 100644 index 0000000000000..d0838b9889996 --- /dev/null +++ b/patches/chromium/cherry-pick-ac9dc1235e28.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ilya Nikolaevskiy +Date: Mon, 17 May 2021 08:34:41 +0000 +Subject: Add locks and empty string checks to FakeV4L2Impl + +FakeV4L2Impl is crashed by fuzzer with some weird ASAN errors, which +turned out to be a threading issue. + +Bug: 1205059,1196302 +Change-Id: Ieb3a917c9a4549b655862e69214774e183a70bc3 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2883613 +Reviewed-by: Ricky Liang +Commit-Queue: Ilya Nikolaevskiy +Cr-Commit-Position: refs/heads/master@{#883390} + +diff --git a/media/capture/video/linux/fake_v4l2_impl.cc b/media/capture/video/linux/fake_v4l2_impl.cc +index 4a976815d009e68b3740aa71e8c8fd641bf91493..09647474bed3c7c7b34cd9fb161edd2a7d2e170f 100644 +--- a/media/capture/video/linux/fake_v4l2_impl.cc ++++ b/media/capture/video/linux/fake_v4l2_impl.cc +@@ -380,10 +380,16 @@ FakeV4L2Impl::~FakeV4L2Impl() = default; + + void FakeV4L2Impl::AddDevice(const std::string& device_name, + const FakeV4L2DeviceConfig& config) { ++ base::AutoLock lock(lock_); + device_configs_.emplace(device_name, config); + } + + int FakeV4L2Impl::open(const char* device_name, int flags) { ++ if (!device_name) ++ return kInvalidId; ++ ++ base::AutoLock lock(lock_); ++ + std::string device_name_as_string(device_name); + auto device_configs_iter = device_configs_.find(device_name_as_string); + if (device_configs_iter == device_configs_.end()) +@@ -403,6 +409,7 @@ int FakeV4L2Impl::open(const char* device_name, int flags) { + } + + int FakeV4L2Impl::close(int fd) { ++ base::AutoLock lock(lock_); + auto device_iter = opened_devices_.find(fd); + if (device_iter == opened_devices_.end()) + return kErrorReturnValue; +@@ -412,6 +419,7 @@ int FakeV4L2Impl::close(int fd) { + } + + int FakeV4L2Impl::ioctl(int fd, int request, void* argp) { ++ base::AutoLock lock(lock_); + auto device_iter = opened_devices_.find(fd); + if (device_iter == opened_devices_.end()) + return EBADF; +@@ -518,6 +526,7 @@ void* FakeV4L2Impl::mmap(void* /*start*/, + int flags, + int fd, + off_t offset) { ++ base::AutoLock lock(lock_); + if (flags & MAP_FIXED) { + errno = EINVAL; + return MAP_FAILED; +@@ -543,10 +552,12 @@ void* FakeV4L2Impl::mmap(void* /*start*/, + } + + int FakeV4L2Impl::munmap(void* start, size_t length) { ++ base::AutoLock lock(lock_); + return kSuccessReturnValue; + } + + int FakeV4L2Impl::poll(struct pollfd* ufds, unsigned int nfds, int timeout) { ++ base::AutoLock lock(lock_); + if (nfds != 1) { + // We only support polling of a single device. + errno = EINVAL; +diff --git a/media/capture/video/linux/fake_v4l2_impl.h b/media/capture/video/linux/fake_v4l2_impl.h +index 0a035a97dd8761b08eb4cfdbe2865efc346f0e23..ae7167f95163581c756ab4951717fd4352c67757 100644 +--- a/media/capture/video/linux/fake_v4l2_impl.h ++++ b/media/capture/video/linux/fake_v4l2_impl.h +@@ -10,6 +10,7 @@ + + #include + ++#include "base/synchronization/lock.h" + #include "media/capture/capture_export.h" + #include "media/capture/video/linux/v4l2_capture_device.h" + #include "media/capture/video/video_capture_device_descriptor.h" +@@ -52,11 +53,13 @@ class CAPTURE_EXPORT FakeV4L2Impl : public V4L2CaptureDevice { + private: + class OpenedDevice; + +- int next_id_to_return_from_open_; +- std::map device_configs_; +- std::map device_name_to_open_id_map_; ++ base::Lock lock_; ++ ++ int next_id_to_return_from_open_ GUARDED_BY(lock_); ++ std::map device_configs_ GUARDED_BY(lock_); ++ std::map device_name_to_open_id_map_ GUARDED_BY(lock_); + std::map> +- opened_devices_; ++ opened_devices_ GUARDED_BY(lock_); + }; + + } // namespace media diff --git a/patches/chromium/cherry-pick-b77b38a3380c.patch b/patches/chromium/cherry-pick-b77b38a3380c.patch new file mode 100644 index 0000000000000..ba445dab6a2b8 --- /dev/null +++ b/patches/chromium/cherry-pick-b77b38a3380c.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Brandon Jones +Date: Wed, 16 Jun 2021 21:47:09 +0000 +Subject: Ensure that XRLayer includes base EventTarget in Trace + +Trace was skipping a level in the class hierarchy and calling +ScriptWrappable::Trace() instead. This was likely the result of the +class inheritance changing in the spec a while back and getting updated +elsewhere but not here, since it didn't raise any warnings. + +(cherry picked from commit 01b6f7e0a70648d7c7302454993f0bf86d5a0241) + +Bug: 1219857 +Change-Id: I4ac9f7b037ac5e5dd0e6d670f1d5a30e6344862f +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2964533 +Commit-Queue: Brandon Jones +Reviewed-by: Alexander Cooper +Cr-Original-Commit-Position: refs/heads/master@{#892650} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2967199 +Auto-Submit: Brandon Jones +Commit-Queue: Rubber Stamper +Bot-Commit: Rubber Stamper +Cr-Commit-Position: refs/branch-heads/4472@{#1492} +Cr-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} + +diff --git a/third_party/blink/renderer/modules/xr/xr_layer.cc b/third_party/blink/renderer/modules/xr/xr_layer.cc +index eaa8603c7354a5c8e71e9a2e6161824063804fb6..aa30a4cec88fa11b342695dc675ab572320e9166 100644 +--- a/third_party/blink/renderer/modules/xr/xr_layer.cc ++++ b/third_party/blink/renderer/modules/xr/xr_layer.cc +@@ -21,7 +21,7 @@ const AtomicString& XRLayer::InterfaceName() const { + + void XRLayer::Trace(Visitor* visitor) const { + visitor->Trace(session_); +- ScriptWrappable::Trace(visitor); ++ EventTargetWithInlineData::Trace(visitor); + } + + } // namespace blink diff --git a/patches/chromium/cherry-pick-cd98d7c0dae9.patch b/patches/chromium/cherry-pick-cd98d7c0dae9.patch new file mode 100644 index 0000000000000..e8efedb8601f5 --- /dev/null +++ b/patches/chromium/cherry-pick-cd98d7c0dae9.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peng Huang +Date: Wed, 7 Jul 2021 20:50:53 +0000 +Subject: Fix UAF problem in SharedImageInterfaceInProcess + +(cherry picked from commit 38b4905f8d877b27bc2d4ccd4cfc0f82b636deea) + +Bug: 1216822 +Change-Id: I8ae1f7c406e1899e500ee7ddeaaf18230b1cbcb2 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2971144 +Commit-Queue: Peng Huang +Commit-Queue: Vasiliy Telezhnikov +Auto-Submit: Peng Huang +Reviewed-by: Vasiliy Telezhnikov +Cr-Original-Commit-Position: refs/heads/master@{#893931} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3011895 +Auto-Submit: Mason Freed +Reviewed-by: Peng Huang +Cr-Commit-Position: refs/branch-heads/4515@{#1369} +Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287} + +diff --git a/gpu/ipc/shared_image_interface_in_process.cc b/gpu/ipc/shared_image_interface_in_process.cc +index d6169e6b04b4ad4fd17b48507331dee059338610..e28ec015c8efce5d7af3c660d29f325869137b65 100644 +--- a/gpu/ipc/shared_image_interface_in_process.cc ++++ b/gpu/ipc/shared_image_interface_in_process.cc +@@ -93,6 +93,8 @@ void SharedImageInterfaceInProcess::DestroyOnGpu( + sync_point_client_state_->Destroy(); + sync_point_client_state_ = nullptr; + } ++ ++ context_state_ = nullptr; + completion->Signal(); + } + +diff --git a/gpu/ipc/shared_image_interface_in_process.h b/gpu/ipc/shared_image_interface_in_process.h +index 7ce2bc3eb52083b2505fb6d648f192cb5c7247e2..23bc32c0b35145c1cf7a14e519f205c66d5e7f47 100644 +--- a/gpu/ipc/shared_image_interface_in_process.h ++++ b/gpu/ipc/shared_image_interface_in_process.h +@@ -237,10 +237,7 @@ class GL_IN_PROCESS_CONTEXT_EXPORT SharedImageInterfaceInProcess + // Accessed on GPU thread. + // TODO(weiliangc): Check whether can be removed when !UsesSync(). + MailboxManager* mailbox_manager_; +- // Used to check if context is lost at destruction time. +- // TODO(weiliangc): SharedImageInterface should become active observer of +- // whether context is lost. +- SharedContextState* context_state_; ++ scoped_refptr context_state_; + // Created and only used by this SharedImageInterface. + SyncPointManager* sync_point_manager_; + scoped_refptr sync_point_client_state_; diff --git a/patches/chromium/cherry-pick-d9556a80a790.patch b/patches/chromium/cherry-pick-d9556a80a790.patch new file mode 100644 index 0000000000000..17d8125b87221 --- /dev/null +++ b/patches/chromium/cherry-pick-d9556a80a790.patch @@ -0,0 +1,197 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ted Meyer +Date: Mon, 7 Jun 2021 20:41:16 +0000 +Subject: A few fixes to the D3D11H264Accelerator + + - Adds a AsD3D11H264Picture method to H264Pictures because sometimes + there can be just normal H264Pictures in the DPB and this could cause + some invalid variable access as we were statically casting the + pointer before. + + - Adds a bounds check just in case there are more than 16 items in the + DPB. + +(cherry picked from commit 5a3cf91d0f2352e697017e13f4754989f46f2f3e) + +Bug: 1194689 +Change-Id: Ief2e1d00b451fbc0585dd0b22b5aff7a6918fa11 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2923118 +Commit-Queue: Ted Meyer +Reviewed-by: Frank Liberato +Cr-Original-Commit-Position: refs/heads/master@{#888267} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2944579 +Auto-Submit: Ted Meyer +Commit-Queue: Rubber Stamper +Bot-Commit: Rubber Stamper +Cr-Commit-Position: refs/branch-heads/4472@{#1455} +Cr-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} + +diff --git a/media/gpu/h264_dpb.cc b/media/gpu/h264_dpb.cc +index 8ef3bafb255349c6ac602c02a30615f0dd0b7d06..859e184b129f21f6af41b276051ca3a3adc03a7f 100644 +--- a/media/gpu/h264_dpb.cc ++++ b/media/gpu/h264_dpb.cc +@@ -55,6 +55,10 @@ VaapiH264Picture* H264Picture::AsVaapiH264Picture() { + return nullptr; + } + ++D3D11H264Picture* H264Picture::AsD3D11H264Picture() { ++ return nullptr; ++} ++ + H264DPB::H264DPB() : max_num_pics_(0) {} + H264DPB::~H264DPB() = default; + +diff --git a/media/gpu/h264_dpb.h b/media/gpu/h264_dpb.h +index 1395f9ecb35302632c49392d11fecb91436cbdf6..36abd4a8984dcb01ce85fc5fffa5ec916ecbf46a 100644 +--- a/media/gpu/h264_dpb.h ++++ b/media/gpu/h264_dpb.h +@@ -23,6 +23,7 @@ namespace media { + + class V4L2H264Picture; + class VaapiH264Picture; ++class D3D11H264Picture; + + // A picture (a frame or a field) in the H.264 spec sense. + // See spec at http://www.itu.int/rec/T-REC-H.264 +@@ -40,6 +41,7 @@ class MEDIA_GPU_EXPORT H264Picture : public CodecPicture { + + virtual V4L2H264Picture* AsV4L2H264Picture(); + virtual VaapiH264Picture* AsVaapiH264Picture(); ++ virtual D3D11H264Picture* AsD3D11H264Picture(); + + // Values calculated per H.264 specification or taken from slice header. + // See spec for more details on each (some names have been converted from +diff --git a/media/gpu/windows/d3d11_h264_accelerator.cc b/media/gpu/windows/d3d11_h264_accelerator.cc +index e87c1ece44f4c2af44e1c626ae69bfede43a0289..82abc9af5ed4255d5e262d539b4462e6e089fc61 100644 +--- a/media/gpu/windows/d3d11_h264_accelerator.cc ++++ b/media/gpu/windows/d3d11_h264_accelerator.cc +@@ -52,6 +52,8 @@ class D3D11H264Picture : public H264Picture { + D3D11PictureBuffer* picture; + size_t picture_index_; + ++ D3D11H264Picture* AsD3D11H264Picture() override { return this; } ++ + protected: + ~D3D11H264Picture() override; + }; +@@ -101,10 +103,12 @@ DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata( + + HRESULT hr; + for (;;) { ++ D3D11H264Picture* d3d11_pic = pic->AsD3D11H264Picture(); ++ if (!d3d11_pic) ++ return DecoderStatus::kFail; + hr = video_context_->DecoderBeginFrame( +- video_decoder_.Get(), +- static_cast(pic.get())->picture->output_view().Get(), +- 0, nullptr); ++ video_decoder_.Get(), d3d11_pic->picture->output_view().Get(), 0, ++ nullptr); + + if (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING) { + // Hardware is busy. We should make the call again. +@@ -119,7 +123,7 @@ DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata( + } + + sps_ = *sps; +- for (size_t i = 0; i < 16; i++) { ++ for (size_t i = 0; i < media::kRefFrameMaxCount; i++) { + ref_frame_list_[i].bPicEntry = 0xFF; + field_order_cnt_list_[i][0] = 0; + field_order_cnt_list_[i][1] = 0; +@@ -132,8 +136,19 @@ DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata( + + int i = 0; + for (auto it = dpb.begin(); it != dpb.end(); i++, it++) { +- D3D11H264Picture* our_ref_pic = static_cast(it->get()); +- if (!our_ref_pic->ref) ++ // The DPB is supposed to have a maximum of 16 pictures in it, but there's ++ // nothing actually stopping it from having more. If we run into this case, ++ // something is clearly wrong, and we should just fail decoding rather than ++ // try to sort out which pictures really shouldn't be included. ++ if (i >= media::kRefFrameMaxCount) ++ return DecoderStatus::kFail; ++ ++ D3D11H264Picture* our_ref_pic = it->get()->AsD3D11H264Picture(); ++ // How does a non-d3d11 picture get here you might ask? The decoder ++ // inserts blank H264Picture objects that we can't use as part of filling ++ // gaps in frame numbers. If we see one, it's not a reference picture ++ // anyway, so skip it. ++ if (!our_ref_pic || !our_ref_pic->ref) + continue; + ref_frame_list_[i].Index7Bits = our_ref_pic->picture_index_; + ref_frame_list_[i].AssociatedFlag = our_ref_pic->long_term; +@@ -279,9 +294,8 @@ void D3D11H264Accelerator::PicParamsFromSliceHeader( + } + + void D3D11H264Accelerator::PicParamsFromPic(DXVA_PicParams_H264* pic_param, +- scoped_refptr pic) { +- pic_param->CurrPic.Index7Bits = +- static_cast(pic.get())->picture_index_; ++ D3D11H264Picture* pic) { ++ pic_param->CurrPic.Index7Bits = pic->picture_index_; + pic_param->RefPicFlag = pic->ref; + pic_param->frame_num = pic->frame_num; + +@@ -314,7 +328,11 @@ DecoderStatus D3D11H264Accelerator::SubmitSlice( + if (!PicParamsFromPPS(&pic_param, pps)) + return DecoderStatus::kFail; + PicParamsFromSliceHeader(&pic_param, slice_hdr); +- PicParamsFromPic(&pic_param, std::move(pic)); ++ ++ D3D11H264Picture* d3d11_pic = pic->AsD3D11H264Picture(); ++ if (!d3d11_pic) ++ return DecoderStatus::kFail; ++ PicParamsFromPic(&pic_param, d3d11_pic); + + memcpy(pic_param.RefFrameList, ref_frame_list_, + sizeof pic_param.RefFrameList); +@@ -573,9 +591,8 @@ void D3D11H264Accelerator::Reset() { + } + + bool D3D11H264Accelerator::OutputPicture(scoped_refptr pic) { +- D3D11H264Picture* our_pic = static_cast(pic.get()); +- +- return client_->OutputResult(our_pic, our_pic->picture); ++ D3D11H264Picture* our_pic = pic->AsD3D11H264Picture(); ++ return our_pic && client_->OutputResult(our_pic, our_pic->picture); + } + + void D3D11H264Accelerator::RecordFailure(const std::string& reason, +diff --git a/media/gpu/windows/d3d11_h264_accelerator.h b/media/gpu/windows/d3d11_h264_accelerator.h +index 00e2bd5cecd34f947c15aed1a7f5873b2ba4736c..c927706fb58b0637b6cf27516495028ead95325c 100644 +--- a/media/gpu/windows/d3d11_h264_accelerator.h ++++ b/media/gpu/windows/d3d11_h264_accelerator.h +@@ -27,6 +27,8 @@ + + namespace media { + ++constexpr int kRefFrameMaxCount = 16; ++ + class D3D11H264Accelerator; + class MediaLog; + +@@ -74,8 +76,7 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator { + void PicParamsFromSliceHeader(DXVA_PicParams_H264* pic_param, + const H264SliceHeader* pps); + +- void PicParamsFromPic(DXVA_PicParams_H264* pic_param, +- scoped_refptr pic); ++ void PicParamsFromPic(DXVA_PicParams_H264* pic_param, D3D11H264Picture* pic); + + void SetVideoDecoder(ComD3D11VideoDecoder video_decoder); + +@@ -95,10 +96,10 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator { + + // This information set at the beginning of a frame and saved for processing + // all the slices. +- DXVA_PicEntry_H264 ref_frame_list_[16]; ++ DXVA_PicEntry_H264 ref_frame_list_[kRefFrameMaxCount]; + H264SPS sps_; +- INT field_order_cnt_list_[16][2]; +- USHORT frame_num_list_[16]; ++ INT field_order_cnt_list_[kRefFrameMaxCount][2]; ++ USHORT frame_num_list_[kRefFrameMaxCount]; + UINT used_for_reference_flags_; + USHORT non_existing_frame_flags_; + diff --git a/patches/chromium/cherry-pick-e60cc80ff744.patch b/patches/chromium/cherry-pick-e60cc80ff744.patch new file mode 100644 index 0000000000000..7dee0278eac65 --- /dev/null +++ b/patches/chromium/cherry-pick-e60cc80ff744.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shrek Shao +Date: Tue, 29 Jun 2021 01:17:03 +0000 +Subject: Fix multidraw validation drawcount + offset out of bounds + +(cherry picked from commit 7d0a12ce19fed024d56b95a692d888fe3ef14e2f) + +Bug: 1219886 +Change-Id: I8a84664150758370d9a77ee22ac5549bead0e37e +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2977850 +Reviewed-by: Kenneth Russell +Commit-Queue: Kenneth Russell +Cr-Original-Commit-Position: refs/heads/master@{#895423} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2988885 +Reviewed-by: Shrek Shao +Reviewed-by: Austin Eng +Cr-Commit-Position: refs/branch-heads/4515@{#1101} +Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287} + +diff --git a/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc b/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc +index 6720d46394ae0346331e133f0fa8d24e5dd1e95b..bae874bbf23cdb497b92bda7bb87429dafe7d5d4 100644 +--- a/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc ++++ b/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc +@@ -34,6 +34,11 @@ bool WebGLMultiDrawCommon::ValidateArray(WebGLExtensionScopedContext* scoped, + outOfBoundsDescription); + return false; + } ++ if (static_cast(drawcount) + offset > size) { ++ scoped->Context()->SynthesizeGLError(GL_INVALID_OPERATION, function_name, ++ "drawcount plus offset out of bounds"); ++ return false; ++ } + return true; + } + diff --git a/patches/chromium/cherry-pick-f37149c4434f.patch b/patches/chromium/cherry-pick-f37149c4434f.patch new file mode 100644 index 0000000000000..0a61f578a0586 --- /dev/null +++ b/patches/chromium/cherry-pick-f37149c4434f.patch @@ -0,0 +1,314 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jana Grill +Date: Mon, 19 Apr 2021 10:55:05 +0000 +Subject: Don't show autofill dropdown for element outside the viewport + +Details are in the linked bug. + +Also cherry pick crrev/c/2682341 and a part of crrev/c/2628287 to fix +failing tests and compile errors. + +(cherry picked from commit 53a4f38ee5d44bd935d176cc89e3e59fd0a3970e) + +Bug: 1172533,1173297 +Change-Id: Iee429cac167ccdb0cd74acf57fc5f7c3821883b1 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2675932 +Commit-Queue: Mohamed Amir Yosef +Reviewed-by: Vasilii Sukhanov +Reviewed-by: Evan Stade +Cr-Original-Commit-Position: refs/heads/master@{#851718} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2822155 +Commit-Queue: Jana Grill +Reviewed-by: Achuith Bhandarkar +Reviewed-by: Victor-Gabriel Savu +Owners-Override: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1611} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc +index 7dd45b75afcd47aec7b40e1c0ea48ee51c076bb6..cc7d4eb8bbd82acee0a89df275d51438111f9cae 100644 +--- a/chrome/browser/autofill/autofill_interactive_uitest.cc ++++ b/chrome/browser/autofill/autofill_interactive_uitest.cc +@@ -2760,7 +2760,14 @@ class AutofillInteractiveIsolationTest : public AutofillInteractiveTestBase { + } + }; + +-IN_PROC_BROWSER_TEST_F(AutofillInteractiveIsolationTest, SimpleCrossSiteFill) { ++// Flaky on ChromeOS http://crbug.com/1175735 ++#if defined(OS_CHROMEOS) ++#define MAYBE_SimpleCrossSiteFill DISABLED_SimpleCrossSiteFill ++#else ++#define MAYBE_SimpleCrossSiteFill SimpleCrossSiteFill ++#endif ++IN_PROC_BROWSER_TEST_F(AutofillInteractiveIsolationTest, ++ MAYBE_SimpleCrossSiteFill) { + CreateTestProfile(); + + // Main frame is on a.com, iframe is on b.com. +@@ -2803,7 +2810,8 @@ IN_PROC_BROWSER_TEST_F(AutofillInteractiveIsolationTest, SimpleCrossSiteFill) { + // This test verifies that credit card (payment card list) popup works when the + // form is inside an OOPIF. + // Flaky on Windows http://crbug.com/728488 +-#if defined(OS_WIN) ++// Flaky on ChromeOS http://crbug.com/1175735 ++#if defined(OS_WIN) || defined(OS_CHROMEOS) + #define MAYBE_CrossSitePaymentForms DISABLED_CrossSitePaymentForms + #else + #define MAYBE_CrossSitePaymentForms CrossSitePaymentForms +@@ -2841,8 +2849,14 @@ IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, MAYBE_CrossSitePaymentForms) { + {ObservedUiEvents::kSuggestionShown}); + } + ++// Flaky on ChromeOS http://crbug.com/1175735 ++#if defined(OS_CHROMEOS) ++#define MAYBE_DeletingFrameUnderSuggestion DISABLED_DeletingFrameUnderSuggestion ++#else ++#define MAYBE_DeletingFrameUnderSuggestion DeletingFrameUnderSuggestion ++#endif + IN_PROC_BROWSER_TEST_F(AutofillInteractiveIsolationTest, +- DeletingFrameUnderSuggestion) { ++ MAYBE_DeletingFrameUnderSuggestion) { + CreateTestProfile(); + + // Main frame is on a.com, iframe is on b.com. +diff --git a/chrome/browser/autofill/autofill_uitest.cc b/chrome/browser/autofill/autofill_uitest.cc +index b405c197f6a51bfe28aea9790fc73025238be8e2..f136fd6ac034316822d71d3ccaafb906c43a5139 100644 +--- a/chrome/browser/autofill/autofill_uitest.cc ++++ b/chrome/browser/autofill/autofill_uitest.cc +@@ -8,6 +8,7 @@ + #include "base/macros.h" + #include "base/run_loop.h" + #include "chrome/browser/autofill/autofill_uitest.h" ++#include "chrome/browser/autofill/autofill_uitest_util.h" + #include "chrome/browser/autofill/personal_data_manager_factory.h" + #include "chrome/browser/profiles/profile.h" + #include "chrome/browser/ui/browser.h" +@@ -78,6 +79,10 @@ void AutofillUiTest::SetUpOnMainThread() { + /* new_host = */ GetWebContents()->GetMainFrame()); + Observe(GetWebContents()); + ++ // Wait for Personal Data Manager to be fully loaded to prevent that ++ // spurious notifications deceive the tests. ++ WaitForPersonalDataManagerToBeLoaded(browser()); ++ + disable_animation_ = std::make_unique( + ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); + +diff --git a/chrome/browser/autofill/autofill_uitest_util.cc b/chrome/browser/autofill/autofill_uitest_util.cc +index 5bee77a2b6b451c4e15b0329379961164ac4f973..69d62b4f506e83e46db57d7afbff5ddf3911fd79 100644 +--- a/chrome/browser/autofill/autofill_uitest_util.cc ++++ b/chrome/browser/autofill/autofill_uitest_util.cc +@@ -113,4 +113,12 @@ void WaitForPersonalDataChange(Browser* browser) { + observer.Wait(); + } + ++// Adjusted from crrev/c/2628287 to fix failure in crrev/c/2822155 ++void WaitForPersonalDataManagerToBeLoaded(Browser* browser) { ++ PersonalDataManager* pdm = ++ autofill::PersonalDataManagerFactory::GetForProfile(browser->profile()); ++ while (!pdm->IsDataLoaded()) ++ WaitForPersonalDataChange(browser); ++} ++ + } // namespace autofill +diff --git a/chrome/browser/autofill/autofill_uitest_util.h b/chrome/browser/autofill/autofill_uitest_util.h +index df333ecf76a98def05aa55fa0c332010fb4eebd0..c95a3888563d43fa26196b7164c73db96cb80343 100644 +--- a/chrome/browser/autofill/autofill_uitest_util.h ++++ b/chrome/browser/autofill/autofill_uitest_util.h +@@ -24,6 +24,9 @@ void AddTestAutofillData(Browser* browser, + const CreditCard& card); + void WaitForPersonalDataChange(Browser* browser); + ++// Adjusted from crrev/c/2628287 to fix failure in crrev/c/2822155 ++void WaitForPersonalDataManagerToBeLoaded(Browser* browser); ++ + } // namespace autofill + + #endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_UITEST_UTIL_H_ +diff --git a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc +index 7fbfcfe10bb7ed8987aaddabf3562c7a25efbaf4..31cb5347160cc069ac0fd16abd3f76017d82314a 100644 +--- a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc ++++ b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc +@@ -26,9 +26,15 @@ class TestPasswordGenerationPopupController + explicit TestPasswordGenerationPopupController( + content::WebContents* web_contents) + : PasswordGenerationPopupControllerImpl( +- gfx::RectF(0, 0, 10, 10), ++ gfx::RectF(web_contents->GetContainerBounds().x(), ++ web_contents->GetContainerBounds().y(), ++ 10, ++ 10), + autofill::password_generation::PasswordGenerationUIData( +- /*bounds=*/gfx::RectF(0, 0, 10, 10), ++ /*bounds=*/gfx::RectF(web_contents->GetContainerBounds().x(), ++ web_contents->GetContainerBounds().y(), ++ 10, ++ 10), + /*max_length=*/10, + /*generation_element=*/base::string16(), + autofill::FieldRendererId(100), +@@ -70,7 +76,9 @@ IN_PROC_BROWSER_TEST_F(PasswordGenerationPopupViewTest, + new autofill::TestPasswordGenerationPopupController(GetWebContents()); + controller_->Show(PasswordGenerationPopupController::kEditGeneratedPassword); + +- GetViewTester()->SimulateMouseMovementAt(gfx::Point(1, 1)); ++ GetViewTester()->SimulateMouseMovementAt( ++ gfx::Point(GetWebContents()->GetContainerBounds().x() + 1, ++ GetWebContents()->GetContainerBounds().y() + 1)); + + // This hides the popup and destroys the controller. + GetWebContents()->Close(); +diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc +index cb0c7e0b669fcae0f9c842221be47c2866a18d39..f748f66b78f6880dc72037e09b86697f30b696c2 100644 +--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc ++++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc +@@ -259,8 +259,8 @@ bool AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() { + // area so that the user notices the presence of the popup. + int item_height = + children().size() > 0 ? children()[0]->GetPreferredSize().height() : 0; +- if (!HasEnoughHeightForOneRow(item_height, GetContentAreaBounds(), +- element_bounds)) { ++ if (!CanShowDropdownHere(item_height, GetContentAreaBounds(), ++ element_bounds)) { + HideController(PopupHidingReason::kInsufficientSpace); + return false; + } +diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc +index fcf2076ddff00a4a010f8d5c14c9b99070aa6911..92b79871919e971aff7043718b0187d8b9e8972c 100644 +--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc ++++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc +@@ -53,10 +53,13 @@ class AutofillPopupBaseViewTest : public InProcessBrowserTest { + ~AutofillPopupBaseViewTest() override {} + + void SetUpOnMainThread() override { +- gfx::NativeView native_view = +- browser()->tab_strip_model()->GetActiveWebContents()->GetNativeView(); ++ content::WebContents* web_contents = ++ browser()->tab_strip_model()->GetActiveWebContents(); ++ gfx::NativeView native_view = web_contents->GetNativeView(); + EXPECT_CALL(mock_delegate_, container_view()) + .WillRepeatedly(Return(native_view)); ++ EXPECT_CALL(mock_delegate_, GetWebContents()) ++ .WillRepeatedly(Return(web_contents)); + EXPECT_CALL(mock_delegate_, ViewDestroyed()); + + view_ = new AutofillPopupBaseView( +diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc +index 80212002da57695f5eef75d76a33670a2f8ff63a..23e199d21d9140a936e3a6637e935c290650015c 100644 +--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc ++++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc +@@ -1284,8 +1284,9 @@ bool AutofillPopupViewNativeViews::DoUpdateBoundsAndRedrawPopup() { + body_container_ && body_container_->children().size() > 0 + ? body_container_->children()[0]->GetPreferredSize().height() + : 0; +- if (!HasEnoughHeightForOneRow(item_height, GetContentAreaBounds(), +- element_bounds)) { ++ ++ if (!CanShowDropdownHere(item_height, GetContentAreaBounds(), ++ element_bounds)) { + controller_->Hide(PopupHidingReason::kInsufficientSpace); + return false; + } +diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc +index e95fe363ba65c7bf6223820f5f3ec110d395cbeb..82f528c39a6edeb329bc8c19321831caca41e553 100644 +--- a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc ++++ b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc +@@ -87,16 +87,26 @@ gfx::Rect CalculatePopupBounds(const gfx::Size& desired_size, + return popup_bounds; + } + +-bool HasEnoughHeightForOneRow(int item_height, +- const gfx::Rect& content_area_bounds, +- const gfx::Rect& element_bounds) { +- // Ensure that at least one row of the popup can be displayed within the ++bool CanShowDropdownHere(int item_height, ++ const gfx::Rect& content_area_bounds, ++ const gfx::Rect& element_bounds) { ++ // Ensure that at least one row of the popup will be displayed within the + // bounds of the content area so that the user notices the presence of the + // popup. + bool enough_space_for_one_item_in_content_area_above_element = + element_bounds.y() - content_area_bounds.y() >= item_height; ++ bool element_top_is_within_content_area_bounds = ++ element_bounds.y() > content_area_bounds.y() && ++ element_bounds.y() < content_area_bounds.bottom(); ++ + bool enough_space_for_one_item_in_content_area_below_element = + content_area_bounds.bottom() - element_bounds.bottom() >= item_height; +- return enough_space_for_one_item_in_content_area_above_element || +- enough_space_for_one_item_in_content_area_below_element; ++ bool element_bottom_is_within_content_area_bounds = ++ element_bounds.bottom() > content_area_bounds.y() && ++ element_bounds.bottom() < content_area_bounds.bottom(); ++ ++ return (enough_space_for_one_item_in_content_area_above_element && ++ element_top_is_within_content_area_bounds) || ++ (enough_space_for_one_item_in_content_area_below_element && ++ element_bottom_is_within_content_area_bounds); + } +diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h +index 990d67cababe9363fe5ee1b91bee81986846f807..bab1a26c38f7fcbea7da51e92e0d8e3dfe97622b 100644 +--- a/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h ++++ b/chrome/browser/ui/views/autofill/autofill_popup_view_utils.h +@@ -34,9 +34,10 @@ gfx::Rect CalculatePopupBounds(const gfx::Size& desired_size, + bool is_rtl); + + // Returns whether there is enough height within |content_area_bounds| above or +-// below |element_bounds| to display |item_height|. +-bool HasEnoughHeightForOneRow(int item_height, +- const gfx::Rect& content_area_bounds, +- const gfx::Rect& element_bounds); ++// below |element_bounds| to display |item_height|, and that the first dropdown ++// item will actually be visible within the bounds of the content area. ++bool CanShowDropdownHere(int item_height, ++ const gfx::Rect& content_area_bounds, ++ const gfx::Rect& element_bounds); + + #endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_AUTOFILL_POPUP_VIEW_UTILS_H_ +diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc +index 78fa8a09a4791bde5825935e8f2b7e805c100b1f..cecd8d2c3173ea7b48ebebf5ae33fd2c531e6551 100644 +--- a/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc ++++ b/chrome/browser/ui/views/autofill/autofill_popup_view_utils_unittest.cc +@@ -1,3 +1,4 @@ ++ + // Copyright 2020 The Chromium Authors. All rights reserved. + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. +@@ -115,7 +116,34 @@ TEST(AutofillPopupViewUtilsTest, NotEnoughHeightForAnItem) { + gfx::Rect content_area_bounds(x, window_y + 2, width, height - 2); + gfx::Rect element_bounds(x, window_y + 3, width, height - 3); + +- bool enough_height_for_item = HasEnoughHeightForOneRow( +- item_height, content_area_bounds, element_bounds); +- EXPECT_FALSE(enough_height_for_item); ++ EXPECT_FALSE( ++ CanShowDropdownHere(item_height, content_area_bounds, element_bounds)); ++} ++ ++TEST(AutofillPopupViewUtilsTest, ElementOutOfContentAreaBounds) { ++ // In this test, each row of the popup has a height of 8 pixels, and there is ++ // no enough height in the content area to show one row. ++ // ++ // |---------------------| ---> y = 5 ++ // | Window | ++ // | |-----------------| | ---> y = 7 ++ // | | | | ++ // | | Content Area | | ++ // | | | | ++ // |-|-----------------|-| ---> y = 50 ++ // |-------------| ---> y = 53 ++ // | Element | ++ // |-------------| ---> y = 63 ++ ++ constexpr int item_height = 8; ++ constexpr int window_y = 5; ++ constexpr int x = 10; ++ constexpr int width = 5; ++ constexpr int height = 46; ++ ++ gfx::Rect content_area_bounds(x, window_y + 2, width, height - 2); ++ gfx::Rect element_bounds(x, window_y + height + 3, width, 10); ++ ++ EXPECT_FALSE( ++ CanShowDropdownHere(item_height, content_area_bounds, element_bounds)); + } diff --git a/patches/chromium/cherry-pick-fe85e04a1797.patch b/patches/chromium/cherry-pick-fe85e04a1797.patch new file mode 100644 index 0000000000000..0d5954b427bdf --- /dev/null +++ b/patches/chromium/cherry-pick-fe85e04a1797.patch @@ -0,0 +1,291 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ken Rockot +Date: Wed, 31 Mar 2021 18:44:06 +0000 +Subject: Don't use BigBuffer for IPC::Message transport + +M86 merge conflicts and resolution: +* ipc/ipc_message_pipe_reader.cc + Fixed extra include. + +(cherry picked from commit 85bd7c88523545ab0e497d5e7b3e929793813358) + +(cherry picked from commit fad3b9ffe7c7ff82909d911c573bd185aa3b3b50) + +Fixed: 1184399 +Change-Id: Iddd91ae8d7ae63022b61c96239f5e39261dfb735 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2737012 +Commit-Queue: Ken Rockot +Reviewed-by: Daniel Cheng +Cr-Original-Original-Commit-Position: refs/heads/master@{#860010} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2779918 +Auto-Submit: Ken Rockot +Reviewed-by: Adrian Taylor +Reviewed-by: Alex Gough +Commit-Queue: Alex Gough +Cr-Original-Commit-Position: refs/branch-heads/4389@{#1597} +Cr-Original-Branched-From: 9251c5db2b6d5a59fe4eac7aafa5fed37c139bb7-refs/heads/master@{#843830} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2794488 +Reviewed-by: Victor-Gabriel Savu +Reviewed-by: Artem Sumaneev +Reviewed-by: Ken Rockot +Auto-Submit: Artem Sumaneev +Commit-Queue: Artem Sumaneev +Cr-Commit-Position: refs/branch-heads/4240@{#1587} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn +index 48bdc5061eb5ea2c49dccd6d140d6a521d7d282f..54e260d1ba60af64c16868e9a4602ada7c0db302 100644 +--- a/ipc/BUILD.gn ++++ b/ipc/BUILD.gn +@@ -203,10 +203,7 @@ mojom_component("mojom") { + output_prefix = "ipc_mojom" + macro_prefix = "IPC_MOJOM" + sources = [ "ipc.mojom" ] +- public_deps = [ +- "//mojo/public/interfaces/bindings", +- "//mojo/public/mojom/base", +- ] ++ public_deps = [ "//mojo/public/interfaces/bindings" ] + + cpp_typemaps = [ + { +@@ -223,10 +220,7 @@ mojom_component("mojom") { + "//ipc/message_view.cc", + "//ipc/message_view.h", + ] +- traits_public_deps = [ +- "//ipc:message_support", +- "//mojo/public/cpp/base:shared_typemap_traits", +- ] ++ traits_public_deps = [ "//ipc:message_support" ] + }, + ] + +diff --git a/ipc/ipc.mojom b/ipc/ipc.mojom +index c66799642fbee2cef3449ff5d52cd5f187808cfe..4606022b28bca1df06ba6eb8eaac025573475b10 100644 +--- a/ipc/ipc.mojom ++++ b/ipc/ipc.mojom +@@ -4,7 +4,6 @@ + + module IPC.mojom; + +-import "mojo/public/mojom/base/big_buffer.mojom"; + import "mojo/public/interfaces/bindings/native_struct.mojom"; + + // A placeholder interface type since we don't yet support generic associated +@@ -14,7 +13,7 @@ interface GenericInterface {}; + // Typemapped such that arbitrarily large IPC::Message objects can be sent and + // received with minimal copying. + struct Message { +- mojo_base.mojom.BigBuffer buffer; ++ array bytes; + array? handles; + }; + +@@ -24,6 +23,7 @@ interface Channel { + SetPeerPid(int32 pid); + + // Transmits a classical Chrome IPC message. ++ [UnlimitedSize] + Receive(Message message); + + // Requests a Channel-associated interface. +diff --git a/ipc/ipc_message_pipe_reader.cc b/ipc/ipc_message_pipe_reader.cc +index bdc5dd680d0f9107719765334d0a1ea3e864e200..cbf0363a9d941db1ab34ae835e707b7825447659 100644 +--- a/ipc/ipc_message_pipe_reader.cc ++++ b/ipc/ipc_message_pipe_reader.cc +@@ -10,6 +10,7 @@ + + #include "base/bind.h" + #include "base/bind_helpers.h" ++#include "base/containers/span.h" + #include "base/location.h" + #include "base/logging.h" + #include "base/macros.h" +@@ -62,7 +63,9 @@ bool MessagePipeReader::Send(std::unique_ptr message) { + if (!sender_) + return false; + +- sender_->Receive(MessageView(*message, std::move(handles))); ++ base::span bytes(static_cast(message->data()), ++ message->size()); ++ sender_->Receive(MessageView(bytes, std::move(handles))); + DVLOG(4) << "Send " << message->type() << ": " << message->size(); + return true; + } +@@ -82,11 +85,12 @@ void MessagePipeReader::SetPeerPid(int32_t peer_pid) { + } + + void MessagePipeReader::Receive(MessageView message_view) { +- if (!message_view.size()) { ++ if (message_view.bytes().empty()) { + delegate_->OnBrokenDataReceived(); + return; + } +- Message message(message_view.data(), message_view.size()); ++ Message message(reinterpret_cast(message_view.bytes().data()), ++ message_view.bytes().size()); + if (!message.IsValid()) { + delegate_->OnBrokenDataReceived(); + return; +diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc +index 47a7ad79a30165c76041075be10b9be8c13f5e75..b32941da752a54ba7317e439150982adbb9fbcad 100644 +--- a/ipc/ipc_mojo_bootstrap_unittest.cc ++++ b/ipc/ipc_mojo_bootstrap_unittest.cc +@@ -77,7 +77,9 @@ class PeerPidReceiver : public IPC::mojom::Channel { + ASSERT_NE(MessageExpectation::kNotExpected, message_expectation_); + received_message_ = true; + +- IPC::Message message(message_view.data(), message_view.size()); ++ IPC::Message message( ++ reinterpret_cast(message_view.bytes().data()), ++ message_view.bytes().size()); + bool expected_valid = + message_expectation_ == MessageExpectation::kExpectedValid; + EXPECT_EQ(expected_valid, message.IsValid()); +@@ -196,8 +198,7 @@ MULTIPROCESS_TEST_MAIN_WITH_SETUP( + + uint8_t data = 0; + sender->Receive( +- IPC::MessageView(mojo_base::BigBufferView(base::make_span(&data, 0)), +- base::nullopt /* handles */)); ++ IPC::MessageView(base::make_span(&data, 0), base::nullopt /* handles */)); + + base::RunLoop run_loop; + PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure()); +diff --git a/ipc/message_mojom_traits.cc b/ipc/message_mojom_traits.cc +index 4aab9248e9ff6ca8e2d7d085ae3e996ac04666e8..d8ad4a2f919b01362e3e2746bfb7f4fae77b059d 100644 +--- a/ipc/message_mojom_traits.cc ++++ b/ipc/message_mojom_traits.cc +@@ -4,15 +4,13 @@ + + #include "ipc/message_mojom_traits.h" + +-#include "mojo/public/cpp/base/big_buffer_mojom_traits.h" +- + namespace mojo { + + // static +-mojo_base::BigBufferView +-StructTraits::buffer( ++base::span ++StructTraits::bytes( + IPC::MessageView& view) { +- return view.TakeBufferView(); ++ return view.bytes(); + } + + // static +@@ -26,14 +24,14 @@ StructTraits::handles( + bool StructTraits::Read( + IPC::mojom::MessageDataView data, + IPC::MessageView* out) { +- mojo_base::BigBufferView buffer_view; +- if (!data.ReadBuffer(&buffer_view)) +- return false; ++ mojo::ArrayDataView bytes; ++ data.GetBytesDataView(&bytes); ++ + base::Optional> handles; + if (!data.ReadHandles(&handles)) + return false; + +- *out = IPC::MessageView(std::move(buffer_view), std::move(handles)); ++ *out = IPC::MessageView(bytes, std::move(handles)); + return true; + } + +diff --git a/ipc/message_mojom_traits.h b/ipc/message_mojom_traits.h +index 617ffbe37309946464e3f180a0ebde97f56dbd75..6b5064a12191e9a663519e7b5cb7c5f907a75054 100644 +--- a/ipc/message_mojom_traits.h ++++ b/ipc/message_mojom_traits.h +@@ -7,10 +7,10 @@ + + #include + ++#include "base/containers/span.h" + #include "base/optional.h" + #include "ipc/ipc.mojom-shared.h" + #include "ipc/message_view.h" +-#include "mojo/public/cpp/base/big_buffer.h" + #include "mojo/public/cpp/bindings/struct_traits.h" + #include "mojo/public/interfaces/bindings/native_struct.mojom.h" + +@@ -19,7 +19,7 @@ namespace mojo { + template <> + class StructTraits { + public: +- static mojo_base::BigBufferView buffer(IPC::MessageView& view); ++ static base::span bytes(IPC::MessageView& view); + static base::Optional> handles( + IPC::MessageView& view); + +diff --git a/ipc/message_view.cc b/ipc/message_view.cc +index 49a80878e7a92cda13105ea0f2fea36ad7ed05e6..39c6608dd507c3ca051b619d966ae521e95fe8e2 100644 +--- a/ipc/message_view.cc ++++ b/ipc/message_view.cc +@@ -11,16 +11,9 @@ namespace IPC { + MessageView::MessageView() = default; + + MessageView::MessageView( +- const Message& message, ++ base::span bytes, + base::Optional> handles) +- : buffer_view_(base::make_span(static_cast(message.data()), +- message.size())), +- handles_(std::move(handles)) {} +- +-MessageView::MessageView( +- mojo_base::BigBufferView buffer_view, +- base::Optional> handles) +- : buffer_view_(std::move(buffer_view)), handles_(std::move(handles)) {} ++ : bytes_(bytes), handles_(std::move(handles)) {} + + MessageView::MessageView(MessageView&&) = default; + +diff --git a/ipc/message_view.h b/ipc/message_view.h +index 4ec059bf3639b9c75178f2300d0796b433e1d2ed..c7801bb963f06b03c51ba87bffc307792b592dae 100644 +--- a/ipc/message_view.h ++++ b/ipc/message_view.h +@@ -11,7 +11,6 @@ + #include "base/containers/span.h" + #include "base/macros.h" + #include "ipc/ipc_message.h" +-#include "mojo/public/cpp/base/big_buffer.h" + #include "mojo/public/interfaces/bindings/native_struct.mojom-forward.h" + + namespace IPC { +@@ -20,30 +19,18 @@ class COMPONENT_EXPORT(IPC_MOJOM) MessageView { + public: + MessageView(); + MessageView( +- const Message& message, +- base::Optional> handles); +- MessageView( +- mojo_base::BigBufferView buffer_view, ++ base::span bytes, + base::Optional> handles); + MessageView(MessageView&&); + ~MessageView(); + + MessageView& operator=(MessageView&&); + +- const char* data() const { +- return reinterpret_cast(buffer_view_.data().data()); +- } +- +- uint32_t size() const { +- return static_cast(buffer_view_.data().size()); +- } +- +- mojo_base::BigBufferView TakeBufferView() { return std::move(buffer_view_); } +- ++ base::span bytes() const { return bytes_; } + base::Optional> TakeHandles(); + + private: +- mojo_base::BigBufferView buffer_view_; ++ base::span bytes_; + base::Optional> handles_; + + DISALLOW_COPY_AND_ASSIGN(MessageView); diff --git a/patches/chromium/cherry-pick-ff0d013f60fa.patch b/patches/chromium/cherry-pick-ff0d013f60fa.patch new file mode 100644 index 0000000000000..65d804a108189 --- /dev/null +++ b/patches/chromium/cherry-pick-ff0d013f60fa.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Raymond Toy +Date: Wed, 26 May 2021 16:06:10 +0000 +Subject: Add AudioHandler to orphan handlers when context is suspended. + +If the context is suspended, pulling of the audio graph is stopped. +But we still need to add the handler in this case so that when the +context is resumed, the handler is still alive until it can be safely +removed. Hence, we must still add the handler if the context is +suspended. + +Test cases from issue 1176218 manually tested with no failures. Also +this doesn't cause any regressions in issue 1003807 and issue 1017961. +(Manually tested the test cases from those issues.) + +(cherry picked from commit 4a38ea3f1f78e0a0ffc1464e227cee6c1f2fd90b) + +Bug: 1176218 +Change-Id: Icd927c488505dfee9ff716866f98286e286d546a +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2874771 +Commit-Queue: Raymond Toy +Cr-Original-Commit-Position: refs/heads/master@{#881533} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2917093 +Reviewed-by: Raymond Toy +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Jana Grill +Cr-Commit-Position: refs/branch-heads/4240@{#1648} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.cc b/third_party/blink/renderer/modules/webaudio/audio_node.cc +index e1f1a068d233f633e2e2c911f5954632ebf60654..22974448d059f3523e0cbf2d7d1bfd9877d98bbf 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_node.cc ++++ b/third_party/blink/renderer/modules/webaudio/audio_node.cc +@@ -609,13 +609,13 @@ void AudioNode::Dispose() { + BaseAudioContext::GraphAutoLocker locker(context()); + Handler().Dispose(); + +- // Add the handler to the orphan list if the context is pulling on the audio +- // graph. This keeps the handler alive until it can be deleted at a safe +- // point (in pre/post handler task). If graph isn't being pulled, we can +- // delete the handler now since nothing on the audio thread will be touching +- // it. ++ // Add the handler to the orphan list. This keeps the handler alive until it ++ // can be deleted at a safe point (in pre/post handler task). If the graph is ++ // being processed, the handler must be added. If the context is suspended, ++ // the handler still needs to be added in case the context is resumed. + DCHECK(context()); +- if (context()->IsPullingAudioGraph()) { ++ if (context()->IsPullingAudioGraph() || ++ context()->ContextState() == BaseAudioContext::kSuspended) { + context()->GetDeferredTaskHandler().AddRenderingOrphanHandler( + std::move(handler_)); + } diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index f2cff73a31dac..f83c2c1eb06b2 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -33,7 +33,7 @@ index 0ccfe130f00ec3b6c75cd8ee04d5a2777e1fd00c..653829457d58bf92057cc36aa8a28970 DISALLOW_COPY_AND_ASSIGN(StaticHttpUserAgentSettings); }; diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index 3dc5c6d6027be44c1e799bb8e0b509a03bae963a..b2d9b7a74f71b3127f51ea2c4f4ed0caaa2bff05 100644 +index e36e5f9306bda8d9523d14d46dd71ea2f3bb8530..a6e1850aabcaf422513c699fb7bc85820b79a219 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -1082,6 +1082,13 @@ void NetworkContext::SetNetworkConditions( diff --git a/patches/chromium/fileapi_terminate_filereaderloader_before_dispatching_onabort_event.patch b/patches/chromium/fileapi_terminate_filereaderloader_before_dispatching_onabort_event.patch new file mode 100644 index 0000000000000..7bee0b1ce8198 --- /dev/null +++ b/patches/chromium/fileapi_terminate_filereaderloader_before_dispatching_onabort_event.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 12:27:18 +0200 +Subject: FileAPI: Terminate FileReaderLoader before dispatching onabort event. + +Otherwise FileReader could end up in an inconsistent state where a load +is still in progress while the state was set to done. + +(cherry picked from commit a74c980df61dd7367ad1b11e6a735be82d2696f0) + +Bug: 1201073 +Change-Id: Ib2c833537e1badc57d125568d5d35f53f12582a8 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2860442 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2871355 + +diff --git a/third_party/blink/renderer/core/fileapi/file_reader.cc b/third_party/blink/renderer/core/fileapi/file_reader.cc +index 2bbc4db7923c659eda6d63230998ca3414f397d3..93221ba5cf92b9efd702378e1b94cca4fb6ab470 100644 +--- a/third_party/blink/renderer/core/fileapi/file_reader.cc ++++ b/third_party/blink/renderer/core/fileapi/file_reader.cc +@@ -335,7 +335,10 @@ void FileReader::abort() { + loading_state_ = kLoadingStateAborted; + + DCHECK_NE(kDone, state_); +- state_ = kDone; ++ // Synchronously cancel the loader before dispatching events. This way we make ++ // sure the FileReader internal state stays consistent even if another load ++ // is started from one of the event handlers, or right after abort returns. ++ Terminate(); + + base::AutoReset firing_events(&still_firing_events_, true); + +@@ -347,15 +350,12 @@ void FileReader::abort() { + ThrottlingController::RemoveReader(GetExecutionContext(), this); + + FireEvent(event_type_names::kAbort); ++ // TODO(https://crbug.com/1204139): Only fire loadend event if no new load was ++ // started from the abort event handler. + FireEvent(event_type_names::kLoadend); + + // All possible events have fired and we're done, no more pending activity. + ThrottlingController::FinishReader(GetExecutionContext(), this, final_step); +- +- // Also synchronously cancel the loader, as script might initiate a new load +- // right after this method returns, in which case an async termination would +- // terminate the wrong loader. +- Terminate(); + } + + void FileReader::result(StringOrArrayBuffer& result_attribute) const { +@@ -428,6 +428,8 @@ void FileReader::DidFinishLoading() { + ThrottlingController::RemoveReader(GetExecutionContext(), this); + + FireEvent(event_type_names::kLoad); ++ // TODO(https://crbug.com/1204139): Only fire loadend event if no new load was ++ // started from the abort event handler. + FireEvent(event_type_names::kLoadend); + + // All possible events have fired and we're done, no more pending activity. diff --git a/patches/chromium/fix_use-after-free_with_xslt_strip-space.patch b/patches/chromium/fix_use-after-free_with_xslt_strip-space.patch new file mode 100644 index 0000000000000..9c4f22214c388 --- /dev/null +++ b/patches/chromium/fix_use-after-free_with_xslt_strip-space.patch @@ -0,0 +1,431 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Joey Arhar +Date: Wed, 16 Jun 2021 02:41:13 +0000 +Subject: Fix use-after-free with XSLT strip-space + +Fixed: 1219209 +Change-Id: I3baab9d1b419407d964a80f10c6ca05e0294554f +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2965632 +Commit-Queue: Joey Arhar +Reviewed-by: Stephen Chenney +Cr-Commit-Position: refs/heads/master@{#892861} + +diff --git a/third_party/blink/web_tests/external/wpt/xslt/strip-space-crash.xml b/third_party/blink/web_tests/external/wpt/xslt/strip-space-crash.xml +new file mode 100644 +index 0000000000000000000000000000000000000000..61a906a5e74b9c88061c565615187f9970baff72 +--- /dev/null ++++ b/third_party/blink/web_tests/external/wpt/xslt/strip-space-crash.xml +@@ -0,0 +1,33 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/third_party/libxslt/chromium/Fix-use-after-free-in-xsltApplyTemplates.patch b/third_party/libxslt/chromium/Fix-use-after-free-in-xsltApplyTemplates.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..9b4c28d8756d6cf95027fc105ec875be5f71d952 +--- /dev/null ++++ b/third_party/libxslt/chromium/Fix-use-after-free-in-xsltApplyTemplates.patch +@@ -0,0 +1,195 @@ ++From: Nick Wellnhofer ++Date: Sat, 12 Jun 2021 20:02:53 +0200 ++Subject: [PATCH] Fix use-after-free in xsltApplyTemplates ++ ++xsltApplyTemplates without a select expression could delete nodes in ++the source document. ++ ++1. Text nodes with strippable whitespace ++ ++Whitespace from input documents is already stripped, so there's no ++need to strip it again. Under certain circumstances, xsltApplyTemplates ++could be fooled into deleting text nodes that are still referenced, ++resulting in a use-after-free. ++ ++2. The DTD ++ ++The DTD was only unlinked, but there's no good reason to do this just ++now. Maybe it was meant as a micro-optimization. ++ ++3. Unknown nodes ++ ++Useless and dangerous as well, especially with XInclude nodes. ++See https://gitlab.gnome.org/GNOME/libxml2/-/issues/268 ++ ++Simply stop trying to uselessly delete nodes when applying a template. ++This part of the code is probably a leftover from a time where ++xsltApplyStripSpaces wasn't implemented yet. Also note that ++xsltApplyTemplates with a select expression never tried to delete ++nodes. ++ ++Also stop xsltDefaultProcessOneNode from deleting nodes for the same ++reasons. ++--- ++ libxslt/transform.c | 119 +++----------------------------------------- ++ 1 file changed, 7 insertions(+), 112 deletions(-) ++ ++diff --git a/libxslt/transform.c b/libxslt/transform.c ++index 04522154..3aba354f 100644 ++--- a/libxslt/transform.c +++++ b/libxslt/transform.c ++@@ -1895,7 +1895,7 @@ static void ++ xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, ++ xsltStackElemPtr params) { ++ xmlNodePtr copy; ++- xmlNodePtr delete = NULL, cur; +++ xmlNodePtr cur; ++ int nbchild = 0, oldSize; ++ int childno = 0, oldPos; ++ xsltTemplatePtr template; ++@@ -1968,54 +1968,13 @@ xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, ++ return; ++ } ++ /* ++- * Handling of Elements: first pass, cleanup and counting +++ * Handling of Elements: first pass, counting ++ */ ++ cur = node->children; ++ while (cur != NULL) { ++- switch (cur->type) { ++- case XML_TEXT_NODE: ++- case XML_CDATA_SECTION_NODE: ++- case XML_DOCUMENT_NODE: ++- case XML_HTML_DOCUMENT_NODE: ++- case XML_ELEMENT_NODE: ++- case XML_PI_NODE: ++- case XML_COMMENT_NODE: ++- nbchild++; ++- break; ++- case XML_DTD_NODE: ++- /* Unlink the DTD, it's still reachable using doc->intSubset */ ++- if (cur->next != NULL) ++- cur->next->prev = cur->prev; ++- if (cur->prev != NULL) ++- cur->prev->next = cur->next; ++- break; ++- default: ++-#ifdef WITH_XSLT_DEBUG_PROCESS ++- XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, ++- "xsltDefaultProcessOneNode: skipping node type %d\n", ++- cur->type)); ++-#endif ++- delete = cur; ++- } +++ if (IS_XSLT_REAL_NODE(cur)) +++ nbchild++; ++ cur = cur->next; ++- if (delete != NULL) { ++-#ifdef WITH_XSLT_DEBUG_PROCESS ++- XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, ++- "xsltDefaultProcessOneNode: removing ignorable blank node\n")); ++-#endif ++- xmlUnlinkNode(delete); ++- xmlFreeNode(delete); ++- delete = NULL; ++- } ++- } ++- if (delete != NULL) { ++-#ifdef WITH_XSLT_DEBUG_PROCESS ++- XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, ++- "xsltDefaultProcessOneNode: removing ignorable blank node\n")); ++-#endif ++- xmlUnlinkNode(delete); ++- xmlFreeNode(delete); ++- delete = NULL; ++ } ++ ++ /* ++@@ -4864,7 +4823,7 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, ++ xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; ++ #endif ++ int i; ++- xmlNodePtr cur, delNode = NULL, oldContextNode; +++ xmlNodePtr cur, oldContextNode; ++ xmlNodeSetPtr list = NULL, oldList; ++ xsltStackElemPtr withParams = NULL; ++ int oldXPProximityPosition, oldXPContextSize; ++@@ -4998,73 +4957,9 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, ++ else ++ cur = NULL; ++ while (cur != NULL) { ++- switch (cur->type) { ++- case XML_TEXT_NODE: ++- if ((IS_BLANK_NODE(cur)) && ++- (cur->parent != NULL) && ++- (cur->parent->type == XML_ELEMENT_NODE) && ++- (ctxt->style->stripSpaces != NULL)) { ++- const xmlChar *val; ++- ++- if (cur->parent->ns != NULL) { ++- val = (const xmlChar *) ++- xmlHashLookup2(ctxt->style->stripSpaces, ++- cur->parent->name, ++- cur->parent->ns->href); ++- if (val == NULL) { ++- val = (const xmlChar *) ++- xmlHashLookup2(ctxt->style->stripSpaces, ++- BAD_CAST "*", ++- cur->parent->ns->href); ++- } ++- } else { ++- val = (const xmlChar *) ++- xmlHashLookup2(ctxt->style->stripSpaces, ++- cur->parent->name, NULL); ++- } ++- if ((val != NULL) && ++- (xmlStrEqual(val, (xmlChar *) "strip"))) { ++- delNode = cur; ++- break; ++- } ++- } ++- /* Intentional fall-through */ ++- case XML_ELEMENT_NODE: ++- case XML_DOCUMENT_NODE: ++- case XML_HTML_DOCUMENT_NODE: ++- case XML_CDATA_SECTION_NODE: ++- case XML_PI_NODE: ++- case XML_COMMENT_NODE: ++- xmlXPathNodeSetAddUnique(list, cur); ++- break; ++- case XML_DTD_NODE: ++- /* Unlink the DTD, it's still reachable ++- * using doc->intSubset */ ++- if (cur->next != NULL) ++- cur->next->prev = cur->prev; ++- if (cur->prev != NULL) ++- cur->prev->next = cur->next; ++- break; ++- case XML_NAMESPACE_DECL: ++- break; ++- default: ++-#ifdef WITH_XSLT_DEBUG_PROCESS ++- XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, ++- "xsltApplyTemplates: skipping cur type %d\n", ++- cur->type)); ++-#endif ++- delNode = cur; ++- } +++ if (IS_XSLT_REAL_NODE(cur)) +++ xmlXPathNodeSetAddUnique(list, cur); ++ cur = cur->next; ++- if (delNode != NULL) { ++-#ifdef WITH_XSLT_DEBUG_PROCESS ++- XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, ++- "xsltApplyTemplates: removing ignorable blank cur\n")); ++-#endif ++- xmlUnlinkNode(delNode); ++- xmlFreeNode(delNode); ++- delNode = NULL; ++- } ++ } ++ } ++ ++-- ++2.20.1 (Apple Git-117) ++ +diff --git a/third_party/libxslt/chromium/roll.py b/third_party/libxslt/chromium/roll.py +index 352bbd6d937f19c5cbb409f184f5b4e0abf4b7b3..c438a9eb96dcc62ca827fb4a647fb2cf1cc8cc0b 100755 +--- a/third_party/libxslt/chromium/roll.py ++++ b/third_party/libxslt/chromium/roll.py +@@ -67,6 +67,7 @@ import tempfile + PATCHES = [ + 'get-file-attributes-a.patch', + 'xslt-locale.patch', ++ 'Fix-use-after-free-in-xsltApplyTemplates.patch', + ] + + +diff --git a/third_party/libxslt/src/libxslt.spec b/third_party/libxslt/src/libxslt.spec +index 80b320fb86980367cddc579c386c24a2a1708f7c..7fb51e275fa4cc4a2bfc613ffb3868f464deeb5a 100644 +--- a/third_party/libxslt/src/libxslt.spec ++++ b/third_party/libxslt/src/libxslt.spec +@@ -128,5 +128,5 @@ rm -fr %{buildroot} + %doc python/tests/*.xsl + + %changelog +-* Fri Nov 8 2019 Daniel Veillard ++* Tue Jun 15 2021 Daniel Veillard + - upstream release 1.1.34 see http://xmlsoft.org/XSLT/news.html +diff --git a/third_party/libxslt/src/libxslt/transform.c b/third_party/libxslt/src/libxslt/transform.c +index d1c479320eca266c7b0996e3c16d47e7d6c5aaa9..265f5b3856f785f565691e2f5939c99275183e7f 100644 +--- a/third_party/libxslt/src/libxslt/transform.c ++++ b/third_party/libxslt/src/libxslt/transform.c +@@ -1895,7 +1895,7 @@ static void + xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStackElemPtr params) { + xmlNodePtr copy; +- xmlNodePtr delete = NULL, cur; ++ xmlNodePtr cur; + int nbchild = 0, oldSize; + int childno = 0, oldPos; + xsltTemplatePtr template; +@@ -1968,54 +1968,13 @@ xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, + return; + } + /* +- * Handling of Elements: first pass, cleanup and counting ++ * Handling of Elements: first pass, counting + */ + cur = node->children; + while (cur != NULL) { +- switch (cur->type) { +- case XML_TEXT_NODE: +- case XML_CDATA_SECTION_NODE: +- case XML_DOCUMENT_NODE: +- case XML_HTML_DOCUMENT_NODE: +- case XML_ELEMENT_NODE: +- case XML_PI_NODE: +- case XML_COMMENT_NODE: +- nbchild++; +- break; +- case XML_DTD_NODE: +- /* Unlink the DTD, it's still reachable using doc->intSubset */ +- if (cur->next != NULL) +- cur->next->prev = cur->prev; +- if (cur->prev != NULL) +- cur->prev->next = cur->next; +- break; +- default: +-#ifdef WITH_XSLT_DEBUG_PROCESS +- XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, +- "xsltDefaultProcessOneNode: skipping node type %d\n", +- cur->type)); +-#endif +- delete = cur; +- } ++ if (IS_XSLT_REAL_NODE(cur)) ++ nbchild++; + cur = cur->next; +- if (delete != NULL) { +-#ifdef WITH_XSLT_DEBUG_PROCESS +- XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, +- "xsltDefaultProcessOneNode: removing ignorable blank node\n")); +-#endif +- xmlUnlinkNode(delete); +- xmlFreeNode(delete); +- delete = NULL; +- } +- } +- if (delete != NULL) { +-#ifdef WITH_XSLT_DEBUG_PROCESS +- XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, +- "xsltDefaultProcessOneNode: removing ignorable blank node\n")); +-#endif +- xmlUnlinkNode(delete); +- xmlFreeNode(delete); +- delete = NULL; + } + + /* +@@ -4864,7 +4823,7 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; + #endif + int i; +- xmlNodePtr cur, delNode = NULL, oldContextNode; ++ xmlNodePtr cur, oldContextNode; + xmlNodeSetPtr list = NULL, oldList; + xsltStackElemPtr withParams = NULL; + int oldXPProximityPosition, oldXPContextSize; +@@ -4998,73 +4957,9 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, + else + cur = NULL; + while (cur != NULL) { +- switch (cur->type) { +- case XML_TEXT_NODE: +- if ((IS_BLANK_NODE(cur)) && +- (cur->parent != NULL) && +- (cur->parent->type == XML_ELEMENT_NODE) && +- (ctxt->style->stripSpaces != NULL)) { +- const xmlChar *val; +- +- if (cur->parent->ns != NULL) { +- val = (const xmlChar *) +- xmlHashLookup2(ctxt->style->stripSpaces, +- cur->parent->name, +- cur->parent->ns->href); +- if (val == NULL) { +- val = (const xmlChar *) +- xmlHashLookup2(ctxt->style->stripSpaces, +- BAD_CAST "*", +- cur->parent->ns->href); +- } +- } else { +- val = (const xmlChar *) +- xmlHashLookup2(ctxt->style->stripSpaces, +- cur->parent->name, NULL); +- } +- if ((val != NULL) && +- (xmlStrEqual(val, (xmlChar *) "strip"))) { +- delNode = cur; +- break; +- } +- } +- /* Intentional fall-through */ +- case XML_ELEMENT_NODE: +- case XML_DOCUMENT_NODE: +- case XML_HTML_DOCUMENT_NODE: +- case XML_CDATA_SECTION_NODE: +- case XML_PI_NODE: +- case XML_COMMENT_NODE: +- xmlXPathNodeSetAddUnique(list, cur); +- break; +- case XML_DTD_NODE: +- /* Unlink the DTD, it's still reachable +- * using doc->intSubset */ +- if (cur->next != NULL) +- cur->next->prev = cur->prev; +- if (cur->prev != NULL) +- cur->prev->next = cur->next; +- break; +- case XML_NAMESPACE_DECL: +- break; +- default: +-#ifdef WITH_XSLT_DEBUG_PROCESS +- XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, +- "xsltApplyTemplates: skipping cur type %d\n", +- cur->type)); +-#endif +- delNode = cur; +- } ++ if (IS_XSLT_REAL_NODE(cur)) ++ xmlXPathNodeSetAddUnique(list, cur); + cur = cur->next; +- if (delNode != NULL) { +-#ifdef WITH_XSLT_DEBUG_PROCESS +- XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, +- "xsltApplyTemplates: removing ignorable blank cur\n")); +-#endif +- xmlUnlinkNode(delNode); +- xmlFreeNode(delNode); +- delNode = NULL; +- } + } + } + diff --git a/patches/chromium/guard_webcontents_downloadimage_against_malformed_renderer.patch b/patches/chromium/guard_webcontents_downloadimage_against_malformed_renderer.patch new file mode 100644 index 0000000000000..fcee223cb0d37 --- /dev/null +++ b/patches/chromium/guard_webcontents_downloadimage_against_malformed_renderer.patch @@ -0,0 +1,232 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 10:54:46 +0200 +Subject: Guard WebContents::DownloadImage() against malformed renderer + response + +Callers expect that ImageDownloadCallback gets invoked with two vectors +having the same number of elements (one containing the bitmaps and the +other one the corresponding sizes). + +However, these vectors are populated directly from the Mojo response, +so there needs to be some browser-process sanitization to protect +against buggy or compromised renderers. + +In this patch, WebContentsImpl::OnDidDownloadImage() mimics a 400 error +if the response is malformed, similarly to how it's done in other edge +cases (renderer process dead upon download). Because this scenario is +a violation of the Mojo API contract, the browser process also issues +a bad message log (newly-introduced WCI_INVALID_DOWNLOAD_IMAGE_RESULT) +and shuts down the renderer process. + +(cherry picked from commit 034ba14e44f08e8ca84b42350f3238f847e08e5f) + +Change-Id: Ic0843e10efc26809fabd8f1bbe506ba1703d1486 +Fixed: 1201446 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2871796 + +diff --git a/components/favicon/core/favicon_handler.cc b/components/favicon/core/favicon_handler.cc +index 3d278c8e7cb1212c266e3b1838be8f658165a795..197901e64c7cd8d8e2750c28d502ee5428b35df4 100644 +--- a/components/favicon/core/favicon_handler.cc ++++ b/components/favicon/core/favicon_handler.cc +@@ -507,6 +507,8 @@ void FaviconHandler::OnDidDownloadFavicon( + const GURL& image_url, + const std::vector& bitmaps, + const std::vector& original_bitmap_sizes) { ++ DCHECK_EQ(bitmaps.size(), original_bitmap_sizes.size()); ++ + // Mark download as finished. + image_download_request_.Cancel(); + +diff --git a/components/favicon/core/favicon_handler.h b/components/favicon/core/favicon_handler.h +index 98706de7a7bcdeee857acd5a9113c45ecf82dca7..574d88ac91a6322644cbe5fda9e964807830c20e 100644 +--- a/components/favicon/core/favicon_handler.h ++++ b/components/favicon/core/favicon_handler.h +@@ -238,7 +238,9 @@ class FaviconHandler { + void ScheduleImageDownload(const GURL& image_url, + favicon_base::IconType icon_type); + +- // Triggered when a download of an image has finished. ++ // Triggered when a download of an image has finished. |bitmaps| and ++ // |original_bitmap_sizes| must contain the same number of elements (i.e. same ++ // vector size). + void OnDidDownloadFavicon( + favicon_base::IconType icon_type, + int id, +diff --git a/components/favicon/ios/web_favicon_driver.mm b/components/favicon/ios/web_favicon_driver.mm +index 963a4dbcc5c971ea53c5d603cc73d1d2dab4f3ae..1dd5f4f2b38b55b8831aa527dd52ae11d45632fb 100644 +--- a/components/favicon/ios/web_favicon_driver.mm ++++ b/components/favicon/ios/web_favicon_driver.mm +@@ -75,6 +75,7 @@ + for (const auto& frame : frames) { + sizes.push_back(gfx::Size(frame.width(), frame.height())); + } ++ DCHECK_EQ(frames.size(), sizes.size()); + } + std::move(local_callback) + .Run(local_download_id, metadata.http_response_code, local_url, +diff --git a/components/favicon_base/select_favicon_frames.cc b/components/favicon_base/select_favicon_frames.cc +index 90b58e621492d0e503f7965de91581c0a451d163..73cd7dd5331a818f413d831b4ae596bc88805a8a 100644 +--- a/components/favicon_base/select_favicon_frames.cc ++++ b/components/favicon_base/select_favicon_frames.cc +@@ -216,6 +216,7 @@ gfx::ImageSkia CreateFaviconImageSkia( + const std::vector& original_sizes, + int desired_size_in_dip, + float* score) { ++ DCHECK_EQ(bitmaps.size(), original_sizes.size()); + + const std::vector& favicon_scales = favicon_base::GetFaviconScales(); + std::vector desired_sizes; +diff --git a/components/favicon_base/select_favicon_frames.h b/components/favicon_base/select_favicon_frames.h +index 573a38c79cddf839622488589b71b4d19fbdfba6..eab1e54466763e5a6a6069a29e877da054972fa8 100644 +--- a/components/favicon_base/select_favicon_frames.h ++++ b/components/favicon_base/select_favicon_frames.h +@@ -38,6 +38,8 @@ extern const float kSelectFaviconFramesInvalidScore; + // it inspired by this method. + // If an unsupported scale (not in the favicon_base::GetFaviconScales()) + // is requested, the ImageSkia will automatically scales using lancoz3. ++// |original_sizes| represents the pixel sizes of the favicon bitmaps in ++// |bitmaps|, which also means both vectors must have the same size. + gfx::ImageSkia CreateFaviconImageSkia( + const std::vector& bitmaps, + const std::vector& original_sizes, +diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h +index c6675ef501de6cbaa175793ac0b13ba9b93a12e6..e198d661c711dc648a4e4c2249e0a60f69c406da 100644 +--- a/content/browser/bad_message.h ++++ b/content/browser/bad_message.h +@@ -260,6 +260,15 @@ enum BadMessageReason { + RFH_RECEIVED_ASSOCIATED_MESSAGE_WHILE_BFCACHED = 232, + RWH_CLOSE_PORTAL = 233, + MSDH_INVALID_STREAM_TYPE = 234, ++ RFH_CREATE_CHILD_FRAME_TOKENS_NOT_FOUND = 235, ++ ASGH_ASSOCIATED_INTERFACE_REQUEST = 236, ++ ASGH_RECEIVED_CONTROL_MESSAGE = 237, ++ CSDH_BAD_OWNER = 238, ++ SYNC_COMPOSITOR_NO_LOCAL_SURFACE_ID = 239, ++ WCI_INVALID_FULLSCREEN_OPTIONS = 240, ++ PAYMENTS_WITHOUT_PERMISSION = 241, ++ WEB_BUNDLE_INVALID_NAVIGATION_URL = 242, ++ WCI_INVALID_DOWNLOAD_IMAGE_RESULT = 243, + + // Please add new elements here. The naming convention is abbreviated class + // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the +diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc +index 750944d45d66f70a5e32219148a4f0cb9edd351c..d0e25934cda73577524b80051b7e4cd6150077e8 100644 +--- a/content/browser/web_contents/web_contents_impl.cc ++++ b/content/browser/web_contents/web_contents_impl.cc +@@ -169,6 +169,7 @@ + #include "third_party/blink/public/common/web_preferences/web_preferences.h" + #include "third_party/blink/public/mojom/frame/frame.mojom.h" + #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h" ++#include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom.h" + #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h" + #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h" + #include "third_party/skia/include/core/SkBitmap.h" +@@ -5066,18 +5067,18 @@ int WebContentsImpl::DownloadImageInFrame( + // respond with a 400 HTTP error code to indicate that something went wrong. + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, +- base::BindOnce(&WebContentsImpl::OnDidDownloadImage, +- weak_factory_.GetWeakPtr(), std::move(callback), +- download_id, url, 400, std::vector(), +- std::vector())); ++ base::BindOnce( ++ &WebContentsImpl::OnDidDownloadImage, weak_factory_.GetWeakPtr(), ++ initiator_frame->GetWeakPtr(), std::move(callback), download_id, ++ url, 400, std::vector(), std::vector())); + return download_id; + } + + mojo_image_downloader->DownloadImage( + url, is_favicon, preferred_size, max_bitmap_size, bypass_cache, + base::BindOnce(&WebContentsImpl::OnDidDownloadImage, +- weak_factory_.GetWeakPtr(), std::move(callback), +- download_id, url)); ++ weak_factory_.GetWeakPtr(), initiator_frame->GetWeakPtr(), ++ std::move(callback), download_id, url)); + return download_id; + } + +@@ -8044,6 +8045,7 @@ bool WebContentsImpl::CompletedFirstVisuallyNonEmptyPaint() { + } + + void WebContentsImpl::OnDidDownloadImage( ++ base::WeakPtr rfh, + ImageDownloadCallback callback, + int id, + const GURL& image_url, +@@ -8053,6 +8055,21 @@ void WebContentsImpl::OnDidDownloadImage( + OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnDidDownloadImage", + "image_url", + base::trace_event::ValueToString(image_url)); ++ ++ // Guard against buggy or compromised renderers that could violate the API ++ // contract that |images| and |original_image_sizes| must have the same ++ // length. ++ if (images.size() != original_image_sizes.size()) { ++ if (rfh) { ++ ReceivedBadMessage(rfh->GetProcess(), ++ bad_message::WCI_INVALID_DOWNLOAD_IMAGE_RESULT); ++ } ++ // Respond with a 400 to indicate that something went wrong. ++ std::move(callback).Run(id, 400, image_url, std::vector(), ++ std::vector()); ++ return; ++ } ++ + std::move(callback).Run(id, http_status_code, image_url, images, + original_image_sizes); + } +diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h +index b78cb8f558052e01d61456ca282359eb4b342c35..54a780a2cbbe1259b13d5d46c9a90aaffbb516a9 100644 +--- a/content/browser/web_contents/web_contents_impl.h ++++ b/content/browser/web_contents/web_contents_impl.h +@@ -1470,7 +1470,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, + std::set& result); + + // Called with the result of a DownloadImage() request. +- void OnDidDownloadImage(ImageDownloadCallback callback, ++ void OnDidDownloadImage(base::WeakPtr rfh, ++ ImageDownloadCallback callback, + int id, + const GURL& image_url, + int32_t http_status_code, +diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h +index a43c9972377f6365c3fe0e33f0d29700f597bd2d..8260da280d387847e18ca22782e71c0d6614385f 100644 +--- a/content/public/browser/web_contents.h ++++ b/content/public/browser/web_contents.h +@@ -918,8 +918,9 @@ class WebContents : public PageNavigator, + // |bitmaps| will be empty on download failure. + // |sizes| are the sizes in pixels of the bitmaps before they were resized due + // to the max bitmap size passed to DownloadImage(). Each entry in the bitmaps +- // vector corresponds to an entry in the sizes vector. If a bitmap was +- // resized, there should be a single returned bitmap. ++ // vector corresponds to an entry in the sizes vector (both vector sizes are ++ // guaranteed to be equal). If a bitmap was resized, there should be a single ++ // returned bitmap. + using ImageDownloadCallback = + base::OnceCallback& unfiltered, + uint32_t max_image_size, +@@ -202,6 +203,8 @@ void ImageDownloaderImpl::DidDownloadImage( + FilterAndResizeImagesForMaximalSize(images, max_image_size, &result_images, + &result_original_image_sizes); + ++ DCHECK_EQ(result_images.size(), result_original_image_sizes.size()); ++ + std::move(callback).Run(http_status_code, result_images, + result_original_image_sizes); + } diff --git a/patches/chromium/m86-lts_add_null_pointer_check_in_renderwidgethostinputeventrouter.patch b/patches/chromium/m86-lts_add_null_pointer_check_in_renderwidgethostinputeventrouter.patch new file mode 100644 index 0000000000000..f571eb348a900 --- /dev/null +++ b/patches/chromium/m86-lts_add_null_pointer_check_in_renderwidgethostinputeventrouter.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Lan Wei +Date: Tue, 20 Apr 2021 17:08:53 +0000 +Subject: M86-LTS: Add null pointer check in RenderWidgetHostInputEventRouter + +We have some crashes in RenderWidgetHostInputEventRouter class, we are +adding some null pointer check in this class to avoid the crash. + +(cherry picked from commit 5f47666b79ac7ded20e1c7657037498561bd3352) + +Bug: 1155297 +Change-Id: I3b63d5748523ae2ce8ab469832adfc75d586e411 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2818680 +Reviewed-by: Charlie Reis +Commit-Queue: Lan Wei +Cr-Original-Commit-Position: refs/heads/master@{#871108} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2838329 +Reviewed-by: Lan Wei +Commit-Queue: Achuith Bhandarkar +Owners-Override: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1617} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc +index 0f6b23277a83d398a738f651779278fd25bab1d2..ad11cebc476702414792dd2c63774c4fe6283382 100644 +--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc ++++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc +@@ -1949,7 +1949,7 @@ void RenderWidgetHostInputEventRouter::OnAggregatedHitTestRegionListUpdated( + const std::vector& hit_test_data) { + for (auto& region : hit_test_data) { + auto iter = owner_map_.find(region.frame_sink_id); +- if (iter != owner_map_.end()) ++ if (iter != owner_map_.end() && iter->second) + iter->second->NotifyHitTestRegionUpdated(region); + } + } diff --git a/patches/chromium/m86-lts_add_weak_pointer_to_rwhier_framesinkidownermap_and.patch b/patches/chromium/m86-lts_add_weak_pointer_to_rwhier_framesinkidownermap_and.patch new file mode 100644 index 0000000000000..8a5adfa9eb620 --- /dev/null +++ b/patches/chromium/m86-lts_add_weak_pointer_to_rwhier_framesinkidownermap_and.patch @@ -0,0 +1,168 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Lan Wei +Date: Tue, 20 Apr 2021 16:32:33 +0000 +Subject: M86-LTS: Add weak pointer to RWHIER::FrameSinkIdOwnerMap and + RWHIER::TargetMap + +In RWHIER::FrameSinkIdOwnerMap and RWHIER::TargetMap, we change raw +pointer of RenderWidgetHostViewBase to weak pointer, such as +using FrameSinkIdOwnerMap = std::unordered_map, + viz::FrameSinkIdHash>; +using TargetMap = std::map>; + +This CL should fix the crash of stale pointer. + +(cherry picked from commit 3e3e3cf7036d7e33a4d68b8416ae25730f9eee1d) + +Bug: 1155297 +Change-Id: I5b3270882ef06ae48c86bd460261723c7113953d +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2792344 +Reviewed-by: James MacLean +Reviewed-by: Aaron Colwell +Commit-Queue: Lan Wei +Cr-Original-Commit-Position: refs/heads/master@{#870013} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2838587 +Owners-Override: Achuith Bhandarkar +Auto-Submit: Achuith Bhandarkar +Reviewed-by: Lan Wei +Cr-Commit-Position: refs/branch-heads/4240@{#1616} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc +index ad11cebc476702414792dd2c63774c4fe6283382..8ee7c8aff4b12da9e2257caa95ccfab1f6177d46 100644 +--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc ++++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc +@@ -345,7 +345,7 @@ void RenderWidgetHostInputEventRouter::OnRenderWidgetHostViewBaseDestroyed( + + // Remove this view from the owner_map. + for (auto entry : owner_map_) { +- if (entry.second == view) { ++ if (entry.second.get() == view) { + owner_map_.erase(entry.first); + // There will only be one instance of a particular view in the map. + break; +@@ -368,7 +368,7 @@ void RenderWidgetHostInputEventRouter::OnRenderWidgetHostViewBaseDestroyed( + // replace it with nullptr so that we maintain the 1:1 correspondence between + // map entries and the touch sequences that underly them. + for (auto it : touchscreen_gesture_target_map_) { +- if (it.second == view) ++ if (it.second.get() == view) + it.second = nullptr; + } + +@@ -417,8 +417,10 @@ void RenderWidgetHostInputEventRouter::OnRenderWidgetHostViewBaseDestroyed( + void RenderWidgetHostInputEventRouter::ClearAllObserverRegistrations() { + // Since we're shutting down, it's safe to call RenderWidgetHostViewBase:: + // RemoveObserver() directly here. +- for (auto entry : owner_map_) +- entry.second->RemoveObserver(this); ++ for (auto entry : owner_map_) { ++ if (entry.second) ++ entry.second->RemoveObserver(this); ++ } + owner_map_.clear(); + viz::HostFrameSinkManager* manager = GetHostFrameSinkManager(); + if (manager) +@@ -840,7 +842,7 @@ void RenderWidgetHostInputEventRouter::DispatchTouchEvent( + touch_event.unique_touch_event_id) == + touchscreen_gesture_target_map_.end()); + touchscreen_gesture_target_map_[touch_event.unique_touch_event_id] = +- touch_target_; ++ touch_target_->GetWeakPtr(); + } else if (touch_event.GetType() == blink::WebInputEvent::Type::kTouchStart) { + active_touches_ += CountChangedTouchPoints(touch_event); + } +@@ -1352,7 +1354,7 @@ void RenderWidgetHostInputEventRouter::AddFrameSinkIdOwner( + // We want to be notified if the owner is destroyed so we can remove it from + // our map. + owner->AddObserver(this); +- owner_map_.insert(std::make_pair(id, owner)); ++ owner_map_.insert(std::make_pair(id, owner->GetWeakPtr())); + } + + void RenderWidgetHostInputEventRouter::RemoveFrameSinkIdOwner( +@@ -1364,7 +1366,8 @@ void RenderWidgetHostInputEventRouter::RemoveFrameSinkIdOwner( + // stale values if the view destructs and isn't an observer anymore. + // Note: the view the iterator points at will be deleted in the following + // call, and shouldn't be used after this point. +- OnRenderWidgetHostViewBaseDestroyed(it_to_remove->second); ++ if (it_to_remove->second) ++ OnRenderWidgetHostViewBaseDestroyed(it_to_remove->second.get()); + } + } + +@@ -1415,7 +1418,7 @@ RenderWidgetHostInputEventRouter::FindTouchscreenGestureEventTarget( + bool RenderWidgetHostInputEventRouter::IsViewInMap( + const RenderWidgetHostViewBase* view) const { + DCHECK(!is_registered(view->GetFrameSinkId()) || +- owner_map_.find(view->GetFrameSinkId())->second == view); ++ owner_map_.find(view->GetFrameSinkId())->second.get() == view); + return is_registered(view->GetFrameSinkId()); + } + +@@ -1552,7 +1555,7 @@ void RenderWidgetHostInputEventRouter::DispatchTouchscreenGestureEvent( + target = result.view; + fallback_target_location = transformed_point; + } else if (is_gesture_start) { +- target = gesture_target_it->second; ++ target = gesture_target_it->second.get(); + touchscreen_gesture_target_map_.erase(gesture_target_it); + + // Abort any scroll bubbling in progress to avoid double entry. +@@ -1738,7 +1741,7 @@ RenderWidgetHostInputEventRouter::FindViewFromFrameSinkId( + // If the point hit a Surface whose namspace is no longer in the map, then + // it likely means the RenderWidgetHostView has been destroyed but its + // parent frame has not sent a new compositor frame since that happened. +- return iter == owner_map_.end() ? nullptr : iter->second; ++ return iter == owner_map_.end() ? nullptr : iter->second.get(); + } + + bool RenderWidgetHostInputEventRouter::ShouldContinueHitTesting( +@@ -1758,8 +1761,10 @@ bool RenderWidgetHostInputEventRouter::ShouldContinueHitTesting( + std::vector + RenderWidgetHostInputEventRouter::GetRenderWidgetHostViewsForTests() const { + std::vector hosts; +- for (auto entry : owner_map_) +- hosts.push_back(entry.second); ++ for (auto entry : owner_map_) { ++ DCHECK(entry.second); ++ hosts.push_back(entry.second.get()); ++ } + + return hosts; + } +@@ -1928,8 +1933,10 @@ void RenderWidgetHostInputEventRouter::SetCursor(const WebCursor& cursor) { + last_device_scale_factor_ = + last_mouse_move_root_view_->current_device_scale_factor(); + if (auto* cursor_manager = last_mouse_move_root_view_->GetCursorManager()) { +- for (auto it : owner_map_) +- cursor_manager->UpdateCursor(it.second, cursor); ++ for (auto it : owner_map_) { ++ if (it.second) ++ cursor_manager->UpdateCursor(it.second.get(), cursor); ++ } + } + } + +diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h +index 42629b133b883865bebfa27f5d29eb5e2d153d0b..c4ce54a5a6beb509d6242ee0e5ebdf4c88f01251 100644 +--- a/content/browser/renderer_host/render_widget_host_input_event_router.h ++++ b/content/browser/renderer_host/render_widget_host_input_event_router.h +@@ -195,10 +195,11 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter + FRIEND_TEST_ALL_PREFIXES(BrowserSideFlingBrowserTest, + InertialGSUBubblingStopsWhenParentCannotScroll); + +- using FrameSinkIdOwnerMap = std::unordered_map; +- using TargetMap = std::map; ++ using FrameSinkIdOwnerMap = ++ std::unordered_map, ++ viz::FrameSinkIdHash>; ++ using TargetMap = std::map>; + + void ClearAllObserverRegistrations(); + RenderWidgetTargetResult FindViewAtLocation( diff --git a/patches/chromium/m86-lts_longtaskdetector_remove_container_mutation_during.patch b/patches/chromium/m86-lts_longtaskdetector_remove_container_mutation_during.patch new file mode 100644 index 0000000000000..726cd2be674f5 --- /dev/null +++ b/patches/chromium/m86-lts_longtaskdetector_remove_container_mutation_during.patch @@ -0,0 +1,99 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yutaka Hirano +Date: Fri, 11 Jun 2021 08:07:55 +0000 +Subject: Remove container mutation during iteration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On LongTaskDetector, we call OnLongTaskDetected for all registered +observers. Some observers call LongTaskDetector::UnregisterObserver +in the callback, which is problematic because container mutation is +not allowed during iteration. + +Copy the observer set to avoid the violation. + +(cherry picked from commit 702f4d4ddb963cafb0d133972282dfc803510b75) + +(cherry picked from commit e88c656a9fb4a7bb1c66ddcedae8049a448ebef4) + +Bug: 1210487 +Change-Id: Iccea748ac144def6884be8cf542cdc3572bed81a +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2909934 +Reviewed-by: Deep Roy +Reviewed-by: Nicolás Peña Moreno +Commit-Queue: Yutaka Hirano +Cr-Original-Original-Commit-Position: refs/heads/master@{#885033} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2939704 +Auto-Submit: Yutaka Hirano +Owners-Override: Prudhvi Kumar Bommana +Reviewed-by: Prudhvi Kumar Bommana +Cr-Original-Commit-Position: refs/branch-heads/4472@{#1443} +Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2945787 +Owners-Override: Victor-Gabriel Savu +Reviewed-by: Artem Sumaneev +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/4240@{#1669} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/third_party/blink/renderer/core/loader/long_task_detector.cc b/third_party/blink/renderer/core/loader/long_task_detector.cc +index 7e1499b1ddde30db2344db6fd9a9d3e7be574033..f040ae5053265fb136c629f106aeefa0b01130f1 100644 +--- a/third_party/blink/renderer/core/loader/long_task_detector.cc ++++ b/third_party/blink/renderer/core/loader/long_task_detector.cc +@@ -43,7 +43,10 @@ void LongTaskDetector::DidProcessTask(base::TimeTicks start_time, + if ((end_time - start_time) < LongTaskDetector::kLongTaskThreshold) + return; + +- for (auto& observer : observers_) { ++ // We copy `observers_` because it might be mutated in OnLongTaskDetected, ++ // and container mutation is not allowed during iteration. ++ const HeapHashSet> observers = observers_; ++ for (auto& observer : observers) { + observer->OnLongTaskDetected(start_time, end_time); + } + } +diff --git a/third_party/blink/renderer/core/loader/long_task_detector_test.cc b/third_party/blink/renderer/core/loader/long_task_detector_test.cc +index 3384fa8ebfb0bd3ad1c408390db3fcb26edc4118..04959d3b682ddbf40577adc5799fe57a9ae9d500 100644 +--- a/third_party/blink/renderer/core/loader/long_task_detector_test.cc ++++ b/third_party/blink/renderer/core/loader/long_task_detector_test.cc +@@ -27,9 +27,24 @@ class TestLongTaskObserver : + last_long_task_start = start_time; + last_long_task_end = end_time; + } +-}; // Anonymous namespace ++}; ++ ++class SelfUnregisteringObserver ++ : public GarbageCollected, ++ public LongTaskObserver { ++ public: ++ void OnLongTaskDetected(base::TimeTicks, base::TimeTicks) override { ++ called_ = true; ++ LongTaskDetector::Instance().UnregisterObserver(this); ++ } ++ bool IsCalled() const { return called_; } ++ ++ private: ++ bool called_ = false; ++}; + + } // namespace ++ + class LongTaskDetectorTest : public testing::Test { + public: + // Public because it's executed on a task queue. +@@ -126,4 +141,13 @@ TEST_F(LongTaskDetectorTest, RegisterSameObserverTwice) { + long_task_end_when_registered); + } + ++TEST_F(LongTaskDetectorTest, SelfUnregisteringObserver) { ++ auto* observer = MakeGarbageCollected(); ++ ++ LongTaskDetector::Instance().RegisterObserver(observer); ++ SimulateTask(LongTaskDetector::kLongTaskThreshold + ++ base::TimeDelta::FromMilliseconds(10)); ++ EXPECT_TRUE(observer->IsCalled()); ++} ++ + } // namespace blink diff --git a/patches/chromium/m86-lts_reduce_memory_consumption_on.patch b/patches/chromium/m86-lts_reduce_memory_consumption_on.patch new file mode 100644 index 0000000000000..7ebb245a3d298 --- /dev/null +++ b/patches/chromium/m86-lts_reduce_memory_consumption_on.patch @@ -0,0 +1,118 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yutaka Hirano +Date: Fri, 11 Jun 2021 08:42:36 +0000 +Subject: Reduce memory consumption on LongTaskObserver::DidProcessTask + +https://crrev.com/c/2909934 fixed a security issue, but it introduced a +copy operation for each DidProcessTask for a long task. We see a memory +regression on the change, and this is an attempt to mitigate the +regression. + +(cherry picked from commit 8097e73295a88e64d8318d982847a5e4f2bcc4d2) + +(cherry picked from commit 7be6a34fe2f01af881bb074bc616bf5b6b5f7c31) + +Bug: 1210487, 1211539 +Change-Id: Ib9101e29d70fadb11b7967754e847bb5cc754feb +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2915153 +Reviewed-by: Benoit L +Commit-Queue: Yutaka Hirano +Cr-Original-Original-Commit-Position: refs/heads/master@{#886221} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2944320 +Bot-Commit: Rubber Stamper +Cr-Original-Commit-Position: refs/branch-heads/4472@{#1460} +Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2952502 +Owners-Override: Victor-Gabriel Savu +Reviewed-by: Artem Sumaneev +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/4240@{#1670} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/third_party/blink/renderer/core/loader/long_task_detector.cc b/third_party/blink/renderer/core/loader/long_task_detector.cc +index f040ae5053265fb136c629f106aeefa0b01130f1..3816779cfafaef06295734b4a8a2f033bf752691 100644 +--- a/third_party/blink/renderer/core/loader/long_task_detector.cc ++++ b/third_party/blink/renderer/core/loader/long_task_detector.cc +@@ -24,6 +24,7 @@ LongTaskDetector::LongTaskDetector() = default; + void LongTaskDetector::RegisterObserver(LongTaskObserver* observer) { + DCHECK(IsMainThread()); + DCHECK(observer); ++ DCHECK(!iterating_); + if (observers_.insert(observer).is_new_entry && observers_.size() == 1) { + // Number of observers just became non-zero. + Thread::Current()->AddTaskTimeObserver(this); +@@ -32,6 +33,10 @@ void LongTaskDetector::RegisterObserver(LongTaskObserver* observer) { + + void LongTaskDetector::UnregisterObserver(LongTaskObserver* observer) { + DCHECK(IsMainThread()); ++ if (iterating_) { ++ observers_to_be_removed_.push_back(observer); ++ return; ++ } + observers_.erase(observer); + if (observers_.size() == 0) { + Thread::Current()->RemoveTaskTimeObserver(this); +@@ -43,16 +48,21 @@ void LongTaskDetector::DidProcessTask(base::TimeTicks start_time, + if ((end_time - start_time) < LongTaskDetector::kLongTaskThreshold) + return; + +- // We copy `observers_` because it might be mutated in OnLongTaskDetected, +- // and container mutation is not allowed during iteration. +- const HeapHashSet> observers = observers_; +- for (auto& observer : observers) { ++ iterating_ = true; ++ for (auto& observer : observers_) { + observer->OnLongTaskDetected(start_time, end_time); + } ++ iterating_ = false; ++ ++ for (const auto& observer : observers_to_be_removed_) { ++ UnregisterObserver(observer); ++ } ++ observers_to_be_removed_.clear(); + } + + void LongTaskDetector::Trace(Visitor* visitor) const { + visitor->Trace(observers_); ++ visitor->Trace(observers_to_be_removed_); + } + + } // namespace blink +diff --git a/third_party/blink/renderer/core/loader/long_task_detector.h b/third_party/blink/renderer/core/loader/long_task_detector.h +index dc6f0dbab5c059b83bfe4212f0126e9690ab1a7f..5fd4bb8d2abcc67dd4e47927daa260fa37bc4aca 100644 +--- a/third_party/blink/renderer/core/loader/long_task_detector.h ++++ b/third_party/blink/renderer/core/loader/long_task_detector.h +@@ -49,6 +49,8 @@ class CORE_EXPORT LongTaskDetector final + base::TimeTicks end_time) override; + + HeapHashSet> observers_; ++ HeapVector> observers_to_be_removed_; ++ bool iterating_ = false; + + DISALLOW_COPY_AND_ASSIGN(LongTaskDetector); + }; +diff --git a/third_party/blink/renderer/core/loader/long_task_detector_test.cc b/third_party/blink/renderer/core/loader/long_task_detector_test.cc +index 04959d3b682ddbf40577adc5799fe57a9ae9d500..403ba452362a3fa2a6b24f238bad35d9eb1bac0c 100644 +--- a/third_party/blink/renderer/core/loader/long_task_detector_test.cc ++++ b/third_party/blink/renderer/core/loader/long_task_detector_test.cc +@@ -39,6 +39,8 @@ class SelfUnregisteringObserver + } + bool IsCalled() const { return called_; } + ++ void Reset() { called_ = false; } ++ + private: + bool called_ = false; + }; +@@ -148,6 +150,11 @@ TEST_F(LongTaskDetectorTest, SelfUnregisteringObserver) { + SimulateTask(LongTaskDetector::kLongTaskThreshold + + base::TimeDelta::FromMilliseconds(10)); + EXPECT_TRUE(observer->IsCalled()); ++ observer->Reset(); ++ ++ SimulateTask(LongTaskDetector::kLongTaskThreshold + ++ base::TimeDelta::FromMilliseconds(10)); ++ EXPECT_FALSE(observer->IsCalled()); + } + + } // namespace blink diff --git a/patches/chromium/m90_devtools_expect_pagehandler_may_be_destroyed_during.patch b/patches/chromium/m90_devtools_expect_pagehandler_may_be_destroyed_during.patch new file mode 100644 index 0000000000000..0e5277e498bc9 --- /dev/null +++ b/patches/chromium/m90_devtools_expect_pagehandler_may_be_destroyed_during.patch @@ -0,0 +1,132 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Kosyakov +Date: Thu, 1 Apr 2021 06:51:26 +0000 +Subject: DevTools: expect PageHandler may be destroyed during Page.navigate + +(cherry picked from commit ff5e70191ec701cce4f84aaa25cd676376253a8a) + +Bug: 1188889 +Change-Id: I5c2fcca84834d66c46d77a70683212c2330177a5 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2787756 +Commit-Queue: Andrey Kosyakov +Reviewed-by: Dmitry Gozman +Reviewed-by: Karan Bhatia +Cr-Original-Commit-Position: refs/heads/master@{#867507} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2798538 +Cr-Commit-Position: refs/branch-heads/4430@{#968} +Cr-Branched-From: e5ce7dc4f7518237b3d9bb93cccca35d25216cbe-refs/heads/master@{#857950} + +diff --git a/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chrome/browser/extensions/api/debugger/debugger_apitest.cc +index afd7e870a8dbe036cd03dd37d57d767d4104b630..3df62159c397b057bf8e306271b02959c8ce77c1 100644 +--- a/chrome/browser/extensions/api/debugger/debugger_apitest.cc ++++ b/chrome/browser/extensions/api/debugger/debugger_apitest.cc +@@ -24,6 +24,7 @@ + #include "components/sessions/content/session_tab_helper.h" + #include "content/public/test/browser_test.h" + #include "content/public/test/browser_test_utils.h" ++#include "content/public/test/no_renderer_crashes_assertion.h" + #include "extensions/browser/extension_function.h" + #include "extensions/common/extension.h" + #include "extensions/common/extension_builder.h" +@@ -364,6 +365,15 @@ IN_PROC_BROWSER_TEST_F(DebuggerExtensionApiTest, AttachToEmptyUrls) { + ASSERT_TRUE(RunExtensionTest("debugger_attach_to_empty_urls")) << message_; + } + ++// Tests that navigation to a forbidden URL is properly denied and ++// does not cause a crash. ++// This is a regression test for https://crbug.com/1188889. ++IN_PROC_BROWSER_TEST_F(DebuggerExtensionApiTest, NavigateToForbiddenUrl) { ++ content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; ++ ASSERT_TRUE(RunExtensionTest("debugger_navigate_to_forbidden_url")) ++ << message_; ++} ++ + class SitePerProcessDebuggerExtensionApiTest : public DebuggerExtensionApiTest { + public: + void SetUpCommandLine(base::CommandLine* command_line) override { +diff --git a/chrome/test/data/extensions/api_test/debugger_navigate_to_forbidden_url/background.js b/chrome/test/data/extensions/api_test/debugger_navigate_to_forbidden_url/background.js +new file mode 100644 +index 0000000000000000000000000000000000000000..e2ef32fffd3e5d49e7dc10d53f8c891ddb0f3872 +--- /dev/null ++++ b/chrome/test/data/extensions/api_test/debugger_navigate_to_forbidden_url/background.js +@@ -0,0 +1,28 @@ ++// Copyright 2021 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++const protocolVersion = '1.3'; ++const DETACHED_WHILE_HANDLING = 'Detached while handling command.'; ++ ++chrome.test.runTests([ ++ async function testNavigateToForbiddenUrl() { ++ const {openTab} = await import('/_test_resources/test_util/tabs_util.js'); ++ const tab = await openTab('about:blank'); ++ const debuggee = {tabId: tab.id}; ++ await new Promise(resolve => ++ chrome.debugger.attach(debuggee, protocolVersion, resolve)); ++ chrome.debugger.sendCommand(debuggee, 'Page.crash'); ++ await new Promise(resolve => ++ chrome.debugger.onEvent.addListener((source, method, params) => { ++ if (method === 'Inspector.targetCrashed') ++ resolve(); ++ })); ++ const result = await new Promise(resolve => ++ chrome.debugger.sendCommand(debuggee, 'Page.navigate', { ++ url: 'chrome://version' ++ }, resolve)); ++ chrome.test.assertLastError(DETACHED_WHILE_HANDLING); ++ chrome.test.succeed(); ++ } ++]); +diff --git a/chrome/test/data/extensions/api_test/debugger_navigate_to_forbidden_url/manifest.json b/chrome/test/data/extensions/api_test/debugger_navigate_to_forbidden_url/manifest.json +new file mode 100644 +index 0000000000000000000000000000000000000000..05db294ed7f49893431b0039a5f338d20e08f27d +--- /dev/null ++++ b/chrome/test/data/extensions/api_test/debugger_navigate_to_forbidden_url/manifest.json +@@ -0,0 +1,11 @@ ++{ ++ "name": "Debugger API test for CDP-initiated navigation to forbidden URLs", ++ "version": "1.0", ++ "manifest_version": 2, ++ "background": { ++ "scripts": ["background.js"] ++ }, ++ "permissions": [ ++ "debugger" ++ ] ++} +diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc +index 770f61f64d5c997aa75873c634a79caccdc353ff..c23d575ca94a7ea9cef8d05e987253f31699f631 100644 +--- a/content/browser/devtools/protocol/page_handler.cc ++++ b/content/browser/devtools/protocol/page_handler.cc +@@ -501,8 +501,12 @@ void PageHandler::Navigate(const std::string& url, + params.referrer = Referrer(GURL(referrer.fromMaybe("")), policy); + params.transition_type = type; + params.frame_tree_node_id = frame_tree_node->frame_tree_node_id(); ++ // Handler may be destroyed while navigating if the session ++ // gets disconnected as a result of access checks. ++ base::WeakPtr weak_self = weak_factory_.GetWeakPtr(); + frame_tree_node->navigator().GetController()->LoadURLWithParams(params); +- ++ if (!weak_self) ++ return; + base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token(); + auto navigate_callback = navigate_callbacks_.find(frame_token); + if (navigate_callback != navigate_callbacks_.end()) { +diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc +index 01c855696f4fc8e6e432d78178fafe8c9a337134..331fc4bbbb458517693a5351a37f6361ace39479 100644 +--- a/content/browser/devtools/render_frame_devtools_agent_host.cc ++++ b/content/browser/devtools/render_frame_devtools_agent_host.cc +@@ -474,8 +474,11 @@ void RenderFrameDevToolsAgentHost::UpdateFrameHost( + if (!ShouldAllowSession(session)) + restricted_sessions.push_back(session); + } +- if (!restricted_sessions.empty()) ++ scoped_refptr protect; ++ if (!restricted_sessions.empty()) { ++ protect = this; + ForceDetachRestrictedSessions(restricted_sessions); ++ } + + UpdateFrameAlive(); + } diff --git a/patches/chromium/media_feeds_disable_media_feeds_and_related_features.patch b/patches/chromium/media_feeds_disable_media_feeds_and_related_features.patch new file mode 100644 index 0000000000000..b72815626f340 --- /dev/null +++ b/patches/chromium/media_feeds_disable_media_feeds_and_related_features.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 14:12:59 +0200 +Subject: Media Feeds: Disable Media Feeds and related features + +Media Feeds is deleted in M91 and later and is unused in previous +versions as well. There is a security issue with Media Feeds though, so +we'd like to force it to be disabled in previous versions, so this CL +turns it off for M90. + +(cherry picked from commit b064a73431541e520d273c227e762983c2f177b7) + +Bug: 1195340 +Change-Id: I29e18be2abe4c1b4560d6324af3b6da93a97d947 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2847504 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2883741 + +diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc +index 0c20d23e273405a78f59d91196f12160dba2e824..37fc954bffa26cad04a8d31e2838416f84670281 100644 +--- a/media/base/media_switches.cc ++++ b/media/base/media_switches.cc +@@ -730,15 +730,16 @@ const base::Feature kMediaEngagementHTTPSOnly{ + + // Enables Media Feeds to allow sites to provide specific recommendations for + // users. +-const base::Feature kMediaFeeds{"MediaFeeds", base::FEATURE_ENABLED_BY_DEFAULT}; ++const base::Feature kMediaFeeds{"MediaFeeds", ++ base::FEATURE_DISABLED_BY_DEFAULT}; + + // Enables fetching Media Feeds periodically in the background. + const base::Feature kMediaFeedsBackgroundFetching{ +- "MediaFeedsBackgroundFetching", base::FEATURE_ENABLED_BY_DEFAULT}; ++ "MediaFeedsBackgroundFetching", base::FEATURE_DISABLED_BY_DEFAULT}; + + // Enables checking Media Feeds against safe search to prevent adult content. + const base::Feature kMediaFeedsSafeSearch{"MediaFeedsSafeSearch", +- base::FEATURE_ENABLED_BY_DEFAULT}; ++ base::FEATURE_DISABLED_BY_DEFAULT}; + + // Send events to devtools rather than to chrome://media-internals + const base::Feature kMediaInspectorLogging{"MediaInspectorLogging", 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 2563ee05bf62f..49d5b4063e346 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 1e9e1d93cb783c104c2672189df7c8410a3dfbed..3dc5c6d6027be44c1e799bb8e0b509a03bae963a 100644 +index 1e9e1d93cb783c104c2672189df7c8410a3dfbed..e36e5f9306bda8d9523d14d46dd71ea2f3bb8530 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -115,6 +115,11 @@ @@ -116,67 +116,17 @@ index 1e9e1d93cb783c104c2672189df7c8410a3dfbed..3dc5c6d6027be44c1e799bb8e0b509a0 void NetworkContext::CreateURLLoaderFactory( mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) { -@@ -1820,8 +1905,9 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( - "NetworkContext should pass CertVerifierServiceRemoteParams."; - - std::unique_ptr cert_verifier; -+ std::unique_ptr temp_verifier; - if (g_cert_verifier_for_testing) { -- cert_verifier = std::make_unique(); -+ temp_verifier = std::make_unique(); - } else { - if (params_->cert_verifier_params && - params_->cert_verifier_params->is_remote_params()) { -@@ -1849,14 +1935,14 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( - cert_net_fetcher_ = - base::MakeRefCounted(); - -- cert_verifier = CreateCertVerifier(creation_params, cert_net_fetcher_); -+ temp_verifier = CreateCertVerifier(creation_params, cert_net_fetcher_); +@@ -1852,6 +1937,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( + cert_verifier = CreateCertVerifier(creation_params, cert_net_fetcher_); } - // Whether the cert verifier is remote or in-process, we should wrap it in - // caching and coalescing layers to avoid extra verifications and IPCs. -- cert_verifier = std::make_unique( -+ temp_verifier = std::make_unique( - std::make_unique( -- std::move(cert_verifier))); -+ std::move(temp_verifier))); - - #if defined(OS_CHROMEOS) - cert_verifier_with_trust_anchors_ = -@@ -1865,13 +1951,27 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( - UpdateAdditionalCertificates( - std::move(params_->initial_additional_certificates)); - cert_verifier_with_trust_anchors_->InitializeOnIOThread( -- std::move(cert_verifier)); -- cert_verifier = base::WrapUnique(cert_verifier_with_trust_anchors_); -+ std::move(temp_verifier)); -+ temp_verifier = base::WrapUnique(cert_verifier_with_trust_anchors_); - #endif // defined(OS_CHROMEOS) -+ if (!temp_verifier) { -+#if !defined(OS_LINUX) -+ temp_verifier = std::make_unique( -+ net::CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher_))); -+#else -+ temp_verifier = std::make_unique( -+ net::CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher_))); -+#endif -+ } -+ auto remote_cert_verifier = std::make_unique(std::move(temp_verifier)); ++ auto remote_cert_verifier = std::make_unique(std::move(cert_verifier)); + remote_cert_verifier_ = remote_cert_verifier.get(); -+ cert_verifier = std::make_unique(std::move(remote_cert_verifier)); - } - -- builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( -- *command_line, nullptr, std::move(cert_verifier))); -+ cert_verifier = IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( -+ *command_line, nullptr, std::move(cert_verifier)); ++ cert_verifier = std::move(remote_cert_verifier); + -+ builder.SetCertVerifier(std::move(cert_verifier)); - - std::unique_ptr network_delegate = - std::make_unique( + // Whether the cert verifier is remote or in-process, we should wrap it in + // caching and coalescing layers to avoid extra verifications and IPCs. + cert_verifier = std::make_unique( diff --git a/services/network/network_context.h b/services/network/network_context.h index e1a8746bcdaf61c181566369b380af5ead3a7796..1372f6f6ca4899cc7b230a3cd1b26db4c16325b5 100644 --- a/services/network/network_context.h diff --git a/patches/chromium/notifications_crash_if_improper_action_icons_sent_from_renderer.patch b/patches/chromium/notifications_crash_if_improper_action_icons_sent_from_renderer.patch new file mode 100644 index 0000000000000..5661927b006dc --- /dev/null +++ b/patches/chromium/notifications_crash_if_improper_action_icons_sent_from_renderer.patch @@ -0,0 +1,149 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 12:54:32 +0200 +Subject: Notifications: crash if improper action icons sent from renderer. + +Previously, the code only called DCHECK but as this data is from a +renderer we should probably crash the browser. + +(cherry picked from commit 3b28dc50187b22e080ad9c1e4e6c4f3b08f3136d) + +Bug: 1200019 +Change-Id: If4d9d48c8e18a3ed9c8bb3a50b952591259e0db5 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2838205 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2872493 + +diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc +index 403cf8f11bec68504e32ebace2f80e04ad136491..5d5f7fafd484ff5cb9fe31b459b5b23b740cf513 100644 +--- a/chrome/browser/notifications/platform_notification_service_impl.cc ++++ b/chrome/browser/notifications/platform_notification_service_impl.cc +@@ -403,8 +403,10 @@ PlatformNotificationServiceImpl::CreateNotificationFromData( + const std::string& notification_id, + const blink::PlatformNotificationData& notification_data, + const blink::NotificationResources& notification_resources) const { +- DCHECK_EQ(notification_data.actions.size(), +- notification_resources.action_icons.size()); ++ // Blink always populates action icons to match the actions, even if no icon ++ // was fetched, so this indicates a compromised renderer. ++ CHECK_EQ(notification_data.actions.size(), ++ notification_resources.action_icons.size()); + + message_center::RichNotificationData optional_fields; + +diff --git a/content/browser/notifications/blink_notification_service_impl.cc b/content/browser/notifications/blink_notification_service_impl.cc +index 7105781e0019b456f287fd0ebb6e309efe2cecad..8fcbb96ed39da9ec468cbe310bf8f499e338a235 100644 +--- a/content/browser/notifications/blink_notification_service_impl.cc ++++ b/content/browser/notifications/blink_notification_service_impl.cc +@@ -39,6 +39,9 @@ const char kBadMessageImproperNotificationImage[] = + "disabled."; + const char kBadMessageInvalidNotificationTriggerTimestamp[] = + "Received an invalid notification trigger timestamp."; ++const char kBadMessageInvalidNotificationActionButtons[] = ++ "Received a notification with a number of action images that does not " ++ "match the number of actions."; + + // Returns the implementation of the PlatformNotificationService. May be NULL. + PlatformNotificationService* GetNotificationService( +@@ -134,7 +137,8 @@ void BlinkNotificationServiceImpl::DisplayNonPersistentNotification( + mojo::PendingRemote + event_listener_remote) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +- if (!ValidateNotificationResources(notification_resources)) ++ if (!ValidateNotificationDataAndResources(platform_notification_data, ++ notification_resources)) + return; + + if (!GetNotificationService(browser_context_)) +@@ -190,28 +194,32 @@ BlinkNotificationServiceImpl::CheckPermissionStatus() { + origin_.GetURL()); + } + +-bool BlinkNotificationServiceImpl::ValidateNotificationResources( ++bool BlinkNotificationServiceImpl::ValidateNotificationDataAndResources( ++ const blink::PlatformNotificationData& platform_notification_data, + const blink::NotificationResources& notification_resources) { +- if (notification_resources.image.drawsNothing() || +- base::FeatureList::IsEnabled(features::kNotificationContentImage)) +- return true; +- receiver_.ReportBadMessage(kBadMessageImproperNotificationImage); +- // The above ReportBadMessage() closes |binding_| but does not trigger its +- // connection error handler, so we need to call the error handler explicitly +- // here to do some necessary work. +- OnConnectionError(); +- return false; +-} ++ if (platform_notification_data.actions.size() != ++ notification_resources.action_icons.size()) { ++ receiver_.ReportBadMessage(kBadMessageInvalidNotificationActionButtons); ++ OnConnectionError(); ++ return false; ++ } + +-// Checks if this notification has a valid trigger. +-bool BlinkNotificationServiceImpl::ValidateNotificationData( +- const blink::PlatformNotificationData& notification_data) { +- if (!CheckNotificationTriggerRange(notification_data)) { ++ if (!CheckNotificationTriggerRange(platform_notification_data)) { + receiver_.ReportBadMessage(kBadMessageInvalidNotificationTriggerTimestamp); + OnConnectionError(); + return false; + } + ++ if (!notification_resources.image.drawsNothing() && ++ !base::FeatureList::IsEnabled(features::kNotificationContentImage)) { ++ receiver_.ReportBadMessage(kBadMessageImproperNotificationImage); ++ // The above ReportBadMessage() closes |binding_| but does not trigger its ++ // connection error handler, so we need to call the error handler explicitly ++ // here to do some necessary work. ++ OnConnectionError(); ++ return false; ++ } ++ + return true; + } + +@@ -221,10 +229,8 @@ void BlinkNotificationServiceImpl::DisplayPersistentNotification( + const blink::NotificationResources& notification_resources, + DisplayPersistentNotificationCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +- if (!ValidateNotificationResources(notification_resources)) +- return; +- +- if (!ValidateNotificationData(platform_notification_data)) ++ if (!ValidateNotificationDataAndResources(platform_notification_data, ++ notification_resources)) + return; + + if (!GetNotificationService(browser_context_)) { +diff --git a/content/browser/notifications/blink_notification_service_impl.h b/content/browser/notifications/blink_notification_service_impl.h +index dc5307e6500b0bfb5da83e8d8ff8886b91133522..fe1abadd2bc196914cb7b6d9fe29a75435f08988 100644 +--- a/content/browser/notifications/blink_notification_service_impl.h ++++ b/content/browser/notifications/blink_notification_service_impl.h +@@ -72,20 +72,15 @@ class CONTENT_EXPORT BlinkNotificationServiceImpl + // Check the permission status for the current |origin_|. + blink::mojom::PermissionStatus CheckPermissionStatus(); + +- // Validate |notification_resources| received in a Mojo IPC message. +- // If the validation failed, we'd close the Mojo connection |binding_| and +- // destroy |this| by calling OnConnectionError() directly, then return false. +- // So, please do not touch |this| again after you got a false return value. +- bool ValidateNotificationResources( ++ // Validate |notification_data| and |notification_resources| received in a ++ // Mojo IPC message. If the validation failed, we'd close the Mojo connection ++ // |binding_| and destroy |this| by calling OnConnectionError() directly, then ++ // return false. So, please do not touch |this| again after you got a false ++ // return value. ++ bool ValidateNotificationDataAndResources( ++ const blink::PlatformNotificationData& notification_data, + const blink::NotificationResources& notification_resources); + +- // Validate |notification_data| received in a Mojo IPC message. +- // If the validation failed, we'd close the Mojo connection |binding_| and +- // destroy |this| by calling OnConnectionError() directly, then return false. +- // So, please do not touch |this| again after you got a false return value. +- bool ValidateNotificationData( +- const blink::PlatformNotificationData& notification_data); +- + void DidWriteNotificationData(DisplayPersistentNotificationCallback callback, + bool success, + const std::string& notification_id); diff --git a/patches/chromium/privacy_budget_remove_unnecessary_kcanvasreadback_metrics.patch b/patches/chromium/privacy_budget_remove_unnecessary_kcanvasreadback_metrics.patch new file mode 100644 index 0000000000000..015ab51a7f3a5 --- /dev/null +++ b/patches/chromium/privacy_budget_remove_unnecessary_kcanvasreadback_metrics.patch @@ -0,0 +1,91 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Asanka Herath +Date: Wed, 27 Jan 2021 03:34:58 +0000 +Subject: Remove unnecessary kCanvasReadback metrics. + +The identifiability metrics recorded under kCanvasReadback surface type +used two conflicting sources as inputs: the CanvasRenderingContext +type, and the paint-op digest. + +There are known collisions between resulting IdentifiableSurface values +from the two sources, which makes it impossible to losslessly separate +the two during analysis. + +While the fact that a canvas readback happened is interesting, it +doesn't help determine the observed diversity of clients. Hence this +change removes one of those sources: the CanvasRenderingContext type. + +Bug: 1161379 +Change-Id: I770cb631c9c4afe4c36d1b129aaf61410db25d43 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2600386 +Commit-Queue: Asanka Herath +Reviewed-by: Caleb Raitto +Reviewed-by: Kentaro Hara +Cr-Commit-Position: refs/heads/master@{#847480} + +diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc +index 6ca96f58319ecafdce623e883dc69ce774449efb..9d9089b48ee138fbd73a35f23537e316d219b440 100644 +--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc ++++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc +@@ -222,13 +222,6 @@ ImageBitmap* OffscreenCanvas::transferToImageBitmap( + "ImageBitmap construction failed"); + } + +- RecordIdentifiabilityMetric( +- blink::IdentifiableSurface::FromTypeAndToken( +- blink::IdentifiableSurface::Type::kCanvasReadback, +- context_ ? context_->GetContextType() +- : CanvasRenderingContext::kContextTypeUnknown), +- 0); +- + return image; + } + +diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc +index cca2abb108c8a94992ac15048154d02badc1f766..ade14e0102ae403a042dae2d06a5aa07741ea012 100644 +--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc ++++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc +@@ -38,8 +38,6 @@ + #include "third_party/blink/public/common/features.h" + #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h" + #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h" +-#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h" +-#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h" + #include "third_party/blink/public/platform/platform.h" + #include "third_party/blink/public/platform/task_type.h" + #include "third_party/blink/renderer/bindings/modules/v8/rendering_context.h" +@@ -682,13 +680,6 @@ ImageData* CanvasRenderingContext2D::getImageData( + int sw, + int sh, + ExceptionState& exception_state) { +- const IdentifiableSurface surface = IdentifiableSurface::FromTypeAndToken( +- IdentifiableSurface::Type::kCanvasReadback, GetContextType()); +- if (IdentifiabilityStudySettings::Get()->ShouldSample(surface)) { +- blink::IdentifiabilityMetricBuilder(ukm_source_id_) +- .Set(surface, 0) +- .Record(ukm_recorder_); +- } + return BaseRenderingContext2D::getImageData(sx, sy, sw, sh, exception_state); + } + +diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +index 30460118bfe0263e770f9a0fe02b41efe3ea476e..eddb540a7a34a85dc07f1e85f63c75910b8457fb 100644 +--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc ++++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +@@ -4667,16 +4667,6 @@ void WebGLRenderingContextBase::readPixels( + GLenum format, + GLenum type, + MaybeShared pixels) { +- if (IdentifiabilityStudySettings::Get()->ShouldSample( +- blink::IdentifiableSurface::Type::kCanvasReadback)) { +- const auto& ukm_params = GetUkmParameters(); +- blink::IdentifiabilityMetricBuilder(ukm_params.source_id) +- .Set(blink::IdentifiableSurface::FromTypeAndToken( +- blink::IdentifiableSurface::Type::kCanvasReadback, +- GetContextType()), +- 0) +- .Record(ukm_params.ukm_recorder); +- } + ReadPixelsHelper(x, y, width, height, format, type, pixels.View(), 0); + } + diff --git a/patches/chromium/reland_views_handle_deletion_when_toggling_fullscreen.patch b/patches/chromium/reland_views_handle_deletion_when_toggling_fullscreen.patch new file mode 100644 index 0000000000000..7f3f0ec27a738 --- /dev/null +++ b/patches/chromium/reland_views_handle_deletion_when_toggling_fullscreen.patch @@ -0,0 +1,149 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 13:18:02 +0200 +Subject: views: handle deletion when toggling fullscreen + +This differs from the first in so far as needing to add more early +outs in the windows side if destroyed. This was caught by the asan +bot. + +Toggling fullscreen means the bounds change. There are some +code paths that may delete the Widget when the bounds changes. +This patch ensures the right thing happens if the Widget is +deleted when this happens. + +BUG=1197436 + +(cherry picked from commit 60fe7a686c0620855c28a60721f668a99e409ee4) + +Change-Id: I8ce8f2045878b6f6de530f58e386149189900498 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2857227 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2868317 + +diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +index 6a1fcf7fe75d8fadbcb024c7434651bfaf05dc73..e539f14cb2bd56000dccfc557ca6573a3b53dcf1 100644 +--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc ++++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +@@ -583,7 +583,10 @@ void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) { + if (IsFullscreen() == fullscreen) + return; + ++ auto weak_ptr = GetWeakPtr(); + platform_window()->ToggleFullscreen(); ++ if (!weak_ptr) ++ return; + + // The state must change synchronously to let media react on fullscreen + // changes. +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 af2c2f2bbc1f52f4455fb973ab2fc0d0dd013ca5..cf0c64cebe0c9c1acb789527a0406d74daeb250a 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 +@@ -463,7 +463,10 @@ void DesktopWindowTreeHostWin::FrameTypeChanged() { + } + + void DesktopWindowTreeHostWin::SetFullscreen(bool fullscreen) { ++ auto weak_ptr = GetWeakPtr(); + message_handler_->SetFullscreen(fullscreen); ++ if (!weak_ptr) ++ return; + // TODO(sky): workaround for ScopedFullscreenVisibility showing window + // directly. Instead of this should listen for visibility changes and then + // update window. +diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc +index d784b8eae003ba79b109845f0d7f40c99c007791..b54f42885c976ebcd13f08ccf666447791124063 100644 +--- a/ui/views/widget/widget.cc ++++ b/ui/views/widget/widget.cc +@@ -724,7 +724,10 @@ void Widget::SetFullscreen(bool fullscreen) { + if (IsFullscreen() == fullscreen) + return; + ++ auto weak_ptr = GetWeakPtr(); + native_widget_->SetFullscreen(fullscreen); ++ if (!weak_ptr) ++ return; + + if (non_client_view_) + non_client_view_->InvalidateLayout(); +diff --git a/ui/views/win/fullscreen_handler.cc b/ui/views/win/fullscreen_handler.cc +index 8791362556fcd7544b79982dd6535d55ecd25a50..708d28f45028ee10459c7973d51caecfe0d09097 100644 +--- a/ui/views/win/fullscreen_handler.cc ++++ b/ui/views/win/fullscreen_handler.cc +@@ -70,6 +70,7 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen) { + + fullscreen_ = fullscreen; + ++ auto ref = weak_ptr_factory_.GetWeakPtr(); + if (fullscreen_) { + // Set new window style and size. + SetWindowLong(hwnd_, GWL_STYLE, +@@ -102,6 +103,8 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen) { + new_rect.height(), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + } ++ if (!ref) ++ return; + + MarkFullscreen(fullscreen); + } +diff --git a/ui/views/win/fullscreen_handler.h b/ui/views/win/fullscreen_handler.h +index fe17c7f0368b1dd35a37006033ddf34d35ea3982..c76ef18a6f59e9239d5a281d26c6e34646b68ee3 100644 +--- a/ui/views/win/fullscreen_handler.h ++++ b/ui/views/win/fullscreen_handler.h +@@ -11,6 +11,7 @@ + #include + + #include "base/macros.h" ++#include "base/memory/weak_ptr.h" + + namespace gfx { + class Rect; +@@ -54,6 +55,8 @@ class FullscreenHandler { + // Used to mark a window as fullscreen. + Microsoft::WRL::ComPtr task_bar_list_; + ++ base::WeakPtrFactory weak_ptr_factory_{this}; ++ + DISALLOW_COPY_AND_ASSIGN(FullscreenHandler); + }; + +diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc +index bb8b1c4fd7f1e93b6d50978ccb701393df956425..d7f91e4d70dcd7224aa8569e8773f69efac7a4e5 100644 +--- a/ui/views/win/hwnd_message_handler.cc ++++ b/ui/views/win/hwnd_message_handler.cc +@@ -900,7 +900,10 @@ void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, + + void HWNDMessageHandler::SetFullscreen(bool fullscreen) { + background_fullscreen_hack_ = false; ++ auto ref = msg_handler_weak_factory_.GetWeakPtr(); + fullscreen_handler()->SetFullscreen(fullscreen); ++ if (!ref) ++ return; + + // Add the fullscreen window to the fullscreen window map which is used to + // handle window activations. +@@ -1404,8 +1407,10 @@ void HWNDMessageHandler::ClientAreaSizeChanged() { + // Ignore size changes due to fullscreen windows losing activation. + if (background_fullscreen_hack_ && !sent_window_size_changing_) + return; +- gfx::Size s = GetClientAreaBounds().size(); +- delegate_->HandleClientSizeChanged(s); ++ auto ref = msg_handler_weak_factory_.GetWeakPtr(); ++ delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); ++ if (!ref) ++ return; + + current_window_size_message_++; + sent_window_size_changing_ = false; +@@ -2930,8 +2935,11 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { + void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { + TRACE_EVENT0("ui", "HWNDMessageHandler::OnWindowPosChanged"); + ++ base::WeakPtr ref(msg_handler_weak_factory_.GetWeakPtr()); + if (DidClientAreaSizeChange(window_pos)) + ClientAreaSizeChanged(); ++ if (!ref) ++ return; + if (window_pos->flags & SWP_FRAMECHANGED) + SetDwmFrameExtension(DwmFrameState::kOn); + if (window_pos->flags & SWP_SHOWWINDOW) { diff --git a/patches/chromium/remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch b/patches/chromium/remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch new file mode 100644 index 0000000000000..b17b843ce6350 --- /dev/null +++ b/patches/chromium/remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Wed, 19 May 2021 17:11:00 +0200 +Subject: Remove tabs and line breaks from the middle of app names when + parsing. + +(cherry picked from commit f9b0a09d60acabadfcb9ddeacc9d943cc9811199) + +Bug: 1180126 +Change-Id: Ie6f08d45f97214c4f1ab766aa8af001b8fb8599c +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821876 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2877715 + +diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.cc b/third_party/blink/renderer/modules/manifest/manifest_parser.cc +index d50f50ca92f0f622bb26f2b0a29b3fb8d70eed85..ed45e9fff703dd07c8f14f60eaa3c97a213c1b4f 100644 +--- a/third_party/blink/renderer/modules/manifest/manifest_parser.cc ++++ b/third_party/blink/renderer/modules/manifest/manifest_parser.cc +@@ -46,6 +46,10 @@ bool URLIsWithinScope(const KURL& url, const KURL& scope) { + url.GetPath().StartsWith(scope.GetPath()); + } + ++static bool IsCrLfOrTabChar(UChar c) { ++ return c == '\n' || c == '\r' || c == '\t'; ++} ++ + } // anonymous namespace + + ManifestParser::ManifestParser(const String& data, +@@ -256,11 +260,21 @@ KURL ManifestParser::ParseURL(const JSONObject* object, + + String ManifestParser::ParseName(const JSONObject* object) { + base::Optional name = ParseString(object, "name", Trim); ++ if (name.has_value()) { ++ name = name->RemoveCharacters(IsCrLfOrTabChar); ++ if (name->length() == 0) ++ name = base::nullopt; ++ } + return name.has_value() ? *name : String(); + } + + String ManifestParser::ParseShortName(const JSONObject* object) { + base::Optional short_name = ParseString(object, "short_name", Trim); ++ if (short_name.has_value()) { ++ short_name = short_name->RemoveCharacters(IsCrLfOrTabChar); ++ if (short_name->length() == 0) ++ short_name = base::nullopt; ++ } + return short_name.has_value() ? *short_name : String(); + } + diff --git a/patches/chromium/replace_first_of_two_waitableevents_in_creditcardaccessmanager.patch b/patches/chromium/replace_first_of_two_waitableevents_in_creditcardaccessmanager.patch new file mode 100644 index 0000000000000..79ad0c3edf9a8 --- /dev/null +++ b/patches/chromium/replace_first_of_two_waitableevents_in_creditcardaccessmanager.patch @@ -0,0 +1,574 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dominic Battre +Date: Wed, 7 Jul 2021 20:02:45 +0000 +Subject: Replace first of two WaitableEvents in CreditCardAccessManager +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +(cherry picked from commit 48cf01e4039fecbe119d8223d1f6072aaf44f258) + +Bug: 1214234 +Change-Id: I38171be7b38982f25abfbb3dff7a41f19a167764 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3001123 +Reviewed-by: Jared Saul +Commit-Queue: Dominic Battré +Cr-Original-Commit-Position: refs/heads/master@{#898237} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3011066 +Reviewed-by: Dominic Battré +Reviewed-by: Prudhvi Kumar Bommana +Owners-Override: Prudhvi Kumar Bommana +Cr-Commit-Position: refs/branch-heads/4515@{#1366} +Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287} + +diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn +index 3447ad7a30331d3fc31653083f1411784c244198..ed26a8dd8e0e444ec8e37b292c5fbde4ed0bbc91 100644 +--- a/components/autofill/core/browser/BUILD.gn ++++ b/components/autofill/core/browser/BUILD.gn +@@ -213,6 +213,8 @@ static_library("browser") { + "payments/payments_util.cc", + "payments/payments_util.h", + "payments/risk_data_loader.h", ++ "payments/wait_for_signal_or_timeout.cc", ++ "payments/wait_for_signal_or_timeout.h", + "payments/strike_database.cc", + "payments/strike_database.h", + "payments/strike_database_integrator_base.cc", +@@ -638,6 +640,7 @@ source_set("unit_tests") { + "payments/payments_client_unittest.cc", + "payments/payments_service_url_unittest.cc", + "payments/payments_util_unittest.cc", ++ "payments/wait_for_signal_or_timeout_unittest.cc", + "payments/strike_database_integrator_test_strike_database_unittest.cc", + "payments/strike_database_unittest.cc", + "personal_data_manager_unittest.cc", +diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc +index cbd1018b3dd6c30cec3a92f6e0108fadf7a48dc8..46b143fcc5e1736736e913c8a32f21d15a85be33 100644 +--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc ++++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc +@@ -18,7 +18,6 @@ + #include "base/task/post_task.h" + #include "base/task/task_traits.h" + #include "base/task/thread_pool.h" +-#include "base/threading/sequenced_task_runner_handle.h" + #include "base/time/time.h" + #include "build/build_config.h" + #include "components/autofill/core/browser/autofill_client.h" +@@ -47,12 +46,6 @@ constexpr int64_t kUnmaskDetailsResponseTimeoutMs = 3 * 1000; // 3 sec + // Time to wait between multiple calls to GetUnmaskDetails(). + constexpr int64_t kDelayForGetUnmaskDetails = 3 * 60 * 1000; // 3 min + +-// Used for asynchronously waiting for |event| to be signaled. +-bool WaitForEvent(base::WaitableEvent* event) { +- event->declare_only_used_while_idle(); +- return event->TimedWait( +- base::TimeDelta::FromMilliseconds(kUnmaskDetailsResponseTimeoutMs)); +-} + } // namespace + + CreditCardAccessManager::CreditCardAccessManager( +@@ -65,9 +58,6 @@ CreditCardAccessManager::CreditCardAccessManager( + payments_client_(client_->GetPaymentsClient()), + personal_data_manager_(personal_data_manager), + form_event_logger_(form_event_logger), +- ready_to_start_authentication_( +- base::WaitableEvent::ResetPolicy::AUTOMATIC, +- base::WaitableEvent::InitialState::NOT_SIGNALED), + can_fetch_unmask_details_(base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::SIGNALED) { + } +@@ -341,12 +331,10 @@ void CreditCardAccessManager::FetchCreditCard( + + // Wait for |ready_to_start_authentication_| to be signaled by + // OnDidGetUnmaskDetails() or until timeout before calling Authenticate(). +- auto task_runner = base::ThreadPool::CreateTaskRunner({base::MayBlock()}); +- cancelable_authenticate_task_tracker_.PostTaskAndReplyWithResult( +- task_runner.get(), FROM_HERE, +- base::BindOnce(&WaitForEvent, &ready_to_start_authentication_), ++ ready_to_start_authentication_.OnEventOrTimeOut( + base::BindOnce(&CreditCardAccessManager::Authenticate, +- weak_ptr_factory_.GetWeakPtr())); ++ weak_ptr_factory_.GetWeakPtr()), ++ base::TimeDelta::FromMilliseconds(kUnmaskDetailsResponseTimeoutMs)); + } else { + Authenticate(get_unmask_details_returned); + } +@@ -711,7 +699,6 @@ void CreditCardAccessManager::HandleDialogUserResponse( + case WebauthnDialogCallbackType::kVerificationCancelled: + // TODO(crbug.com/949269): Add tests and logging for canceling verify + // pending dialog. +- cancelable_authenticate_task_tracker_.TryCancelAll(); + payments_client_->CancelRequest(); + SignalCanFetchUnmaskDetails(); + ready_to_start_authentication_.Reset(); +diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h +index 6bdf8b8c647f885aad5784f737e117b3a55bd2be..99f6581b7320312776f52383b8e8e3b7845268a8 100644 +--- a/components/autofill/core/browser/payments/credit_card_access_manager.h ++++ b/components/autofill/core/browser/payments/credit_card_access_manager.h +@@ -22,6 +22,7 @@ + #include "components/autofill/core/browser/metrics/credit_card_form_event_logger.h" + #include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h" + #include "components/autofill/core/browser/payments/payments_client.h" ++#include "components/autofill/core/browser/payments/wait_for_signal_or_timeout.h" + #include "components/autofill/core/browser/personal_data_manager.h" + + #if !defined(OS_IOS) +@@ -295,11 +296,7 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, + // Resets when PrepareToFetchCreditCard() is called, if not already reset. + // Signaled when OnDidGetUnmaskDetails() is called or after timeout. + // Authenticate() is called when signaled. +- base::WaitableEvent ready_to_start_authentication_; +- +- // Tracks the Authenticate() task that is signaled by +- // |ready_to_start_authentication_|, allowing it to be canceled if necessary. +- base::CancelableTaskTracker cancelable_authenticate_task_tracker_; ++ WaitForSignalOrTimeout ready_to_start_authentication_; + + // Required to avoid any unnecessary preflight calls to Payments servers. + // Initial state is signaled. Resets when PrepareToFetchCreditCard() is +diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc +index 68b1b1ccf5ac5c7000c09de4b57932834735aaf6..ecfebce277b3b757b83d7c663b2b753f952cf02e 100644 +--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc ++++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc +@@ -139,8 +139,23 @@ class CreditCardAccessManagerTest : public testing::Test { + public: + CreditCardAccessManagerTest() + : task_environment_( ++ base::test::TaskEnvironment::TimeSource::MOCK_TIME, + base::test::TaskEnvironment::MainThreadType::DEFAULT, +- base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {} ++ base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) { ++ // Advance the mock clock to 2021-01-01, 00:00:00.000. ++ base::Time year_2021; ++ CHECK(base::Time::FromUTCExploded({.year = 2021, ++ .month = 1, ++ .day_of_week = 4, ++ .day_of_month = 1, ++ .hour = 0, ++ .minute = 0, ++ .second = 0, ++ .millisecond = 0}, ++ &year_2021)); ++ task_environment_.AdvanceClock(year_2021 - ++ task_environment_.GetMockClock()->Now()); ++ } + + void SetUp() override { + autofill_client_.SetPrefs(test::PrefServiceForTesting()); +@@ -929,6 +944,7 @@ TEST_F(CreditCardAccessManagerTest, + + ResetFetchCreditCard(); + credit_card_access_manager_->PrepareToFetchCreditCard(); ++ task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(4)); + WaitForCallbacks(); + + credit_card_access_manager_->FetchCreditCard(local_card, +@@ -949,6 +965,7 @@ TEST_F(CreditCardAccessManagerTest, + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(server_card, + accessor_->GetWeakPtr()); ++ task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(4)); + WaitForCallbacks(); + + histogram_tester.ExpectUniqueSample( +@@ -1022,6 +1039,8 @@ TEST_F(CreditCardAccessManagerTest, Metrics_LoggingTimedOutCvcFallback) { + + // Mock a delayed response. + InvokeDelayedGetUnmaskDetailsResponse(); ++ ++ task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(4)); + WaitForCallbacks(); + + histogram_tester.ExpectUniqueSample( +@@ -1043,6 +1062,7 @@ TEST_F(CreditCardAccessManagerTest, Metrics_LoggingTimedOutCvcFallback) { + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(server_card, + accessor_->GetWeakPtr()); ++ task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(4)); + WaitForCallbacks(); + + histogram_tester.ExpectUniqueSample( +diff --git a/components/autofill/core/browser/payments/wait_for_signal_or_timeout.cc b/components/autofill/core/browser/payments/wait_for_signal_or_timeout.cc +new file mode 100644 +index 0000000000000000000000000000000000000000..713a53e0f006e51f5d9bd64466712bf75b3ba095 +--- /dev/null ++++ b/components/autofill/core/browser/payments/wait_for_signal_or_timeout.cc +@@ -0,0 +1,78 @@ ++// Copyright 2021 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "components/autofill/core/browser/payments/wait_for_signal_or_timeout.h" ++ ++#include "base/threading/sequenced_task_runner_handle.h" ++ ++WaitForSignalOrTimeout::WaitForSignalOrTimeout() = default; ++WaitForSignalOrTimeout::~WaitForSignalOrTimeout() = default; ++ ++void WaitForSignalOrTimeout::Signal() { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); ++ SignalHandler(/*triggered_by_signal=*/true); ++} ++ ++bool WaitForSignalOrTimeout::IsSignaled() const { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); ++ return state_ == State::kSignalReceived || state_ == State::kDone; ++} ++ ++void WaitForSignalOrTimeout::OnEventOrTimeOut(Callback callback, ++ base::TimeDelta timeout) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); ++ switch (state_) { ++ case State::kInitialState: ++ callback_ = std::move(callback); ++ ++generation_id_; // Invalidate previous OnTimeOut tasks. ++ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( ++ FROM_HERE, ++ base::BindOnce(&WaitForSignalOrTimeout::OnTimeOut, ++ weak_factory_.GetWeakPtr(), generation_id_), ++ timeout); ++ break; ++ ++ case State::kSignalReceived: ++ state_ = State::kDone; ++ std::move(callback).Run( ++ /*triggered_by_signal=*/in_state_signal_received_due_to_signal_call_); ++ break; ++ ++ case State::kDone: ++ Reset(); ++ OnEventOrTimeOut(std::move(callback), timeout); ++ break; ++ } ++} ++ ++void WaitForSignalOrTimeout::Reset() { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); ++ state_ = State::kInitialState; ++ ++generation_id_; ++ callback_ = Callback(); ++} ++ ++void WaitForSignalOrTimeout::OnTimeOut(int generation_id) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); ++ if (generation_id == generation_id_) ++ SignalHandler(/*triggered_by_signal=*/false); ++} ++ ++void WaitForSignalOrTimeout::SignalHandler(bool triggered_by_signal) { ++ switch (state_) { ++ case State::kInitialState: ++ if (callback_.is_null()) { ++ state_ = State::kSignalReceived; ++ in_state_signal_received_due_to_signal_call_ = triggered_by_signal; ++ } else { ++ state_ = State::kDone; ++ std::move(callback_).Run(triggered_by_signal); ++ } ++ break; ++ ++ case State::kSignalReceived: ++ case State::kDone: ++ break; ++ } ++} +diff --git a/components/autofill/core/browser/payments/wait_for_signal_or_timeout.h b/components/autofill/core/browser/payments/wait_for_signal_or_timeout.h +new file mode 100644 +index 0000000000000000000000000000000000000000..6b2c451245441f784938e4132e2ab03e7d85f9b6 +--- /dev/null ++++ b/components/autofill/core/browser/payments/wait_for_signal_or_timeout.h +@@ -0,0 +1,100 @@ ++// Copyright 2021 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_WAIT_FOR_SIGNAL_OR_TIMEOUT_H_ ++#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_WAIT_FOR_SIGNAL_OR_TIMEOUT_H_ ++ ++#include "base/callback.h" ++#include "base/memory/weak_ptr.h" ++#include "base/sequence_checker.h" ++#include "base/time/time.h" ++ ++// A WaitForSignalOrTimeout waits for Signal() or a time out and calls a ++// callback when either of these happens for the first time. ++// ++// The WaitForSignalOrTimeout is Reset()-able and ensures that the callback will ++// be called at most once (unless Reset() resets the state). The ++// WaitForSignalOrTimeout can be destroyed at any time without negative ++// side-effects. The callback won't be called in this case. If the Signal() ++// arrives before a call for OnEventOrTimeOut(), the callback will be called ++// immediately. If a second Signal() arrives, nothing happens. The ++// WaitForSignalOrTimeout must be used on single task sequence. ++// ++// This class provides the bare minimum needed for a Payment task. If there are ++// more use cases, feel free to spice it up and move it to base/. ++class WaitForSignalOrTimeout { ++ public: ++ // The passed boolean is true if the callback happened by a call of Signal() ++ // (as opposed to a timeout). ++ using Callback = base::OnceCallback; ++ ++ WaitForSignalOrTimeout(); ++ ~WaitForSignalOrTimeout(); ++ WaitForSignalOrTimeout(const WaitForSignalOrTimeout&) = delete; ++ WaitForSignalOrTimeout& operator=(const WaitForSignalOrTimeout&) = delete; ++ ++ // Triggers |callback_| if it has not been called before, or registers that ++ // the signal occurred, so that |callback| of OnEventOrTimeOut() can be ++ // called immediately. ++ void Signal(); ++ ++ // Returns whether Signal() was called at least once or a timeout happened, ++ // and Reset() has not been called afterwards. Note that this function does ++ // not discriminate whether Signal() was called or a timeout happened. ++ // The |callback_|'s parameter has this distinction, though. ++ bool IsSignaled() const; ++ ++ // Registers the |callback| and calls it immediately if Signal() was called ++ // already. Starts a timeout task, so that |callback| is called if no call of ++ // Signal() is observed within |timeout|. A previous timeout is replaced by a ++ // new one. ++ void OnEventOrTimeOut(Callback callback, base::TimeDelta timeout); ++ ++ // Resets the state machine so that no Signal() was observed, no callback is ++ // registered and no timeout task is running. ++ void Reset(); ++ ++ private: ++ enum class State { ++ // Signal() has not been called, yet. ++ kInitialState, ++ // Signal() has been called, but callback is not specified. ++ kSignalReceived, ++ // callback has been called. ++ kDone, ++ }; ++ ++ // Internal callback for the timeout. |generation_id| is a generation counter ++ // to ensure that old, delayed timeout tasks are ignored. ++ void OnTimeOut(int generation_id); ++ ++ // Handler for Signal() and OnTimeOut(). Calls |callback_| if appropriate. ++ // The parameter is true if this function is called via Signal() and false if ++ // the function is called via OnTimeOut(). This parameter is passed to ++ // callback. ++ void SignalHandler(bool triggered_by_signal); ++ ++ State state_ = State::kInitialState; ++ ++ // This variable is only valid if state_ == State::kSignalReceived. It is ++ // true if we moved into this state due to a Signal() call, and false if ++ // we moved into this state due to an OnTimeOut() call. ++ bool in_state_signal_received_due_to_signal_call_; ++ ++ // As the base::ThreadPool does not support cancelable tasks, we just rely on ++ // a generation counter. Every time Reset() or OnEventOrTimeOut() are called, ++ // the generation id is incremented. If outdated delayed OnTimeOut() tasks ++ // trickle in, we recognize them as tasks for which the |generation_id| ++ // parameter is less than the current generation_id_ and ignore them. ++ int generation_id_ = 0; ++ ++ // Callback to be called in case of a Signal() or a time out. ++ Callback callback_; ++ ++ SEQUENCE_CHECKER(my_sequence_checker_); ++ ++ base::WeakPtrFactory weak_factory_{this}; ++}; ++ ++#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_WAIT_FOR_SIGNAL_OR_TIMEOUT_H_ +diff --git a/components/autofill/core/browser/payments/wait_for_signal_or_timeout_unittest.cc b/components/autofill/core/browser/payments/wait_for_signal_or_timeout_unittest.cc +new file mode 100644 +index 0000000000000000000000000000000000000000..eda3fd362221cb9c08c893af02ce33a2826c5d6f +--- /dev/null ++++ b/components/autofill/core/browser/payments/wait_for_signal_or_timeout_unittest.cc +@@ -0,0 +1,188 @@ ++// Copyright 2021 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "components/autofill/core/browser/payments/wait_for_signal_or_timeout.h" ++ ++#include "base/test/task_environment.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class WaitForSignalOrTimeoutTest : public testing::Test { ++ public: ++ WaitForSignalOrTimeoutTest() = default; ++ ~WaitForSignalOrTimeoutTest() override = default; ++ ++ WaitForSignalOrTimeout::Callback GetCallback() { ++ return base::BindOnce(&WaitForSignalOrTimeoutTest::Callback, ++ base::Unretained(this)); ++ } ++ ++ protected: ++ void Callback(bool triggered_by_signal) { ++ callbacks_++; ++ last_callback_triggered_by_signal_ = triggered_by_signal; ++ } ++ ++ // Number of observed callbacks. ++ int callbacks_ = 0; ++ ++ bool last_callback_triggered_by_signal_ = false; ++ ++ base::test::TaskEnvironment task_env_{ ++ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; ++}; ++ ++// WaitForSignalOrTimeout is initialized with a callback and then the Signal() ++// happens. ++TEST_F(WaitForSignalOrTimeoutTest, InitThenSignal) { ++ WaitForSignalOrTimeout wait; ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ EXPECT_EQ(0, callbacks_); ++ EXPECT_FALSE(wait.IsSignaled()); ++ wait.Signal(); ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_TRUE(last_callback_triggered_by_signal_); ++ ++ // Another signal call should be ignored. ++ wait.Signal(); ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_TRUE(wait.IsSignaled()); ++ ++ // Also the pending timeout should not trigger further callbacks. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(35)); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(1, callbacks_); ++} ++ ++// A Signal() is registered before the callback. ++TEST_F(WaitForSignalOrTimeoutTest, SignalThenInit) { ++ WaitForSignalOrTimeout wait; ++ EXPECT_FALSE(wait.IsSignaled()); ++ ++ // Trigger the signal before a callback handler is registered. ++ wait.Signal(); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(0, callbacks_); ++ ++ // Once the callback handler is registered, it should be called immediately. ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_TRUE(last_callback_triggered_by_signal_); ++ ++ // Another signal call should be ignored. ++ wait.Signal(); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(1, callbacks_); ++ ++ // Also the pending timeout should not trigger further callbacks. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(35)); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(1, callbacks_); ++} ++ ++// A timeout occurs before Signal() is called. ++TEST_F(WaitForSignalOrTimeoutTest, InitThenTimeout) { ++ WaitForSignalOrTimeout wait; ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ EXPECT_FALSE(wait.IsSignaled()); ++ EXPECT_EQ(0, callbacks_); ++ ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(35)); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_FALSE(last_callback_triggered_by_signal_); ++ ++ // A late signal will be ignored. ++ wait.Signal(); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_EQ(1, callbacks_); ++} ++ ++// The WaitForSignalOrTimeout gets destroyed before a Signal() or timeout ++// happens. ++TEST_F(WaitForSignalOrTimeoutTest, DestroyedBeforeSignal) { ++ { ++ WaitForSignalOrTimeout wait; ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ } ++ EXPECT_EQ(0, callbacks_); ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(35)); ++ EXPECT_EQ(0, callbacks_); ++} ++ ++// The WaitForSignalOrTimeout gets signaled, reset, and signaled again. ++TEST_F(WaitForSignalOrTimeoutTest, Reset) { ++ WaitForSignalOrTimeout wait; ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ EXPECT_EQ(0, callbacks_); ++ wait.Signal(); ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_TRUE(last_callback_triggered_by_signal_); ++ ++ wait.Reset(); ++ ++ EXPECT_FALSE(wait.IsSignaled()); ++ ++ // This signal does not trigger a callback because none is registered. ++ wait.Signal(); ++ EXPECT_EQ(1, callbacks_); ++ // Now the callback happens immediately. ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ EXPECT_EQ(2, callbacks_); ++ EXPECT_TRUE(last_callback_triggered_by_signal_); ++ ++ wait.Reset(); ++ ++ // Finally, we simulate a timeout after the reset. ++ EXPECT_FALSE(wait.IsSignaled()); ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(35)); ++ EXPECT_EQ(3, callbacks_); ++ EXPECT_FALSE(last_callback_triggered_by_signal_); ++} ++ ++TEST_F(WaitForSignalOrTimeoutTest, OnEventOrTimeOutCalledTwice) { ++ WaitForSignalOrTimeout wait; ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ EXPECT_EQ(0, callbacks_); ++ ++ // Wait some time but not long enough for the timeout to trigger. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(25)); ++ EXPECT_EQ(0, callbacks_); ++ EXPECT_FALSE(wait.IsSignaled()); ++ ++ // This resets the state machine (currently waiting for a signal or timeout) ++ // and starts a new wait. ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ ++ // Wait some time but not long enough for the timeout to trigger. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(25)); ++ // The first timeout should not have triggered anything. ++ EXPECT_EQ(0, callbacks_); ++ EXPECT_FALSE(wait.IsSignaled()); ++ ++ // Wait some more time for the second timeout to kick in. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(10)); ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_FALSE(last_callback_triggered_by_signal_); ++ ++ // This resets the state machine (currently in done state) once more and ++ // starts a new wait. ++ wait.OnEventOrTimeOut(GetCallback(), base::TimeDelta::FromSeconds(30)); ++ ++ // Wait some time but not long enough for the timeout to trigger. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(25)); ++ // The first timeout should not have triggered anything. ++ EXPECT_EQ(1, callbacks_); ++ EXPECT_FALSE(wait.IsSignaled()); ++ ++ // Wait some more time for the second timeout to kick in. ++ task_env_.FastForwardBy(base::TimeDelta::FromSeconds(10)); ++ EXPECT_EQ(2, callbacks_); ++ EXPECT_TRUE(wait.IsSignaled()); ++ EXPECT_FALSE(last_callback_triggered_by_signal_); ++} diff --git a/patches/chromium/replace_std_vector_with_base_observerlist_to_support_container.patch b/patches/chromium/replace_std_vector_with_base_observerlist_to_support_container.patch new file mode 100644 index 0000000000000..efa73957988cf --- /dev/null +++ b/patches/chromium/replace_std_vector_with_base_observerlist_to_support_container.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Tue, 18 May 2021 23:22:27 +0200 +Subject: Replace std::vector with base::ObserverList to support container + modification while iterating + +TaskTracker saves list of viewers in vector, that needs to be notified +when distillation is completed. At the time of notifying the viewers, +we are indirectly erasing viewers from vector while iterating. + +This is causing container-overflow in asan build when vector has more +than one viewer while notifying. + +This change is to replace vector with ObserverList that can be modified +during iteration without invalidating the iterator. + +(cherry picked from commit be19f42dab0706d5fdd74acd6eaa424e9277e9c4) + +Bug: 1203590 +Change-Id: I7c7b8237584c48c9ebc2639b9268a6a78c2db4b2 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2856118 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2883743 + +diff --git a/base/observer_list.h b/base/observer_list.h +index 28369a25941dd62ba63a14dccfb46b8092eb6a24..a2f830191f36f74c12aa8e67db978356309e2691 100644 +--- a/base/observer_list.h ++++ b/base/observer_list.h +@@ -272,6 +272,7 @@ class ObserverList { + NOTREACHED() << "Observers can only be added once!"; + return; + } ++ observers_count_++; + observers_.emplace_back(ObserverStorageType(obs)); + } + +@@ -284,7 +285,8 @@ class ObserverList { + [obs](const auto& o) { return o.IsEqual(obs); }); + if (it == observers_.end()) + return; +- ++ if (!it->IsMarkedForRemoval()) ++ observers_count_--; + if (live_iterators_.empty()) { + observers_.erase(it); + } else { +@@ -314,8 +316,12 @@ class ObserverList { + for (auto& observer : observers_) + observer.MarkForRemoval(); + } ++ ++ observers_count_ = 0; + } + ++ bool empty() const { return !observers_count_; } ++ + bool might_have_observers() const { return !observers_.empty(); } + + private: +@@ -334,6 +340,8 @@ class ObserverList { + + base::LinkedList> live_iterators_; + ++ size_t observers_count_{0}; ++ + const ObserverListPolicy policy_; + + SEQUENCE_CHECKER(iteration_sequence_checker_); +diff --git a/components/dom_distiller/core/task_tracker.cc b/components/dom_distiller/core/task_tracker.cc +index e66a62c4091e44183253ba7221db6dedcca4a1a2..f22c88967bc7d7b1a32339657b5fc2bf8248bbde 100644 +--- a/components/dom_distiller/core/task_tracker.cc ++++ b/components/dom_distiller/core/task_tracker.cc +@@ -85,7 +85,7 @@ void TaskTracker::AddSaveCallback(SaveCallback callback) { + + std::unique_ptr TaskTracker::AddViewer( + ViewRequestDelegate* delegate) { +- viewers_.push_back(delegate); ++ viewers_.AddObserver(delegate); + if (content_ready_) { + // Distillation for this task has already completed, and so the delegate can + // be immediately told of the result. +@@ -115,7 +115,7 @@ bool TaskTracker::HasUrl(const GURL& url) const { + } + + void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) { +- base::Erase(viewers_, delegate); ++ viewers_.RemoveObserver(delegate); + if (viewers_.empty()) { + MaybeCancel(); + } +@@ -219,8 +219,8 @@ void TaskTracker::DistilledArticleReady( + } + + void TaskTracker::NotifyViewersAndCallbacks() { +- for (auto* viewer : viewers_) { +- NotifyViewer(viewer); ++ for (auto& viewer : viewers_) { ++ NotifyViewer(&viewer); + } + + // Already inside a callback run SaveCallbacks directly. +@@ -242,8 +242,8 @@ void TaskTracker::DoSaveCallbacks(bool success) { + + void TaskTracker::OnArticleDistillationUpdated( + const ArticleDistillationUpdate& article_update) { +- for (auto* viewer : viewers_) { +- viewer->OnArticleUpdated(article_update); ++ for (auto& viewer : viewers_) { ++ viewer.OnArticleUpdated(article_update); + } + } + +diff --git a/components/dom_distiller/core/task_tracker.h b/components/dom_distiller/core/task_tracker.h +index 484145cf7d176fd0c3f2fa73da4cf94c23cc0bda..cc13e7272923ec3de52bcea186fdc30391c8cd2b 100644 +--- a/components/dom_distiller/core/task_tracker.h ++++ b/components/dom_distiller/core/task_tracker.h +@@ -11,6 +11,7 @@ + #include "base/bind.h" + #include "base/callback.h" + #include "base/memory/weak_ptr.h" ++#include "base/observer_list.h" + #include "components/dom_distiller/core/article_distillation_update.h" + #include "components/dom_distiller/core/article_entry.h" + #include "components/dom_distiller/core/distiller.h" +@@ -40,9 +41,9 @@ class ViewerHandle { + + // Interface for a DOM distiller entry viewer. Implement this to make a view + // request and receive the data for an entry when it becomes available. +-class ViewRequestDelegate { ++class ViewRequestDelegate : public base::CheckedObserver { + public: +- virtual ~ViewRequestDelegate() = default; ++ ~ViewRequestDelegate() override = default; + + // Called when the distilled article contents are available. The + // DistilledArticleProto is owned by a TaskTracker instance and is invalidated +@@ -140,7 +141,7 @@ class TaskTracker { + std::vector save_callbacks_; + // A ViewRequestDelegate will be added to this list when a view request is + // made and removed when the corresponding ViewerHandle is destroyed. +- std::vector viewers_; ++ base::ObserverList viewers_; + + std::unique_ptr distiller_; + bool blob_fetcher_running_; diff --git a/patches/chromium/use_idtype_for_permission_change_subscriptions.patch b/patches/chromium/use_idtype_for_permission_change_subscriptions.patch new file mode 100644 index 0000000000000..f217a82176952 --- /dev/null +++ b/patches/chromium/use_idtype_for_permission_change_subscriptions.patch @@ -0,0 +1,978 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jana Grill +Date: Thu, 15 Apr 2021 19:35:42 +0000 +Subject: Use IDType for permission change subscriptions. + +(cherry picked from commit ad1489b7c3ed705fc623cdffdc292324be9fcbfa) + +Bug: 1025683 +Change-Id: I3b44ba7833138e8a657a4192e1a36c978695db32 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2791431 +Reviewed-by: Richard Coles +Reviewed-by: Yuchen Liu +Reviewed-by: Nasko Oskov +Reviewed-by: Andrey Kosyakov +Reviewed-by: Fabrice de Gans-Riberi +Reviewed-by: Arthur Sonzogni +Reviewed-by: Illia Klimov +Auto-Submit: Balazs Engedy +Commit-Queue: Balazs Engedy +Cr-Original-Commit-Position: refs/heads/master@{#867999} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2817980 +Reviewed-by: Victor-Gabriel Savu +Reviewed-by: Achuith Bhandarkar +Commit-Queue: Achuith Bhandarkar +Owners-Override: Achuith Bhandarkar +Cr-Commit-Position: refs/branch-heads/4240@{#1607} +Cr-Branched-From: f297677702651916bbf65e59c0d4bbd4ce57d1ee-refs/heads/master@{#800218} + +diff --git a/android_webview/browser/aw_permission_manager.cc b/android_webview/browser/aw_permission_manager.cc +index 4b982411c699d85e6d8c24a3011cb790a862eb86..6b4c474aa98f78ffa38bb8dbb0d62efec535e361 100644 +--- a/android_webview/browser/aw_permission_manager.cc ++++ b/android_webview/browser/aw_permission_manager.cc +@@ -470,16 +470,17 @@ PermissionStatus AwPermissionManager::GetPermissionStatusForFrame( + .GetOrigin()); + } + +-int AwPermissionManager::SubscribePermissionStatusChange( ++AwPermissionManager::SubscriptionId ++AwPermissionManager::SubscribePermissionStatusChange( + PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) { +- return content::PermissionController::kNoPendingOperation; ++ return SubscriptionId(); + } + + void AwPermissionManager::UnsubscribePermissionStatusChange( +- int subscription_id) {} ++ SubscriptionId subscription_id) {} + + void AwPermissionManager::CancelPermissionRequest(int request_id) { + PendingRequest* pending_request = pending_requests_.Lookup(request_id); +diff --git a/android_webview/browser/aw_permission_manager.h b/android_webview/browser/aw_permission_manager.h +index d9670cac33b84016568e9693b62e83c5e7ee0969..7439e78199783b8ebe2c303ebebf0e1cf62dc718 100644 +--- a/android_webview/browser/aw_permission_manager.h ++++ b/android_webview/browser/aw_permission_manager.h +@@ -49,13 +49,14 @@ class AwPermissionManager : public content::PermissionControllerDelegate { + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin) override; +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override; +- void UnsubscribePermissionStatusChange(int subscription_id) override; ++ void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) override; + + protected: + void CancelPermissionRequest(int request_id); +diff --git a/chrome/browser/permissions/permission_manager_browsertest.cc b/chrome/browser/permissions/permission_manager_browsertest.cc +index 440203ce6eca40070e09eae8bafe2a50bea75060..d48e6d85611b2ea4560f56d5e09fafa3a3453e7a 100644 +--- a/chrome/browser/permissions/permission_manager_browsertest.cc ++++ b/chrome/browser/permissions/permission_manager_browsertest.cc +@@ -49,13 +49,13 @@ class SubscriptionInterceptingPermissionManager + callback_ = std::move(callback); + } + +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override { +- int result = ++ SubscriptionId result = + permissions::PermissionManager::SubscribePermissionStatusChange( + permission, render_frame_host, requesting_origin, callback); + std::move(callback_).Run(); +diff --git a/chromecast/browser/cast_permission_manager.cc b/chromecast/browser/cast_permission_manager.cc +index b358fc3bdb5c6af93a1e6568a667049574367741..b3a226dabff062e591fee38682409dec5e38a213 100644 +--- a/chromecast/browser/cast_permission_manager.cc ++++ b/chromecast/browser/cast_permission_manager.cc +@@ -63,17 +63,17 @@ CastPermissionManager::GetPermissionStatusForFrame( + return blink::mojom::PermissionStatus::GRANTED; + } + +-int CastPermissionManager::SubscribePermissionStatusChange( ++CastPermissionManager::SubscriptionId ++CastPermissionManager::SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) { +- return content::PermissionController::kNoPendingOperation; ++ return SubscriptionId(); + } + + void CastPermissionManager::UnsubscribePermissionStatusChange( +- int subscription_id) { +-} ++ SubscriptionId subscription_id) {} + + } // namespace shell + } // namespace chromecast +diff --git a/chromecast/browser/cast_permission_manager.h b/chromecast/browser/cast_permission_manager.h +index 564ac2b304596027cf7096892dfaf9796500419c..f9da64c6110307cf9912e897f87fcbb2ca123d75 100644 +--- a/chromecast/browser/cast_permission_manager.h ++++ b/chromecast/browser/cast_permission_manager.h +@@ -43,13 +43,14 @@ class CastPermissionManager : public content::PermissionControllerDelegate { + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin) override; +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override; +- void UnsubscribePermissionStatusChange(int subscription_id) override; ++ void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) override; + + private: + DISALLOW_COPY_AND_ASSIGN(CastPermissionManager); +diff --git a/components/permissions/permission_manager.cc b/components/permissions/permission_manager.cc +index d5ed9f78abbe8b05364c647bb7f8bd02ed5a196c..a0cacc0cb77708e4ba9f1b8c6ff0056a134c9cca 100644 +--- a/components/permissions/permission_manager.cc ++++ b/components/permissions/permission_manager.cc +@@ -536,14 +536,15 @@ bool PermissionManager::IsPermissionOverridableByDevTools( + origin->GetURL()); + } + +-int PermissionManager::SubscribePermissionStatusChange( ++PermissionManager::SubscriptionId ++PermissionManager::SubscribePermissionStatusChange( + PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (is_shutting_down_) +- return 0; ++ return SubscriptionId(); + + if (subscriptions_.IsEmpty()) + PermissionsClient::Get() +@@ -580,16 +581,20 @@ int PermissionManager::SubscribePermissionStatusChange( + subscription->callback = + base::BindRepeating(&SubscriptionCallbackWrapper, std::move(callback)); + +- return subscriptions_.Add(std::move(subscription)); ++ auto id = subscription_id_generator_.GenerateNextId(); ++ subscriptions_.AddWithID(std::move(subscription), id); ++ return id; + } + +-void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id) { ++void PermissionManager::UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (is_shutting_down_) + return; + +- // Whether |subscription_id| is known will be checked by the Remove() call. +- subscriptions_.Remove(subscription_id); ++ if (subscriptions_.Lookup(subscription_id)) { ++ subscriptions_.Remove(subscription_id); ++ } + + if (subscriptions_.IsEmpty()) { + PermissionsClient::Get() +diff --git a/components/permissions/permission_manager.h b/components/permissions/permission_manager.h +index d11fb4b2c4a1a360ae01154995091439e13bd9a0..19d29dde0392adff318e82bd1c3091c4e1dcd926 100644 +--- a/components/permissions/permission_manager.h ++++ b/components/permissions/permission_manager.h +@@ -114,13 +114,14 @@ class PermissionManager : public KeyedService, + bool IsPermissionOverridableByDevTools( + content::PermissionType permission, + const base::Optional& origin) override; +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override; +- void UnsubscribePermissionStatusChange(int subscription_id) override; ++ void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) override; + + // TODO(raymes): Rather than exposing this, use the denial reason from + // GetPermissionStatus in callers to determine whether a permission is +@@ -153,7 +154,8 @@ class PermissionManager : public KeyedService, + class PermissionResponseCallback; + + struct Subscription; +- using SubscriptionsMap = base::IDMap>; ++ using SubscriptionsMap = ++ base::IDMap, SubscriptionId>; + + PermissionContextBase* GetPermissionContext(ContentSettingsType type); + +@@ -186,6 +188,7 @@ class PermissionManager : public KeyedService, + content::BrowserContext* browser_context_; + PendingRequestsMap pending_requests_; + SubscriptionsMap subscriptions_; ++ SubscriptionId::Generator subscription_id_generator_; + + PermissionContextMap permission_contexts_; + using ContentSettingsTypeOverrides = +diff --git a/components/permissions/permission_manager_unittest.cc b/components/permissions/permission_manager_unittest.cc +index 85064839c8f7865f28b0b1e5c16c12b449915d4e..421c22e3c29849e661fac23cc5d1b022c1ea0be9 100644 +--- a/components/permissions/permission_manager_unittest.cc ++++ b/components/permissions/permission_manager_unittest.cc +@@ -339,7 +339,7 @@ TEST_F(PermissionManagerTest, SubscriptionDestroyedCleanlyWithoutUnsubscribe) { + } + + TEST_F(PermissionManagerTest, SubscribeUnsubscribeAfterShutdown) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -354,7 +354,7 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAfterShutdown) { + subscription_id); + + // Check that subscribe/unsubscribe after shutdown don't crash. +- int subscription2_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription2_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -365,7 +365,7 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAfterShutdown) { + } + + TEST_F(PermissionManagerTest, SameTypeChangeNotifies) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -383,7 +383,7 @@ TEST_F(PermissionManagerTest, SameTypeChangeNotifies) { + } + + TEST_F(PermissionManagerTest, DifferentTypeChangeDoesNotNotify) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -400,7 +400,7 @@ TEST_F(PermissionManagerTest, DifferentTypeChangeDoesNotNotify) { + } + + TEST_F(PermissionManagerTest, ChangeAfterUnsubscribeDoesNotNotify) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -417,7 +417,7 @@ TEST_F(PermissionManagerTest, ChangeAfterUnsubscribeDoesNotNotify) { + } + + TEST_F(PermissionManagerTest, DifferentPrimaryUrlDoesNotNotify) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -434,7 +434,7 @@ TEST_F(PermissionManagerTest, DifferentPrimaryUrlDoesNotNotify) { + } + + TEST_F(PermissionManagerTest, DifferentSecondaryUrlDoesNotNotify) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::STORAGE_ACCESS_GRANT, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -451,7 +451,7 @@ TEST_F(PermissionManagerTest, DifferentSecondaryUrlDoesNotNotify) { + } + + TEST_F(PermissionManagerTest, WildCardPatternNotifies) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -472,7 +472,7 @@ TEST_F(PermissionManagerTest, ClearSettingsNotifies) { + url(), url(), ContentSettingsType::GEOLOCATION, std::string(), + CONTENT_SETTING_ALLOW); + +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -489,7 +489,7 @@ TEST_F(PermissionManagerTest, ClearSettingsNotifies) { + } + + TEST_F(PermissionManagerTest, NewValueCorrectlyPassed) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -511,7 +511,7 @@ TEST_F(PermissionManagerTest, ChangeWithoutPermissionChangeDoesNotNotify) { + url(), url(), ContentSettingsType::GEOLOCATION, std::string(), + CONTENT_SETTING_ALLOW); + +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -532,7 +532,7 @@ TEST_F(PermissionManagerTest, ChangesBackAndForth) { + url(), url(), ContentSettingsType::GEOLOCATION, std::string(), + CONTENT_SETTING_ASK); + +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -563,7 +563,7 @@ TEST_F(PermissionManagerTest, ChangesBackAndForthWorker) { + url(), url(), ContentSettingsType::GEOLOCATION, std::string(), + CONTENT_SETTING_ASK); + +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, nullptr, url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -590,7 +590,7 @@ TEST_F(PermissionManagerTest, ChangesBackAndForthWorker) { + } + + TEST_F(PermissionManagerTest, SubscribeMIDIPermission) { +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::MIDI, main_rfh(), url(), + base::Bind(&PermissionManagerTest::OnPermissionChange, +@@ -802,7 +802,7 @@ TEST_F(PermissionManagerTest, SubscribeWithPermissionDelegation) { + content::RenderFrameHost* parent = main_rfh(); + content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2); + +- int subscription_id = ++ content::PermissionControllerDelegate::SubscriptionId subscription_id = + GetPermissionControllerDelegate()->SubscribePermissionStatusChange( + PermissionType::GEOLOCATION, child, GURL(kOrigin2), + base::Bind(&PermissionManagerTest::OnPermissionChange, +diff --git a/content/browser/android/nfc_host.cc b/content/browser/android/nfc_host.cc +index cf0ab7b5d488ccc278a41145b11e7741b4f32791..31ff344ec682cac9186ad11744a0b4945e8c2b7d 100644 +--- a/content/browser/android/nfc_host.cc ++++ b/content/browser/android/nfc_host.cc +@@ -50,7 +50,7 @@ void NFCHost::GetNFC(RenderFrameHost* render_frame_host, + return; + } + +- if (subscription_id_ == PermissionController::kNoPendingOperation) { ++ if (!subscription_id_) { + // base::Unretained() is safe here because the subscription is canceled when + // this object is destroyed. + subscription_id_ = permission_controller_->SubscribePermissionStatusChange( +@@ -101,10 +101,8 @@ void NFCHost::OnPermissionStatusChange(blink::mojom::PermissionStatus status) { + + void NFCHost::Close() { + nfc_provider_.reset(); +- if (subscription_id_ != PermissionController::kNoPendingOperation) { +- permission_controller_->UnsubscribePermissionStatusChange(subscription_id_); +- subscription_id_ = PermissionController::kNoPendingOperation; +- } ++ permission_controller_->UnsubscribePermissionStatusChange(subscription_id_); ++ subscription_id_ = PermissionController::SubscriptionId(); + } + + } // namespace content +diff --git a/content/browser/android/nfc_host.h b/content/browser/android/nfc_host.h +index 1d203274b6f7d4131152fc7353de0b236f6fd541..6494d103ed4ac41fd5486312dc98ba3f00142b80 100644 +--- a/content/browser/android/nfc_host.h ++++ b/content/browser/android/nfc_host.h +@@ -45,7 +45,7 @@ class NFCHost : public WebContentsObserver { + mojo::Remote nfc_provider_; + + // Permission change subscription ID provided by |permission_controller_|. +- int subscription_id_ = PermissionController::kNoPendingOperation; ++ PermissionController::SubscriptionId subscription_id_; + + DISALLOW_COPY_AND_ASSIGN(NFCHost); + }; +diff --git a/content/browser/android/nfc_host_unittest.cc b/content/browser/android/nfc_host_unittest.cc +index cac46256649b5ffd96f1e1ae13c9182cfaaefc40..f40b16d92f76b9dd55afec7cb7a368e8bb686d0a 100644 +--- a/content/browser/android/nfc_host_unittest.cc ++++ b/content/browser/android/nfc_host_unittest.cc +@@ -48,7 +48,7 @@ class NFCHostTest : public RenderViewHostImplTestHarness { + }; + + TEST_F(NFCHostTest, GetNFCTwice) { +- constexpr int kSubscriptionId = 42; ++ constexpr MockPermissionManager::SubscriptionId kSubscriptionId(42); + + NavigateAndCommit(GURL(kTestUrl)); + +diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc +index ddd6656e035c4448905c7d9beb0896a13ad17ad0..35ff74b1e7cc825f9bb980d55cd84592d272d61c 100644 +--- a/content/browser/permissions/permission_controller_impl.cc ++++ b/content/browser/permissions/permission_controller_impl.cc +@@ -133,7 +133,8 @@ struct PermissionControllerImpl::Subscription { + int render_frame_id = -1; + int render_process_id = -1; + base::RepeatingCallback callback; +- int delegate_subscription_id; ++ // This is default-initialized to an invalid ID. ++ PermissionControllerDelegate::SubscriptionId delegate_subscription_id; + }; + + PermissionControllerImpl::~PermissionControllerImpl() { +@@ -389,7 +390,8 @@ void PermissionControllerImpl::OnDelegatePermissionStatusChange( + subscription->callback.Run(status); + } + +-int PermissionControllerImpl::SubscribePermissionStatusChange( ++PermissionControllerImpl::SubscriptionId ++PermissionControllerImpl::SubscribePermissionStatusChange( + PermissionType permission, + RenderFrameHost* render_frame_host, + const GURL& requesting_origin, +@@ -423,21 +425,21 @@ int PermissionControllerImpl::SubscribePermissionStatusChange( + base::BindRepeating( + &PermissionControllerImpl::OnDelegatePermissionStatusChange, + base::Unretained(this), subscription.get())); +- } else { +- subscription->delegate_subscription_id = kNoPendingOperation; + } +- return subscriptions_.Add(std::move(subscription)); ++ ++ auto id = subscription_id_generator_.GenerateNextId(); ++ subscriptions_.AddWithID(std::move(subscription), id); ++ return id; + } + + void PermissionControllerImpl::UnsubscribePermissionStatusChange( +- int subscription_id) { ++ SubscriptionId subscription_id) { + Subscription* subscription = subscriptions_.Lookup(subscription_id); + if (!subscription) + return; + PermissionControllerDelegate* delegate = + browser_context_->GetPermissionControllerDelegate(); +- if (delegate && +- subscription->delegate_subscription_id != kNoPendingOperation) { ++ if (delegate) { + delegate->UnsubscribePermissionStatusChange( + subscription->delegate_subscription_id); + } +diff --git a/content/browser/permissions/permission_controller_impl.h b/content/browser/permissions/permission_controller_impl.h +index 7ebf3c48a0e863d9f4312b37e014ce0f89e5c3c7..d85788867f746547f80405c46660e055631d9208 100644 +--- a/content/browser/permissions/permission_controller_impl.h ++++ b/content/browser/permissions/permission_controller_impl.h +@@ -72,18 +72,19 @@ class CONTENT_EXPORT PermissionControllerImpl : public PermissionController { + const GURL& requesting_origin, + const GURL& embedding_origin); + +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + PermissionType permission, + RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + const base::RepeatingCallback& + callback); + +- void UnsubscribePermissionStatusChange(int subscription_id); ++ void UnsubscribePermissionStatusChange(SubscriptionId subscription_id); + + private: + struct Subscription; +- using SubscriptionsMap = base::IDMap>; ++ using SubscriptionsMap = ++ base::IDMap, SubscriptionId>; + using SubscriptionsStatusMap = + base::flat_map; + +@@ -98,7 +99,13 @@ class CONTENT_EXPORT PermissionControllerImpl : public PermissionController { + const base::Optional& origin); + + DevToolsPermissionOverrides devtools_permission_overrides_; ++ ++ // Note that SubscriptionId is distinct from ++ // PermissionControllerDelegate::SubscriptionId, and the concrete ID values ++ // may be different as well. + SubscriptionsMap subscriptions_; ++ SubscriptionId::Generator subscription_id_generator_; ++ + BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(PermissionControllerImpl); +diff --git a/content/browser/permissions/permission_service_context.cc b/content/browser/permissions/permission_service_context.cc +index c3ab81294edbb297108c3b8f59a35f1cfb8131f9..f15abf265eb690b5dfd66bb1c2d5e13b005ddbd0 100644 +--- a/content/browser/permissions/permission_service_context.cc ++++ b/content/browser/permissions/permission_service_context.cc +@@ -11,7 +11,6 @@ + #include "content/browser/permissions/permission_service_impl.h" + #include "content/public/browser/browser_context.h" + #include "content/public/browser/navigation_handle.h" +-#include "content/public/browser/permission_controller.h" + #include "content/public/browser/render_frame_host.h" + #include "content/public/browser/render_process_host.h" + #include "content/public/browser/web_contents.h" +@@ -32,7 +31,7 @@ class PermissionServiceContext::PermissionSubscription { + PermissionSubscription& operator=(const PermissionSubscription&) = delete; + + ~PermissionSubscription() { +- DCHECK_NE(id_, 0); ++ DCHECK(id_); + BrowserContext* browser_context = context_->GetBrowserContext(); + if (browser_context) { + PermissionControllerImpl::FromBrowserContext(browser_context) +@@ -41,7 +40,7 @@ class PermissionServiceContext::PermissionSubscription { + } + + void OnConnectionError() { +- DCHECK_NE(id_, 0); ++ DCHECK(id_); + context_->ObserverHadConnectionError(id_); + } + +@@ -49,12 +48,12 @@ class PermissionServiceContext::PermissionSubscription { + observer_->OnPermissionStatusChange(status); + } + +- void set_id(int id) { id_ = id; } ++ void set_id(PermissionController::SubscriptionId id) { id_ = id; } + + private: + PermissionServiceContext* const context_; + mojo::Remote observer_; +- int id_ = 0; ++ PermissionController::SubscriptionId id_; + }; + + PermissionServiceContext::PermissionServiceContext( +@@ -108,7 +107,7 @@ void PermissionServiceContext::CreateSubscription( + } + + GURL requesting_origin(origin.Serialize()); +- int subscription_id = ++ auto subscription_id = + PermissionControllerImpl::FromBrowserContext(browser_context) + ->SubscribePermissionStatusChange( + permission_type, render_frame_host_, requesting_origin, +@@ -119,7 +118,8 @@ void PermissionServiceContext::CreateSubscription( + subscriptions_[subscription_id] = std::move(subscription); + } + +-void PermissionServiceContext::ObserverHadConnectionError(int subscription_id) { ++void PermissionServiceContext::ObserverHadConnectionError( ++ PermissionController::SubscriptionId subscription_id) { + size_t erased = subscriptions_.erase(subscription_id); + DCHECK_EQ(1u, erased); + } +diff --git a/content/browser/permissions/permission_service_context.h b/content/browser/permissions/permission_service_context.h +index 4f93be504fd854b50bea96dedbc5d324d25ea6f1..0680c70c8ee4a79bb85c2fd1e3769a29f339816e 100644 +--- a/content/browser/permissions/permission_service_context.h ++++ b/content/browser/permissions/permission_service_context.h +@@ -9,6 +9,7 @@ + #include + + #include "content/common/content_export.h" ++#include "content/public/browser/permission_controller.h" + #include "content/public/browser/permission_type.h" + #include "content/public/browser/web_contents_observer.h" + #include "mojo/public/cpp/bindings/pending_receiver.h" +@@ -52,7 +53,8 @@ class CONTENT_EXPORT PermissionServiceContext : public WebContentsObserver { + mojo::PendingRemote observer); + + // Called when the connection to a PermissionObserver has an error. +- void ObserverHadConnectionError(int subscription_id); ++ void ObserverHadConnectionError( ++ PermissionController::SubscriptionId subscription_id); + + // May return nullptr during teardown, or when showing an interstitial. + BrowserContext* GetBrowserContext() const; +@@ -78,7 +80,8 @@ class CONTENT_EXPORT PermissionServiceContext : public WebContentsObserver { + RenderFrameHost* const render_frame_host_; + RenderProcessHost* const render_process_host_; + mojo::UniqueReceiverSet services_; +- std::unordered_map> ++ std::unordered_map> + subscriptions_; + }; + +diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc +index 483c7e3c3f20adf52af7b2723d0d6536d9b89dca..4c389ca0ecbf762833f1d4edeab13eab0cef1ef6 100644 +--- a/content/browser/renderer_host/media/media_stream_manager.cc ++++ b/content/browser/renderer_host/media/media_stream_manager.cc +@@ -668,9 +668,9 @@ class MediaStreamManager::DeviceRequest { + + std::string tab_capture_device_id; + +- int audio_subscription_id = PermissionControllerImpl::kNoPendingOperation; ++ PermissionController::SubscriptionId audio_subscription_id; + +- int video_subscription_id = PermissionControllerImpl::kNoPendingOperation; ++ PermissionController::SubscriptionId video_subscription_id; + + private: + std::vector state_; +@@ -2673,8 +2673,8 @@ void MediaStreamManager::SubscribeToPermissionControllerOnUIThread( + if (!controller) + return; + +- int audio_subscription_id = PermissionControllerImpl::kNoPendingOperation; +- int video_subscription_id = PermissionControllerImpl::kNoPendingOperation; ++ PermissionController::SubscriptionId audio_subscription_id; ++ PermissionController::SubscriptionId video_subscription_id; + + if (is_audio_request) { + // It is safe to bind base::Unretained(this) because MediaStreamManager is +@@ -2716,8 +2716,8 @@ void MediaStreamManager::SetPermissionSubscriptionIDs( + const std::string& label, + int requesting_process_id, + int requesting_frame_id, +- int audio_subscription_id, +- int video_subscription_id) { ++ PermissionController::SubscriptionId audio_subscription_id, ++ PermissionController::SubscriptionId video_subscription_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + DeviceRequest* const request = FindRequest(label); +@@ -2744,8 +2744,8 @@ void MediaStreamManager::SetPermissionSubscriptionIDs( + void MediaStreamManager::UnsubscribeFromPermissionControllerOnUIThread( + int requesting_process_id, + int requesting_frame_id, +- int audio_subscription_id, +- int video_subscription_id) { ++ PermissionController::SubscriptionId audio_subscription_id, ++ PermissionController::SubscriptionId video_subscription_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + PermissionControllerImpl* controller = +diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h +index 045e5f5c00d6223ed7c7eb1c0aea5084a1bbec9a..7898abfbf9954571aa1986e6c2ef98d3fd823bc2 100644 +--- a/content/browser/renderer_host/media/media_stream_manager.h ++++ b/content/browser/renderer_host/media/media_stream_manager.h +@@ -50,6 +50,7 @@ + #include "content/public/browser/desktop_media_id.h" + #include "content/public/browser/media_request_state.h" + #include "content/public/browser/media_stream_request.h" ++#include "content/public/browser/permission_controller.h" + #include "media/base/video_facing.h" + #include "third_party/blink/public/common/mediastream/media_devices.h" + #include "third_party/blink/public/common/mediastream/media_stream_controls.h" +@@ -557,19 +558,20 @@ class CONTENT_EXPORT MediaStreamManager + + // Store the subscription ids on a DeviceRequest in order to allow + // unsubscribing when the request is deleted. +- void SetPermissionSubscriptionIDs(const std::string& label, +- int requesting_process_id, +- int requesting_frame_id, +- int audio_subscription_id, +- int video_subscription_id); ++ void SetPermissionSubscriptionIDs( ++ const std::string& label, ++ int requesting_process_id, ++ int requesting_frame_id, ++ PermissionController::SubscriptionId audio_subscription_id, ++ PermissionController::SubscriptionId video_subscription_id); + + // Unsubscribe from following permission updates for the two specified + // subscription IDs. Called when a request is deleted. + static void UnsubscribeFromPermissionControllerOnUIThread( + int requesting_process_id, + int requesting_frame_id, +- int audio_subscription_id, +- int video_subscription_id); ++ PermissionController::SubscriptionId audio_subscription_id, ++ PermissionController::SubscriptionId video_subscription_id); + + // Callback that the PermissionController calls when a permission is updated. + void PermissionChangedCallback(int requesting_process_id, +diff --git a/content/public/browser/permission_controller.h b/content/public/browser/permission_controller.h +index b9b42def49b35d31c22ee0d6c158737bdc0824b6..77fe96a1c33aea5e887e171b92f199acdf7dd6df 100644 +--- a/content/public/browser/permission_controller.h ++++ b/content/public/browser/permission_controller.h +@@ -6,6 +6,7 @@ + #define CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_H_ + + #include "base/supports_user_data.h" ++#include "base/util/type_safety/id_type.h" + #include "content/common/content_export.h" + #include "content/public/browser/permission_type.h" + #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h" +@@ -20,8 +21,13 @@ class RenderFrameHost; + class CONTENT_EXPORT PermissionController + : public base::SupportsUserData::Data { + public: +- // Constant retured when registering and subscribing if +- // cancelling/unsubscribing at a later stage would have no effect. ++ // Identifier for an active subscription. This is intentionally a distinct ++ // type from PermissionControllerDelegate::SubscriptionId as the concrete ++ // identifier values may be different. ++ using SubscriptionId = util::IdType64; ++ ++ // Constant returned when requesting a permission if cancelling at a later ++ // stage would have no effect. + static const int kNoPendingOperation = -1; + + ~PermissionController() override {} +@@ -48,4 +54,17 @@ class CONTENT_EXPORT PermissionController + + } // namespace content + ++namespace std { ++ ++template <> ++struct hash { ++ std::size_t operator()( ++ const content::PermissionController::SubscriptionId& v) const { ++ content::PermissionController::SubscriptionId::Hasher hasher; ++ return hasher(v); ++ } ++}; ++ ++} // namespace std ++ + #endif // CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_H_ +diff --git a/content/public/browser/permission_controller_delegate.h b/content/public/browser/permission_controller_delegate.h +index e47de2a278e67091442c63bb7130022eae587041..82a1d4f0efd384386d8215f39d735ba488e4bc61 100644 +--- a/content/public/browser/permission_controller_delegate.h ++++ b/content/public/browser/permission_controller_delegate.h +@@ -5,6 +5,7 @@ + #ifndef CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_DELEGATE_H_ + #define CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_DELEGATE_H_ + ++#include "base/util/type_safety/id_type.h" + #include "content/common/content_export.h" + #include "content/public/browser/devtools_permission_overrides.h" + #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h" +@@ -18,6 +19,10 @@ class RenderFrameHost; + class CONTENT_EXPORT PermissionControllerDelegate { + public: + using PermissionOverrides = DevToolsPermissionOverrides::PermissionOverrides; ++ ++ // Identifier for an active subscription. ++ using SubscriptionId = util::IdType64; ++ + virtual ~PermissionControllerDelegate() = default; + + // Requests a permission on behalf of a frame identified by +@@ -80,21 +85,21 @@ class CONTENT_EXPORT PermissionControllerDelegate { + + // Runs the given |callback| whenever the |permission| associated with the + // given RenderFrameHost changes. A nullptr should be passed if the request +- // is from a worker. Returns the subscription_id to be used to unsubscribe. +- // Can be kNoPendingOperation if the subscribe was not successful. +- virtual int SubscribePermissionStatusChange( ++ // is from a worker. Returns the ID to be used to unsubscribe, which can be ++ // `is_null()` if the subscribe was not successful. ++ virtual SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback + callback) = 0; + +- // Unregisters from permission status change notifications. +- // The |subscription_id| must match the value returned by the +- // SubscribePermissionStatusChange call. Unsubscribing +- // an already unsubscribed |subscription_id| or providing the +- // |subscription_id| kNoPendingOperation is a no-op. +- virtual void UnsubscribePermissionStatusChange(int subscription_id) = 0; ++ // Unregisters from permission status change notifications. The ++ // |subscription_id| must match the value returned by the ++ // SubscribePermissionStatusChange call. Unsubscribing an already ++ // unsubscribed |subscription_id| or an `is_null()` ID is a no-op. ++ virtual void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) = 0; + + // Manually overrides default permission settings of delegate, if overrides + // are tracked by the delegate. This method should only be called by the +@@ -116,4 +121,17 @@ class CONTENT_EXPORT PermissionControllerDelegate { + + } // namespace content + ++namespace std { ++ ++template <> ++struct hash { ++ std::size_t operator()( ++ const content::PermissionControllerDelegate::SubscriptionId& v) const { ++ content::PermissionControllerDelegate::SubscriptionId::Hasher hasher; ++ return hasher(v); ++ } ++}; ++ ++} // namespace std ++ + #endif // CONTENT_PUBLIC_BROWSER_PERMISSION_CONTROLLER_DELEGATE_H_ +diff --git a/content/public/test/mock_permission_manager.h b/content/public/test/mock_permission_manager.h +index aaaf623d534de1e57df072404c67db76ed2e3803..287fc019a3bf889d2b0f955f5aa11c02db7101e3 100644 +--- a/content/public/test/mock_permission_manager.h ++++ b/content/public/test/mock_permission_manager.h +@@ -50,12 +50,14 @@ class MockPermissionManager : public PermissionControllerDelegate { + const GURL& requesting_origin, + const GURL& embedding_origin) override {} + MOCK_METHOD4(SubscribePermissionStatusChange, +- int(PermissionType permission, ++ SubscriptionId( ++ PermissionType permission, + RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback + callback)); +- MOCK_METHOD1(UnsubscribePermissionStatusChange, void(int subscription_id)); ++ MOCK_METHOD1(UnsubscribePermissionStatusChange, ++ void(SubscriptionId subscription_id)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockPermissionManager); +diff --git a/content/shell/browser/shell_permission_manager.cc b/content/shell/browser/shell_permission_manager.cc +index 23533f8455653a0362db4b451cdbbdc9b8ca61fd..df2f7879cd6148802fc8b983f69dcb7573f8853a 100644 +--- a/content/shell/browser/shell_permission_manager.cc ++++ b/content/shell/browser/shell_permission_manager.cc +@@ -134,16 +134,16 @@ ShellPermissionManager::GetPermissionStatusForFrame( + .GetOrigin()); + } + +-int ShellPermissionManager::SubscribePermissionStatusChange( ++ShellPermissionManager::SubscriptionId ++ShellPermissionManager::SubscribePermissionStatusChange( + PermissionType permission, + RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) { +- return PermissionController::kNoPendingOperation; ++ return SubscriptionId(); + } + + void ShellPermissionManager::UnsubscribePermissionStatusChange( +- int subscription_id) { +-} ++ SubscriptionId subscription_id) {} + + } // namespace content +diff --git a/content/shell/browser/shell_permission_manager.h b/content/shell/browser/shell_permission_manager.h +index 85477665a9dd643f50c4567c1133973bc258a94f..ecda464779c39df4a8b4b1726414cf2763033f53 100644 +--- a/content/shell/browser/shell_permission_manager.h ++++ b/content/shell/browser/shell_permission_manager.h +@@ -42,13 +42,14 @@ class ShellPermissionManager : public PermissionControllerDelegate { + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin) override; +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + PermissionType permission, + RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override; +- void UnsubscribePermissionStatusChange(int subscription_id) override; ++ void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellPermissionManager); +diff --git a/fuchsia/engine/browser/web_engine_permission_delegate.cc b/fuchsia/engine/browser/web_engine_permission_delegate.cc +index 98592f05b6d56108119b106a42d839c28131a324..c18b8be7cdf73720cf02afce9ea75ab98d3f1f64 100644 +--- a/fuchsia/engine/browser/web_engine_permission_delegate.cc ++++ b/fuchsia/engine/browser/web_engine_permission_delegate.cc +@@ -83,20 +83,21 @@ WebEnginePermissionDelegate::GetPermissionStatusForFrame( + permission, url::Origin::Create(requesting_origin)); + } + +-int WebEnginePermissionDelegate::SubscribePermissionStatusChange( ++WebEnginePermissionDelegate::SubscriptionId ++WebEnginePermissionDelegate::SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) { + // TODO(crbug.com/1063094): Implement permission status subscription. It's + // used in blink to emit PermissionStatus.onchange notifications. +- NOTIMPLEMENTED() << ": " << static_cast(permission); +- return content::PermissionController::kNoPendingOperation; ++ NOTIMPLEMENTED_LOG_ONCE() << ": " << static_cast(permission); ++ return SubscriptionId(); + } + + void WebEnginePermissionDelegate::UnsubscribePermissionStatusChange( +- int subscription_id) { ++ SubscriptionId subscription_id) { + // TODO(crbug.com/1063094): Implement permission status subscription. It's + // used in blink to emit PermissionStatus.onchange notifications. +- NOTREACHED(); ++ NOTIMPLEMENTED_LOG_ONCE(); + } +diff --git a/fuchsia/engine/browser/web_engine_permission_delegate.h b/fuchsia/engine/browser/web_engine_permission_delegate.h +index 036207b75d33b752981007a41aa51f3e64db4f0e..c39989b471c2a74ee1a9140e12f205866a0b7aff 100644 +--- a/fuchsia/engine/browser/web_engine_permission_delegate.h ++++ b/fuchsia/engine/browser/web_engine_permission_delegate.h +@@ -45,13 +45,14 @@ class WebEnginePermissionDelegate + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin) override; +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override; +- void UnsubscribePermissionStatusChange(int subscription_id) override; ++ void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) override; + }; + + #endif // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_PERMISSION_DELEGATE_H_ +diff --git a/headless/lib/browser/headless_permission_manager.cc b/headless/lib/browser/headless_permission_manager.cc +index 5d4d609fc0c10e57ef3c6a730340dc409789dcdd..359ecdc4b72d560da830c51f23374150e6b30a2d 100644 +--- a/headless/lib/browser/headless_permission_manager.cc ++++ b/headless/lib/browser/headless_permission_manager.cc +@@ -71,15 +71,16 @@ HeadlessPermissionManager::GetPermissionStatusForFrame( + return blink::mojom::PermissionStatus::ASK; + } + +-int HeadlessPermissionManager::SubscribePermissionStatusChange( ++HeadlessPermissionManager::SubscriptionId ++HeadlessPermissionManager::SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) { +- return content::PermissionController::kNoPendingOperation; ++ return SubscriptionId(); + } + + void HeadlessPermissionManager::UnsubscribePermissionStatusChange( +- int subscription_id) {} ++ SubscriptionId subscription_id) {} + + } // namespace headless +diff --git a/headless/lib/browser/headless_permission_manager.h b/headless/lib/browser/headless_permission_manager.h +index 4b83309ab3ada17f7f2ac3323ba5f13ab76b9409..ac30670cb384a79457033a25c13da0c305b8eff2 100644 +--- a/headless/lib/browser/headless_permission_manager.h ++++ b/headless/lib/browser/headless_permission_manager.h +@@ -46,13 +46,14 @@ class HeadlessPermissionManager : public content::PermissionControllerDelegate { + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin) override; +- int SubscribePermissionStatusChange( ++ SubscriptionId SubscribePermissionStatusChange( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + base::RepeatingCallback callback) + override; +- void UnsubscribePermissionStatusChange(int subscription_id) override; ++ void UnsubscribePermissionStatusChange( ++ SubscriptionId subscription_id) override; + + private: + content::BrowserContext* browser_context_; diff --git a/patches/chromium/x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch b/patches/chromium/x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch new file mode 100644 index 0000000000000..ce7d7d5445229 --- /dev/null +++ b/patches/chromium/x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch @@ -0,0 +1,95 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tudor Brindus +Date: Tue, 4 May 2021 07:17:23 +0000 +Subject: x11: Fix window enumeration order when WM doesn't set + |_NET_CLIENT_LIST_STACKING| + +Chrome needs to know what window to send Xdnd events to during a +drag-and-drop operation. + +It does so through |X11TopmostWindowFinder::FindWindowAt|, which calls +into |EnumerateWindows| when the WM has not set +|_NET_CLIENT_LIST_STACKING|. GNOME/mutter set this property, so this is +a little-tested code path. + +However, setting this property is not mandated by the spec, and in +practice wlroots/Sway do not currently set it. + +Chrome's current |EnumerateWindows| fallback path is incorrect, +resulting in downstream bugs like +https://github.com/swaywm/sway/issues/5692 and +https://github.com/swaywm/wlroots/issues/2889. + +|EnumerateWindows| ends up calling |EnumerateChildren|, which uses +|XQueryTree| to determine the stacking order. |XQueryTree| returns +windows in bottom-to-top order, so the list has to be reverse-iterated +to get a top-down order that |FindWindowAt| expects (and to match the +order reported by |_NET_CLIENT_LIST_STACKING|). + +The original code introduced in da11eed did so; however, it regressed in +3c64537 -- currently, the code comments are inconsistent with the actual +logic. + +This commit switches |EnumerateChildren| to use a reverse iterator. It +is not used anywhere but as a fallback when |_NET_CLIENT_LIST_STACKING| +is not present, so this should be a safe change. + +I have not touched the iteration order of Chrome's X11 menus. I suspect +that these are in the right order already. + +TEST=manually tested on Sway 1.6, with Chromium overlapping an X11 gedit + window + +Change-Id: I8f777aa566db1e8d0614187fa4b3d156caa1e0f9 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2844104 +Reviewed-by: Maksim Sisov +Commit-Queue: Maksim Sisov +Cr-Commit-Position: refs/heads/master@{#878767} + +diff --git a/AUTHORS b/AUTHORS +index ae48471f7360a946447c915621236155e1399f86..4b48c543aa3c70fa5f785fb294f5dfe1a48217be 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -1036,6 +1036,7 @@ Toshihito Kikuchi + Trent Willis + Trevor Perrin + Tripta Gupta ++Tudor Brindus + Tuukka Toivonen + U. Artie Eoff + Umar Hansa +diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc +index c210014f2b07e731fb2e7c3ccbce44dec43dda25..482fa1d3528fe1ca7280af2f3a32f55128e0ce96 100644 +--- a/ui/base/x/x11_util.cc ++++ b/ui/base/x/x11_util.cc +@@ -811,10 +811,10 @@ bool EnumerateChildren(EnumerateWindowsDelegate* delegate, + return false; + + std::vector windows; +- std::vector::iterator iter; + if (depth == 0) { + XMenuList::GetInstance()->InsertMenuWindows(&windows); + // Enumerate the menus first. ++ std::vector::iterator iter; + for (iter = windows.begin(); iter != windows.end(); iter++) { + if (delegate->ShouldStopIterating(*iter)) + return true; +@@ -829,7 +829,8 @@ bool EnumerateChildren(EnumerateWindowsDelegate* delegate, + + // XQueryTree returns the children of |window| in bottom-to-top order, so + // reverse-iterate the list to check the windows from top-to-bottom. +- for (iter = windows.begin(); iter != windows.end(); iter++) { ++ std::vector::reverse_iterator iter; ++ for (iter = windows.rbegin(); iter != windows.rend(); iter++) { + if (IsWindowNamed(*iter) && delegate->ShouldStopIterating(*iter)) + return true; + } +@@ -839,7 +840,7 @@ bool EnumerateChildren(EnumerateWindowsDelegate* delegate, + // loop because the recursion and call to XQueryTree are expensive and is only + // needed for a small number of cases. + if (++depth <= max_depth) { +- for (iter = windows.begin(); iter != windows.end(); iter++) { ++ for (iter = windows.rbegin(); iter != windows.rend(); iter++) { + if (EnumerateChildren(delegate, *iter, max_depth, depth)) + return true; + } diff --git a/patches/config.json b/patches/config.json index af04ffbd84043..4ff406c7514ad 100644 --- a/patches/config.json +++ b/patches/config.json @@ -21,5 +21,9 @@ "src/electron/patches/usrsctp": "src/third_party/usrsctp/usrsctplib", - "src/electron/patches/pdfium": "src/third_party/pdfium" + "src/electron/patches/pdfium": "src/third_party/pdfium", + + "src/electron/patches/webrtc": "src/third_party/webrtc", + + "src/electron/patches/angle": "src/third_party/angle" } diff --git a/patches/node/.patches b/patches/node/.patches index b3a7bca5a5845..546aa1217d5af 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -48,3 +48,5 @@ chore_expose_v8_initialization_isolate_callbacks.patch fix_add_safeforterminationscopes_for_sigint_interruptions.patch allow_preventing_preparestacktracecallback.patch src_inline_asynccleanuphookhandle_in_headers.patch +src_make_freeenvironment_perform_all_necessary_cleanup.patch +process_correctly_parse_unicode_in_node_options.patch diff --git a/patches/node/process_correctly_parse_unicode_in_node_options.patch b/patches/node/process_correctly_parse_unicode_in_node_options.patch new file mode 100644 index 0000000000000..2f55d7e5cc368 --- /dev/null +++ b/patches/node/process_correctly_parse_unicode_in_node_options.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bartosz Sosnowski +Date: Wed, 22 Jul 2020 12:55:11 +0200 +Subject: process: correctly parse Unicode in NODE_OPTIONS + +Fixes an issue on Windows, where Unicode in NODE_OPTIONS was not parsed +correctly. + +Fixes: https://github.com/nodejs/node/issues/34399 + +PR-URL: https://github.com/nodejs/node/pull/34476 +Reviewed-By: Anna Henningsen +Reviewed-By: James M Snell +Reviewed-By: Gus Caplan +Reviewed-By: Denys Otrishko + +diff --git a/src/node_credentials.cc b/src/node_credentials.cc +index d552a501726396b0d38c55f6f7cb5c2287189fc8..7c7a4f84c860844641d6931a6352ea4473ab4eac 100644 +--- a/src/node_credentials.cc ++++ b/src/node_credentials.cc +@@ -58,8 +58,20 @@ bool SafeGetenv(const char* key, std::string* text, Environment* env) { + + { + Mutex::ScopedLock lock(per_process::env_var_mutex); +- if (const char* value = getenv(key)) { +- *text = value; ++ ++ size_t init_sz = 256; ++ MaybeStackBuffer val; ++ int ret = uv_os_getenv(key, *val, &init_sz); ++ ++ if (ret == UV_ENOBUFS) { ++ // Buffer is not large enough, reallocate to the updated init_sz ++ // and fetch env value again. ++ val.AllocateSufficientStorage(init_sz); ++ ret = uv_os_getenv(key, *val, &init_sz); ++ } ++ ++ if (ret >= 0) { // Env key value fetch success. ++ *text = *val; + return true; + } + } +diff --git a/test/parallel/test-unicode-node-options.js b/test/parallel/test-unicode-node-options.js +new file mode 100644 +index 0000000000000000000000000000000000000000..e5a40d118791d3748fa2a82e9c8bddefd78ad17e +--- /dev/null ++++ b/test/parallel/test-unicode-node-options.js +@@ -0,0 +1,26 @@ ++'use strict'; ++// Flags: --expose-internals ++require('../common'); ++const { getOptionValue } = require('internal/options'); ++const assert = require('assert'); ++const cp = require('child_process'); ++ ++const expected_redirect_value = 'foó'; ++ ++if (process.argv.length === 2) { ++ const NODE_OPTIONS = `--redirect-warnings=${expected_redirect_value}`; ++ const result = cp.spawnSync(process.argv0, ++ ['--expose-internals', __filename, 'test'], ++ { ++ env: { ++ ...process.env, ++ NODE_OPTIONS ++ }, ++ stdio: 'inherit' ++ }); ++ assert.strictEqual(result.status, 0); ++} else { ++ const redirect_value = getOptionValue('--redirect-warnings'); ++ console.log(`--redirect-warings=${redirect_value}`); ++ assert.strictEqual(redirect_value, expected_redirect_value); ++} diff --git a/patches/node/src_make_freeenvironment_perform_all_necessary_cleanup.patch b/patches/node/src_make_freeenvironment_perform_all_necessary_cleanup.patch new file mode 100644 index 0000000000000..2a5dcf496b9a3 --- /dev/null +++ b/patches/node/src_make_freeenvironment_perform_all_necessary_cleanup.patch @@ -0,0 +1,191 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Anna Henningsen +Date: Sun, 10 Nov 2019 19:47:03 +0000 +Subject: src: make `FreeEnvironment()` perform all necessary cleanup + +Make the calls `stop_sub_worker_contexts()`, `RunCleanup()` +part of the public API for easier embedding. + +(Note that calling `RunAtExit()` is idempotent because the +at-exit callback queue is cleared after each call.) + +PR-URL: https://github.com/nodejs/node/pull/30467 +Reviewed-By: James M Snell +Reviewed-By: Gireesh Punathil + +diff --git a/src/api/environment.cc b/src/api/environment.cc +index adf033f2e1855ad1c9738f9746677566aabedd87..38e8b0927884316991239cb74496156510e8a91e 100644 +--- a/src/api/environment.cc ++++ b/src/api/environment.cc +@@ -332,7 +332,23 @@ Environment* CreateEnvironment(IsolateData* isolate_data, + } + + void FreeEnvironment(Environment* env) { +- env->RunCleanup(); ++ { ++ // TODO(addaleax): This should maybe rather be in a SealHandleScope. ++ HandleScope handle_scope(env->isolate()); ++ Context::Scope context_scope(env->context()); ++ env->set_stopping(true); ++ env->stop_sub_worker_contexts(); ++ env->RunCleanup(); ++ RunAtExit(env); ++ } ++ ++ // This call needs to be made while the `Environment` is still alive ++ // because we assume that it is available for async tracking in the ++ // NodePlatform implementation. ++ MultiIsolatePlatform* platform = env->isolate_data()->platform(); ++ if (platform != nullptr) ++ platform->DrainTasks(env->isolate()); ++ + delete env; + } + +diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc +index 91bb30cb4e3ee67786f2d50a335db8b714f82203..c6533321bd6b6498980978fd78e8ff89214bd86c 100644 +--- a/src/node_main_instance.cc ++++ b/src/node_main_instance.cc +@@ -110,7 +110,8 @@ int NodeMainInstance::Run() { + HandleScope handle_scope(isolate_); + + int exit_code = 0; +- std::unique_ptr env = CreateMainEnvironment(&exit_code); ++ DeleteFnPtr env = ++ CreateMainEnvironment(&exit_code); + + CHECK_NOT_NULL(env); + Context::Scope context_scope(env->context()); +@@ -149,10 +150,7 @@ int NodeMainInstance::Run() { + exit_code = EmitExit(env.get()); + } + +- env->set_can_call_into_js(false); +- env->stop_sub_worker_contexts(); + ResetStdio(); +- env->RunCleanup(); + + // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really + // make sense here. +@@ -167,10 +165,6 @@ int NodeMainInstance::Run() { + } + #endif + +- RunAtExit(env.get()); +- +- per_process::v8_platform.DrainVMTasks(isolate_); +- + #if defined(LEAK_SANITIZER) + __lsan_do_leak_check(); + #endif +@@ -180,8 +174,8 @@ int NodeMainInstance::Run() { + + // TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h + // and the environment creation routine in workers somehow. +-std::unique_ptr NodeMainInstance::CreateMainEnvironment( +- int* exit_code) { ++DeleteFnPtr ++NodeMainInstance::CreateMainEnvironment(int* exit_code) { + *exit_code = 0; // Reset the exit code to 0 + + HandleScope handle_scope(isolate_); +@@ -205,14 +199,14 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( + CHECK(!context.IsEmpty()); + Context::Scope context_scope(context); + +- std::unique_ptr env = std::make_unique( ++ DeleteFnPtr env { new Environment( + isolate_data_.get(), + context, + args_, + exec_args_, + static_cast(Environment::kIsMainThread | + Environment::kOwnsProcessState | +- Environment::kOwnsInspector)); ++ Environment::kOwnsInspector)) }; + env->InitializeLibuv(per_process::v8_is_profiling); + env->InitializeDiagnostics(); + +diff --git a/src/node_main_instance.h b/src/node_main_instance.h +index 5bc18cb3de63c02256ef7a5980166e75e60c1608..cc9f50b9222de33b52322254c4088f26145e1a93 100644 +--- a/src/node_main_instance.h ++++ b/src/node_main_instance.h +@@ -61,7 +61,8 @@ class NodeMainInstance { + + // TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h + // and the environment creation routine in workers somehow. +- std::unique_ptr CreateMainEnvironment(int* exit_code); ++ DeleteFnPtr CreateMainEnvironment( ++ int* exit_code); + + // If nullptr is returned, the binary is not built with embedded + // snapshot. +diff --git a/src/node_platform.cc b/src/node_platform.cc +index a0ea118861867277d8f5f15625227d49505d1c6a..0225678be331109465264c655ff1029436945f54 100644 +--- a/src/node_platform.cc ++++ b/src/node_platform.cc +@@ -402,6 +402,7 @@ void PerIsolatePlatformData::RunForegroundTask(uv_timer_t* handle) { + + void NodePlatform::DrainTasks(Isolate* isolate) { + std::shared_ptr per_isolate = ForIsolate(isolate); ++ if (!per_isolate) return; + + do { + // Worker tasks aren't associated with an Isolate. +@@ -463,12 +464,14 @@ std::shared_ptr + NodePlatform::ForIsolate(Isolate* isolate) { + Mutex::ScopedLock lock(per_isolate_mutex_); + std::shared_ptr data = per_isolate_[isolate]; +- CHECK(data); ++ CHECK_NOT_NULL(data); + return data; + } + + bool NodePlatform::FlushForegroundTasks(Isolate* isolate) { +- return ForIsolate(isolate)->FlushForegroundTasksInternal(); ++ std::shared_ptr per_isolate = ForIsolate(isolate); ++ if (!per_isolate) return false; ++ return per_isolate->FlushForegroundTasksInternal(); + } + + bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { return false; } +diff --git a/src/node_worker.cc b/src/node_worker.cc +index 367541bea6aa0f66203466ff60e25f15e28608db..bd405e20ae6c8118c86ad7f3c12c07f3d6adc4f2 100644 +--- a/src/node_worker.cc ++++ b/src/node_worker.cc +@@ -279,26 +279,18 @@ void Worker::Run() { + + if (!env_) return; + env_->set_can_call_into_js(false); +- Isolate::DisallowJavascriptExecutionScope disallow_js(isolate_, +- Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); + + { +- Context::Scope context_scope(env_->context()); +- { +- Mutex::ScopedLock lock(mutex_); +- stopped_ = true; +- this->env_ = nullptr; +- } +- env_->set_stopping(true); +- env_->stop_sub_worker_contexts(); +- env_->RunCleanup(); +- RunAtExit(env_.get()); +- +- // This call needs to be made while the `Environment` is still alive +- // because we assume that it is available for async tracking in the +- // NodePlatform implementation. +- platform_->DrainTasks(isolate_); ++ Mutex::ScopedLock lock(mutex_); ++ stopped_ = true; ++ this->env_ = nullptr; + } ++ ++ // TODO(addaleax): Try moving DisallowJavascriptExecutionScope into ++ // FreeEnvironment(). ++ Isolate::DisallowJavascriptExecutionScope disallow_js(isolate_, ++ Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); ++ env_.reset(); + }); + + if (is_stopped()) return; diff --git a/patches/skia/.patches b/patches/skia/.patches index 9603e45ea16cd..d42ca54323823 100644 --- a/patches/skia/.patches +++ b/patches/skia/.patches @@ -1 +1,2 @@ cherry-pick-b0d3d3e85fa6.patch +skscalercontext_getimage_less_brittle.patch diff --git a/patches/skia/skscalercontext_getimage_less_brittle.patch b/patches/skia/skscalercontext_getimage_less_brittle.patch new file mode 100644 index 0000000000000..1b4b7a070dcf2 --- /dev/null +++ b/patches/skia/skscalercontext_getimage_less_brittle.patch @@ -0,0 +1,199 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ben Wagner +Date: Thu, 1 Apr 2021 15:02:21 -0400 +Subject: SkScalerContext::getImage less brittle. + +Properly handle edge cases like + * the temporary glyph being a different size than expected + * filters which reduce in size + * filters which return false to indicate no filtering has been done + +Bug: chromium:1190525 +Change-Id: Ibc53eb1d7014210019e96cd6bae3e256d967be54 +Reviewed-on: https://skia-review.googlesource.com/c/skia/+/392156 +Commit-Queue: Ben Wagner +Reviewed-by: Herb Derby +(cherry picked from commit 348ee387a96d7d94733d46ad9e82b19cb890dd16) +Reviewed-on: https://skia-review.googlesource.com/c/skia/+/392437 + +diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp +index 30035aba11b3a7fb83d459a833ca13e2be4ded70..b29928f06de09efeffb0d8c1437b97f00ea6e43e 100644 +--- a/src/core/SkScalerContext.cpp ++++ b/src/core/SkScalerContext.cpp +@@ -541,41 +541,39 @@ static void generateMask(const SkMask& mask, const SkPath& path, + } + + void SkScalerContext::getImage(const SkGlyph& origGlyph) { +- const SkGlyph* glyph = &origGlyph; ++ const SkGlyph* unfilteredGlyph = &origGlyph; + SkGlyph tmpGlyph{origGlyph.getPackedID()}; + + // in case we need to call generateImage on a mask-format that is different + // (i.e. larger) than what our caller allocated by looking at origGlyph. + SkAutoMalloc tmpGlyphImageStorage; + +- if (fMaskFilter) { // restore the prefilter bounds +- ++ if (fMaskFilter) { + // need the original bounds, sans our maskfilter + sk_sp mf = std::move(fMaskFilter); + this->getMetrics(&tmpGlyph); + fMaskFilter = std::move(mf); + +- // we need the prefilter bounds to be <= filter bounds +- SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth); +- SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight); +- +- if (tmpGlyph.fMaskFormat == origGlyph.fMaskFormat) { ++ // Use the origGlyph storage for the temporary unfiltered mask if it will fit. ++ if (tmpGlyph.fMaskFormat == origGlyph.fMaskFormat && ++ tmpGlyph.imageSize() <= origGlyph.imageSize()) ++ { + tmpGlyph.fImage = origGlyph.fImage; + } else { + tmpGlyphImageStorage.reset(tmpGlyph.imageSize()); + tmpGlyph.fImage = tmpGlyphImageStorage.get(); + } +- glyph = &tmpGlyph; ++ unfilteredGlyph = &tmpGlyph; + } + + if (!fGenerateImageFromPath) { +- generateImage(*glyph); ++ generateImage(*unfilteredGlyph); + } else { + SkPath devPath; +- SkMask mask = glyph->mask(); ++ SkMask mask = unfilteredGlyph->mask(); + +- if (!this->internalGetPath(glyph->getPackedID(), &devPath)) { +- generateImage(*glyph); ++ if (!this->internalGetPath(unfilteredGlyph->getPackedID(), &devPath)) { ++ generateImage(*unfilteredGlyph); + } else { + SkASSERT(SkMask::kARGB32_Format != origGlyph.fMaskFormat); + SkASSERT(SkMask::kARGB32_Format != mask.fFormat); +@@ -587,39 +585,98 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { + } + + if (fMaskFilter) { +- // the src glyph image shouldn't be 3D +- SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); ++ // k3D_Format should not be mask filtered. ++ SkASSERT(SkMask::k3D_Format != unfilteredGlyph->fMaskFormat); ++ ++ SkMask filteredMask; ++ SkMask srcMask; ++ SkMatrix m; ++ fRec.getMatrixFrom2x2(&m); ++ ++ if (as_MFB(fMaskFilter)->filterMask(&filteredMask, unfilteredGlyph->mask(), m, nullptr)) { ++ // Filter succeeded; filteredMask.fImage was allocated. ++ srcMask = filteredMask; ++ } else if (unfilteredGlyph->fImage == tmpGlyphImageStorage.get()) { ++ // Filter did nothing; unfiltered mask is independent of origGlyph.fImage. ++ srcMask = unfilteredGlyph->mask(); ++ } else if (origGlyph.iRect() == unfilteredGlyph->iRect()) { ++ // Filter did nothing; the unfiltered mask is in origGlyph.fImage and matches. ++ return; ++ } else { ++ // Filter did nothing; the unfiltered mask is in origGlyph.fImage and conflicts. ++ srcMask = unfilteredGlyph->mask(); ++ size_t imageSize = unfilteredGlyph->imageSize(); ++ tmpGlyphImageStorage.reset(imageSize); ++ srcMask.fImage = static_cast(tmpGlyphImageStorage.get()); ++ memcpy(srcMask.fImage, unfilteredGlyph->fImage, imageSize); ++ } + +- SkMask srcM = glyph->mask(), +- dstM; +- SkMatrix matrix; ++ SkASSERT_RELEASE(srcMask.fFormat == origGlyph.fMaskFormat); ++ SkMask dstMask = origGlyph.mask(); ++ SkIRect origBounds = dstMask.fBounds; + +- fRec.getMatrixFrom2x2(&matrix); ++ // Find the intersection of src and dst while updating the fImages. ++ if (srcMask.fBounds.fTop < dstMask.fBounds.fTop) { ++ int32_t topDiff = dstMask.fBounds.fTop - srcMask.fBounds.fTop; ++ srcMask.fImage += srcMask.fRowBytes * topDiff; ++ srcMask.fBounds.fTop = dstMask.fBounds.fTop; ++ } ++ if (dstMask.fBounds.fTop < srcMask.fBounds.fTop) { ++ int32_t topDiff = srcMask.fBounds.fTop - dstMask.fBounds.fTop; ++ dstMask.fImage += dstMask.fRowBytes * topDiff; ++ dstMask.fBounds.fTop = srcMask.fBounds.fTop; ++ } + +- if (as_MFB(fMaskFilter)->filterMask(&dstM, srcM, matrix, nullptr)) { +- int width = std::min(origGlyph.fWidth, dstM.fBounds.width()); +- int height = std::min(origGlyph.fHeight, dstM.fBounds.height()); +- int dstRB = origGlyph.rowBytes(); +- int srcRB = dstM.fRowBytes; ++ if (srcMask.fBounds.fLeft < dstMask.fBounds.fLeft) { ++ int32_t leftDiff = dstMask.fBounds.fLeft - srcMask.fBounds.fLeft; ++ srcMask.fImage += leftDiff; ++ srcMask.fBounds.fLeft = dstMask.fBounds.fLeft; ++ } ++ if (dstMask.fBounds.fLeft < srcMask.fBounds.fLeft) { ++ int32_t leftDiff = srcMask.fBounds.fLeft - dstMask.fBounds.fLeft; ++ dstMask.fImage += leftDiff; ++ dstMask.fBounds.fLeft = srcMask.fBounds.fLeft; ++ } + +- const uint8_t* src = (const uint8_t*)dstM.fImage; +- uint8_t* dst = (uint8_t*)origGlyph.fImage; ++ if (srcMask.fBounds.fBottom < dstMask.fBounds.fBottom) { ++ dstMask.fBounds.fBottom = srcMask.fBounds.fBottom; ++ } ++ if (dstMask.fBounds.fBottom < srcMask.fBounds.fBottom) { ++ srcMask.fBounds.fBottom = dstMask.fBounds.fBottom; ++ } + +- if (SkMask::k3D_Format == dstM.fFormat) { +- // we have to copy 3 times as much +- height *= 3; +- } ++ if (srcMask.fBounds.fRight < dstMask.fBounds.fRight) { ++ dstMask.fBounds.fRight = srcMask.fBounds.fRight; ++ } ++ if (dstMask.fBounds.fRight < srcMask.fBounds.fRight) { ++ srcMask.fBounds.fRight = dstMask.fBounds.fRight; ++ } + +- // clean out our glyph, since it may be larger than dstM +- //sk_bzero(dst, height * dstRB); ++ SkASSERT(srcMask.fBounds == dstMask.fBounds); ++ int width = srcMask.fBounds.width(); ++ int height = srcMask.fBounds.height(); ++ int dstRB = dstMask.fRowBytes; ++ int srcRB = srcMask.fRowBytes; + +- while (--height >= 0) { +- memcpy(dst, src, width); +- src += srcRB; +- dst += dstRB; +- } +- SkMask::FreeImage(dstM.fImage); ++ const uint8_t* src = srcMask.fImage; ++ uint8_t* dst = dstMask.fImage; ++ ++ if (SkMask::k3D_Format == filteredMask.fFormat) { ++ // we have to copy 3 times as much ++ height *= 3; ++ } ++ ++ // If not filling the full original glyph, clear it out first. ++ if (dstMask.fBounds != origBounds) { ++ sk_bzero(origGlyph.fImage, origGlyph.fHeight * origGlyph.rowBytes()); ++ } ++ ++ while (--height >= 0) { ++ memcpy(dst, src, width); ++ src += srcRB; ++ dst += dstRB; + } ++ SkMask::FreeImage(filteredMask.fImage); + } + } + diff --git a/patches/sqlite/.patches b/patches/sqlite/.patches index c842e852f3c98..7109ed15cc4c5 100644 --- a/patches/sqlite/.patches +++ b/patches/sqlite/.patches @@ -1 +1,3 @@ fix_a_problem_handling_sub-queries_with_both_a_correlated_where.patch +utf-8_q_when_20constructing_20the_20synthensized_20select_20sta.patch +sqlite_fix_an_undefined-integer-overflow_problem_in_fts3_c.patch diff --git a/patches/sqlite/sqlite_fix_an_undefined-integer-overflow_problem_in_fts3_c.patch b/patches/sqlite/sqlite_fix_an_undefined-integer-overflow_problem_in_fts3_c.patch new file mode 100644 index 0000000000000..5acc09a5eb729 --- /dev/null +++ b/patches/sqlite/sqlite_fix_an_undefined-integer-overflow_problem_in_fts3_c.patch @@ -0,0 +1,157 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Darwin Huang +Date: Wed, 19 May 2021 14:13:15 -0700 +Subject: sqlite: Fix an undefined-integer-overflow problem in fts3.c. + +Original change: https://sqlite.org/src/info/a0bf931bd712037e + +Bug: 1204066 +Change-Id: I34704f1cfe36672d10065f4103c91fb4f35d3895 + +diff --git a/amalgamation/sqlite3.c b/amalgamation/sqlite3.c +index 9e3a46624d09f7abe645d1d9e4b2c430c8acc99e..8dff8dcb9be5fd5d1010a44d30d27461659f89c0 100644 +--- a/amalgamation/sqlite3.c ++++ b/amalgamation/sqlite3.c +@@ -168279,7 +168279,7 @@ static int fts3ScanInteriorNode( + char *zBuffer = 0; /* Buffer to load terms into */ + i64 nAlloc = 0; /* Size of allocated buffer */ + int isFirstTerm = 1; /* True when processing first term on page */ +- sqlite3_int64 iChild; /* Block id of child node to descend to */ ++ u64 iChild; /* Block id of child node to descend to */ + int nBuffer = 0; /* Total term size */ + + /* Skip over the 'height' varint that occurs at the start of every +@@ -168295,8 +168295,8 @@ static int fts3ScanInteriorNode( + ** table, then there are always 20 bytes of zeroed padding following the + ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). + */ +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); + if( zCsr>zEnd ){ + return FTS_CORRUPT_VTAB; + } +@@ -168349,20 +168349,20 @@ static int fts3ScanInteriorNode( + */ + cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); + if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ +- *piFirst = iChild; ++ *piFirst = (i64)iChild; + piFirst = 0; + } + + if( piLast && cmp<0 ){ +- *piLast = iChild; ++ *piLast = (i64)iChild; + piLast = 0; + } + + iChild++; + }; + +- if( piFirst ) *piFirst = iChild; +- if( piLast ) *piLast = iChild; ++ if( piFirst ) *piFirst = (i64)iChild; ++ if( piLast ) *piLast = (i64)iChild; + + finish_scan: + sqlite3_free(zBuffer); +diff --git a/amalgamation_dev/sqlite3.c b/amalgamation_dev/sqlite3.c +index 689d52d5f51e8f552d4191b6811f556a2b997303..5e56254000b0225d24b332cb4fa6b2b0507c68ea 100644 +--- a/amalgamation_dev/sqlite3.c ++++ b/amalgamation_dev/sqlite3.c +@@ -168779,7 +168779,7 @@ static int fts3ScanInteriorNode( + char *zBuffer = 0; /* Buffer to load terms into */ + i64 nAlloc = 0; /* Size of allocated buffer */ + int isFirstTerm = 1; /* True when processing first term on page */ +- sqlite3_int64 iChild; /* Block id of child node to descend to */ ++ u64 iChild; /* Block id of child node to descend to */ + int nBuffer = 0; /* Total term size */ + + /* Skip over the 'height' varint that occurs at the start of every +@@ -168795,8 +168795,8 @@ static int fts3ScanInteriorNode( + ** table, then there are always 20 bytes of zeroed padding following the + ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). + */ +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); + if( zCsr>zEnd ){ + return FTS_CORRUPT_VTAB; + } +@@ -168849,20 +168849,20 @@ static int fts3ScanInteriorNode( + */ + cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); + if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ +- *piFirst = iChild; ++ *piFirst = (i64)iChild; + piFirst = 0; + } + + if( piLast && cmp<0 ){ +- *piLast = iChild; ++ *piLast = (i64)iChild; + piLast = 0; + } + + iChild++; + }; + +- if( piFirst ) *piFirst = iChild; +- if( piLast ) *piLast = iChild; ++ if( piFirst ) *piFirst = (i64)iChild; ++ if( piLast ) *piLast = (i64)iChild; + + finish_scan: + sqlite3_free(zBuffer); +diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c +index 79dc5c88ceacb823d16889bd36250597361d6186..62b31373c3c3e9b61b3e1daae8d87d9393779b61 100644 +--- a/ext/fts3/fts3.c ++++ b/ext/fts3/fts3.c +@@ -1897,7 +1897,7 @@ static int fts3ScanInteriorNode( + char *zBuffer = 0; /* Buffer to load terms into */ + i64 nAlloc = 0; /* Size of allocated buffer */ + int isFirstTerm = 1; /* True when processing first term on page */ +- sqlite3_int64 iChild; /* Block id of child node to descend to */ ++ u64 iChild; /* Block id of child node to descend to */ + int nBuffer = 0; /* Total term size */ + + /* Skip over the 'height' varint that occurs at the start of every +@@ -1913,8 +1913,8 @@ static int fts3ScanInteriorNode( + ** table, then there are always 20 bytes of zeroed padding following the + ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). + */ +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); + if( zCsr>zEnd ){ + return FTS_CORRUPT_VTAB; + } +@@ -1967,20 +1967,20 @@ static int fts3ScanInteriorNode( + */ + cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); + if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ +- *piFirst = iChild; ++ *piFirst = (i64)iChild; + piFirst = 0; + } + + if( piLast && cmp<0 ){ +- *piLast = iChild; ++ *piLast = (i64)iChild; + piLast = 0; + } + + iChild++; + }; + +- if( piFirst ) *piFirst = iChild; +- if( piLast ) *piLast = iChild; ++ if( piFirst ) *piFirst = (i64)iChild; ++ if( piLast ) *piLast = (i64)iChild; + + finish_scan: + sqlite3_free(zBuffer); diff --git a/patches/sqlite/utf-8_q_when_20constructing_20the_20synthensized_20select_20sta.patch b/patches/sqlite/utf-8_q_when_20constructing_20the_20synthensized_20select_20sta.patch new file mode 100644 index 0000000000000..3d74441b7e633 --- /dev/null +++ b/patches/sqlite/utf-8_q_when_20constructing_20the_20synthensized_20select_20sta.patch @@ -0,0 +1,178 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: drh <> +Date: Wed, 19 May 2021 21:55:56 +0000 +Subject: When constructing the synthensized SELECT statement that is used to + choose the rows in an UPDATE FROM, make sure the first table is really the + table being updated, and not some common-table expression that happens to + have the same name. [forum:/forumpost/a274248080|forum post a274248080]. More + changes associated with CTE name resolution are pending. + +FossilOrigin-Name: 0f0959c6f95046e8e7887716e0a7de95da18d1e926ab1f919527083a56541db5 +(cherry picked from commit 1168f810929ede4d8d323a6acf721ff9cd89de90) + +diff --git a/amalgamation/sqlite3.c b/amalgamation/sqlite3.c +index 439a4c8f7a3162be775bea2d37e7b821cf2acd33..9e3a46624d09f7abe645d1d9e4b2c430c8acc99e 100644 +--- a/amalgamation/sqlite3.c ++++ b/amalgamation/sqlite3.c +@@ -1173,7 +1173,7 @@ extern "C" { + */ + #define SQLITE_VERSION "3.33.0" + #define SQLITE_VERSION_NUMBER 3033000 +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 ba9c5a7088bb97ded8e889d7e21e6afe229baf8e670ea4f20d9625b61c8a3984" ++#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 71b8cad5197c8a6074b4b716e311aab34b4a9952f37f56e7d755c4e96a8324d0" + + /* + ** CAPI3REF: Run-Time Library Version Numbers +@@ -110833,7 +110833,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem( + struct SrcList_item *p + ){ + const char *zDb; +- assert( p->pSchema==0 || p->zDatabase==0 ); ++ /* assert( p->pSchema==0 || p->zDatabase==0 ); FIX-ME */ + if( p->pSchema ){ + int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + zDb = pParse->db->aDb[iDb].zDbSName; +@@ -137810,6 +137810,10 @@ static void updateFromSelect( + + assert( pTabList->nSrc>1 ); + if( pSrc ){ ++ if( pSrc->a[0].zDatabase==0 ){ ++ int iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); ++ pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iSchema].zDbSName); ++ } + pSrc->a[0].iCursor = -1; + pSrc->a[0].pTab->nTabRef--; + pSrc->a[0].pTab = 0; +@@ -230010,9 +230014,9 @@ SQLITE_API int sqlite3_stmt_init( + #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ + + /************** End of stmt.c ************************************************/ +-#if __LINE__!=230013 ++#if __LINE__!=230017 + #undef SQLITE_SOURCE_ID +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 ba9c5a7088bb97ded8e889d7e21e6afe229baf8e670ea4f20d9625b61c8aalt2" ++#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 71b8cad5197c8a6074b4b716e311aab34b4a9952f37f56e7d755c4e96a83alt2" + #endif + /* Return the source-id for this library */ + SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } +diff --git a/amalgamation/sqlite3.h b/amalgamation/sqlite3.h +index fa3733fc1e68b49317e9531f690d634a3a508d74..231a1846bcf1f6ac789b589a7d7bef57c3567b88 100644 +--- a/amalgamation/sqlite3.h ++++ b/amalgamation/sqlite3.h +@@ -125,7 +125,7 @@ extern "C" { + */ + #define SQLITE_VERSION "3.33.0" + #define SQLITE_VERSION_NUMBER 3033000 +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 ba9c5a7088bb97ded8e889d7e21e6afe229baf8e670ea4f20d9625b61c8a3984" ++#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 71b8cad5197c8a6074b4b716e311aab34b4a9952f37f56e7d755c4e96a8324d0" + + /* + ** CAPI3REF: Run-Time Library Version Numbers +diff --git a/amalgamation_dev/sqlite3.c b/amalgamation_dev/sqlite3.c +index b98eb8b971f979d50a74bfb6b738625c515064ca..689d52d5f51e8f552d4191b6811f556a2b997303 100644 +--- a/amalgamation_dev/sqlite3.c ++++ b/amalgamation_dev/sqlite3.c +@@ -1173,7 +1173,7 @@ extern "C" { + */ + #define SQLITE_VERSION "3.33.0" + #define SQLITE_VERSION_NUMBER 3033000 +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 ba9c5a7088bb97ded8e889d7e21e6afe229baf8e670ea4f20d9625b61c8a3984" ++#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 71b8cad5197c8a6074b4b716e311aab34b4a9952f37f56e7d755c4e96a8324d0" + + /* + ** CAPI3REF: Run-Time Library Version Numbers +@@ -110846,7 +110846,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem( + struct SrcList_item *p + ){ + const char *zDb; +- assert( p->pSchema==0 || p->zDatabase==0 ); ++ /* assert( p->pSchema==0 || p->zDatabase==0 ); FIX-ME */ + if( p->pSchema ){ + int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + zDb = pParse->db->aDb[iDb].zDbSName; +@@ -137823,6 +137823,10 @@ static void updateFromSelect( + + assert( pTabList->nSrc>1 ); + if( pSrc ){ ++ if( pSrc->a[0].zDatabase==0 ){ ++ int iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); ++ pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iSchema].zDbSName); ++ } + pSrc->a[0].iCursor = -1; + pSrc->a[0].pTab->nTabRef--; + pSrc->a[0].pTab = 0; +@@ -230510,9 +230514,9 @@ SQLITE_API int sqlite3_stmt_init( + #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ + + /************** End of stmt.c ************************************************/ +-#if __LINE__!=230513 ++#if __LINE__!=230517 + #undef SQLITE_SOURCE_ID +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 ba9c5a7088bb97ded8e889d7e21e6afe229baf8e670ea4f20d9625b61c8aalt2" ++#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 71b8cad5197c8a6074b4b716e311aab34b4a9952f37f56e7d755c4e96a83alt2" + #endif + /* Return the source-id for this library */ + SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } +diff --git a/amalgamation_dev/sqlite3.h b/amalgamation_dev/sqlite3.h +index fa3733fc1e68b49317e9531f690d634a3a508d74..231a1846bcf1f6ac789b589a7d7bef57c3567b88 100644 +--- a/amalgamation_dev/sqlite3.h ++++ b/amalgamation_dev/sqlite3.h +@@ -125,7 +125,7 @@ extern "C" { + */ + #define SQLITE_VERSION "3.33.0" + #define SQLITE_VERSION_NUMBER 3033000 +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 ba9c5a7088bb97ded8e889d7e21e6afe229baf8e670ea4f20d9625b61c8a3984" ++#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 71b8cad5197c8a6074b4b716e311aab34b4a9952f37f56e7d755c4e96a8324d0" + + /* + ** CAPI3REF: Run-Time Library Version Numbers +diff --git a/manifest b/manifest +index dedff0947551e496fd7bd174d7ce47d362552987..989b65317019a735ef8ef7055fa7248d07fd4bd9 100644 +--- a/manifest ++++ b/manifest +@@ -479,7 +479,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 + F src/btree.c 1439fd9b45d4d1883c53752daef42af489adaa1a1508fa39dedbc9c80ea21a2f + F src/btree.h 7af72bbb4863c331c8f6753277ab40ee67d2a2125a63256d5c25489722ec162b + F src/btreeInt.h 83166f6daeb91062b6ae9ee6247b3ad07e40eba58f3c05ba9e8dedad4ab1ea38 +-F src/build.c dbdaee54ffef924a070eb6202017e10d6be56baab953ef0a8e714a6def683198 ++F src/build.c 961d09149c1273b4137f5a31c9c4c4ed51e3080049d945de19602d7456f30242 + F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c + F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e + F src/ctime.c e98518d2d3d4029a13c805e07313fb60c877be56db76e90dd5f3af73085d0ce6 +@@ -602,7 +602,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c + F src/tokenize.c 4dc01b267593537e2a0d0efe9f80dabe24c5b6f7627bc6971c487fa6a1dacbbf + F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda + F src/trigger.c ef67bde309a831515dc3c2173d792574309f2f42d45f8c078743fae9f7f98c75 +-F src/update.c fb15bec5b54fd098f4b84f6abc83c7103b45ba8484011fff8edf5ae31656eab6 ++F src/update.c 7c5cbbf9c15ff5c281246cfd7d7450501de02c49ec963557f9f1e4355845daeb + F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78 + F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 + F src/util.c c8bf30c4356b091bcc3b624d0e24b2b4d11b8be4d6c90d8e0705971e15cc819b +diff --git a/src/build.c b/src/build.c +index aa0f919bc6f2a2b59614233f86086868050aa9af..789ed80bb075f5394b7d241c468af76ba54d7f55 100644 +--- a/src/build.c ++++ b/src/build.c +@@ -452,7 +452,7 @@ Table *sqlite3LocateTableItem( + struct SrcList_item *p + ){ + const char *zDb; +- assert( p->pSchema==0 || p->zDatabase==0 ); ++ /* assert( p->pSchema==0 || p->zDatabase==0 ); FIX-ME */ + if( p->pSchema ){ + int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + zDb = pParse->db->aDb[iDb].zDbSName; +diff --git a/src/update.c b/src/update.c +index a9c43d62eb3e407426462e0536e0b94c0f19c006..807584e6c79daeec857cbeb98adc5045fbe54816 100644 +--- a/src/update.c ++++ b/src/update.c +@@ -220,6 +220,10 @@ static void updateFromSelect( + + assert( pTabList->nSrc>1 ); + if( pSrc ){ ++ if( pSrc->a[0].zDatabase==0 ){ ++ int iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); ++ pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iSchema].zDbSName); ++ } + pSrc->a[0].iCursor = -1; + pSrc->a[0].pTab->nTabRef--; + pSrc->a[0].pTab = 0; diff --git a/patches/usrsctp/.patches b/patches/usrsctp/.patches index a679b67919e29..04537a78dfef6 100644 --- a/patches/usrsctp/.patches +++ b/patches/usrsctp/.patches @@ -1,3 +1,4 @@ cherry_picking_improve_the_input_validation_and_processing_of.patch cherry_picking_clean_up_more_resources_of_an_existing_sctp.patch cherry_picking_harden_the_handling_of_outgoing_streams.patch +improve_restart_handling.patch diff --git a/patches/usrsctp/improve_restart_handling.patch b/patches/usrsctp/improve_restart_handling.patch new file mode 100644 index 0000000000000..b68646d2f2c29 --- /dev/null +++ b/patches/usrsctp/improve_restart_handling.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Tuexen +Date: Mon, 3 May 2021 02:29:58 +0200 +Subject: Improve restart handling. + +This fixes in particular a possible use after free bug reported +Anatoly Korniltsev and Taylor Brandstetter for the userland stack. + +diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c +index 6a5bdba4264b47e10766467255dd9ebd5d135556..c36743b1fd2c3ab82cacbbf78e2e07547da2e457 100755 +--- a/usrsctplib/netinet/sctp_input.c ++++ b/usrsctplib/netinet/sctp_input.c +@@ -2015,11 +2015,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, + /* temp code */ + if (how_indx < sizeof(asoc->cookie_how)) + asoc->cookie_how[how_indx] = 12; +- sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net, +- SCTP_FROM_SCTP_INPUT + SCTP_LOC_16); +- sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, +- SCTP_FROM_SCTP_INPUT + SCTP_LOC_17); +- ++ sctp_stop_association_timers(stcb, false); + /* notify upper layer */ + *notification = SCTP_NOTIFY_ASSOC_RESTART; + atomic_add_int(&stcb->asoc.refcnt, 1); +@@ -2054,6 +2050,10 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, + asoc->str_reset_seq_in = asoc->init_seq_number; + + asoc->advanced_peer_ack_point = asoc->last_acked_seq; ++ asoc->data_pkts_seen = 0; ++ asoc->last_data_chunk_from = NULL; ++ asoc->last_control_chunk_from = NULL; ++ asoc->last_net_cmt_send_started = NULL; + if (asoc->mapping_array) { + memset(asoc->mapping_array, 0, + asoc->mapping_array_size); +@@ -2118,6 +2118,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, + SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); + SCTP_DECR_CHK_COUNT(); + } ++ asoc->ctrl_queue_cnt = 0; ++ asoc->str_reset = NULL; ++ asoc->stream_reset_outstanding = 0; + TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, nchk) { + TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next); + if (chk->data) { +@@ -2188,12 +2191,13 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, + return (NULL); + } + /* respond with a COOKIE-ACK */ +- sctp_stop_all_cookie_timers(stcb); +- sctp_toss_old_cookies(stcb, asoc); + sctp_send_cookie_ack(stcb); + if (how_indx < sizeof(asoc->cookie_how)) + asoc->cookie_how[how_indx] = 15; +- ++ if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE) && ++ (asoc->sctp_autoclose_ticks > 0)) { ++ sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL); ++ } + return (stcb); + } + if (how_indx < sizeof(asoc->cookie_how)) diff --git a/patches/v8/.patches b/patches/v8/.patches index 02b789289d7ef..59833e6b216c7 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -17,3 +17,22 @@ mac_wasm_work_around_macos_11_2_code_page_decommit_failures.patch merged_interpreter_store_accumulator_to_callee_after_optional.patch reland_regexp_hard-crash_on_invalid_offsets_in.patch regexp_throw_when_length_of_text_nodes_in_alternatives_is_too.patch +cherry-pick-02f84c745fc0.patch +merged_deoptimizer_fix_bug_in_optimizedframe_summarize.patch +cherry-pick-512cd5e179f4.patch +merged_squashed_multiple_commits.patch +lts-m86_builtins_fix_array_prototype_concat_with_species.patch +lts-m86_builtins_harden_array_prototype_concat.patch +merged_compiler_fix_a_bug_in_visitspeculativeintegeradditiveop.patch +merged_turbofan_harden_arrayprototypepop_and_arrayprototypeshift.patch +m86-lts_compiler_fix_off-by-one_error_in_kadditivesafeinteger.patch +merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch +merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch +reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch +cherry-pick-9da8fb7c4b80.patch +m86-lts_squashed_multiple_commits.patch +cherry-pick-fd8cbdf7b888.patch +cherry-pick-fd9ce58ecd13.patch +merged_json_fix_gc_issue_in_buildjsonobject.patch +merged_compiler_fix_a_bug_in.patch +cherry-pick-1234764.patch diff --git a/patches/v8/cherry-pick-02f84c745fc0.patch b/patches/v8/cherry-pick-02f84c745fc0.patch new file mode 100644 index 0000000000000..69b65d45cd0e5 --- /dev/null +++ b/patches/v8/cherry-pick-02f84c745fc0.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Mon, 12 Apr 2021 09:42:03 +0200 +Subject: Fix bug in InstructionSelector::ChangeInt32ToInt64 + +Bug: chromium:1196683 +Change-Id: Ib4ea738b47b64edc81450583be4c80a41698c3d1 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2820971 +Commit-Queue: Georg Neis +Reviewed-by: Nico Hartmann +Cr-Commit-Position: refs/heads/master@{#73903} + +diff --git a/src/compiler/backend/x64/instruction-selector-x64.cc b/src/compiler/backend/x64/instruction-selector-x64.cc +index db212677ea889a5027e5bf74cbe1fb7974a4984d..11c78a9d72ad5c4c2d59277b70caafefb7495083 100644 +--- a/src/compiler/backend/x64/instruction-selector-x64.cc ++++ b/src/compiler/backend/x64/instruction-selector-x64.cc +@@ -1279,7 +1279,9 @@ void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { + opcode = load_rep.IsSigned() ? kX64Movsxwq : kX64Movzxwq; + break; + case MachineRepresentation::kWord32: +- opcode = load_rep.IsSigned() ? kX64Movsxlq : kX64Movl; ++ // ChangeInt32ToInt64 must interpret its input as a _signed_ 32-bit ++ // integer, so here we must sign-extend the loaded value in any case. ++ opcode = kX64Movsxlq; + break; + default: + UNREACHABLE(); +diff --git a/test/mjsunit/compiler/regress-1196683.js b/test/mjsunit/compiler/regress-1196683.js +new file mode 100644 +index 0000000000000000000000000000000000000000..abd7d6b2f8da45991e1e3b6c72582bc716d63efb +--- /dev/null ++++ b/test/mjsunit/compiler/regress-1196683.js +@@ -0,0 +1,56 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Flags: --allow-natives-syntax ++ ++ ++(function() { ++ const arr = new Uint32Array([2**31]); ++ function foo() { ++ return (arr[0] ^ 0) + 1; ++ } ++ %PrepareFunctionForOptimization(foo); ++ assertEquals(-(2**31) + 1, foo()); ++ %OptimizeFunctionOnNextCall(foo); ++ assertEquals(-(2**31) + 1, foo()); ++}); ++ ++ ++// The remaining tests already passed without the bugfix. ++ ++ ++(function() { ++ const arr = new Uint16Array([2**15]); ++ function foo() { ++ return (arr[0] ^ 0) + 1; ++ } ++ %PrepareFunctionForOptimization(foo); ++ assertEquals(2**15 + 1, foo()); ++ %OptimizeFunctionOnNextCall(foo); ++ assertEquals(2**15 + 1, foo()); ++})(); ++ ++ ++(function() { ++ const arr = new Uint8Array([2**7]); ++ function foo() { ++ return (arr[0] ^ 0) + 1; ++ } ++ %PrepareFunctionForOptimization(foo); ++ assertEquals(2**7 + 1, foo()); ++ %OptimizeFunctionOnNextCall(foo); ++ assertEquals(2**7 + 1, foo()); ++})(); ++ ++ ++(function() { ++ const arr = new Int32Array([-(2**31)]); ++ function foo() { ++ return (arr[0] >>> 0) + 1; ++ } ++ %PrepareFunctionForOptimization(foo); ++ assertEquals(2**31 + 1, foo()); ++ %OptimizeFunctionOnNextCall(foo); ++ assertEquals(2**31 + 1, foo()); ++})(); diff --git a/patches/v8/cherry-pick-1234764.patch b/patches/v8/cherry-pick-1234764.patch new file mode 100644 index 0000000000000..de7d4de08aefe --- /dev/null +++ b/patches/v8/cherry-pick-1234764.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Tue, 10 Aug 2021 09:29:33 +0200 +Subject: Merged: [compiler] Harden + JSCallReducer::ReduceArrayIteratorPrototypeNext + +Revision: 65b20a0e65e1078f5dd230a5203e231bec790ab4 + +BUG=chromium:1234764 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true +R=vahl@chromium.org + +Change-Id: I45faf253695011092de144c8e29bafac5337adec +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3084363 +Reviewed-by: Lutz Vahl +Commit-Queue: Georg Neis +Cr-Commit-Position: refs/branch-heads/9.2@{#53} +Cr-Branched-From: 51238348f95a1f5e0acc321efac7942d18a687a2-refs/heads/9.2.230@{#1} +Cr-Branched-From: 587a04f02ab0487d194b55a7137dc2045e071597-refs/heads/master@{#74656} + +diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc +index b77094b7e1f0c57552fc7c8d3cea1f9d9ed7a269..a34b33e3a88b72f3ef6ca665f67cf3e9c7fff173 100644 +--- a/src/compiler/js-call-reducer.cc ++++ b/src/compiler/js-call-reducer.cc +@@ -5826,11 +5826,12 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { + Node* etrue = effect; + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + { +- // We know that the {index} is range of the {length} now. ++ // This extra check exists to refine the type of {index} but also to break ++ // an exploitation technique that abuses typer mismatches. + index = etrue = graph()->NewNode( +- common()->TypeGuard( +- Type::Range(0.0, length_access.type.Max() - 1.0, graph()->zone())), +- index, etrue, if_true); ++ simplified()->CheckBounds(p.feedback(), ++ CheckBoundsFlag::kAbortOnOutOfBounds), ++ index, length, etrue, if_true); + + done_true = jsgraph()->FalseConstant(); + if (iteration_kind == IterationKind::kKeys) { diff --git a/patches/v8/cherry-pick-512cd5e179f4.patch b/patches/v8/cherry-pick-512cd5e179f4.patch new file mode 100644 index 0000000000000..40b3b169583ab --- /dev/null +++ b/patches/v8/cherry-pick-512cd5e179f4.patch @@ -0,0 +1,109 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Wed, 14 Apr 2021 13:19:44 +0200 +Subject: Merged: [compiler] Fix bug in + RepresentationChanger::GetWord32RepresentationFor + +Revision: fd29e246f65a7cee130e72cd10f618f3b82af232 + +BUG=chromium:1195777 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true +R=nicohartmann@chromium.org + +Change-Id: I0400b3ae5736ef86dbeae558d15bfcca2e9f351a +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2826114 +Commit-Queue: Georg Neis +Reviewed-by: Nico Hartmann +Cr-Commit-Position: refs/branch-heads/9.0@{#34} +Cr-Branched-From: bd0108b4c88e0d6f2350cb79b5f363fbd02f3eb7-refs/heads/9.0.257@{#1} +Cr-Branched-From: 349bcc6a075411f1a7ce2d866c3dfeefc2efa39d-refs/heads/master@{#73001} + +diff --git a/src/compiler/representation-change.cc b/src/compiler/representation-change.cc +index 46207a8b4ed496f1e6f752ecd35e19c7c0cd6a4d..7e7940f780a7ab4ecfde416888c47da68531f771 100644 +--- a/src/compiler/representation-change.cc ++++ b/src/compiler/representation-change.cc +@@ -949,10 +949,10 @@ Node* RepresentationChanger::GetWord32RepresentationFor( + return node; + } else if (output_rep == MachineRepresentation::kWord64) { + if (output_type.Is(Type::Signed32()) || +- output_type.Is(Type::Unsigned32())) { +- op = machine()->TruncateInt64ToInt32(); +- } else if (output_type.Is(cache_->kSafeInteger) && +- use_info.truncation().IsUsedAsWord32()) { ++ (output_type.Is(Type::Unsigned32()) && ++ use_info.type_check() == TypeCheckKind::kNone) || ++ (output_type.Is(cache_->kSafeInteger) && ++ use_info.truncation().IsUsedAsWord32())) { + op = machine()->TruncateInt64ToInt32(); + } else if (use_info.type_check() == TypeCheckKind::kSignedSmall || + use_info.type_check() == TypeCheckKind::kSigned32 || +diff --git a/test/mjsunit/compiler/regress-1195777.js b/test/mjsunit/compiler/regress-1195777.js +new file mode 100644 +index 0000000000000000000000000000000000000000..b122f4f0169af573723d4318b9f1127c857c9e35 +--- /dev/null ++++ b/test/mjsunit/compiler/regress-1195777.js +@@ -0,0 +1,62 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Flags: --allow-natives-syntax ++ ++ ++(function() { ++ function foo(b) { ++ let y = (new Date(42)).getMilliseconds(); ++ let x = -1; ++ if (b) x = 0xFFFF_FFFF; ++ return y < Math.max(1 << y, x, 1 + y); ++ } ++ assertTrue(foo(true)); ++ %PrepareFunctionForOptimization(foo); ++ assertTrue(foo(false)); ++ %OptimizeFunctionOnNextCall(foo); ++ assertTrue(foo(true)); ++})(); ++ ++ ++(function() { ++ function foo(b) { ++ let x = 0; ++ if (b) x = -1; ++ return x == Math.max(-1, x >>> Infinity); ++ } ++ assertFalse(foo(true)); ++ %PrepareFunctionForOptimization(foo); ++ assertTrue(foo(false)); ++ %OptimizeFunctionOnNextCall(foo); ++ assertFalse(foo(true)); ++})(); ++ ++ ++(function() { ++ function foo(b) { ++ let x = -1; ++ if (b) x = 0xFFFF_FFFF; ++ return -1 < Math.max(0, x, -1); ++ } ++ assertTrue(foo(true)); ++ %PrepareFunctionForOptimization(foo); ++ assertTrue(foo(false)); ++ %OptimizeFunctionOnNextCall(foo); ++ assertTrue(foo(true)); ++})(); ++ ++ ++(function() { ++ function foo(b) { ++ let x = 0x7FFF_FFFF; ++ if (b) x = 0; ++ return 0 < (Math.max(-5 >>> x, -5) % -5); ++ } ++ assertTrue(foo(true)); ++ %PrepareFunctionForOptimization(foo); ++ assertTrue(foo(false)); ++ %OptimizeFunctionOnNextCall(foo); ++ assertTrue(foo(true)); ++})(); diff --git a/patches/v8/cherry-pick-9da8fb7c4b80.patch b/patches/v8/cherry-pick-9da8fb7c4b80.patch new file mode 100644 index 0000000000000..ce5dcefcb43fc --- /dev/null +++ b/patches/v8/cherry-pick-9da8fb7c4b80.patch @@ -0,0 +1,137 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Mon, 7 Jun 2021 10:41:38 +0200 +Subject: Squashed multiple commits. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Merged: Disable left-trimming when optimizing compile jobs exist +Revision: ac0605a1a486b8d074f116cc365de9d2b6d7c9e5 + +Merged: [heap] Don't assume that optimizing-compile-dispatcher exists +Revision: 022b312d55e75935cfa99cca7729ae2d3f795bd0 + +BUG=chromium:1211215,chromium:1215514 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true + +(cherry picked from commit 8704c7c0b2f79cbe745f293b30d68f4505da7416) + +Change-Id: I3b3a37d64402ea464c8e653517928522a1c5e0da +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2940899 +Reviewed-by: Dominik Inführ +Commit-Queue: Georg Neis +Cr-Original-Commit-Position: refs/branch-heads/9.1@{#67} +Cr-Original-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1} +Cr-Original-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2948657 +Reviewed-by: Artem Sumaneev +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/8.6@{#107} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/compiler-dispatcher/optimizing-compile-dispatcher.cc b/src/compiler-dispatcher/optimizing-compile-dispatcher.cc +index 528a9babe33041ff85c82e0ba8afbdb975783af8..b47aa5431ca634f677cd4b2441f75d42b3f0a930 100644 +--- a/src/compiler-dispatcher/optimizing-compile-dispatcher.cc ++++ b/src/compiler-dispatcher/optimizing-compile-dispatcher.cc +@@ -47,7 +47,6 @@ class OptimizingCompileDispatcher::CompileTask : public CancelableTask { + worker_thread_runtime_call_stats_( + isolate->counters()->worker_thread_runtime_call_stats()), + dispatcher_(dispatcher) { +- base::MutexGuard lock_guard(&dispatcher_->ref_count_mutex_); + ++dispatcher_->ref_count_; + } + +@@ -95,12 +94,7 @@ class OptimizingCompileDispatcher::CompileTask : public CancelableTask { + }; + + OptimizingCompileDispatcher::~OptimizingCompileDispatcher() { +-#ifdef DEBUG +- { +- base::MutexGuard lock_guard(&ref_count_mutex_); +- DCHECK_EQ(0, ref_count_); +- } +-#endif ++ DCHECK_EQ(0, ref_count_); + DCHECK_EQ(0, input_queue_length_); + DeleteArray(input_queue_); + } +@@ -227,6 +221,14 @@ void OptimizingCompileDispatcher::InstallOptimizedFunctions() { + } + } + ++bool OptimizingCompileDispatcher::HasJobs() { ++ DCHECK_EQ(ThreadId::Current(), isolate_->thread_id()); ++ // Note: This relies on {output_queue_} being mutated by a background thread ++ // only when {ref_count_} is not zero. Also, {ref_count_} is never incremented ++ // by a background thread. ++ return !(ref_count_ == 0 && output_queue_.empty()); ++} ++ + void OptimizingCompileDispatcher::QueueForOptimization( + OptimizedCompilationJob* job) { + DCHECK(IsQueueAvailable()); +diff --git a/src/compiler-dispatcher/optimizing-compile-dispatcher.h b/src/compiler-dispatcher/optimizing-compile-dispatcher.h +index 51803822d15353af31c446b960c3ee43cc7533fe..390e90b4ab8b7d1680545905ac8ef90c3a9099c9 100644 +--- a/src/compiler-dispatcher/optimizing-compile-dispatcher.h ++++ b/src/compiler-dispatcher/optimizing-compile-dispatcher.h +@@ -52,6 +52,9 @@ class V8_EXPORT_PRIVATE OptimizingCompileDispatcher { + + static bool Enabled() { return FLAG_concurrent_recompilation; } + ++ // This method must be called on the main thread. ++ bool HasJobs(); ++ + private: + class CompileTask; + +@@ -87,7 +90,7 @@ class V8_EXPORT_PRIVATE OptimizingCompileDispatcher { + + int blocked_jobs_; + +- int ref_count_; ++ std::atomic ref_count_; + base::Mutex ref_count_mutex_; + base::ConditionVariable ref_count_zero_; + +diff --git a/src/heap/heap.cc b/src/heap/heap.cc +index 6755a991df16a9c9d0e9efb2f378e4d68463532c..a017905bfcb0059aa12dbd1bd5a477bcca2dd616 100644 +--- a/src/heap/heap.cc ++++ b/src/heap/heap.cc +@@ -22,6 +22,7 @@ + #include "src/codegen/compilation-cache.h" + #include "src/common/assert-scope.h" + #include "src/common/globals.h" ++#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" + #include "src/debug/debug.h" + #include "src/deoptimizer/deoptimizer.h" + #include "src/execution/isolate-utils-inl.h" +@@ -3036,6 +3037,12 @@ bool Heap::CanMoveObjectStart(HeapObject object) { + + if (IsLargeObject(object)) return false; + ++ // Compilation jobs may have references to the object. ++ if (isolate()->concurrent_recompilation_enabled() && ++ isolate()->optimizing_compile_dispatcher()->HasJobs()) { ++ return false; ++ } ++ + // We can move the object start if the page was already swept. + return Page::FromHeapObject(object)->SweepingDone(); + } +diff --git a/test/mjsunit/compiler/regress-1215514.js b/test/mjsunit/compiler/regress-1215514.js +new file mode 100644 +index 0000000000000000000000000000000000000000..a597b310498458fd7219c33ff188ca2a6e543f45 +--- /dev/null ++++ b/test/mjsunit/compiler/regress-1215514.js +@@ -0,0 +1,7 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Flags: --no-concurrent-recompilation ++ ++new Array(4242).shift(); diff --git a/patches/v8/cherry-pick-fd8cbdf7b888.patch b/patches/v8/cherry-pick-fd8cbdf7b888.patch new file mode 100644 index 0000000000000..6a30c4667ec65 --- /dev/null +++ b/patches/v8/cherry-pick-fd8cbdf7b888.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Thu, 27 May 2021 13:04:30 +0200 +Subject: Reland "Merged: [compiler] Always record constness dependency for + FastDataConstant" + +This is a reland of 638d1b238d510a349bdd38648add8d5c85bc5f7d after a +one-character change. A local variable still has a non-optional type +in this version of V8. + +Original change's description: +> Merged: [compiler] Always record constness dependency for FastDataConstant +> +> Revision: 1bfa5139966fe0c9e8036fe6362b61c483675775 +> +> BUG=chromium:1209558 +> NOTRY=true +> NOPRESUBMIT=true +> NOTREECHECKS=true +> +> Change-Id: If4f7243647bcc12ed482796c1353f0717630f6b9 +> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2919823 +> Commit-Queue: Georg Neis +> Reviewed-by: Igor Sheludko +> Cr-Commit-Position: refs/branch-heads/9.1@{#59} +> Cr-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1} +> Cr-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847} + +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true + +(cherry picked from commit 73666e3f6d6bdbc93ab81cf8b3803dd04930e293) + +Bug: chromium:1209558 +Change-Id: I0c81353882b0f17942fd92ad4181732f941bcb1d +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2939991 +Commit-Queue: Georg Neis +Reviewed-by: Igor Sheludko +Cr-Original-Commit-Position: refs/branch-heads/9.1@{#63} +Cr-Original-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1} +Cr-Original-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2948652 +Reviewed-by: Artem Sumaneev +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/8.6@{#108} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc +index 046927e9430e89596bd4077af37fb9374d279282..7f7a8fee63ed8bdd322bbbca8444c9345b3d32f7 100644 +--- a/src/compiler/access-info.cc ++++ b/src/compiler/access-info.cc +@@ -892,7 +892,7 @@ PropertyAccessInfo AccessInfoFactory::LookupTransition( + // Transitioning stores *may* store to const fields. The resulting + // DataConstant access infos can be distinguished from later, i.e. redundant, + // stores to the same constant field by the presence of a transition map. +- switch (details.constness()) { ++ switch (dependencies()->DependOnFieldConstness(transition_map_ref, number)) { + case PropertyConstness::kMutable: + return PropertyAccessInfo::DataField( + zone(), map, std::move(unrecorded_dependencies), field_index, diff --git a/patches/v8/cherry-pick-fd9ce58ecd13.patch b/patches/v8/cherry-pick-fd9ce58ecd13.patch new file mode 100644 index 0000000000000..22eae1f236880 --- /dev/null +++ b/patches/v8/cherry-pick-fd9ce58ecd13.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kim-Anh Tran +Date: Thu, 6 May 2021 10:02:01 +0200 +Subject: M86-LTS: [debugger] Return ServerError if debugger agent is disabled + +This returns a server error on setting breakpoints if the +agent is disabled. + +(cherry picked from commit 5aa2de8128f885c44df79d38fb4aa5c6a5d94306) + +Also-by: bmeurer@chromium.org +Fixed: chromium:1202534 +No-Try: true +No-Presubmit: true +No-Tree-Checks: true +Change-Id: I87c80a4bd785fa5c59a8dd0d5ac5f4b31b015ed8 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2874662 +Commit-Queue: Kim-Anh Tran +Commit-Queue: Benedikt Meurer +Auto-Submit: Kim-Anh Tran +Reviewed-by: Benedikt Meurer +Cr-Original-Commit-Position: refs/heads/master@{#74399} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2940882 +Reviewed-by: Achuith Bhandarkar +Commit-Queue: Artem Sumaneev +Cr-Commit-Position: refs/branch-heads/8.6@{#105} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/inspector/v8-debugger-agent-impl.cc b/src/inspector/v8-debugger-agent-impl.cc +index 399fa4c4093450db89309d5aff680a2613614192..bd948cd8ac1c1492df191b63b83298fb9b3c5c5c 100644 +--- a/src/inspector/v8-debugger-agent-impl.cc ++++ b/src/inspector/v8-debugger-agent-impl.cc +@@ -499,6 +499,8 @@ Response V8DebuggerAgentImpl::setBreakpointByUrl( + Maybe optionalColumnNumber, Maybe optionalCondition, + String16* outBreakpointId, + std::unique_ptr>* locations) { ++ if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); ++ + *locations = std::make_unique>(); + + int specified = (optionalURL.isJust() ? 1 : 0) + +@@ -587,6 +589,8 @@ Response V8DebuggerAgentImpl::setBreakpoint( + String16 breakpointId = generateBreakpointId( + BreakpointType::kByScriptId, location->getScriptId(), + location->getLineNumber(), location->getColumnNumber(0)); ++ if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); ++ + if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) != + m_breakpointIdToDebuggerBreakpointIds.end()) { + return Response::ServerError( +@@ -605,6 +609,8 @@ Response V8DebuggerAgentImpl::setBreakpoint( + Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall( + const String16& functionObjectId, Maybe optionalCondition, + String16* outBreakpointId) { ++ if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); ++ + InjectedScript::ObjectScope scope(m_session, functionObjectId); + Response response = scope.initialize(); + if (!response.IsSuccess()) return response; +diff --git a/test/inspector/debugger/set-breakpoint-before-enabling-expected.txt b/test/inspector/debugger/set-breakpoint-before-enabling-expected.txt +index 02bfe0d80cdecd96d37988d9d6850b49c5d7e39d..a85aab6fe0c71f3346fe79694d1a334e2cb12fb2 100644 +--- a/test/inspector/debugger/set-breakpoint-before-enabling-expected.txt ++++ b/test/inspector/debugger/set-breakpoint-before-enabling-expected.txt +@@ -1,7 +1,13 @@ + Tests that setting breakpoint before enabling debugger produces an error +-setBreakpointByUrl error: undefined ++setBreakpointByUrl error: { ++ "code": -32000, ++ "message": "Debugger agent is not enabled" ++} + setBreakpoint error: { +- "code": -32602, +- "message": "Invalid parameters", +- "data": "Failed to deserialize params.location - BINDINGS: mandatory field missing at " ++ "code": -32000, ++ "message": "Debugger agent is not enabled" ++} ++setBreakpointOnFunctionCall error: { ++ "code": -32000, ++ "message": "Debugger agent is not enabled" + } +diff --git a/test/inspector/debugger/set-breakpoint-before-enabling.js b/test/inspector/debugger/set-breakpoint-before-enabling.js +index 5af1085c8747089dea15550949130b8ea243b524..4401466a921692bbe94b52e60083d92769407ee3 100644 +--- a/test/inspector/debugger/set-breakpoint-before-enabling.js ++++ b/test/inspector/debugger/set-breakpoint-before-enabling.js +@@ -10,12 +10,19 @@ function didSetBreakpointByUrlBeforeEnable(message) + { + InspectorTest.log("setBreakpointByUrl error: " + JSON.stringify( + InspectorTest.trimErrorMessage(message).error, null, 2)); +- Protocol.Debugger.setBreakpoint().then(didSetBreakpointBeforeEnable); ++ Protocol.Debugger.setBreakpoint({location: { scriptId: "4", lineNumber: 0, columnNumber: 0 }}).then(didSetBreakpointBeforeEnable); + } + + function didSetBreakpointBeforeEnable(message) + { + InspectorTest.log("setBreakpoint error: " + JSON.stringify( + InspectorTest.trimErrorMessage(message).error, null, 2)); ++ Protocol.Debugger.setBreakpointOnFunctionCall({objectId: "4"}).then(didSetBreakpointOnFunctionCallBeforeEnable); ++} ++ ++function didSetBreakpointOnFunctionCallBeforeEnable(message) ++{ ++ InspectorTest.log("setBreakpointOnFunctionCall error: " + JSON.stringify( ++ InspectorTest.trimErrorMessage(message).error, null, 2)); + InspectorTest.completeTest(); + } diff --git a/patches/v8/lts-m86_builtins_fix_array_prototype_concat_with_species.patch b/patches/v8/lts-m86_builtins_fix_array_prototype_concat_with_species.patch new file mode 100644 index 0000000000000..6a7eb4ad68280 --- /dev/null +++ b/patches/v8/lts-m86_builtins_fix_array_prototype_concat_with_species.patch @@ -0,0 +1,96 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Igor Sheludko +Date: Wed, 7 Apr 2021 19:12:32 +0200 +Subject: Fix Array.prototype.concat with @@species + +(cherry picked from commit 7989e04979c3195e60a6814e8263063eb91f7b47) + +No-Try: true +No-Presubmit: true +No-Tree-Checks: true +Bug: chromium:1195977 +Change-Id: I16843bce2e9f776abca0f2b943b898ab5e597e42 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2810787 +Reviewed-by: Camillo Bruni +Commit-Queue: Igor Sheludko +Cr-Original-Commit-Position: refs/heads/master@{#73842} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2823829 +Commit-Queue: Jana Grill +Reviewed-by: Igor Sheludko +Reviewed-by: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/8.6@{#77} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc +index 3c2fe33c5b4b330c509d2926bc1e30daa1e09dba..938fb96c1d42d8152f974df33c3bed4cc1b542d3 100644 +--- a/src/builtins/builtins-array.cc ++++ b/src/builtins/builtins-array.cc +@@ -649,11 +649,14 @@ class ArrayConcatVisitor { + index_offset_(0u), + bit_field_(FastElementsField::encode(fast_elements) | + ExceedsLimitField::encode(false) | +- IsFixedArrayField::encode(storage->IsFixedArray()) | ++ IsFixedArrayField::encode(storage->IsFixedArray(isolate)) | + HasSimpleElementsField::encode( +- storage->IsFixedArray() || +- !storage->map().IsCustomElementsReceiverMap())) { +- DCHECK(!(this->fast_elements() && !is_fixed_array())); ++ storage->IsFixedArray(isolate) || ++ // Don't take fast path for storages that might have ++ // side effects when storing to them. ++ (!storage->map(isolate).IsCustomElementsReceiverMap() && ++ !storage->IsJSTypedArray(isolate)))) { ++ DCHECK_IMPLIES(this->fast_elements(), is_fixed_array()); + } + + ~ArrayConcatVisitor() { clear_storage(); } +@@ -1063,8 +1066,8 @@ bool IterateElements(Isolate* isolate, Handle receiver, + return IterateElementsSlow(isolate, receiver, length, visitor); + } + +- if (!HasOnlySimpleElements(isolate, *receiver) || +- !visitor->has_simple_elements()) { ++ if (!visitor->has_simple_elements() || ++ !HasOnlySimpleElements(isolate, *receiver)) { + return IterateElementsSlow(isolate, receiver, length, visitor); + } + Handle array = Handle::cast(receiver); +diff --git a/src/objects/fixed-array-inl.h b/src/objects/fixed-array-inl.h +index e60224315826c793ea8e9327db6c7a62786530c3..e4796b00a33ab56c1bdf7b9db570bf5add406a27 100644 +--- a/src/objects/fixed-array-inl.h ++++ b/src/objects/fixed-array-inl.h +@@ -336,7 +336,7 @@ int Search(T* array, Name name, int valid_entries, int* out_insertion_index, + double FixedDoubleArray::get_scalar(int index) { + DCHECK(map() != GetReadOnlyRoots().fixed_cow_array_map() && + map() != GetReadOnlyRoots().fixed_array_map()); +- DCHECK(index >= 0 && index < this->length()); ++ DCHECK_LT(static_cast(index), static_cast(length())); + DCHECK(!is_the_hole(index)); + return ReadField(kHeaderSize + index * kDoubleSize); + } +@@ -344,7 +344,7 @@ double FixedDoubleArray::get_scalar(int index) { + uint64_t FixedDoubleArray::get_representation(int index) { + DCHECK(map() != GetReadOnlyRoots().fixed_cow_array_map() && + map() != GetReadOnlyRoots().fixed_array_map()); +- DCHECK(index >= 0 && index < this->length()); ++ DCHECK_LT(static_cast(index), static_cast(length())); + int offset = kHeaderSize + index * kDoubleSize; + // Bug(v8:8875): Doubles may be unaligned. + return base::ReadUnalignedValue(field_address(offset)); +@@ -362,6 +362,7 @@ Handle FixedDoubleArray::get(FixedDoubleArray array, int index, + void FixedDoubleArray::set(int index, double value) { + DCHECK(map() != GetReadOnlyRoots().fixed_cow_array_map() && + map() != GetReadOnlyRoots().fixed_array_map()); ++ DCHECK_LT(static_cast(index), static_cast(length())); + int offset = kHeaderSize + index * kDoubleSize; + if (std::isnan(value)) { + WriteField(offset, std::numeric_limits::quiet_NaN()); +@@ -378,6 +379,7 @@ void FixedDoubleArray::set_the_hole(Isolate* isolate, int index) { + void FixedDoubleArray::set_the_hole(int index) { + DCHECK(map() != GetReadOnlyRoots().fixed_cow_array_map() && + map() != GetReadOnlyRoots().fixed_array_map()); ++ DCHECK_LT(static_cast(index), static_cast(length())); + int offset = kHeaderSize + index * kDoubleSize; + base::WriteUnalignedValue(field_address(offset), kHoleNanInt64); + } diff --git a/patches/v8/lts-m86_builtins_harden_array_prototype_concat.patch b/patches/v8/lts-m86_builtins_harden_array_prototype_concat.patch new file mode 100644 index 0000000000000..7905f8f089680 --- /dev/null +++ b/patches/v8/lts-m86_builtins_harden_array_prototype_concat.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jana Grill +Date: Tue, 13 Apr 2021 16:54:14 +0200 +Subject: Harden Array.prototype.concat. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Defence in depth patch to prevent JavaScript from executing +from within IterateElements. + +R=​ishell@chromium.org +R=​cbruni@chromium.org + +(cherry picked from commit 8284359ed0607e452a4dda2ce89811fb019b4aaa) + +No-Try: true +No-Presubmit: true +No-Tree-Checks: true +Bug: chromium:1195977 +Change-Id: Ie59d468b73b94818cea986a3ded0804f6dddd10b +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2819941 +Reviewed-by: Camillo Bruni +Reviewed-by: Igor Sheludko +Commit-Queue: Igor Sheludko +Cr-Original-Commit-Position: refs/heads/master@{#73898} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2821961 +Commit-Queue: Jana Grill +Reviewed-by: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/8.6@{#76} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/AUTHORS b/AUTHORS +index 054e2bee4b6e2a5ee99cb3229c98095fce52f0a5..209548ea4b67d13826fea1f1b93e7320f8279fc0 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -69,6 +69,7 @@ Ben Newman + Ben Noordhuis + Benjamin Tan + Bert Belder ++Brendon Tiszka + Burcu Dogan + Caitlin Potter + Craig Schlenter +diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc +index 938fb96c1d42d8152f974df33c3bed4cc1b542d3..8055d8382d48f618d8fcd3b18f48b2da04fa3f69 100644 +--- a/src/builtins/builtins-array.cc ++++ b/src/builtins/builtins-array.cc +@@ -1083,6 +1083,9 @@ bool IterateElements(Isolate* isolate, Handle receiver, + case HOLEY_SEALED_ELEMENTS: + case HOLEY_NONEXTENSIBLE_ELEMENTS: + case HOLEY_ELEMENTS: { ++ // Disallow execution so the cached elements won't change mid execution. ++ DisallowJavascriptExecution no_js(isolate); ++ + // Run through the elements FixedArray and use HasElement and GetElement + // to check the prototype for missing elements. + Handle elements(FixedArray::cast(array->elements()), isolate); +@@ -1109,6 +1112,9 @@ bool IterateElements(Isolate* isolate, Handle receiver, + } + case HOLEY_DOUBLE_ELEMENTS: + case PACKED_DOUBLE_ELEMENTS: { ++ // Disallow execution so the cached elements won't change mid execution. ++ DisallowJavascriptExecution no_js(isolate); ++ + // Empty array is FixedArray but not FixedDoubleArray. + if (length == 0) break; + // Run through the elements FixedArray and use HasElement and GetElement +@@ -1145,6 +1151,9 @@ bool IterateElements(Isolate* isolate, Handle receiver, + } + + case DICTIONARY_ELEMENTS: { ++ // Disallow execution so the cached dictionary won't change mid execution. ++ DisallowJavascriptExecution no_js(isolate); ++ + Handle dict(array->element_dictionary(), isolate); + std::vector indices; + indices.reserve(dict->Capacity() / 2); diff --git a/patches/v8/m86-lts_compiler_fix_off-by-one_error_in_kadditivesafeinteger.patch b/patches/v8/m86-lts_compiler_fix_off-by-one_error_in_kadditivesafeinteger.patch new file mode 100644 index 0000000000000..3d169ee38e603 --- /dev/null +++ b/patches/v8/m86-lts_compiler_fix_off-by-one_error_in_kadditivesafeinteger.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Mon, 19 Apr 2021 13:12:46 +0200 +Subject: M86-LTS: [compiler] Fix off-by-one error in kAdditiveSafeInteger + +(cherry picked from commit 798fbcb0a3e5a292fb775c37c19d9fe73bbac17c) + +No-Try: true +No-Presubmit: true +No-Tree-Checks: true +Bug: chromium:1198705 +Change-Id: I6b3ad82754e1ca72701ce57f16c4f085f8c87f77 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2835705 +Auto-Submit: Georg Neis +Commit-Queue: Nico Hartmann +Reviewed-by: Nico Hartmann +Cr-Original-Commit-Position: refs/heads/master@{#74033} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2850708 +Commit-Queue: Artem Sumaneev +Reviewed-by: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/8.6@{#87} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/compiler/type-cache.h b/src/compiler/type-cache.h +index ada95a37219fc01bb01b413b9eb88fcb4c592e98..2ade5f68a3b1d9b0eeb7cac52435b4b224301003 100644 +--- a/src/compiler/type-cache.h ++++ b/src/compiler/type-cache.h +@@ -80,7 +80,7 @@ class V8_EXPORT_PRIVATE TypeCache final { + Type::Union(kPositiveIntegerOrMinusZero, Type::NaN(), zone()); + + Type const kAdditiveSafeInteger = +- CreateRange(-4503599627370496.0, 4503599627370496.0); ++ CreateRange(-4503599627370495.0, 4503599627370495.0); + Type const kSafeInteger = CreateRange(-kMaxSafeInteger, kMaxSafeInteger); + Type const kAdditiveSafeIntegerOrMinusZero = + Type::Union(kAdditiveSafeInteger, Type::MinusZero(), zone()); diff --git a/patches/v8/m86-lts_squashed_multiple_commits.patch b/patches/v8/m86-lts_squashed_multiple_commits.patch new file mode 100644 index 0000000000000..8f9a3f1c3856d --- /dev/null +++ b/patches/v8/m86-lts_squashed_multiple_commits.patch @@ -0,0 +1,299 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "ishell@chromium.org" +Date: Tue, 8 Jun 2021 17:33:32 +0200 +Subject: Squashed multiple commits. + +Merged: [runtime] Fix handling of interceptors +Revision: f9857fdf74 + +Merged: [runtime] Fix handling of interceptors, pt.2 +Revision: 1f5113816c + +BUG=chromium:1216437 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true + +(cherry picked from commit 1936d568193b37d50d99218724ebbb76785a30d2) + +Change-Id: Ief3da51866c8d0b5e85c76fad00b25ac2379f615 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2947407 +Reviewed-by: Leszek Swirski +Cr-Original-Commit-Position: refs/branch-heads/9.1@{#71} +Cr-Original-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1} +Cr-Original-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2948662 +Reviewed-by: Igor Sheludko +Reviewed-by: Artem Sumaneev +Commit-Queue: Victor-Gabriel Savu +Cr-Commit-Position: refs/branch-heads/8.6@{#110} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/objects/objects.cc b/src/objects/objects.cc +index 51b9caef8b3531804f8aa615a4c612c1c2bdf34d..37ac3dd1063a0a750bed4340c360ff7e6fbb3a84 100644 +--- a/src/objects/objects.cc ++++ b/src/objects/objects.cc +@@ -2488,9 +2488,21 @@ Maybe Object::SetPropertyInternal(LookupIterator* it, + if ((maybe_attributes.FromJust() & READ_ONLY) != 0) { + return WriteToReadOnlyProperty(it, value, should_throw); + } +- if (maybe_attributes.FromJust() == ABSENT) break; +- *found = false; +- return Nothing(); ++ // At this point we might have called interceptor's query or getter ++ // callback. Assuming that the callbacks have side effects, we use ++ // Object::SetSuperProperty() which works properly regardless on ++ // whether the property was present on the receiver or not when ++ // storing to the receiver. ++ if (maybe_attributes.FromJust() == ABSENT) { ++ // Proceed lookup from the next state. ++ it->Next(); ++ } else { ++ // Finish lookup in order to make Object::SetSuperProperty() store ++ // property to the receiver. ++ it->NotFound(); ++ } ++ return Object::SetSuperProperty(it, value, store_origin, ++ should_throw); + } + break; + } +@@ -2565,6 +2577,8 @@ Maybe Object::SetProperty(LookupIterator* it, Handle value, + if (found) return result; + } + ++ // TODO(ishell): refactor this: both SetProperty and and SetSuperProperty have ++ // this piece of code. + // If the receiver is the JSGlobalObject, the store was contextual. In case + // the property did not exist yet on the global object itself, we have to + // throw a reference error in strict mode. In sloppy mode, we continue. +@@ -2601,6 +2615,8 @@ Maybe Object::SetSuperProperty(LookupIterator* it, Handle value, + } + Handle receiver = Handle::cast(it->GetReceiver()); + ++ // Note, the callers rely on the fact that this code is redoing the full own ++ // lookup from scratch. + LookupIterator::Configuration c = LookupIterator::OWN; + LookupIterator own_lookup = + it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c) +@@ -2663,6 +2679,25 @@ Maybe Object::SetSuperProperty(LookupIterator* it, Handle value, + } + } + ++ // TODO(ishell): refactor this: both SetProperty and and SetSuperProperty have ++ // this piece of code. ++ // If the receiver is the JSGlobalObject, the store was contextual. In case ++ // the property did not exist yet on the global object itself, we have to ++ // throw a reference error in strict mode. In sloppy mode, we continue. ++ if (receiver->IsJSGlobalObject() && ++ (GetShouldThrow(isolate, should_throw) == ShouldThrow::kThrowOnError)) { ++ if (own_lookup.state() == LookupIterator::TRANSITION) { ++ // The property cell that we have created is garbage because we are going ++ // to throw now instead of putting it into the global dictionary. However, ++ // the cell might already have been stored into the feedback vector, so ++ // we must invalidate it nevertheless. ++ own_lookup.transition_cell()->ClearAndInvalidate(ReadOnlyRoots(isolate)); ++ } ++ isolate->Throw(*isolate->factory()->NewReferenceError( ++ MessageTemplate::kNotDefined, own_lookup.GetName())); ++ return Nothing(); ++ } ++ + return AddDataProperty(&own_lookup, value, NONE, should_throw, store_origin); + } + +diff --git a/test/cctest/test-api-interceptors.cc b/test/cctest/test-api-interceptors.cc +index 812768b461a471075becd83c7c9481d12191ff51..05edb1fdd4c4a3aa21e618269a1a8ad0822788e6 100644 +--- a/test/cctest/test-api-interceptors.cc ++++ b/test/cctest/test-api-interceptors.cc +@@ -877,9 +877,11 @@ THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { + CHECK(!value->BooleanValue(isolate)); + } + +-static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, +- v8::GenericNamedPropertyQueryCallback query, +- const char* source, int expected) { ++namespace { ++ ++void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, ++ v8::GenericNamedPropertyQueryCallback query, ++ const char* source, int expected) { + v8::Isolate* isolate = CcTest::isolate(); + v8::HandleScope scope(isolate); + v8::Local templ = ObjectTemplate::New(isolate); +@@ -894,14 +896,13 @@ static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, + CHECK_EQ(expected, value->Int32Value(context.local()).FromJust()); + } + +-static void CheckInterceptorLoadIC( +- v8::GenericNamedPropertyGetterCallback getter, const char* source, +- int expected) { ++void CheckInterceptorLoadIC(v8::GenericNamedPropertyGetterCallback getter, ++ const char* source, int expected) { + CheckInterceptorIC(getter, nullptr, source, expected); + } + +-static void InterceptorLoadICGetter( +- Local name, const v8::PropertyCallbackInfo& info) { ++void InterceptorLoadICGetter(Local name, ++ const v8::PropertyCallbackInfo& info) { + ApiTestFuzzer::Fuzz(); + v8::Isolate* isolate = CcTest::isolate(); + CHECK_EQ(isolate, info.GetIsolate()); +@@ -911,6 +912,7 @@ static void InterceptorLoadICGetter( + info.GetReturnValue().Set(v8::Integer::New(isolate, 42)); + } + ++} // namespace + + // This test should hit the load IC for the interceptor case. + THREADED_TEST(InterceptorLoadIC) { +@@ -927,9 +929,23 @@ THREADED_TEST(InterceptorLoadIC) { + // configurations of interceptor and explicit fields works fine + // (those cases are special cased to get better performance). + +-static void InterceptorLoadXICGetter( ++namespace { ++ ++void InterceptorLoadXICGetter(Local name, ++ const v8::PropertyCallbackInfo& info) { ++ ApiTestFuzzer::Fuzz(); ++ info.GetReturnValue().Set( ++ v8_str("x") ++ ->Equals(info.GetIsolate()->GetCurrentContext(), name) ++ .FromJust() ++ ? v8::Local(v8::Integer::New(info.GetIsolate(), 42)) ++ : v8::Local()); ++} ++ ++void InterceptorLoadXICGetterWithSideEffects( + Local name, const v8::PropertyCallbackInfo& info) { + ApiTestFuzzer::Fuzz(); ++ CompileRun("interceptor_getter_side_effect()"); + info.GetReturnValue().Set( + v8_str("x") + ->Equals(info.GetIsolate()->GetCurrentContext(), name) +@@ -938,6 +954,7 @@ static void InterceptorLoadXICGetter( + : v8::Local()); + } + ++} // namespace + + THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { + CheckInterceptorLoadIC(InterceptorLoadXICGetter, +@@ -1461,6 +1478,18 @@ void HasICQueryToggle(TKey name, + isolate, toggle ? v8::internal::ABSENT : v8::internal::NONE)); + } + ++template ++void HasICQuerySideEffect(TKey name, ++ const v8::PropertyCallbackInfo& info) { ++ ApiTestFuzzer::Fuzz(); ++ v8::Isolate* isolate = CcTest::isolate(); ++ CHECK_EQ(isolate, info.GetIsolate()); ++ CompileRun("interceptor_query_side_effect()"); ++ if (attribute != v8::internal::ABSENT) { ++ info.GetReturnValue().Set(v8::Integer::New(isolate, attribute)); ++ } ++} ++ + int named_query_counter = 0; + void NamedQueryCallback(Local name, + const v8::PropertyCallbackInfo& info) { +@@ -1526,6 +1555,42 @@ THREADED_TEST(InterceptorHasICQueryToggle) { + 500); + } + ++THREADED_TEST(InterceptorStoreICWithSideEffectfulCallbacks) { ++ CheckInterceptorIC(EmptyInterceptorGetter, ++ HasICQuerySideEffect, v8::internal::ABSENT>, ++ "let r;" ++ "let inside_side_effect = false;" ++ "let interceptor_query_side_effect = function() {" ++ " if (!inside_side_effect) {" ++ " inside_side_effect = true;" ++ " r.x = 153;" ++ " inside_side_effect = false;" ++ " }" ++ "};" ++ "for (var i = 0; i < 20; i++) {" ++ " r = { __proto__: o };" ++ " r.x = i;" ++ "}", ++ 19); ++ ++ CheckInterceptorIC(InterceptorLoadXICGetterWithSideEffects, ++ nullptr, // query callback is not provided ++ "let r;" ++ "let inside_side_effect = false;" ++ "let interceptor_getter_side_effect = function() {" ++ " if (!inside_side_effect) {" ++ " inside_side_effect = true;" ++ " r.y = 153;" ++ " inside_side_effect = false;" ++ " }" ++ "};" ++ "for (var i = 0; i < 20; i++) {" ++ " r = { __proto__: o };" ++ " r.y = i;" ++ "}", ++ 19); ++} ++ + static void InterceptorStoreICSetter( + Local key, Local value, + const v8::PropertyCallbackInfo& info) { +@@ -1575,6 +1640,52 @@ THREADED_TEST(InterceptorStoreICWithNoSetter) { + CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust()); + } + ++THREADED_TEST(EmptyInterceptorDoesNotShadowReadOnlyProperty) { ++ // Interceptor should not shadow readonly property 'x' on the prototype, and ++ // attempt to store to 'x' must throw. ++ CheckInterceptorIC(EmptyInterceptorGetter, ++ HasICQuery, v8::internal::ABSENT>, ++ "'use strict';" ++ "let p = {};" ++ "Object.defineProperty(p, 'x', " ++ " {value: 153, writable: false});" ++ "o.__proto__ = p;" ++ "let result = 0;" ++ "let r;" ++ "for (var i = 0; i < 20; i++) {" ++ " r = { __proto__: o };" ++ " try {" ++ " r.x = i;" ++ " } catch (e) {" ++ " result++;" ++ " }" ++ "}" ++ "result", ++ 20); ++} ++ ++THREADED_TEST(InterceptorShadowsReadOnlyProperty) { ++ // Interceptor claims that it has a writable property 'x', so the existence ++ // of the readonly property 'x' on the prototype should not cause exceptions. ++ CheckInterceptorIC(InterceptorLoadXICGetter, ++ nullptr, // query callback ++ "'use strict';" ++ "let p = {};" ++ "Object.defineProperty(p, 'x', " ++ " {value: 153, writable: false});" ++ "o.__proto__ = p;" ++ "let result = 0;" ++ "let r;" ++ "for (var i = 0; i < 20; i++) {" ++ " r = { __proto__: o };" ++ " try {" ++ " r.x = i;" ++ " result++;" ++ " } catch (e) {}" ++ "}" ++ "result", ++ 20); ++} + + THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { + v8::HandleScope scope(CcTest::isolate()); diff --git a/patches/v8/merged_compiler_fix_a_bug_in.patch b/patches/v8/merged_compiler_fix_a_bug_in.patch new file mode 100644 index 0000000000000..e17421b9f3b3d --- /dev/null +++ b/patches/v8/merged_compiler_fix_a_bug_in.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Tue, 13 Jul 2021 17:26:47 +0200 +Subject: Merged: [compiler] Fix a bug in + CodeGenerator::AddTranslationForOperand + +(cherry picked from commit 374354bfe4a30740b96936b33e522d6fcd1cda67) + +Bug: chromium:1228407 +No-Try: true +No-Presubmit: true +No-Tree-Checks: true +Change-Id: I358d8736b7b5f87300496cbb39a7689d8207d85f +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3027260 +Bot-Commit: Rubber Stamper +Reviewed-by: Adam Klein +Commit-Queue: Adam Klein +Cr-Commit-Position: refs/branch-heads/9.1@{#77} +Cr-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1} +Cr-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847} + +diff --git a/src/compiler/backend/code-generator.cc b/src/compiler/backend/code-generator.cc +index 33a80f52d0d6b502eefa62a97b1f24400abec3a9..6f25ce706fd178682b764bd3fade01bb6bffcdd6 100644 +--- a/src/compiler/backend/code-generator.cc ++++ b/src/compiler/backend/code-generator.cc +@@ -1306,7 +1306,8 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation, + default: + UNREACHABLE(); + } +- if (literal.object().equals(info()->closure())) { ++ if (literal.object().equals(info()->closure()) && ++ info()->function_context_specializing()) { + translation->StoreJSFrameFunction(); + } else { + int literal_id = DefineDeoptimizationLiteral(literal); diff --git a/patches/v8/merged_compiler_fix_a_bug_in_visitspeculativeintegeradditiveop.patch b/patches/v8/merged_compiler_fix_a_bug_in_visitspeculativeintegeradditiveop.patch new file mode 100644 index 0000000000000..ac80dd8e08948 --- /dev/null +++ b/patches/v8/merged_compiler_fix_a_bug_in_visitspeculativeintegeradditiveop.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Tue, 20 Apr 2021 13:48:07 +0200 +Subject: Merged: [compiler] Fix a bug in VisitSpeculativeIntegerAdditiveOp + +Revision: 9313c4ce3f32ad81df1c65becccec7e129181ce3 + +BUG=chromium:1199345 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true +R=nicohartmann@chromium.org + +Change-Id: I0ee9f13815b1a7d248d4caa506c6930697e1866c +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2839559 +Commit-Queue: Georg Neis +Reviewed-by: Nico Hartmann +Cr-Commit-Position: refs/branch-heads/9.0@{#41} +Cr-Branched-From: bd0108b4c88e0d6f2350cb79b5f363fbd02f3eb7-refs/heads/9.0.257@{#1} +Cr-Branched-From: 349bcc6a075411f1a7ce2d866c3dfeefc2efa39d-refs/heads/master@{#73001} + +diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc +index 93a0c71cd72eb7f66a8037490666bc4ab2052583..c2f0d744c2fb698427dd876fb9a37eb280e6f583 100644 +--- a/src/compiler/simplified-lowering.cc ++++ b/src/compiler/simplified-lowering.cc +@@ -1453,10 +1453,15 @@ class RepresentationSelector { + Type right_feedback_type = TypeOf(node->InputAt(1)); + + // Using Signed32 as restriction type amounts to promising there won't be +- // signed overflow. This is incompatible with relying on a Word32 +- // truncation in order to skip the overflow check. ++ // signed overflow. This is incompatible with relying on a Word32 truncation ++ // in order to skip the overflow check. Similarly, we must not drop -0 from ++ // the result type unless we deopt for -0 inputs. + Type const restriction = +- truncation.IsUsedAsWord32() ? Type::Any() : Type::Signed32(); ++ truncation.IsUsedAsWord32() ++ ? Type::Any() ++ : (truncation.identify_zeros() == kIdentifyZeros) ++ ? Type::Signed32OrMinusZero() ++ : Type::Signed32(); + + // Handle the case when no int32 checks on inputs are necessary (but + // an overflow check is needed on the output). Note that we do not diff --git a/patches/v8/merged_deoptimizer_fix_bug_in_optimizedframe_summarize.patch b/patches/v8/merged_deoptimizer_fix_bug_in_optimizedframe_summarize.patch new file mode 100644 index 0000000000000..47f3ba837540c --- /dev/null +++ b/patches/v8/merged_deoptimizer_fix_bug_in_optimizedframe_summarize.patch @@ -0,0 +1,209 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Tue, 23 Mar 2021 17:37:21 +0100 +Subject: Merged: [deoptimizer] Fix bug in OptimizedFrame::Summarize +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Revision: 3353a7d0b017146d543434be4036a81aaf7d25ae + +BUG=chromium:1182647 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true +R=​bmeurer@chromium.org + +(cherry picked from commit c0c96b768a7d3463b11403874549e6496529740d) + +Change-Id: I86abd6a3f34169be5f99aa9f54bb7bb3706fa85a +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2780300 +Reviewed-by: Georg Neis +Reviewed-by: Benedikt Meurer +Commit-Queue: Georg Neis +Cr-Original-Commit-Position: refs/branch-heads/8.9@{#49} +Cr-Original-Branched-From: 16b9bbbd581c25391981aa03180b76aa60463a3e-refs/heads/8.9.255@{#1} +Cr-Original-Branched-From: d16a2a688498bd1c3e6a49edb25d8c4ca56232dc-refs/heads/master@{#72039} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2794427 +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Artem Sumaneev +Cr-Commit-Position: refs/branch-heads/8.6@{#72} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/deoptimizer/deoptimizer.cc b/src/deoptimizer/deoptimizer.cc +index a225bac2b73f3fe61e611aaca19c129374b64a44..f90f9f6cbc87db38082724342791b172fe8d4321 100644 +--- a/src/deoptimizer/deoptimizer.cc ++++ b/src/deoptimizer/deoptimizer.cc +@@ -3549,7 +3549,8 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) { + } + } + +-TranslatedState::TranslatedState(const JavaScriptFrame* frame) { ++TranslatedState::TranslatedState(const JavaScriptFrame* frame) ++ : purpose_(kFrameInspection) { + int deopt_index = Safepoint::kNoDeoptimizationIndex; + DeoptimizationData data = + static_cast(frame)->GetDeoptimizationData( +@@ -3946,25 +3947,63 @@ void TranslatedState::EnsureCapturedObjectAllocatedAt( + } + + default: +- CHECK(map->IsJSObjectMap()); + EnsureJSObjectAllocated(slot, map); +- TranslatedValue* properties_slot = &(frame->values_[value_index]); +- value_index++; ++ int remaining_children_count = slot->GetChildrenCount() - 1; ++ ++ TranslatedValue* properties_slot = frame->ValueAt(value_index); ++ value_index++, remaining_children_count--; + if (properties_slot->kind() == TranslatedValue::kCapturedObject) { +- // If we are materializing the property array, make sure we put +- // the mutable heap numbers at the right places. ++ // We are materializing the property array, so make sure we put the ++ // mutable heap numbers at the right places. + EnsurePropertiesAllocatedAndMarked(properties_slot, map); + EnsureChildrenAllocated(properties_slot->GetChildrenCount(), frame, + &value_index, worklist); ++ } else { ++ CHECK_EQ(properties_slot->kind(), TranslatedValue::kTagged); + } +- // Make sure all the remaining children (after the map and properties) are +- // allocated. +- return EnsureChildrenAllocated(slot->GetChildrenCount() - 2, frame, ++ ++ TranslatedValue* elements_slot = frame->ValueAt(value_index); ++ value_index++, remaining_children_count--; ++ if (elements_slot->kind() == TranslatedValue::kCapturedObject || ++ !map->IsJSArrayMap()) { ++ // Handle this case with the other remaining children below. ++ value_index--, remaining_children_count++; ++ } else { ++ CHECK_EQ(elements_slot->kind(), TranslatedValue::kTagged); ++ elements_slot->GetValue(); ++ if (purpose_ == kFrameInspection) { ++ // We are materializing a JSArray for the purpose of frame inspection. ++ // If we were to construct it with the above elements value then an ++ // actual deopt later on might create another JSArray instance with ++ // the same elements store. That would violate the key assumption ++ // behind left-trimming. ++ elements_slot->ReplaceElementsArrayWithCopy(); ++ } ++ } ++ ++ // Make sure all the remaining children (after the map, properties store, ++ // and possibly elements store) are allocated. ++ return EnsureChildrenAllocated(remaining_children_count, frame, + &value_index, worklist); + } + UNREACHABLE(); + } + ++void TranslatedValue::ReplaceElementsArrayWithCopy() { ++ DCHECK_EQ(kind(), TranslatedValue::kTagged); ++ DCHECK_EQ(materialization_state(), TranslatedValue::kFinished); ++ auto elements = Handle::cast(GetValue()); ++ DCHECK(elements->IsFixedArray() || elements->IsFixedDoubleArray()); ++ if (elements->IsFixedDoubleArray()) { ++ DCHECK(!elements->IsCowArray()); ++ set_storage(isolate()->factory()->CopyFixedDoubleArray( ++ Handle::cast(elements))); ++ } else if (!elements->IsCowArray()) { ++ set_storage(isolate()->factory()->CopyFixedArray( ++ Handle::cast(elements))); ++ } ++} ++ + void TranslatedState::EnsureChildrenAllocated(int count, TranslatedFrame* frame, + int* value_index, + std::stack* worklist) { +@@ -4029,6 +4068,7 @@ Handle TranslatedState::AllocateStorageFor(TranslatedValue* slot) { + + void TranslatedState::EnsureJSObjectAllocated(TranslatedValue* slot, + Handle map) { ++ CHECK(map->IsJSObjectMap()); + CHECK_EQ(map->instance_size(), slot->GetChildrenCount() * kTaggedSize); + + Handle object_storage = AllocateStorageFor(slot); +diff --git a/src/deoptimizer/deoptimizer.h b/src/deoptimizer/deoptimizer.h +index 1adc3f1eeb856ca5cee9acc4723e9003d069fe3a..3050819431eed8a04b9371c1008c44b700f26404 100644 +--- a/src/deoptimizer/deoptimizer.h ++++ b/src/deoptimizer/deoptimizer.h +@@ -121,6 +121,8 @@ class TranslatedValue { + return storage_; + } + ++ void ReplaceElementsArrayWithCopy(); ++ + Kind kind_; + MaterializationState materialization_state_ = kUninitialized; + TranslatedState* container_; // This is only needed for materialization of +@@ -317,7 +319,15 @@ class TranslatedFrame { + + class TranslatedState { + public: +- TranslatedState() = default; ++ // There are two constructors, each for a different purpose: ++ ++ // The default constructor is for the purpose of deoptimizing an optimized ++ // frame (replacing it with one or several unoptimized frames). It is used by ++ // the Deoptimizer. ++ TranslatedState() : purpose_(kDeoptimization) {} ++ ++ // This constructor is for the purpose of merely inspecting an optimized ++ // frame. It is used by stack trace generation and various debugging features. + explicit TranslatedState(const JavaScriptFrame* frame); + + void Prepare(Address stack_frame_pointer); +@@ -352,6 +362,12 @@ class TranslatedState { + private: + friend TranslatedValue; + ++ // See the description of the constructors for an explanation of the two ++ // purposes. The only actual difference is that in the kFrameInspection case ++ // extra work is needed to not violate assumptions made by left-trimming. For ++ // details, see the code around ReplaceElementsArrayWithCopy. ++ enum Purpose { kDeoptimization, kFrameInspection }; ++ + TranslatedFrame CreateNextTranslatedFrame(TranslationIterator* iterator, + FixedArray literal_array, + Address fp, FILE* trace_file); +@@ -408,6 +424,7 @@ class TranslatedState { + static Float32 GetFloatSlot(Address fp, int slot_index); + static Float64 GetDoubleSlot(Address fp, int slot_index); + ++ Purpose const purpose_; + std::vector frames_; + Isolate* isolate_ = nullptr; + Address stack_frame_pointer_ = kNullAddress; +diff --git a/test/mjsunit/compiler/regress-1182647.js b/test/mjsunit/compiler/regress-1182647.js +new file mode 100644 +index 0000000000000000000000000000000000000000..e0582f7cbfb4f1e5d081443374248c1b5eb30a2e +--- /dev/null ++++ b/test/mjsunit/compiler/regress-1182647.js +@@ -0,0 +1,25 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Flags: --allow-natives-syntax --verify-heap ++ ++function foo() { ++ const arr = Array(1000); ++ ++ function bar() { ++ try { ({a: p4nda, b: arr.length}); } catch(e) {} ++ } ++ ++ for (var i = 0; i < 25; i++) bar(); ++ ++ /p4nda/.test({}); // Deopt here. ++ ++ arr.shift(); ++} ++ ++%PrepareFunctionForOptimization(foo); ++foo(); ++foo(); ++%OptimizeFunctionOnNextCall(foo); ++foo(); diff --git a/patches/v8/merged_json_fix_gc_issue_in_buildjsonobject.patch b/patches/v8/merged_json_fix_gc_issue_in_buildjsonobject.patch new file mode 100644 index 0000000000000..145baf27643b9 --- /dev/null +++ b/patches/v8/merged_json_fix_gc_issue_in_buildjsonobject.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Victor Gomes +Date: Mon, 31 May 2021 13:16:54 +0200 +Subject: Merged: [JSON] Fix GC issue in BuildJsonObject + +We must ensure that the sweeper is not running or has already swept +mutable_double_buffer. Otherwise the GC can add it to the free list. + +Change-Id: If0fc7617acdb6690f0567215b78f8728e1643ec0 +No-Try: true +No-Presubmit: true +No-Tree-Checks: true +Bug: v8:11837, chromium:1214842 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2993033 +Reviewed-by: Michael Lippautz +Reviewed-by: Toon Verwaest +Commit-Queue: Victor Gomes +Cr-Commit-Position: refs/branch-heads/9.1@{#75} +Cr-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1} +Cr-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847} + +diff --git a/src/heap/heap.cc b/src/heap/heap.cc +index a017905bfcb0059aa12dbd1bd5a477bcca2dd616..f079fd333fb1e4571d8bbe8f6041fa6bc458bcb7 100644 +--- a/src/heap/heap.cc ++++ b/src/heap/heap.cc +@@ -2148,6 +2148,10 @@ size_t Heap::PerformGarbageCollection( + return freed_global_handles; + } + ++void Heap::EnsureSweepingCompleted() { ++ mark_compact_collector()->EnsureSweepingCompleted(); ++} ++ + void Heap::RecomputeLimits(GarbageCollector collector) { + if (!((collector == MARK_COMPACTOR) || + (HasLowYoungGenerationAllocationRate() && +diff --git a/src/heap/heap.h b/src/heap/heap.h +index b8220dad5eb08cd8bffa9ff0c11d9f149e6fad10..cff57d94e822856f607e4a16cece3ca08c6e0e3c 100644 +--- a/src/heap/heap.h ++++ b/src/heap/heap.h +@@ -1065,6 +1065,8 @@ class Heap { + Reservation* reservations, const std::vector& large_objects, + const std::vector
& maps); + ++ void EnsureSweepingCompleted(); ++ + IncrementalMarking* incremental_marking() { + return incremental_marking_.get(); + } +diff --git a/src/json/json-parser.cc b/src/json/json-parser.cc +index d099fa36cba13daa0ffe915f8a4a067f3f392685..75e78923a4bc30fcfb16fccb40759408cfa42b83 100644 +--- a/src/json/json-parser.cc ++++ b/src/json/json-parser.cc +@@ -633,6 +633,11 @@ Handle JsonParser::BuildJsonObject( + DCHECK_EQ(mutable_double_address, end); + } + #endif ++ // Before setting the length of mutable_double_buffer back to zero, we ++ // must ensure that the sweeper is not running or has already swept the ++ // object's page. Otherwise the GC can add the contents of ++ // mutable_double_buffer to the free list. ++ isolate()->heap()->EnsureSweepingCompleted(); + mutable_double_buffer->set_length(0); + } + } diff --git a/patches/v8/merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch b/patches/v8/merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch new file mode 100644 index 0000000000000..05463706258c3 --- /dev/null +++ b/patches/v8/merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch @@ -0,0 +1,120 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Tue, 18 May 2021 21:05:35 +0200 +Subject: Merged: [liftoff] Fix >=2GB memory accesses on 32-bit + +We were inconsistent in handling offsets >= 2GB on 32-bit systems. The +code was still relying on this being detected as statically out of +bounds, but with the increase of {kV8MaxWasmMemoryPages} to support 4GB +memories, this is not the case any more. + +This CL fixes this by again detecting such situations as statically OOB. +We do not expect to be able to allocate memories of size >2GB on such +systems. If this assumptions turns out to be wrong, we will erroneously +trap. If that happens, we will have to explicitly disallow memories of +such size on 32-bit systems. + +(cherry picked from commit 7ad5b961553d7d9bc30da1bb839726be2b92bb51) + +Bug: v8:7881, chromium:1201340 +Change-Id: Ib3d32b8d303eb047eb7811a045a8fa2b4ecb8cda +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2853596 + +diff --git a/src/wasm/baseline/arm/liftoff-assembler-arm.h b/src/wasm/baseline/arm/liftoff-assembler-arm.h +index 9379a3b78a2fc93f3558f58790f63b6bac7b6d1b..c6b4cc1276ccdc60314a0e6eb839d325eeea7ebf 100644 +--- a/src/wasm/baseline/arm/liftoff-assembler-arm.h ++++ b/src/wasm/baseline/arm/liftoff-assembler-arm.h +@@ -689,13 +689,8 @@ void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr, + Register offset_reg, uint32_t offset_imm, + LoadType type, LiftoffRegList pinned, + uint32_t* protected_load_pc, bool is_load_mem) { +- // If offset_imm cannot be converted to int32 safely, we abort as a separate +- // check should cause this code to never be executed. +- // TODO(7881): Support when >2GB is required. +- if (!is_uint31(offset_imm)) { +- TurboAssembler::Abort(AbortReason::kOffsetOutOfRange); +- return; +- } ++ // Offsets >=2GB are statically OOB on 32-bit systems. ++ DCHECK_LE(offset_imm, std::numeric_limits::max()); + liftoff::LoadInternal(this, dst, src_addr, offset_reg, + static_cast(offset_imm), type, pinned, + protected_load_pc, is_load_mem); +@@ -705,13 +700,8 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister src, + StoreType type, LiftoffRegList pinned, + uint32_t* protected_store_pc, bool is_store_mem) { +- // If offset_imm cannot be converted to int32 safely, we abort as a separate +- // check should cause this code to never be executed. +- // TODO(7881): Support when >2GB is required. +- if (!is_uint31(offset_imm)) { +- TurboAssembler::Abort(AbortReason::kOffsetOutOfRange); +- return; +- } ++ // Offsets >=2GB are statically OOB on 32-bit systems. ++ DCHECK_LE(offset_imm, std::numeric_limits::max()); + UseScratchRegisterScope temps(this); + if (type.value() == StoreType::kF64Store) { + Register actual_dst_addr = liftoff::CalculateActualAddress( +diff --git a/src/wasm/baseline/ia32/liftoff-assembler-ia32.h b/src/wasm/baseline/ia32/liftoff-assembler-ia32.h +index 9bfad9313b9aa06ccee4dc50868b27007bb94fec..9005fe4303adb0cf20a23eb2f70f084bc3b08413 100644 +--- a/src/wasm/baseline/ia32/liftoff-assembler-ia32.h ++++ b/src/wasm/baseline/ia32/liftoff-assembler-ia32.h +@@ -324,13 +324,8 @@ void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr, + Register offset_reg, uint32_t offset_imm, + LoadType type, LiftoffRegList pinned, + uint32_t* protected_load_pc, bool is_load_mem) { +- if (offset_imm > static_cast(std::numeric_limits::max())) { +- // We do not generate code here, because such an offset should never pass +- // the bounds check. However, the spec requires us to compile code with such +- // an offset. +- Trap(); +- return; +- } ++ // Offsets >=2GB are statically OOB on 32-bit systems. ++ DCHECK_LE(offset_imm, std::numeric_limits::max()); + DCHECK_EQ(type.value_type() == kWasmI64, dst.is_gp_pair()); + Operand src_op = offset_reg == no_reg + ? Operand(src_addr, offset_imm) +@@ -406,6 +401,7 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, + StoreType type, LiftoffRegList pinned, + uint32_t* protected_store_pc, bool is_store_mem) { + DCHECK_EQ(type.value_type() == kWasmI64, src.is_gp_pair()); ++ // Offsets >=2GB are statically OOB on 32-bit systems. + DCHECK_LE(offset_imm, std::numeric_limits::max()); + Operand dst_op = offset_reg == no_reg + ? Operand(dst_addr, offset_imm) +diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc +index c9625b06d8a46ee9aa632142cf9491f0e4c96bd4..8103f5b7c14f8e87ae0e175ef7bfffd25d490539 100644 +--- a/src/wasm/baseline/liftoff-compiler.cc ++++ b/src/wasm/baseline/liftoff-compiler.cc +@@ -2100,10 +2100,7 @@ class LiftoffCompiler { + bool BoundsCheckMem(FullDecoder* decoder, uint32_t access_size, + uint64_t offset, Register index, LiftoffRegList pinned, + ForceCheck force_check) { +- // If the offset does not fit in a uintptr_t, this can never succeed on this +- // machine. + const bool statically_oob = +- offset > std::numeric_limits::max() || + !base::IsInBounds(offset, access_size, + env_->max_memory_size); + +diff --git a/src/wasm/compilation-environment.h b/src/wasm/compilation-environment.h +index d730161ad1f9d0590bc1302093e3fea48b8d230f..fc9b2fcbc2e07434b14f0ccb1e37b12550f6c9e6 100644 +--- a/src/wasm/compilation-environment.h ++++ b/src/wasm/compilation-environment.h +@@ -63,9 +63,11 @@ struct CompilationEnv { + + const LowerSimd lower_simd; + +- static constexpr uint32_t kMaxMemoryPagesAtRuntime = +- std::min(kV8MaxWasmMemoryPages, +- std::numeric_limits::max() / kWasmPageSize); ++ // We assume that memories of size >= half of the virtual address space ++ // cannot be allocated (see https://crbug.com/1201340). ++ static constexpr uint32_t kMaxMemoryPagesAtRuntime = std::min( ++ kV8MaxWasmMemoryPages, ++ (uintptr_t{1} << (kSystemPointerSize == 4 ? 31 : 63)) / kWasmPageSize); + + constexpr CompilationEnv(const WasmModule* module, + UseTrapHandler use_trap_handler, diff --git a/patches/v8/merged_squashed_multiple_commits.patch b/patches/v8/merged_squashed_multiple_commits.patch new file mode 100644 index 0000000000000..fcc9652e42b40 --- /dev/null +++ b/patches/v8/merged_squashed_multiple_commits.patch @@ -0,0 +1,202 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Wed, 10 Mar 2021 09:45:36 +0100 +Subject: Merged: Squashed multiple commits. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Merged: [const-tracking] Mark const field as mutable when reconfiguring +Revision: 7535b91f7cb22274de734d5da7d0324d8653d626 + +Merged: [const-tracking] Fix incorrect DCHECK in MapUpdater +Revision: f95db8916a731e6e5ccc0282616bc907ce06012f + +BUG=chromium:1161847,chromium:1185463,v8:9233 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true +R=​ishell@chromium.org + +(cherry picked from commit 56518020bff4d0e8b82cff843c9f618c90084e42) + +Change-Id: I7f46a701646e1dd67a049b2aa4ac32d05b6885f3 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2748079 +Commit-Queue: Georg Neis +Reviewed-by: Igor Sheludko +Cr-Original-Commit-Position: refs/branch-heads/8.9@{#43} +Cr-Original-Branched-From: 16b9bbbd581c25391981aa03180b76aa60463a3e-refs/heads/8.9.255@{#1} +Cr-Original-Branched-From: d16a2a688498bd1c3e6a49edb25d8c4ca56232dc-refs/heads/master@{#72039} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2794428 +Reviewed-by: Victor-Gabriel Savu +Commit-Queue: Artem Sumaneev +Cr-Commit-Position: refs/branch-heads/8.6@{#73} +Cr-Branched-From: a64aed2333abf49e494d2a5ce24bbd14fff19f60-refs/heads/8.6.395@{#1} +Cr-Branched-From: a626bc036236c9bf92ac7b87dc40c9e538b087e3-refs/heads/master@{#69472} + +diff --git a/src/objects/map-updater.cc b/src/objects/map-updater.cc +index b4b158749381efcf780d5c8ba07c286be6ba6b30..047750ebbd454a5f3f1fce7bc06ac042085245a4 100644 +--- a/src/objects/map-updater.cc ++++ b/src/objects/map-updater.cc +@@ -121,6 +121,41 @@ Handle MapUpdater::ReconfigureToDataField(InternalIndex descriptor, + PropertyDetails old_details = + old_descriptors_->GetDetails(modified_descriptor_); + ++ // If the {descriptor} was "const" data field so far, we need to update the ++ // {old_map_} here, otherwise we could get the constants wrong, i.e. ++ // ++ // o.x = 1; ++ // change o.x's attributes to something else ++ // delete o.x; ++ // o.x = 2; ++ // ++ // could trick V8 into thinking that `o.x` is still 1 even after the second ++ // assignment. ++ // This situation is similar to what might happen with property deletion. ++ if (old_details.constness() == PropertyConstness::kConst && ++ old_details.location() == kField && ++ old_details.attributes() != new_attributes_) { ++ Handle field_type( ++ old_descriptors_->GetFieldType(modified_descriptor_), isolate_); ++ Map::GeneralizeField(isolate_, old_map_, descriptor, ++ PropertyConstness::kMutable, ++ old_details.representation(), field_type); ++ // The old_map_'s property must become mutable. ++ // Note, that the {old_map_} and {old_descriptors_} are not expected to be ++ // updated by the generalization if the map is already deprecated. ++ DCHECK_IMPLIES( ++ !old_map_->is_deprecated(), ++ PropertyConstness::kMutable == ++ old_descriptors_->GetDetails(modified_descriptor_).constness()); ++ // Although the property in the old map is marked as mutable we still ++ // treat it as constant when merging with the new path in transition tree. ++ // This is fine because up until this reconfiguration the field was ++ // known to be constant, so it's fair to proceed treating it as such ++ // during this reconfiguration session. The issue is that after the ++ // reconfiguration the original field might become mutable (see the delete ++ // example above). ++ } ++ + // If property kind is not reconfigured merge the result with + // representation/field type from the old descriptor. + if (old_details.kind() == new_kind_) { +diff --git a/test/cctest/test-field-type-tracking.cc b/test/cctest/test-field-type-tracking.cc +index 2f59d7bff83a2c0aab8ae815ed0ac5e3825f4262..6b2b6dadf14ae836e11d7ff78108c99a10d38765 100644 +--- a/test/cctest/test-field-type-tracking.cc ++++ b/test/cctest/test-field-type-tracking.cc +@@ -1081,20 +1081,31 @@ void TestReconfigureDataFieldAttribute_GeneralizeField( + Handle code_field_type = CreateDummyOptimizedCode(isolate); + Handle code_field_repr = CreateDummyOptimizedCode(isolate); + Handle code_field_const = CreateDummyOptimizedCode(isolate); +- Handle field_owner( +- map->FindFieldOwner(isolate, InternalIndex(kSplitProp)), isolate); +- DependentCode::InstallDependency(isolate, +- MaybeObjectHandle::Weak(code_field_type), +- field_owner, DependentCode::kFieldTypeGroup); +- DependentCode::InstallDependency( +- isolate, MaybeObjectHandle::Weak(code_field_repr), field_owner, +- DependentCode::kFieldRepresentationGroup); +- DependentCode::InstallDependency( +- isolate, MaybeObjectHandle::Weak(code_field_const), field_owner, +- DependentCode::kFieldConstGroup); ++ Handle code_src_field_const = CreateDummyOptimizedCode(isolate); ++ { ++ Handle field_owner( ++ map->FindFieldOwner(isolate, InternalIndex(kSplitProp)), isolate); ++ DependentCode::InstallDependency( ++ isolate, MaybeObjectHandle::Weak(code_field_type), field_owner, ++ DependentCode::kFieldTypeGroup); ++ DependentCode::InstallDependency( ++ isolate, MaybeObjectHandle::Weak(code_field_repr), field_owner, ++ DependentCode::kFieldRepresentationGroup); ++ DependentCode::InstallDependency( ++ isolate, MaybeObjectHandle::Weak(code_field_const), field_owner, ++ DependentCode::kFieldConstGroup); ++ } ++ { ++ Handle field_owner( ++ map2->FindFieldOwner(isolate, InternalIndex(kSplitProp)), isolate); ++ DependentCode::InstallDependency( ++ isolate, MaybeObjectHandle::Weak(code_src_field_const), field_owner, ++ DependentCode::kFieldConstGroup); ++ } + CHECK(!code_field_type->marked_for_deoptimization()); + CHECK(!code_field_repr->marked_for_deoptimization()); + CHECK(!code_field_const->marked_for_deoptimization()); ++ CHECK(!code_src_field_const->marked_for_deoptimization()); + + // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which + // should generalize representations in |map1|. +@@ -1102,10 +1113,21 @@ void TestReconfigureDataFieldAttribute_GeneralizeField( + Map::ReconfigureExistingProperty(isolate, map2, InternalIndex(kSplitProp), + kData, NONE, PropertyConstness::kConst); + +- // |map2| should be left unchanged but marked unstable. ++ // |map2| should be mosly left unchanged but marked unstable and if the ++ // source property was constant it should also be transitioned to kMutable. + CHECK(!map2->is_stable()); + CHECK(!map2->is_deprecated()); + CHECK_NE(*map2, *new_map); ++ // If the "source" property was const then update constness expectations for ++ // "source" map and ensure the deoptimization dependency was triggered. ++ if (to.constness == PropertyConstness::kConst) { ++ expectations2.SetDataField(kSplitProp, READ_ONLY, ++ PropertyConstness::kMutable, to.representation, ++ to.type); ++ CHECK(code_src_field_const->marked_for_deoptimization()); ++ } else { ++ CHECK(!code_src_field_const->marked_for_deoptimization()); ++ } + CHECK(expectations2.Check(*map2)); + + for (int i = kSplitProp; i < kPropCount; i++) { +diff --git a/test/mjsunit/regress/regress-crbug-1161847-1.js b/test/mjsunit/regress/regress-crbug-1161847-1.js +new file mode 100644 +index 0000000000000000000000000000000000000000..282d9b878718105db40fee0283d15227fb724a3a +--- /dev/null ++++ b/test/mjsunit/regress/regress-crbug-1161847-1.js +@@ -0,0 +1,19 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Flags: --allow-natives-syntax ++ ++function foo(first_run) { ++ let o = { x: 0 }; ++ if (first_run) assertTrue(%HasOwnConstDataProperty(o, 'x')); ++ Object.defineProperty(o, 'x', { writable: false }); ++ delete o.x; ++ o.x = 23; ++ if (first_run) assertFalse(%HasOwnConstDataProperty(o, 'x')); ++} ++%PrepareFunctionForOptimization(foo); ++foo(true); ++foo(false); ++%OptimizeFunctionOnNextCall(foo); ++foo(false); +diff --git a/test/mjsunit/regress/regress-crbug-1161847-2.js b/test/mjsunit/regress/regress-crbug-1161847-2.js +new file mode 100644 +index 0000000000000000000000000000000000000000..ec61fee068acea0ea259164816142a01851f3669 +--- /dev/null ++++ b/test/mjsunit/regress/regress-crbug-1161847-2.js +@@ -0,0 +1,19 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Flags: --allow-natives-syntax ++ ++function foo(first_run) { ++ let o = { x: 0 }; ++ if (first_run) assertTrue(%HasOwnConstDataProperty(o, 'x')); ++ Object.defineProperty(o, 'x', { get() { return 1; }, configurable: true, enumerable: true }); ++ delete o.x; ++ o.x = 23; ++ if (first_run) assertFalse(%HasOwnConstDataProperty(o, 'x')); ++} ++%PrepareFunctionForOptimization(foo); ++foo(true); ++foo(false); ++%OptimizeFunctionOnNextCall(foo); ++foo(false); diff --git a/patches/v8/merged_turbofan_harden_arrayprototypepop_and_arrayprototypeshift.patch b/patches/v8/merged_turbofan_harden_arrayprototypepop_and_arrayprototypeshift.patch new file mode 100644 index 0000000000000..3e6107fad8aed --- /dev/null +++ b/patches/v8/merged_turbofan_harden_arrayprototypepop_and_arrayprototypeshift.patch @@ -0,0 +1,94 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Georg Neis +Date: Sun, 18 Apr 2021 09:46:25 +0200 +Subject: Merged: [turbofan] Harden ArrayPrototypePop and ArrayPrototypeShift + +Revision: d4aafa4022b718596b3deadcc3cdcb9209896154 + +TBR=glazunov@chromium.org +BUG=chromium:1198696 +NOTRY=true +NOPRESUBMIT=true +NOTREECHECKS=true + +Change-Id: I1840ffabbed3a3caab75b0abea1d37d9ed446d3f +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2833911 +Reviewed-by: Georg Neis +Commit-Queue: Georg Neis +Cr-Commit-Position: refs/branch-heads/9.0@{#39} +Cr-Branched-From: bd0108b4c88e0d6f2350cb79b5f363fbd02f3eb7-refs/heads/9.0.257@{#1} +Cr-Branched-From: 349bcc6a075411f1a7ce2d866c3dfeefc2efa39d-refs/heads/master@{#73001} + +diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc +index 94a6b3a7c792724f8add265ddaf4e0c4cdb3b3b3..b77094b7e1f0c57552fc7c8d3cea1f9d9ed7a269 100644 +--- a/src/compiler/js-call-reducer.cc ++++ b/src/compiler/js-call-reducer.cc +@@ -5251,24 +5251,31 @@ Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) { + } + + // Compute the new {length}. +- length = graph()->NewNode(simplified()->NumberSubtract(), length, +- jsgraph()->OneConstant()); ++ Node* new_length = graph()->NewNode(simplified()->NumberSubtract(), ++ length, jsgraph()->OneConstant()); ++ ++ // This extra check exists solely to break an exploitation technique ++ // that abuses typer mismatches. ++ new_length = efalse = graph()->NewNode( ++ simplified()->CheckBounds(p.feedback(), ++ CheckBoundsFlag::kAbortOnOutOfBounds), ++ new_length, length, efalse, if_false); + + // Store the new {length} to the {receiver}. + efalse = graph()->NewNode( + simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), +- receiver, length, efalse, if_false); ++ receiver, new_length, efalse, if_false); + + // Load the last entry from the {elements}. + vfalse = efalse = graph()->NewNode( + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)), +- elements, length, efalse, if_false); ++ elements, new_length, efalse, if_false); + + // Store a hole to the element we just removed from the {receiver}. + efalse = graph()->NewNode( + simplified()->StoreElement( + AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))), +- elements, length, jsgraph()->TheHoleConstant(), efalse, if_false); ++ elements, new_length, jsgraph()->TheHoleConstant(), efalse, if_false); + } + + control = graph()->NewNode(common()->Merge(2), if_true, if_false); +@@ -5444,19 +5451,27 @@ Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { + } + + // Compute the new {length}. +- length = graph()->NewNode(simplified()->NumberSubtract(), length, +- jsgraph()->OneConstant()); ++ Node* new_length = graph()->NewNode(simplified()->NumberSubtract(), ++ length, jsgraph()->OneConstant()); ++ ++ // This extra check exists solely to break an exploitation technique ++ // that abuses typer mismatches. ++ new_length = etrue1 = graph()->NewNode( ++ simplified()->CheckBounds(p.feedback(), ++ CheckBoundsFlag::kAbortOnOutOfBounds), ++ new_length, length, etrue1, if_true1); + + // Store the new {length} to the {receiver}. + etrue1 = graph()->NewNode( + simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), +- receiver, length, etrue1, if_true1); ++ receiver, new_length, etrue1, if_true1); + + // Store a hole to the element we just removed from the {receiver}. + etrue1 = graph()->NewNode( + simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( + GetHoleyElementsKind(kind))), +- elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1); ++ elements, new_length, jsgraph()->TheHoleConstant(), etrue1, ++ if_true1); + } + + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); diff --git a/patches/v8/merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch b/patches/v8/merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch new file mode 100644 index 0000000000000..c51c138c7383b --- /dev/null +++ b/patches/v8/merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Tue, 18 May 2021 17:59:17 +0200 +Subject: Merged: [wasm-simd][ia32] Fix f64x2 min max to use registers + +We don't have memory alignment yet, so using memory operands will cause +segv if we try to access the unaligned operands (on non-AVX systems). + +The fix here is kept simple (the logic can be cleaned up a bit and +optimized to not use unique registers), in order to keep the cherry-pick +and back-merge as small and safe as possible. + +(cherry picked from commit 7f2d41fa3748ecc8fc888d93f82d77718b1dd6b0) + +Bug: chromium:1204071 +Change-Id: I7d7d177ff096ebd3de399fcf1ec7d9ac57bbb80b +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2872565 + +diff --git a/src/compiler/backend/ia32/instruction-selector-ia32.cc b/src/compiler/backend/ia32/instruction-selector-ia32.cc +index fec4053871bd14ac0f732e41e1b03eaa64a8c5cb..8a1d32e7f8096679fb265a6b7b4d84696288b8b4 100644 +--- a/src/compiler/backend/ia32/instruction-selector-ia32.cc ++++ b/src/compiler/backend/ia32/instruction-selector-ia32.cc +@@ -2199,7 +2199,7 @@ void InstructionSelector::VisitF64x2Min(Node* node) { + IA32OperandGenerator g(this); + InstructionOperand temps[] = {g.TempSimd128Register()}; + InstructionOperand operand0 = g.UseUniqueRegister(node->InputAt(0)); +- InstructionOperand operand1 = g.UseUnique(node->InputAt(1)); ++ InstructionOperand operand1 = g.UseUniqueRegister(node->InputAt(1)); + + if (IsSupported(AVX)) { + Emit(kIA32F64x2Min, g.DefineAsRegister(node), operand0, operand1, +@@ -2214,7 +2214,7 @@ void InstructionSelector::VisitF64x2Max(Node* node) { + IA32OperandGenerator g(this); + InstructionOperand temps[] = {g.TempSimd128Register()}; + InstructionOperand operand0 = g.UseUniqueRegister(node->InputAt(0)); +- InstructionOperand operand1 = g.UseUnique(node->InputAt(1)); ++ InstructionOperand operand1 = g.UseUniqueRegister(node->InputAt(1)); + if (IsSupported(AVX)) { + Emit(kIA32F64x2Max, g.DefineAsRegister(node), operand0, operand1, + arraysize(temps), temps); diff --git a/patches/v8/reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch b/patches/v8/reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch new file mode 100644 index 0000000000000..d5e9aee7e48a5 --- /dev/null +++ b/patches/v8/reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch @@ -0,0 +1,138 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrey Belenko +Date: Tue, 18 May 2021 21:52:40 +0200 +Subject: Reland "[compiler] Fix more truncation bugs in SimplifiedLowering" + +This is a reland of 47077d94492cb604e3a7f02c0d7c3c495ff6b713 without +changes. The revert was false alarm. + +Original change's description: +> [compiler] Fix more truncation bugs in SimplifiedLowering +> +> Bug: chromium:1200490 +> Change-Id: I3555b6d99bdb4b4e7c302a43a82c17e8bff84ebe +> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2840452 + +Bug: chromium:1200490 +Change-Id: I75cac59050bc393d157a1ee5bed776c8986a7bbe +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2843817 + +diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc +index c2f0d744c2fb698427dd876fb9a37eb280e6f583..6ed92e855c66a266e1909925caed0140a5c59a67 100644 +--- a/src/compiler/simplified-lowering.cc ++++ b/src/compiler/simplified-lowering.cc +@@ -1398,17 +1398,32 @@ class RepresentationSelector { + return jsgraph_->simplified(); + } + +- void LowerToCheckedInt32Mul(Node* node, Truncation truncation, +- Type input0_type, Type input1_type) { +- // If one of the inputs is positive and/or truncation is being applied, +- // there is no need to return -0. +- CheckForMinusZeroMode mz_mode = +- truncation.IdentifiesZeroAndMinusZero() || +- IsSomePositiveOrderedNumber(input0_type) || +- IsSomePositiveOrderedNumber(input1_type) +- ? CheckForMinusZeroMode::kDontCheckForMinusZero +- : CheckForMinusZeroMode::kCheckForMinusZero; +- NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode)); ++ template ++ void VisitForCheckedInt32Mul(Node* node, Truncation truncation, ++ Type input0_type, Type input1_type, ++ UseInfo input_use) { ++ DCHECK_EQ(node->opcode(), IrOpcode::kSpeculativeNumberMultiply); ++ // A -0 input is impossible or will cause a deopt. ++ DCHECK(BothInputsAre(node, Type::Signed32()) || ++ !input_use.truncation().IdentifiesZeroAndMinusZero()); ++ ++ CheckForMinusZeroMode mz_mode; ++ Type restriction; ++ if (IsSomePositiveOrderedNumber(input0_type) || ++ IsSomePositiveOrderedNumber(input1_type)) { ++ mz_mode = CheckForMinusZeroMode::kDontCheckForMinusZero; ++ restriction = Type::Signed32(); ++ } else if (truncation.IdentifiesZeroAndMinusZero()) { ++ mz_mode = CheckForMinusZeroMode::kDontCheckForMinusZero; ++ restriction = Type::Signed32OrMinusZero(); ++ } else { ++ mz_mode = CheckForMinusZeroMode::kCheckForMinusZero; ++ restriction = Type::Signed32(); ++ } ++ ++ VisitBinop(node, input_use, MachineRepresentation::kWord32, restriction); ++ if (lower()) ++ NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode)); + } + + void ChangeToInt32OverflowOp(Node* node) { +@@ -1600,12 +1615,22 @@ class RepresentationSelector { + VisitBinop(node, lhs_use, rhs_use, MachineRepresentation::kWord32); + if (lower()) DeferReplacement(node, lowering->Int32Mod(node)); + } else if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN())) { ++ Type const restriction = ++ truncation.IdentifiesZeroAndMinusZero() && ++ TypeOf(node->InputAt(0)).Maybe(Type::MinusZero()) ++ ? Type::Unsigned32OrMinusZero() ++ : Type::Unsigned32(); + VisitBinop(node, lhs_use, rhs_use, MachineRepresentation::kWord32, +- Type::Unsigned32()); ++ restriction); + if (lower()) ChangeToUint32OverflowOp(node); + } else { ++ Type const restriction = ++ truncation.IdentifiesZeroAndMinusZero() && ++ TypeOf(node->InputAt(0)).Maybe(Type::MinusZero()) ++ ? Type::Signed32OrMinusZero() ++ : Type::Signed32(); + VisitBinop(node, lhs_use, rhs_use, MachineRepresentation::kWord32, +- Type::Signed32()); ++ restriction); + if (lower()) ChangeToInt32OverflowOp(node); + } + return; +@@ -2165,23 +2190,18 @@ class RepresentationSelector { + // If both inputs and feedback are int32, use the overflow op. + if (hint == NumberOperationHint::kSignedSmall || + hint == NumberOperationHint::kSigned32) { +- VisitBinop(node, UseInfo::TruncatingWord32(), +- MachineRepresentation::kWord32, Type::Signed32()); +- if (lower()) { +- LowerToCheckedInt32Mul(node, truncation, input0_type, +- input1_type); +- } ++ VisitForCheckedInt32Mul(node, truncation, input0_type, ++ input1_type, ++ UseInfo::TruncatingWord32()); + return; + } + } + + if (hint == NumberOperationHint::kSignedSmall || + hint == NumberOperationHint::kSigned32) { +- VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint), +- MachineRepresentation::kWord32, Type::Signed32()); +- if (lower()) { +- LowerToCheckedInt32Mul(node, truncation, input0_type, input1_type); +- } ++ VisitForCheckedInt32Mul(node, truncation, input0_type, ++ input1_type, ++ CheckedUseInfoAsWord32FromHint(hint)); + return; + } + +@@ -3901,7 +3921,6 @@ template <> + void RepresentationSelector::SetOutput( + Node* node, MachineRepresentation representation, Type restriction_type) { + NodeInfo* const info = GetInfo(node); +- DCHECK(info->restriction_type().Is(restriction_type)); + DCHECK(restriction_type.Is(info->restriction_type())); + info->set_output(representation); + } +@@ -3911,7 +3930,6 @@ void RepresentationSelector::SetOutput( + Node* node, MachineRepresentation representation, Type restriction_type) { + NodeInfo* const info = GetInfo(node); + DCHECK_EQ(info->representation(), representation); +- DCHECK(info->restriction_type().Is(restriction_type)); + DCHECK(restriction_type.Is(info->restriction_type())); + USE(info); + } diff --git a/patches/webrtc/.patches b/patches/webrtc/.patches new file mode 100644 index 0000000000000..7e78710f765e0 --- /dev/null +++ b/patches/webrtc/.patches @@ -0,0 +1,2 @@ +merge_m86_-_fix_race_with_sctptransport_destruction_and_usrsctp.patch +merge_m86_-_reland_fix_race_between_destroying_sctptransport_and.patch diff --git a/patches/webrtc/merge_m86_-_fix_race_with_sctptransport_destruction_and_usrsctp.patch b/patches/webrtc/merge_m86_-_fix_race_with_sctptransport_destruction_and_usrsctp.patch new file mode 100644 index 0000000000000..7b4bc9476b85d --- /dev/null +++ b/patches/webrtc/merge_m86_-_fix_race_with_sctptransport_destruction_and_usrsctp.patch @@ -0,0 +1,246 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taylor Brandstetter +Date: Tue, 13 Apr 2021 16:11:47 -0700 +Subject: - Fix race with SctpTransport destruction and usrsctp timer thread. + +The race occurs if the transport is being destroyed at the same time as +a callback occurs on the usrsctp timer thread (for example, for a +retransmission). Fixed by slightly extending the scope of mutex +acquisition to include posting a task to the network thread, where it's +safe to do further work. + +Bug: chromium:1162424 +Change-Id: Ia25c96fa51cd4ba2d8690ba03de8af9e9f1605ea +Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/202560 +Reviewed-by: Harald Alvestrand +Commit-Queue: Taylor +Cr-Original-Commit-Position: refs/heads/master@{#33048} +No-Try: True +No-Presubmit: True +Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/215101 +Reviewed-by: Mirko Bonadei +Cr-Commit-Position: refs/branch-heads/4240@{#18} +Cr-Branched-From: 93a9d19d4eb53b3f4fb4d22e6c54f2e2824437eb-refs/heads/master@{#31969} + +diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc +index a74c66431c1625bfa0fb9b6b22d504afb48b0d7f..f97568e95c8068e47fa902546c27851475348c87 100644 +--- a/media/sctp/sctp_transport.cc ++++ b/media/sctp/sctp_transport.cc +@@ -27,6 +27,7 @@ constexpr int kSctpSuccessReturn = 1; + #include + #include + ++#include + #include + #include + +@@ -79,58 +80,8 @@ enum { + PPID_TEXT_LAST = 51 + }; + +-// Maps SCTP transport ID to SctpTransport object, necessary in send threshold +-// callback and outgoing packet callback. +-// TODO(crbug.com/1076703): Remove once the underlying problem is fixed or +-// workaround is provided in usrsctp. +-class SctpTransportMap { +- public: +- SctpTransportMap() = default; +- +- // Assigns a new unused ID to the following transport. +- uintptr_t Register(cricket::SctpTransport* transport) { +- webrtc::MutexLock lock(&lock_); +- // usrsctp_connect fails with a value of 0... +- if (next_id_ == 0) { +- ++next_id_; +- } +- // In case we've wrapped around and need to find an empty spot from a +- // removed transport. Assumes we'll never be full. +- while (map_.find(next_id_) != map_.end()) { +- ++next_id_; +- if (next_id_ == 0) { +- ++next_id_; +- } +- }; +- map_[next_id_] = transport; +- return next_id_++; +- } +- +- // Returns true if found. +- bool Deregister(uintptr_t id) { +- webrtc::MutexLock lock(&lock_); +- return map_.erase(id) > 0; +- } +- +- cricket::SctpTransport* Retrieve(uintptr_t id) const { +- webrtc::MutexLock lock(&lock_); +- auto it = map_.find(id); +- if (it == map_.end()) { +- return nullptr; +- } +- return it->second; +- } +- +- private: +- mutable webrtc::Mutex lock_; +- +- uintptr_t next_id_ RTC_GUARDED_BY(lock_) = 0; +- std::unordered_map map_ +- RTC_GUARDED_BY(lock_); +-}; +- + // Should only be modified by UsrSctpWrapper. +-ABSL_CONST_INIT SctpTransportMap* g_transport_map_ = nullptr; ++ABSL_CONST_INIT cricket::SctpTransportMap* g_transport_map_ = nullptr; + + // Helper for logging SCTP messages. + #if defined(__GNUC__) +@@ -256,6 +207,83 @@ sctp_sendv_spa CreateSctpSendParams(const cricket::SendDataParams& params) { + + namespace cricket { + ++// Maps SCTP transport ID to SctpTransport object, necessary in send threshold ++// callback and outgoing packet callback. It also provides a facility to ++// safely post a task to an SctpTransport's network thread from another thread. ++class SctpTransportMap { ++ public: ++ SctpTransportMap() = default; ++ ++ // Assigns a new unused ID to the following transport. ++ uintptr_t Register(cricket::SctpTransport* transport) { ++ webrtc::MutexLock lock(&lock_); ++ // usrsctp_connect fails with a value of 0... ++ if (next_id_ == 0) { ++ ++next_id_; ++ } ++ // In case we've wrapped around and need to find an empty spot from a ++ // removed transport. Assumes we'll never be full. ++ while (map_.find(next_id_) != map_.end()) { ++ ++next_id_; ++ if (next_id_ == 0) { ++ ++next_id_; ++ } ++ }; ++ map_[next_id_] = transport; ++ return next_id_++; ++ } ++ ++ // Returns true if found. ++ bool Deregister(uintptr_t id) { ++ webrtc::MutexLock lock(&lock_); ++ return map_.erase(id) > 0; ++ } ++ ++ // Must be called on the transport's network thread to protect against ++ // simultaneous deletion/deregistration of the transport; if that's not ++ // guaranteed, use ExecuteWithLock. ++ SctpTransport* Retrieve(uintptr_t id) const { ++ webrtc::MutexLock lock(&lock_); ++ SctpTransport* transport = RetrieveWhileHoldingLock(id); ++ if (transport) { ++ RTC_DCHECK_RUN_ON(transport->network_thread()); ++ } ++ return transport; ++ } ++ ++ // Posts |action| to the network thread of the transport identified by |id| ++ // and returns true if found, all while holding a lock to protect against the ++ // transport being simultaneously deleted/deregistered, or returns false if ++ // not found. ++ bool PostToTransportThread(uintptr_t id, ++ std::function action) const { ++ webrtc::MutexLock lock(&lock_); ++ SctpTransport* transport = RetrieveWhileHoldingLock(id); ++ if (!transport) { ++ return false; ++ } ++ transport->invoker_.AsyncInvoke( ++ RTC_FROM_HERE, transport->network_thread_, [transport, action]() { ++ action(transport); }); ++ return true; ++ } ++ ++ private: ++ SctpTransport* RetrieveWhileHoldingLock(uintptr_t id) const ++ RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_) { ++ auto it = map_.find(id); ++ if (it == map_.end()) { ++ return nullptr; ++ } ++ return it->second; ++ } ++ ++ mutable webrtc::Mutex lock_; ++ ++ uintptr_t next_id_ RTC_GUARDED_BY(lock_) = 0; ++ std::unordered_map map_ RTC_GUARDED_BY(lock_); ++}; ++ + // Handles global init/deinit, and mapping from usrsctp callbacks to + // SctpTransport calls. + class SctpTransport::UsrSctpWrapper { +@@ -357,14 +385,6 @@ class SctpTransport::UsrSctpWrapper { + << "OnSctpOutboundPacket called after usrsctp uninitialized?"; + return EINVAL; + } +- SctpTransport* transport = +- g_transport_map_->Retrieve(reinterpret_cast(addr)); +- if (!transport) { +- RTC_LOG(LS_ERROR) +- << "OnSctpOutboundPacket: Failed to get transport for socket ID " +- << addr; +- return EINVAL; +- } + RTC_LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" + "addr: " + << addr << "; length: " << length +@@ -372,13 +392,23 @@ class SctpTransport::UsrSctpWrapper { + << "; set_df: " << rtc::ToHex(set_df); + + VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND); ++ + // Note: We have to copy the data; the caller will delete it. + rtc::CopyOnWriteBuffer buf(reinterpret_cast(data), length); +- // TODO(deadbeef): Why do we need an AsyncInvoke here? We're already on the +- // right thread and don't need to unwind the stack. +- transport->invoker_.AsyncInvoke( +- RTC_FROM_HERE, transport->network_thread_, +- rtc::Bind(&SctpTransport::OnPacketFromSctpToNetwork, transport, buf)); ++ ++ // PostsToTransportThread protects against the transport being ++ // simultaneously deregistered/deleted, since this callback may come from ++ // the SCTP timer thread and thus race with the network thread. ++ bool found = g_transport_map_->PostToTransportThread( ++ reinterpret_cast(addr), [buf](SctpTransport* transport) { ++ transport->OnPacketFromSctpToNetwork(buf); ++ }); ++ if (!found) { ++ RTC_LOG(LS_ERROR) ++ << "OnSctpOutboundPacket: Failed to get transport for socket ID " ++ << addr; ++ return EINVAL; ++ } + return 0; + } + +diff --git a/media/sctp/sctp_transport.h b/media/sctp/sctp_transport.h +index 54542af6b3c9664c7af9425b174f9c45a37f3b4f..7aeb6e01bd9a41c903c5ffa08c44b193957f55c4 100644 +--- a/media/sctp/sctp_transport.h ++++ b/media/sctp/sctp_transport.h +@@ -281,6 +281,8 @@ class SctpTransport : public SctpTransportInternal, + // various callbacks. + uintptr_t id_ = 0; + ++ friend class SctpTransportMap; ++ + RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport); + }; + +@@ -299,6 +301,8 @@ class SctpTransportFactory : public webrtc::SctpTransportFactoryInterface { + rtc::Thread* network_thread_; + }; + ++class SctpTransportMap; ++ + } // namespace cricket + + #endif // MEDIA_SCTP_SCTP_TRANSPORT_H_ diff --git a/patches/webrtc/merge_m86_-_reland_fix_race_between_destroying_sctptransport_and.patch b/patches/webrtc/merge_m86_-_reland_fix_race_between_destroying_sctptransport_and.patch new file mode 100644 index 0000000000000..98164cfb4f460 --- /dev/null +++ b/patches/webrtc/merge_m86_-_reland_fix_race_between_destroying_sctptransport_and.patch @@ -0,0 +1,454 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taylor Brandstetter +Date: Wed, 14 Apr 2021 11:09:16 -0700 +Subject: - Reland "Fix race between destroying SctpTransport and receiving + notification on timer thread." + +This reverts commit 8a38b1cf681cd77f0d59a68fb45d8dedbd7d4cee. + +Reason for reland: Problem was identified; has something to do with +the unique_ptr with the custom deleter. + +Original change's description: +> Revert "Fix race between destroying SctpTransport and receiving notification on timer thread." +> +> This reverts commit a88fe7be146b9b85575504d4d5193c007f2e3de4. +> +> Reason for revert: Breaks downstream test, still investigating. +> +> Original change's description: +> > Fix race between destroying SctpTransport and receiving notification on timer thread. +> > +> > This gets rid of the SctpTransportMap::Retrieve method and forces +> > everything to go through PostToTransportThread, which behaves safely +> > with relation to the transport's destruction. +> > +> > Bug: webrtc:12467 +> > Change-Id: Id4a723c2c985be2a368d2cc5c5e62deb04c509ab +> > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/208800 +> > Reviewed-by: Niels Moller +> > Commit-Queue: Taylor +> > Cr-Commit-Position: refs/heads/master@{#33364} +> +> TBR=nisse@webrtc.org +> +> Bug: webrtc:12467 +> Change-Id: Ib5d815a2cbca4feb25f360bff7ed62c02d1910a0 +> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/209820 +> Reviewed-by: Taylor +> Commit-Queue: Taylor +> Cr-Commit-Position: refs/heads/master@{#33386} + +TBR=nisse@webrtc.org + +Bug: webrtc:12467 +Change-Id: I5f9fcd6df7a211e6edfa64577fc953833f4d9b79 +Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/210040 +Reviewed-by: Niels Moller +Reviewed-by: Florent Castelli +Commit-Queue: Taylor +Cr-Original-Commit-Position: refs/heads/master@{#33427} +No-Try: True +No-Presubmit: True +Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/215060 +Reviewed-by: Taylor +Commit-Queue: Mirko Bonadei +Cr-Commit-Position: refs/branch-heads/4240@{#19} +Cr-Branched-From: 93a9d19d4eb53b3f4fb4d22e6c54f2e2824437eb-refs/heads/master@{#31969} + +diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc +index f97568e95c8068e47fa902546c27851475348c87..a48d97ab75bcf171d8c08d42f8a55a1e5d813249 100644 +--- a/media/sctp/sctp_transport.cc ++++ b/media/sctp/sctp_transport.cc +@@ -20,6 +20,7 @@ enum PreservedErrno { + // Successful return value from usrsctp callbacks. Is not actually used by + // usrsctp, but all example programs for usrsctp use 1 as their return value. + constexpr int kSctpSuccessReturn = 1; ++constexpr int kSctpErrorReturn = 0; + + } // namespace + +@@ -27,7 +28,6 @@ constexpr int kSctpSuccessReturn = 1; + #include + #include + +-#include + #include + #include + +@@ -83,6 +83,21 @@ enum { + // Should only be modified by UsrSctpWrapper. + ABSL_CONST_INIT cricket::SctpTransportMap* g_transport_map_ = nullptr; + ++// Helper that will call C's free automatically. ++// TODO(b/181900299): Figure out why unique_ptr with a custom deleter is causing ++// issues in a certain build environment. ++class AutoFreedPointer { ++ public: ++ explicit AutoFreedPointer(void* ptr) : ptr_(ptr) {} ++ AutoFreedPointer(AutoFreedPointer&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; } ++ ~AutoFreedPointer() { free(ptr_); } ++ ++ void* get() const { return ptr_; } ++ ++ private: ++ void* ptr_; ++}; ++ + // Helper for logging SCTP messages. + #if defined(__GNUC__) + __attribute__((__format__(__printf__, 1, 2))) +@@ -239,32 +254,20 @@ class SctpTransportMap { + return map_.erase(id) > 0; + } + +- // Must be called on the transport's network thread to protect against +- // simultaneous deletion/deregistration of the transport; if that's not +- // guaranteed, use ExecuteWithLock. +- SctpTransport* Retrieve(uintptr_t id) const { +- webrtc::MutexLock lock(&lock_); +- SctpTransport* transport = RetrieveWhileHoldingLock(id); +- if (transport) { +- RTC_DCHECK_RUN_ON(transport->network_thread()); +- } +- return transport; +- } +- + // Posts |action| to the network thread of the transport identified by |id| + // and returns true if found, all while holding a lock to protect against the + // transport being simultaneously deleted/deregistered, or returns false if + // not found. +- bool PostToTransportThread(uintptr_t id, +- std::function action) const { ++ template ++ bool PostToTransportThread(uintptr_t id, F action) const { + webrtc::MutexLock lock(&lock_); + SctpTransport* transport = RetrieveWhileHoldingLock(id); + if (!transport) { + return false; + } + transport->invoker_.AsyncInvoke( +- RTC_FROM_HERE, transport->network_thread_, [transport, action]() { +- action(transport); }); ++ RTC_FROM_HERE, transport->network_thread_, ++ [transport, action{std::move(action)}]() { action(transport); }); + return true; + } + +@@ -406,7 +409,7 @@ class SctpTransport::UsrSctpWrapper { + if (!found) { + RTC_LOG(LS_ERROR) + << "OnSctpOutboundPacket: Failed to get transport for socket ID " +- << addr; ++ << addr << "; possibly was already destroyed."; + return EINVAL; + } + return 0; +@@ -423,27 +426,46 @@ class SctpTransport::UsrSctpWrapper { + struct sctp_rcvinfo rcv, + int flags, + void* ulp_info) { +- SctpTransport* transport = GetTransportFromSocket(sock); +- if (!transport) { ++ AutoFreedPointer owned_data(data); ++ ++ absl::optional id = GetTransportIdFromSocket(sock); ++ if (!id) { + RTC_LOG(LS_ERROR) +- << "OnSctpInboundPacket: Failed to get transport for socket " << sock +- << "; possibly was already destroyed."; +- return 0; ++ << "OnSctpInboundPacket: Failed to get transport ID from socket " ++ << sock; ++ return kSctpErrorReturn; ++ } ++ ++ if (!g_transport_map_) { ++ RTC_LOG(LS_ERROR) ++ << "OnSctpInboundPacket called after usrsctp uninitialized?"; ++ return kSctpErrorReturn; + } +- // Sanity check that both methods of getting the SctpTransport pointer +- // yield the same result. +- RTC_CHECK_EQ(transport, static_cast(ulp_info)); +- int result = +- transport->OnDataOrNotificationFromSctp(data, length, rcv, flags); +- free(data); +- return result; ++ // PostsToTransportThread protects against the transport being ++ // simultaneously deregistered/deleted, since this callback may come from ++ // the SCTP timer thread and thus race with the network thread. ++ bool found = g_transport_map_->PostToTransportThread( ++ *id, [owned_data{std::move(owned_data)}, length, rcv, ++ flags](SctpTransport* transport) { ++ transport->OnDataOrNotificationFromSctp(owned_data.get(), length, rcv, ++ flags); ++ }); ++ if (!found) { ++ RTC_LOG(LS_ERROR) ++ << "OnSctpInboundPacket: Failed to get transport for socket ID " ++ << *id << "; possibly was already destroyed."; ++ return kSctpErrorReturn; ++ } ++ return kSctpSuccessReturn; + } + +- static SctpTransport* GetTransportFromSocket(struct socket* sock) { ++ static absl::optional GetTransportIdFromSocket( ++ struct socket* sock) { ++ absl::optional ret; + struct sockaddr* addrs = nullptr; + int naddrs = usrsctp_getladdrs(sock, 0, &addrs); + if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) { +- return nullptr; ++ return ret; + } + // usrsctp_getladdrs() returns the addresses bound to this socket, which + // contains the SctpTransport id as sconn_addr. Read the id, +@@ -452,17 +474,10 @@ class SctpTransport::UsrSctpWrapper { + // id of the transport that created them, so [0] is as good as any other. + struct sockaddr_conn* sconn = + reinterpret_cast(&addrs[0]); +- if (!g_transport_map_) { +- RTC_LOG(LS_ERROR) +- << "GetTransportFromSocket called after usrsctp uninitialized?"; +- usrsctp_freeladdrs(addrs); +- return nullptr; +- } +- SctpTransport* transport = g_transport_map_->Retrieve( +- reinterpret_cast(sconn->sconn_addr)); ++ ret = reinterpret_cast(sconn->sconn_addr); + usrsctp_freeladdrs(addrs); + +- return transport; ++ return ret; + } + + // TODO(crbug.com/webrtc/11899): This is a legacy callback signature, remove +@@ -471,14 +486,26 @@ class SctpTransport::UsrSctpWrapper { + // Fired on our I/O thread. SctpTransport::OnPacketReceived() gets + // a packet containing acknowledgments, which goes into usrsctp_conninput, + // and then back here. +- SctpTransport* transport = GetTransportFromSocket(sock); +- if (!transport) { ++ absl::optional id = GetTransportIdFromSocket(sock); ++ if (!id) { ++ RTC_LOG(LS_ERROR) ++ << "SendThresholdCallback: Failed to get transport ID from socket " ++ << sock; ++ return 0; ++ } ++ if (!g_transport_map_) { + RTC_LOG(LS_ERROR) +- << "SendThresholdCallback: Failed to get transport for socket " +- << sock << "; possibly was already destroyed."; ++ << "SendThresholdCallback called after usrsctp uninitialized?"; + return 0; + } +- transport->OnSendThresholdCallback(); ++ bool found = g_transport_map_->PostToTransportThread( ++ *id, ++ [](SctpTransport* transport) { transport->OnSendThresholdCallback(); }); ++ if (!found) { ++ RTC_LOG(LS_ERROR) ++ << "SendThresholdCallback: Failed to get transport for socket ID " ++ << *id << "; possibly was already destroyed."; ++ } + return 0; + } + +@@ -488,17 +515,26 @@ class SctpTransport::UsrSctpWrapper { + // Fired on our I/O thread. SctpTransport::OnPacketReceived() gets + // a packet containing acknowledgments, which goes into usrsctp_conninput, + // and then back here. +- SctpTransport* transport = GetTransportFromSocket(sock); +- if (!transport) { ++ absl::optional id = GetTransportIdFromSocket(sock); ++ if (!id) { + RTC_LOG(LS_ERROR) +- << "SendThresholdCallback: Failed to get transport for socket " +- << sock << "; possibly was already destroyed."; ++ << "SendThresholdCallback: Failed to get transport ID from socket " ++ << sock; + return 0; + } +- // Sanity check that both methods of getting the SctpTransport pointer +- // yield the same result. +- RTC_CHECK_EQ(transport, static_cast(ulp_info)); +- transport->OnSendThresholdCallback(); ++ if (!g_transport_map_) { ++ RTC_LOG(LS_ERROR) ++ << "SendThresholdCallback called after usrsctp uninitialized?"; ++ return 0; ++ } ++ bool found = g_transport_map_->PostToTransportThread( ++ *id, ++ [](SctpTransport* transport) { transport->OnSendThresholdCallback(); }); ++ if (!found) { ++ RTC_LOG(LS_ERROR) ++ << "SendThresholdCallback: Failed to get transport for socket ID " ++ << *id << "; possibly was already destroyed."; ++ } + return 0; + } + }; +@@ -1149,24 +1185,25 @@ void SctpTransport::OnPacketFromSctpToNetwork( + rtc::PacketOptions(), PF_NORMAL); + } + +-int SctpTransport::InjectDataOrNotificationFromSctpForTesting( ++void SctpTransport::InjectDataOrNotificationFromSctpForTesting( + void* data, + size_t length, + struct sctp_rcvinfo rcv, + int flags) { +- return OnDataOrNotificationFromSctp(data, length, rcv, flags); ++ OnDataOrNotificationFromSctp(data, length, rcv, flags); + } + +-int SctpTransport::OnDataOrNotificationFromSctp(void* data, +- size_t length, +- struct sctp_rcvinfo rcv, +- int flags) { ++void SctpTransport::OnDataOrNotificationFromSctp(void* data, ++ size_t length, ++ struct sctp_rcvinfo rcv, ++ int flags) { ++ RTC_DCHECK_RUN_ON(network_thread_); + // If data is NULL, the SCTP association has been closed. + if (!data) { + RTC_LOG(LS_INFO) << debug_name_ + << "->OnDataOrNotificationFromSctp(...): " + "No data; association closed."; +- return kSctpSuccessReturn; ++ return; + } + + // Handle notifications early. +@@ -1179,13 +1216,10 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data, + << "->OnDataOrNotificationFromSctp(...): SCTP notification" + << " length=" << length; + +- // Copy and dispatch asynchronously + rtc::CopyOnWriteBuffer notification(reinterpret_cast(data), + length); +- invoker_.AsyncInvoke( +- RTC_FROM_HERE, network_thread_, +- rtc::Bind(&SctpTransport::OnNotificationFromSctp, this, notification)); +- return kSctpSuccessReturn; ++ OnNotificationFromSctp(notification); ++ return; + } + + // Log data chunk +@@ -1203,7 +1237,7 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data, + // Unexpected PPID, dropping + RTC_LOG(LS_ERROR) << "Received an unknown PPID " << ppid + << " on an SCTP packet. Dropping."; +- return kSctpSuccessReturn; ++ return; + } + + // Expect only continuation messages belonging to the same SID. The SCTP +@@ -1239,7 +1273,7 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data, + if (partial_incoming_message_.size() < kSctpSendBufferSize) { + // We still have space in the buffer. Continue buffering chunks until + // the message is complete before handing it out. +- return kSctpSuccessReturn; ++ return; + } else { + // The sender is exceeding the maximum message size that we announced. + // Spit out a warning but still hand out the partial message. Note that +@@ -1253,17 +1287,9 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data, + } + } + +- // Dispatch the complete message. +- // The ownership of the packet transfers to |invoker_|. Using +- // CopyOnWriteBuffer is the most convenient way to do this. +- invoker_.AsyncInvoke( +- RTC_FROM_HERE, network_thread_, +- rtc::Bind(&SctpTransport::OnDataFromSctpToTransport, this, params, +- partial_incoming_message_)); +- +- // Reset the message buffer ++ // Dispatch the complete message and reset the message buffer. ++ OnDataFromSctpToTransport(params, partial_incoming_message_); + partial_incoming_message_.Clear(); +- return kSctpSuccessReturn; + } + + void SctpTransport::OnDataFromSctpToTransport( +diff --git a/media/sctp/sctp_transport.h b/media/sctp/sctp_transport.h +index 7aeb6e01bd9a41c903c5ffa08c44b193957f55c4..2e31718b0d07b78f4adf1f7339b6718d7b5268bb 100644 +--- a/media/sctp/sctp_transport.h ++++ b/media/sctp/sctp_transport.h +@@ -96,11 +96,10 @@ class SctpTransport : public SctpTransportInternal, + void set_debug_name_for_testing(const char* debug_name) override { + debug_name_ = debug_name; + } +- int InjectDataOrNotificationFromSctpForTesting(void* data, +- size_t length, +- struct sctp_rcvinfo rcv, +- int flags); +- ++ void InjectDataOrNotificationFromSctpForTesting(void* data, ++ size_t length, ++ struct sctp_rcvinfo rcv, ++ int flags); + // Exposed to allow Post call from c-callbacks. + // TODO(deadbeef): Remove this or at least make it return a const pointer. + rtc::Thread* network_thread() const { return network_thread_; } +@@ -180,12 +179,12 @@ class SctpTransport : public SctpTransportInternal, + // Called using |invoker_| to send packet on the network. + void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer); + +- // Called on the SCTP thread. ++ // Called on the network thread. + // Flags are standard socket API flags (RFC 6458). +- int OnDataOrNotificationFromSctp(void* data, +- size_t length, +- struct sctp_rcvinfo rcv, +- int flags); ++ void OnDataOrNotificationFromSctp(void* data, ++ size_t length, ++ struct sctp_rcvinfo rcv, ++ int flags); + // Called using |invoker_| to decide what to do with the data. + void OnDataFromSctpToTransport(const ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& buffer); +diff --git a/media/sctp/sctp_transport_unittest.cc b/media/sctp/sctp_transport_unittest.cc +index 46fbbc8f13b87437c4f628342f3dbf39f00b44e5..b15a72bc83a1dbb3887f5caf8fb5c602235019f4 100644 +--- a/media/sctp/sctp_transport_unittest.cc ++++ b/media/sctp/sctp_transport_unittest.cc +@@ -282,8 +282,8 @@ TEST_F(SctpTransportTest, MessageInterleavedWithNotification) { + meta.rcv_tsn = 42; + meta.rcv_cumtsn = 42; + chunk.SetData("meow?", 5); +- EXPECT_EQ(1, transport1->InjectDataOrNotificationFromSctpForTesting( +- chunk.data(), chunk.size(), meta, 0)); ++ transport1->InjectDataOrNotificationFromSctpForTesting(chunk.data(), ++ chunk.size(), meta, 0); + + // Inject a notification in between chunks. + union sctp_notification notification; +@@ -292,15 +292,15 @@ TEST_F(SctpTransportTest, MessageInterleavedWithNotification) { + notification.sn_header.sn_type = SCTP_PEER_ADDR_CHANGE; + notification.sn_header.sn_flags = 0; + notification.sn_header.sn_length = sizeof(notification); +- EXPECT_EQ(1, transport1->InjectDataOrNotificationFromSctpForTesting( +- ¬ification, sizeof(notification), {0}, MSG_NOTIFICATION)); ++ transport1->InjectDataOrNotificationFromSctpForTesting( ++ ¬ification, sizeof(notification), {0}, MSG_NOTIFICATION); + + // Inject chunk 2/2 + meta.rcv_tsn = 42; + meta.rcv_cumtsn = 43; + chunk.SetData(" rawr!", 6); +- EXPECT_EQ(1, transport1->InjectDataOrNotificationFromSctpForTesting( +- chunk.data(), chunk.size(), meta, MSG_EOR)); ++ transport1->InjectDataOrNotificationFromSctpForTesting( ++ chunk.data(), chunk.size(), meta, MSG_EOR); + + // Expect the message to contain both chunks. + EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "meow? rawr!"), kDefaultTimeout); diff --git a/script/doc-only-change.js b/script/doc-only-change.js index 39731825964fd..dd052f0333a2f 100644 --- a/script/doc-only-change.js +++ b/script/doc-only-change.js @@ -30,19 +30,22 @@ async function checkIfDocOnlyChange () { } } } - const filesChanged = await octokit.pulls.listFiles({ - owner: 'electron', repo: 'electron', pull_number: pullRequestNumber - }); + const filesChanged = await octokit.paginate(octokit.pulls.listFiles.endpoint.merge({ + owner: 'electron', + repo: 'electron', + pull_number: pullRequestNumber, + per_page: 100 + })); - console.log('Changed Files:', filesChanged.data.map(fileInfo => fileInfo.filename)); + console.log('Changed Files:', filesChanged.map(fileInfo => fileInfo.filename)); - const nonDocChange = filesChanged.data.find((fileInfo) => { + const nonDocChange = filesChanged.find((fileInfo) => { const fileDirs = fileInfo.filename.split('/'); if (fileDirs[0] !== 'docs') { return true; } }); - if (nonDocChange || filesChanged.data.length === 0) { + if (nonDocChange || filesChanged.length === 0) { process.exit(1); } else { process.exit(0); diff --git a/script/lib/util.py b/script/lib/util.py index c717ab989d16b..1f02409f40251 100644 --- a/script/lib/util.py +++ b/script/lib/util.py @@ -69,9 +69,6 @@ def scoped_env(key, value): def download(text, url, path): safe_mkdir(os.path.dirname(path)) with open(path, 'wb') as local_file: - if hasattr(ssl, '_create_unverified_context'): - ssl._create_default_https_context = ssl._create_unverified_context - print("Downloading %s to %s" % (url, path)) web_file = urlopen(url) info = web_file.info() diff --git a/script/lib/utils.js b/script/lib/utils.js index a0c869530d083..55f678b43ed1c 100644 --- a/script/lib/utils.js +++ b/script/lib/utils.js @@ -6,6 +6,9 @@ const ELECTRON_DIR = path.resolve(__dirname, '..', '..'); const SRC_DIR = path.resolve(ELECTRON_DIR, '..'); const RELEASE_BRANCH_PATTERN = /(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))/; +// TODO(main-migration): Simplify once main branch is renamed +const MAIN_BRANCH_PATTERN = /^(main|master)$/; +const ORIGIN_MAIN_BRANCH_PATTERN = /^origin\/(main|master)$/; require('colors'); const pass = '✓'.green; @@ -73,7 +76,7 @@ async function handleGitCall (args, gitDir) { async function getCurrentBranch (gitDir) { let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir); - if (branch !== 'master' && !RELEASE_BRANCH_PATTERN.test(branch)) { + if (!MAIN_BRANCH_PATTERN.test(branch) && !RELEASE_BRANCH_PATTERN.test(branch)) { const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir); const branches = (await handleGitCall([ 'branch', @@ -82,7 +85,7 @@ async function getCurrentBranch (gitDir) { '--remote' ], gitDir)).split('\n'); - branch = branches.filter(b => b.trim() === 'master' || b.trim() === 'origin/master' || RELEASE_BRANCH_PATTERN.test(b.trim()))[0]; + branch = branches.find(b => MAIN_BRANCH_PATTERN.test(b.trim()) || ORIGIN_MAIN_BRANCH_PATTERN.test(b.trim()) || RELEASE_BRANCH_PATTERN.test(b.trim())); if (!branch) { console.log(`${fail} no release branch exists for this ref`); process.exit(1); diff --git a/script/release/ci-release-build.js b/script/release/ci-release-build.js index 511fe7cb5b366..2c7a57996e7f5 100644 --- a/script/release/ci-release-build.js +++ b/script/release/ci-release-build.js @@ -230,16 +230,20 @@ async function callAppVeyor (targetBranch, job, options) { accountName: 'electron-bot', projectSlug: appVeyorJobs[job], branch: targetBranch, + commitId: options.commit || undefined, environmentVariables }), method: 'POST' }; jobRequestedCount++; - const appVeyorResponse = await makeRequest(requestOpts, true).catch(err => { - console.log('Error calling AppVeyor:', err); - }); - const buildUrl = `https://ci.appveyor.com/project/electron-bot/${appVeyorJobs[job]}/build/${appVeyorResponse.version}`; - console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`); + + try { + const { version } = await makeRequest(requestOpts, true); + const buildUrl = `https://ci.appveyor.com/project/electron-bot/${appVeyorJobs[job]}/build/${version}`; + console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`); + } catch (err) { + console.log('Could not call AppVeyor: ', err); + } } function buildCircleCI (targetBranch, options) { @@ -267,6 +271,7 @@ async function buildVSTS (targetBranch, options) { let vstsURL = VSTS_URL; let vstsToken = process.env.VSTS_TOKEN; + assert(vstsToken, `${options.ci} requires the $VSTS_TOKEN environment variable to be provided`); if (options.ci === 'DevOps') { vstsURL = DEVOPS_URL; vstsToken = process.env.DEVOPS_TOKEN; @@ -281,12 +286,16 @@ async function buildVSTS (targetBranch, options) { 'Content-Type': 'application/json' } }; + jobRequestedCount++; - const vstsResponse = await makeRequest(requestOpts, true).catch(err => { - console.log('Error calling VSTS to get build definitions:', err); - }); - const buildToRun = vstsResponse.value.find(build => build.name === options.job); - callVSTSBuild(buildToRun, targetBranch, environmentVariables, vstsURL, vstsToken); + + try { + const vstsResponse = await makeRequest(requestOpts, true); + const buildToRun = vstsResponse.value.find(build => build.name === options.job); + callVSTSBuild(buildToRun, targetBranch, environmentVariables, vstsURL, vstsToken); + } catch (err) { + console.log('Problem calling VSTS to get build definitions: ', err); + } } async function callVSTSBuild (build, targetBranch, environmentVariables, vstsURL, vstsToken) { @@ -310,10 +319,13 @@ async function callVSTSBuild (build, targetBranch, environmentVariables, vstsURL body: JSON.stringify(buildBody), method: 'POST' }; - const vstsResponse = await makeRequest(requestOpts, true).catch(err => { - console.log(`Error calling VSTS for job ${build.name}`, err); - }); - console.log(`VSTS release build request for ${build.name} successful. Check ${vstsResponse._links.web.href} for status.`); + + try { + const { _links } = await makeRequest(requestOpts, true); + console.log(`VSTS release build request for ${build.name} successful. Check ${_links.web.href} for status.`); + } catch (err) { + console.log(`Could not call VSTS for job ${build.name}: `, err); + } } function runRelease (targetBranch, options) { @@ -354,7 +366,7 @@ if (require.main === module) { if (args._.length < 1) { console.log(`Trigger CI to build release builds of electron. Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|VSTS|DevOps] - [--ghRelease] [--armTest] [--circleBuildNum=xxx] [--appveyorJobId=xxx] TARGET_BRANCH + [--ghRelease] [--armTest] [--circleBuildNum=xxx] [--appveyorJobId=xxx] [--commit=sha] TARGET_BRANCH `); process.exit(0); } diff --git a/script/release/notes/index.js b/script/release/notes/index.js index 8cb7650fae1a6..644b76490fe79 100755 --- a/script/release/notes/index.js +++ b/script/release/notes/index.js @@ -8,6 +8,11 @@ const semver = require('semver'); const { ELECTRON_DIR } = require('../../lib/utils'); const notesGenerator = require('./notes.js'); +const { Octokit } = require('@octokit/rest'); +const octokit = new Octokit({ + auth: process.env.ELECTRON_GITHUB_TOKEN +}); + const semverify = version => version.replace(/^origin\//, '').replace(/[xy]/g, '0').replace(/-/g, '.'); const runGit = async (args) => { @@ -37,13 +42,17 @@ const getTagsOf = async (point) => { }; const getTagsOnBranch = async (point) => { - const masterTags = await getTagsOf('master'); - if (point === 'master') { - return masterTags; + const { data: { default_branch: defaultBranch } } = await octokit.repos.get({ + owner: 'electron', + repo: 'electron' + }); + const mainTags = await getTagsOf(defaultBranch); + if (point === defaultBranch) { + return mainTags; } - const masterTagsSet = new Set(masterTags); - return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag)); + const mainTagsSet = new Set(mainTags); + return (await getTagsOf(point)).filter(tag => !mainTagsSet.has(tag)); }; const getBranchOf = async (point) => { @@ -66,7 +75,8 @@ const getAllBranches = async () => { return branches.split('\n') .map(branch => branch.trim()) .filter(branch => !!branch) - .filter(branch => branch !== 'origin/HEAD -> origin/master') + // TODO(main-migration): Simplify once branch rename is complete. + .filter(branch => branch !== 'origin/HEAD -> origin/master' && branch !== 'origin/HEAD -> origin/main') .sort(); } catch (err) { console.error('Failed to fetch all branches'); diff --git a/script/release/prepare-release.js b/script/release/prepare-release.js index bda57bdebc945..0a6309e85f000 100755 --- a/script/release/prepare-release.js +++ b/script/release/prepare-release.js @@ -112,7 +112,7 @@ async function createRelease (branchToTarget, isBeta) { name: `electron ${newVersion}`, body: releaseBody, prerelease: releaseIsPrelease, - target_commitish: newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget + target_commitish: newVersion.indexOf('nightly') !== -1 ? 'main' : branchToTarget }).catch(err => { console.log(`${fail} Error creating new release: `, err); process.exit(1); @@ -181,7 +181,7 @@ async function promptForVersion (version) { }); } -// function to determine if there have been commits to master since the last release +// function to determine if there have been commits to main since the last release async function changesToRelease () { const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g'); const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR); diff --git a/script/release/publish-to-npm.js b/script/release/publish-to-npm.js index 06686ec29e170..61cdffe380daf 100644 --- a/script/release/publish-to-npm.js +++ b/script/release/publish-to-npm.js @@ -111,8 +111,9 @@ new Promise((resolve, reject) => { const currentBranch = await getCurrentBranch(); if (release.tag_name.indexOf('nightly') > 0) { - if (currentBranch === 'master') { - // Nightlies get published to their own module, so master nightlies should be tagged as latest + // TODO(main-migration): Simplify once main branch is renamed. + if (currentBranch === 'master' || currentBranch === 'main') { + // Nightlies get published to their own module, so they should be tagged as latest npmTag = 'latest'; } else { npmTag = `nightly-${currentBranch}`; @@ -127,10 +128,10 @@ new Promise((resolve, reject) => { JSON.stringify(currentJson, null, 2) ); } else { - if (currentBranch === 'master') { - // This should never happen, master releases should be nightly releases + if (currentBranch === 'master' || currentBranch === 'main') { + // This should never happen, main releases should be nightly releases // this is here just-in-case - npmTag = 'master'; + throw new Error('Unreachable release phase, can\'t tag a non-nightly release on the main branch'); } else if (!release.prerelease) { // Tag the release with a `2-0-x` style tag npmTag = currentBranch; @@ -152,7 +153,13 @@ new Promise((resolve, reject) => { resolve(tarballPath); }); }) - .then((tarballPath) => childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`)) + .then((tarballPath) => { + const existingVersionJSON = childProcess.execSync(`npm view electron@${rootPackageJson.version} --json`).toString('utf-8'); + // It's possible this is a re-run and we already have published the package, if not we just publish like normal + if (!existingVersionJSON) { + childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`); + } + }) .then(() => { const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString()); const localVersion = rootPackageJson.version; diff --git a/script/release/release-artifact-cleanup.js b/script/release/release-artifact-cleanup.js index d129207f3fef0..e849ca149f186 100755 --- a/script/release/release-artifact-cleanup.js +++ b/script/release/release-artifact-cleanup.js @@ -27,6 +27,7 @@ function getLastBumpCommit (tag) { async function revertBumpCommit (tag) { const branch = await getCurrentBranch(); const commitToRevert = getLastBumpCommit(tag).hash; + await GitProcess.exec(['pull', '--rebase']); await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR); const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR); if (pushDetails.exitCode === 0) { diff --git a/script/release/uploaders/upload-node-checksums.py b/script/release/uploaders/upload-node-checksums.py index 1774308320c10..41d509f2a6f72 100755 --- a/script/release/uploaders/upload-node-checksums.py +++ b/script/release/uploaders/upload-node-checksums.py @@ -88,7 +88,7 @@ def create_checksum(algorithm, directory, filename, files): lines = [] for path in files: h = hashlib.new(algorithm) - with open(path, 'r') as f: + with open(path, 'rb') as f: h.update(f.read()) lines.append(h.hexdigest() + ' ' + os.path.relpath(path, directory)) diff --git a/script/release/version-utils.js b/script/release/version-utils.js index 58a70c0097150..68974f49ee4eb 100644 --- a/script/release/version-utils.js +++ b/script/release/version-utils.js @@ -65,8 +65,9 @@ async function nextNightly (v) { const pre = `nightly.${getCurrentDate()}`; const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim(); - if (branch === 'master') { - next = semver.inc(await getLastMajorForMaster(), 'major'); + // TODO(main-migration): Simplify once main branch is renamed + if (branch === 'master' || branch === 'main') { + next = semver.inc(await getLastMajorForMain(), 'major'); } else if (isStable(v)) { next = semver.inc(next, 'patch'); } @@ -74,7 +75,7 @@ async function nextNightly (v) { return `${next}-${pre}`; } -async function getLastMajorForMaster () { +async function getLastMajorForMain () { let branchNames; const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR); if (result.exitCode === 0) { diff --git a/shell/app/node_main.cc b/shell/app/node_main.cc index f9da61b9f942b..9de37ace048f8 100644 --- a/shell/app/node_main.cc +++ b/shell/app/node_main.cc @@ -279,16 +279,8 @@ int NodeMain(int argc, char* argv[]) { node::ResetStdio(); node::Stop(env); - env->stop_sub_worker_contexts(); - env->RunCleanup(); - - node::RunAtExit(env); node::FreeEnvironment(env); node::FreeIsolateData(isolate_data); - - gin_env.platform()->DrainTasks(isolate); - gin_env.platform()->CancelPendingDelayedTasks(isolate); - gin_env.platform()->UnregisterIsolate(isolate); } // According to "src/gin/shell/gin_main.cc": diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index 18bb2e90358b5..a623d63f94b1b 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/stl_util.h" +#include "content/common/url_schemes.h" #include "content/public/browser/child_process_security_policy.h" #include "gin/object_template_builder.h" #include "shell/browser/browser.h" @@ -124,6 +125,13 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, } if (custom_scheme.options.allowServiceWorkers) { service_worker_schemes.push_back(custom_scheme.scheme); + // There is no API to add service worker scheme, but there is an API to + // return const reference to the schemes vector. + // If in future the API is changed to return a copy instead of reference, + // the compilation will fail, and we should add a patch at that time. + auto& mutable_schemes = const_cast&>( + content::GetServiceWorkerSchemes()); + mutable_schemes.push_back(custom_scheme.scheme); } if (custom_scheme.options.stream) { g_streaming_schemes.push_back(custom_scheme.scheme); diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 809a3fd58fc04..db14e80e37d08 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1306,9 +1306,10 @@ void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories( context, ukm_source_id, false /* we don't support extensions::WebViewGuest */)); #endif + // Always allow navigating to file:// URLs. auto* protocol_registry = ProtocolRegistry::FromBrowserContext(context); - protocol_registry->RegisterURLLoaderFactories( - URLLoaderFactoryType::kNavigation, factories); + protocol_registry->RegisterURLLoaderFactories(factories, + true /* allow_file_access */); } void ElectronBrowserClient:: @@ -1317,8 +1318,10 @@ void ElectronBrowserClient:: NonNetworkURLLoaderFactoryMap* factories) { auto* protocol_registry = ProtocolRegistry::FromBrowserContext(browser_context); - protocol_registry->RegisterURLLoaderFactories( - URLLoaderFactoryType::kWorkerMainResource, factories); + // Workers are not allowed to request file:// URLs, there is no particular + // reason for it, and we could consider supporting it in future. + protocol_registry->RegisterURLLoaderFactories(factories, + false /* allow_file_access */); } #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) @@ -1384,16 +1387,29 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( int render_frame_id, NonNetworkURLLoaderFactoryDeprecatedMap* uniquely_owned_factories, NonNetworkURLLoaderFactoryMap* factories) { + auto* render_process_host = + content::RenderProcessHost::FromID(render_process_id); + DCHECK(render_process_host); + if (!render_process_host || !render_process_host->GetBrowserContext()) + return; + content::RenderFrameHost* frame_host = content::RenderFrameHost::FromID(render_process_id, render_frame_id); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(frame_host); + // Allow accessing file:// subresources from non-file protocols if web + // security is disabled. + bool allow_file_access = false; if (web_contents) { - ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext()) - ->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource, - factories); + const auto& web_preferences = web_contents->GetOrCreateWebPreferences(); + if (!web_preferences.web_security_enabled) + allow_file_access = true; } + + ProtocolRegistry::FromBrowserContext(render_process_host->GetBrowserContext()) + ->RegisterURLLoaderFactories(factories, allow_file_access); + #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, render_frame_id); diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index ecccc69c2d4ca..2ceecad798b50 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -334,6 +334,9 @@ void ElectronBrowserMainParts::PostEarlyInitialization() { base::FeatureList::ClearInstanceForTesting(); InitializeFeatureList(); + // Initialize field trials. + InitializeFieldTrials(); + // Initialize after user script environment creation. fake_browser_process_->PostEarlyInitialization(); } diff --git a/shell/browser/electron_permission_manager.cc b/shell/browser/electron_permission_manager.cc index c06828be6c36e..b5545829e9d05 100644 --- a/shell/browser/electron_permission_manager.cc +++ b/shell/browser/electron_permission_manager.cc @@ -227,16 +227,17 @@ blink::mojom::PermissionStatus ElectronPermissionManager::GetPermissionStatus( return blink::mojom::PermissionStatus::GRANTED; } -int ElectronPermissionManager::SubscribePermissionStatusChange( +ElectronPermissionManager::SubscriptionId +ElectronPermissionManager::SubscribePermissionStatusChange( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, base::RepeatingCallback callback) { - return -1; + return SubscriptionId(); } void ElectronPermissionManager::UnsubscribePermissionStatusChange( - int subscription_id) {} + SubscriptionId subscription_id) {} bool ElectronPermissionManager::CheckPermissionWithDetails( content::PermissionType permission, diff --git a/shell/browser/electron_permission_manager.h b/shell/browser/electron_permission_manager.h index a347afd8375e7..97aade1b5dc49 100644 --- a/shell/browser/electron_permission_manager.h +++ b/shell/browser/electron_permission_manager.h @@ -90,13 +90,14 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate { content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) override; - int SubscribePermissionStatusChange( + SubscriptionId SubscribePermissionStatusChange( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, base::RepeatingCallback callback) override; - void UnsubscribePermissionStatusChange(int subscription_id) override; + void UnsubscribePermissionStatusChange( + SubscriptionId subscription_id) override; private: class PendingRequest; diff --git a/shell/browser/feature_list.cc b/shell/browser/feature_list.cc index d46da70409447..af7af1bb02975 100644 --- a/shell/browser/feature_list.cc +++ b/shell/browser/feature_list.cc @@ -9,6 +9,7 @@ #include "base/base_switches.h" #include "base/command_line.h" #include "base/feature_list.h" +#include "base/metrics/field_trial.h" #include "content/public/common/content_features.h" #include "electron/buildflags/buildflags.h" #include "media/base/media_switches.h" @@ -48,4 +49,12 @@ void InitializeFeatureList() { base::FeatureList::InitializeInstance(enable_features, disable_features); } +void InitializeFieldTrials() { + auto* cmd_line = base::CommandLine::ForCurrentProcess(); + auto force_fieldtrials = + cmd_line->GetSwitchValueASCII(::switches::kForceFieldTrials); + + base::FieldTrialList::CreateTrialsFromString(force_fieldtrials); +} + } // namespace electron diff --git a/shell/browser/feature_list.h b/shell/browser/feature_list.h index 9048c6913d4f6..3464bba213c55 100644 --- a/shell/browser/feature_list.h +++ b/shell/browser/feature_list.h @@ -7,6 +7,7 @@ namespace electron { void InitializeFeatureList(); -} +void InitializeFieldTrials(); +} // namespace electron #endif // SHELL_BROWSER_FEATURE_LIST_H_ diff --git a/shell/browser/javascript_environment.cc b/shell/browser/javascript_environment.cc index 6969b44867e93..ace9d45df6753 100644 --- a/shell/browser/javascript_environment.cc +++ b/shell/browser/javascript_environment.cc @@ -82,6 +82,9 @@ JavascriptEnvironment::JavascriptEnvironment(uv_loop_t* event_loop) } JavascriptEnvironment::~JavascriptEnvironment() { + DCHECK_NE(platform_, nullptr); + platform_->DrainTasks(isolate_); + { v8::Locker locker(isolate_); v8::HandleScope scope(isolate_); @@ -89,6 +92,9 @@ JavascriptEnvironment::~JavascriptEnvironment() { } isolate_->Exit(); g_isolate = nullptr; + + platform_->CancelPendingDelayedTasks(isolate_); + platform_->UnregisterIsolate(isolate_); } class EnabledStateObserverImpl final @@ -281,14 +287,14 @@ void JavascriptEnvironment::OnMessageLoopDestroying() { gin_helper::CleanedUpAtExit::DoCleanup(); } base::CurrentThread::Get()->RemoveTaskObserver(microtasks_runner_.get()); - platform_->DrainTasks(isolate_); - platform_->UnregisterIsolate(isolate_); } NodeEnvironment::NodeEnvironment(node::Environment* env) : env_(env) {} NodeEnvironment::~NodeEnvironment() { + auto* isolate_data = env_->isolate_data(); node::FreeEnvironment(env_); + node::FreeIsolateData(isolate_data); } } // namespace electron diff --git a/shell/browser/native_browser_view_mac.mm b/shell/browser/native_browser_view_mac.mm index 1b7984f95fd45..b88872c3d78c4 100644 --- a/shell/browser/native_browser_view_mac.mm +++ b/shell/browser/native_browser_view_mac.mm @@ -349,9 +349,7 @@ - (void)drawDebugRect:(NSRect)aRect { std::vector drag_exclude_rects; if (draggable_regions_.empty()) { - const auto bounds = GetBounds(); - drag_exclude_rects.emplace_back(bounds.x(), bounds.y(), webViewWidth, - webViewHeight); + drag_exclude_rects.emplace_back(0, 0, webViewWidth, webViewHeight); } else { drag_exclude_rects = CalculateNonDraggableRegions( DraggableRegionsToSkRegion(draggable_regions_), webViewWidth, diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 72bdc6b6a6b2a..9f657261897c6 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -143,7 +143,7 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { fullscreenable = false; #endif } - // Overriden by 'fullscreenable'. + // Overridden by 'fullscreenable'. options.Get(options::kFullScreenable, &fullscreenable); SetFullScreenable(fullscreenable); if (fullscreen) { @@ -419,11 +419,11 @@ void NativeWindow::NotifyWindowClosed() { if (is_closed_) return; - WindowList::RemoveWindow(this); - is_closed_ = true; for (NativeWindowObserver& observer : observers_) observer.OnWindowClosed(); + + WindowList::RemoveWindow(this); } void NativeWindow::NotifyWindowEndSession() { diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index d8b8d7a5ab089..3008ffd0da169 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -201,6 +201,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetTrafficLightPosition(const gfx::Point& position) = 0; virtual gfx::Point GetTrafficLightPosition() const = 0; virtual void RedrawTrafficLights() = 0; + virtual void UpdateFrame() = 0; #endif // Touchbar API diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 35171d557505a..bd7f8df5f6a05 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -8,12 +8,14 @@ #import #include +#include #include #include #include #include "base/mac/scoped_nsobject.h" #include "shell/browser/native_window.h" +#include "ui/display/display_observer.h" #include "ui/native_theme/native_theme_observer.h" #include "ui/views/controls/native/native_view_host.h" @@ -27,7 +29,9 @@ namespace electron { class RootViewMac; -class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { +class NativeWindowMac : public NativeWindow, + public ui::NativeThemeObserver, + public display::DisplayObserver { public: NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent); ~NativeWindowMac() override; @@ -129,7 +133,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { bool IsVisibleOnAllWorkspaces() override; void SetAutoHideCursor(bool auto_hide) override; - void SelectPreviousTab() override; void SelectNextTab() override; void MergeAllWindows() override; @@ -140,6 +143,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { bool SetWindowButtonVisibility(bool visible) override; void SetVibrancy(const std::string& type) override; + void UpdateFrame() override; void SetTouchBar( std::vector items) override; void RefreshTouchBarItem(const std::string& item_id) override; @@ -159,11 +163,16 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { // Custom traffic light positioning void RedrawTrafficLights() override; - void SetExitingFullScreen(bool flag); void SetTrafficLightPosition(const gfx::Point& position) override; gfx::Point GetTrafficLightPosition() const override; void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; + enum class FullScreenTransitionState { ENTERING, EXITING, NONE }; + + // Handle fullscreen transitions. + void SetFullScreenTransitionState(FullScreenTransitionState state); + void HandlePendingFullscreenTransitions(); + enum class VisualEffectState { FOLLOW_WINDOW, ACTIVE, @@ -183,13 +192,16 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { bool zoom_to_page_width() const { return zoom_to_page_width_; } bool fullscreen_window_title() const { return fullscreen_window_title_; } bool always_simple_fullscreen() const { return always_simple_fullscreen_; } - bool exiting_fullscreen() const { return exiting_fullscreen_; } protected: // views::WidgetDelegate: bool CanResize() const override; views::View* GetContentsView() override; + // display::DisplayObserver: + void OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) override; + private: // Add custom layers to the content view. void AddContentViewLayers(bool minimizable, bool closable); @@ -216,13 +228,18 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { std::unique_ptr root_view_; bool is_kiosk_ = false; - bool was_fullscreen_ = false; bool zoom_to_page_width_ = false; bool fullscreen_window_title_ = false; bool resizable_ = true; - bool exiting_fullscreen_ = false; gfx::Point traffic_light_position_; + std::queue pending_transitions_; + FullScreenTransitionState fullscreen_transition_state() const { + return fullscreen_transition_state_; + } + FullScreenTransitionState fullscreen_transition_state_ = + FullScreenTransitionState::NONE; + NSInteger attention_request_id_ = 0; // identifier from requestUserAttention // The presentation options before entering kiosk mode. diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 56449534dc0b6..945a016ee4e9c 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -38,6 +38,7 @@ #include "shell/common/process_util.h" #include "skia/ext/skia_utils_mac.h" #include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h" +#include "ui/display/screen.h" #include "ui/gfx/skia_util.h" #include "ui/gl/gpu_switching_manager.h" #include "ui/views/background.h" @@ -350,6 +351,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { NativeWindow* parent) : NativeWindow(options, parent), root_view_(new RootViewMac(this)) { ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); + display::Screen::GetScreen()->AddObserver(this); int width = 800, height = 600; options.Get(options::kWidth, &width); @@ -541,6 +543,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { void NativeWindowMac::Cleanup() { DCHECK(!IsClosed()); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); + display::Screen::GetScreen()->RemoveObserver(this); [NSEvent removeMonitor:wheel_event_monitor_]; } @@ -569,7 +572,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { // Hide the container when exiting fullscreen, otherwise traffic light buttons // jump - if (exiting_fullscreen_) { + if (fullscreen_transition_state_ == FullScreenTransitionState::EXITING) { [titleBarContainerView setHidden:YES]; return; } @@ -691,6 +694,12 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } void NativeWindowMac::Hide() { + // If a sheet is attached to the window when we call [window_ orderOut:nil], + // the sheet won't be able to show again on the same window. + // Ensure it's closed before calling [window_ orderOut:nil]. + if ([window_ attachedSheet]) + [window_ endSheet:[window_ attachedSheet]]; + if (is_modal() && parent()) { [window_ orderOut:nil]; [parent()->GetNativeWindow().GetNativeNSWindow() endSheet:window_]; @@ -713,16 +722,17 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return [window_ isVisible] && !occluded && !IsMinimized(); } -void NativeWindowMac::SetExitingFullScreen(bool flag) { - exiting_fullscreen_ = flag; -} - void NativeWindowMac::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { base::PostTask( FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&NativeWindow::RedrawTrafficLights, GetWeakPtr())); } +void NativeWindowMac::SetFullScreenTransitionState( + FullScreenTransitionState state) { + fullscreen_transition_state_ = state; +} + bool NativeWindowMac::IsEnabled() { return [window_ attachedSheet] == nil; } @@ -787,13 +797,48 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return [window_ isMiniaturized]; } +void NativeWindowMac::HandlePendingFullscreenTransitions() { + if (pending_transitions_.empty()) + return; + + bool next_transition = pending_transitions_.front(); + pending_transitions_.pop(); + SetFullScreen(next_transition); +} + void NativeWindowMac::SetFullScreen(bool fullscreen) { + // [NSWindow -toggleFullScreen] is an asynchronous operation, which means + // that it's possible to call it while a fullscreen transition is currently + // in process. This can create weird behavior (incl. phantom windows), + // so we want to schedule a transition for when the current one has completed. + if (fullscreen_transition_state() != FullScreenTransitionState::NONE) { + if (!pending_transitions_.empty()) { + bool last_pending = pending_transitions_.back(); + // Only push new transitions if they're different than the last transition + // in the queue. + if (last_pending != fullscreen) + pending_transitions_.push(fullscreen); + } else { + pending_transitions_.push(fullscreen); + } + return; + } + if (fullscreen == IsFullscreen()) return; // Take note of the current window size if (IsNormal()) original_frame_ = [window_ frame]; + + // This needs to be set here because it can be the case that + // SetFullScreen is called by a user before windowWillEnterFullScreen + // or windowWillExitFullScreen are invoked, and so a potential transition + // could be dropped. + fullscreen_transition_state_ = fullscreen + ? FullScreenTransitionState::ENTERING + : FullScreenTransitionState::EXITING; + [window_ toggleFullScreenMode:nil]; } @@ -1074,6 +1119,17 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { [window setExcludedFromWindowsMenu:excluded]; } +void NativeWindowMac::OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) { + // We only want to force screen recalibration if we're in simpleFullscreen + // mode. + if (!is_simple_fullscreen_) + return; + + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&NativeWindow::UpdateFrame, GetWeakPtr())); +} + void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) { NSWindow* window = GetNativeWindow().GetNativeNSWindow(); @@ -1180,14 +1236,11 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { NSApplicationPresentationDisableHideApplication; [NSApp setPresentationOptions:options]; is_kiosk_ = true; - was_fullscreen_ = IsFullscreen(); - if (!was_fullscreen_) - SetFullScreen(true); + SetFullScreen(true); } else if (!kiosk && is_kiosk_) { - is_kiosk_ = false; - if (!was_fullscreen_) - SetFullScreen(false); [NSApp setPresentationOptions:kiosk_options_]; + is_kiosk_ = false; + SetFullScreen(false); } } @@ -1527,7 +1580,12 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { // Make frameless Vibrant windows have rounded corners. if (!has_frame() && !is_modal()) { - CGFloat radius = 5.0f; // default corner radius + CGFloat radius; + if (@available(macOS 11.0, *)) { + radius = 9.0f; + } else { + radius = 5.0f; // smaller corner radius on older versions + } CGFloat dimension = 2 * radius + 1; NSSize size = NSMakeSize(dimension, dimension); NSImage* maskImage = [NSImage imageWithSize:size @@ -1628,6 +1686,13 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return traffic_light_position_; } +// In simpleFullScreen mode, update the frame for new bounds. +void NativeWindowMac::UpdateFrame() { + NSWindow* window = GetNativeWindow().GetNativeNSWindow(); + NSRect fullscreenFrame = [window.screen frame]; + [window setFrame:fullscreenFrame display:YES animate:YES]; +} + void NativeWindowMac::SetTouchBar( std::vector items) { if (@available(macOS 10.12.2, *)) { diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index bf27c24a5c023..ad963cc8fd9fb 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -72,19 +72,7 @@ namespace electron { -namespace { - #if defined(OS_WIN) -const LPCWSTR kUniqueTaskBarClassName = L"Shell_TrayWnd"; - -void FlipWindowStyle(HWND handle, bool on, DWORD flag) { - DWORD style = ::GetWindowLong(handle, GWL_STYLE); - if (on) - style |= flag; - else - style &= ~flag; - ::SetWindowLong(handle, GWL_STYLE, style); -} // Similar to the ones in display::win::ScreenWin, but with rounded values // These help to avoid problems that arise from unresizable windows where the @@ -98,6 +86,22 @@ gfx::Rect ScreenToDIPRect(HWND hwnd, const gfx::Rect& pixel_bounds) { return dip_rect; } +#endif + +namespace { + +#if defined(OS_WIN) +const LPCWSTR kUniqueTaskBarClassName = L"Shell_TrayWnd"; + +void FlipWindowStyle(HWND handle, bool on, DWORD flag) { + DWORD style = ::GetWindowLong(handle, GWL_STYLE); + if (on) + style |= flag; + else + style &= ~flag; + ::SetWindowLong(handle, GWL_STYLE, style); +} + gfx::Rect DIPToScreenRect(HWND hwnd, const gfx::Rect& pixel_bounds) { float scale_factor = display::win::ScreenWin::GetScaleFactorForHWND(hwnd); gfx::Rect screen_rect = ScaleToRoundedRect(pixel_bounds, scale_factor); @@ -311,6 +315,10 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, aura::Window* window = GetNativeWindow(); if (window) window->AddPreTargetHandler(this); + + // On linux after the widget is initialized we might have to force set the + // bounds if the bounds are smaller than the current display + SetBounds(gfx::Rect(GetPosition(), bounds.size()), false); #endif } @@ -423,6 +431,14 @@ void NativeWindowViews::Hide() { if (global_menu_bar_) global_menu_bar_->OnWindowUnmapped(); #endif + +#if defined(OS_WIN) + // When the window is removed from the taskbar via win.hide(), + // the thumbnail buttons need to be set up again. + // Ensure that when the window is hidden, + // the taskbar host is notified that it should re-add them. + taskbar_host_.SetThumbarButtonsAdded(false); +#endif } bool NativeWindowViews::IsVisible() { @@ -495,7 +511,7 @@ void NativeWindowViews::Maximize() { void NativeWindowViews::Unmaximize() { #if defined(OS_WIN) - if (!(::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME)) { + if (transparent()) { SetBounds(restore_bounds_, false); return; } @@ -505,21 +521,22 @@ void NativeWindowViews::Unmaximize() { } bool NativeWindowViews::IsMaximized() { - // For window without WS_THICKFRAME style, we can not call IsMaximized(). - // This path will be used for transparent windows as well. - + if (widget()->IsMaximized()) { + return true; + } else { #if defined(OS_WIN) - if (!(::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME)) { - // Compare the size of the window with the size of the display - auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( - GetNativeWindow()); - // Maximized if the window is the same dimensions and placement as the - // display - return GetBounds() == display.work_area(); - } + if (transparent()) { + // Compare the size of the window with the size of the display + auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( + GetNativeWindow()); + // Maximized if the window is the same dimensions and placement as the + // display + return GetBounds() == display.work_area(); + } #endif - return widget()->IsMaximized(); + return false; + } } void NativeWindowViews::Minimize() { diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index c2491d07e18e4..085a8b7768815 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -35,6 +35,10 @@ class WindowStateWatcher; class EventDisabler; #endif +#if defined(OS_WIN) +gfx::Rect ScreenToDIPRect(HWND hwnd, const gfx::Rect& pixel_bounds); +#endif + class NativeWindowViews : public NativeWindow, public views::WidgetObserver, public ui::EventHandler { diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 9c0e0908421af..13e369c716c25 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -149,8 +149,8 @@ std::set NativeWindowViews::forwarding_windows_; HHOOK NativeWindowViews::mouse_hook_ = NULL; void NativeWindowViews::Maximize() { - // Only use Maximize() when window has WS_THICKFRAME style - if (::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME) { + // Only use Maximize() when window is NOT transparent style + if (!transparent()) { if (IsVisible()) widget()->Maximize(); else @@ -260,11 +260,12 @@ bool NativeWindowViews::PreHandleMSG(UINT message, case WM_SIZING: { is_resizing_ = true; bool prevent_default = false; - NotifyWindowWillResize(gfx::Rect(*reinterpret_cast(l_param)), - &prevent_default); + gfx::Rect bounds = gfx::Rect(*reinterpret_cast(l_param)); + HWND hwnd = GetAcceleratedWidget(); + gfx::Rect dpi_bounds = ScreenToDIPRect(hwnd, bounds); + NotifyWindowWillResize(dpi_bounds, &prevent_default); if (prevent_default) { - ::GetWindowRect(GetAcceleratedWidget(), - reinterpret_cast(l_param)); + ::GetWindowRect(hwnd, reinterpret_cast(l_param)); return true; // Tells Windows that the Sizing is handled. } return false; @@ -288,11 +289,12 @@ bool NativeWindowViews::PreHandleMSG(UINT message, case WM_MOVING: { is_moving_ = true; bool prevent_default = false; - NotifyWindowWillMove(gfx::Rect(*reinterpret_cast(l_param)), - &prevent_default); + gfx::Rect bounds = gfx::Rect(*reinterpret_cast(l_param)); + HWND hwnd = GetAcceleratedWidget(); + gfx::Rect dpi_bounds = ScreenToDIPRect(hwnd, bounds); + NotifyWindowWillMove(dpi_bounds, &prevent_default); if (!movable_ || prevent_default) { - ::GetWindowRect(GetAcceleratedWidget(), - reinterpret_cast(l_param)); + ::GetWindowRect(hwnd, reinterpret_cast(l_param)); return true; // Tells Windows that the Move is handled. If not true, // frameless windows can be moved using // -webkit-app-region: drag elements. @@ -324,8 +326,31 @@ bool NativeWindowViews::PreHandleMSG(UINT message, GET_Y_LPARAM(l_param), &prevent_default); return prevent_default; } - default: + case WM_SYSCOMMAND: { + // Mask is needed to account for double clicking title bar to maximize + WPARAM max_mask = 0xFFF0; + if (transparent() && ((w_param & max_mask) == SC_MAXIMIZE)) { + return true; + } + return false; + } + case WM_INITMENU: { + // This is handling the scenario where the menu might get triggered by the + // user doing "alt + space" resulting in system maximization and restore + // being used on transparent windows when that does not work. + if (transparent()) { + HMENU menu = GetSystemMenu(GetAcceleratedWidget(), false); + EnableMenuItem(menu, SC_MAXIMIZE, + MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + EnableMenuItem(menu, SC_RESTORE, + MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + return true; + } + return false; + } + default: { return false; + } } } diff --git a/shell/browser/net/url_pipe_loader.cc b/shell/browser/net/url_pipe_loader.cc index f928eaa77325e..2bf28c4886941 100644 --- a/shell/browser/net/url_pipe_loader.cc +++ b/shell/browser/net/url_pipe_loader.cc @@ -87,7 +87,7 @@ void URLPipeLoader::OnDataReceived(base::StringPiece string_piece, producer_->Write( std::make_unique( string_piece, mojo::StringDataSource::AsyncWritingMode:: - STRING_STAYS_VALID_UNTIL_COMPLETION), + STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION), base::BindOnce(&URLPipeLoader::OnWrite, weak_factory_.GetWeakPtr(), std::move(resume))); } diff --git a/shell/browser/protocol_registry.cc b/shell/browser/protocol_registry.cc index bd644cf4b166d..7b5c1fa59a6b1 100644 --- a/shell/browser/protocol_registry.cc +++ b/shell/browser/protocol_registry.cc @@ -6,6 +6,7 @@ #include #include "content/public/browser/non_network_url_loader_factory_base.h" // nogncheck +#include "content/public/browser/web_contents.h" #include "shell/browser/electron_browser_context.h" #include "shell/browser/net/asar/asar_url_loader.h" #include "shell/browser/protocol_registry.h" @@ -61,22 +62,20 @@ ProtocolRegistry::ProtocolRegistry() {} ProtocolRegistry::~ProtocolRegistry() = default; void ProtocolRegistry::RegisterURLLoaderFactories( - URLLoaderFactoryType type, - content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) { - // Override the default FileURLLoaderFactory to support asar archives. - if (type == URLLoaderFactoryType::kNavigation) { - // Always allow navigating to file:// URLs. + content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories, + bool allow_file_access) { + auto file_factory = factories->find(url::kFileScheme); + if (file_factory != factories->end()) { + // If Chromium already allows file access then replace the url factory to + // also loading asar files. + file_factory->second = AsarURLLoaderFactory::Create(); + } else if (allow_file_access) { + // Otherwise only allow file access when it is explicitly allowed. // - // Note that Chromium calls |emplace| to create the default file factory - // after this call, so it won't override our asar factory. - DCHECK(!base::Contains(*factories, url::kFileScheme)); + // Note that Chromium may call |emplace| to create the default file factory + // after this call, it won't override our asar factory, but if asar support + // breaks in future, please check if Chromium has changed the call. factories->emplace(url::kFileScheme, AsarURLLoaderFactory::Create()); - } else if (type == URLLoaderFactoryType::kDocumentSubResource) { - // Only support requesting file:// subresource URLs when Chromium does so, - // it is usually supported under file:// or about:blank documents. - auto file_factory = factories->find(url::kFileScheme); - if (file_factory != factories->end()) - file_factory->second = AsarURLLoaderFactory::Create(); } for (const auto& it : handlers_) { diff --git a/shell/browser/protocol_registry.h b/shell/browser/protocol_registry.h index 7a58513061e40..ea988dc7f7a4d 100644 --- a/shell/browser/protocol_registry.h +++ b/shell/browser/protocol_registry.h @@ -26,8 +26,8 @@ class ProtocolRegistry { content::ContentBrowserClient::URLLoaderFactoryType; void RegisterURLLoaderFactories( - URLLoaderFactoryType type, - content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories); + content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories, + bool allow_file_access); const HandlersMap& intercept_handlers() const { return intercept_handlers_; } diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 663aa32156f0a..546c900b10972 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 11,4,1,0 - PRODUCTVERSION 11,4,1,0 + FILEVERSION 11,5,0,0 + PRODUCTVERSION 11,5,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "11.4.1" + VALUE "FileVersion", "11.5.0" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "11.4.1" + VALUE "ProductVersion", "11.5.0" VALUE "SquirrelAwareVersion", "1" END END diff --git a/shell/browser/ui/cocoa/electron_ns_window.mm b/shell/browser/ui/cocoa/electron_ns_window.mm index 9a0674b31381d..e40fe6685b351 100644 --- a/shell/browser/ui/cocoa/electron_ns_window.mm +++ b/shell/browser/ui/cocoa/electron_ns_window.mm @@ -223,11 +223,23 @@ - (void)toggleFullScreenMode:(id)sender { if (is_simple_fs || always_simple_fs) { shell_->SetSimpleFullScreen(!is_simple_fs); } else { - bool maximizable = shell_->IsMaximizable(); - [super toggleFullScreen:sender]; + if (shell_->IsVisible()) { + // Until 10.13, AppKit would obey a call to -toggleFullScreen: made inside + // windowDidEnterFullScreen & windowDidExitFullScreen. Starting in 10.13, + // it behaves as though the transition is still in progress and just emits + // "not in a fullscreen state" when trying to exit fullscreen in the same + // runloop that entered it. To handle this, invoke -toggleFullScreen: + // asynchronously. + [super performSelector:@selector(toggleFullScreen:) + withObject:nil + afterDelay:0]; + } else { + [super toggleFullScreen:sender]; + } // Exiting fullscreen causes Cocoa to redraw the NSWindow, which resets // the enabled state for NSWindowZoomButton. We need to persist it. + bool maximizable = shell_->IsMaximizable(); shell_->SetMaximizable(maximizable); } } diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm index 9c0fc31974435..3e67397012018 100644 --- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm @@ -17,6 +17,8 @@ #include "ui/views/widget/native_widget_mac.h" using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; +using FullScreenTransitionState = + electron::NativeWindowMac::FullScreenTransitionState; @implementation ElectronNSWindowDelegate @@ -212,7 +214,9 @@ - (void)windowDidEndLiveResize:(NSNotification*)notification { } - (void)windowWillEnterFullScreen:(NSNotification*)notification { - // Setting resizable to true before entering fullscreen + shell_->SetFullScreenTransitionState(FullScreenTransitionState::ENTERING); + + // Setting resizable to true before entering fullscreen. is_resizable_ = shell_->IsResizable(); shell_->SetResizable(true); // Hide the native toolbar before entering fullscreen, so there is no visual @@ -224,6 +228,8 @@ - (void)windowWillEnterFullScreen:(NSNotification*)notification { } - (void)windowDidEnterFullScreen:(NSNotification*)notification { + shell_->SetFullScreenTransitionState(FullScreenTransitionState::NONE); + shell_->NotifyWindowEnterFullScreen(); // For frameless window we don't show set title for normal mode since the @@ -252,9 +258,13 @@ - (void)windowDidEnterFullScreen:(NSNotification*)notification { [window setTitlebarAppearsTransparent:NO]; shell_->SetStyleMask(true, NSWindowStyleMaskFullSizeContentView); } + + shell_->HandlePendingFullscreenTransitions(); } - (void)windowWillExitFullScreen:(NSNotification*)notification { + shell_->SetFullScreenTransitionState(FullScreenTransitionState::EXITING); + // Restore the titlebar visibility. NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow(); if ((shell_->transparent() || !shell_->has_frame()) && @@ -268,19 +278,23 @@ - (void)windowWillExitFullScreen:(NSNotification*)notification { shell_->SetStyleMask(false, NSWindowStyleMaskFullSizeContentView); [window setTitlebarAppearsTransparent:YES]; } - shell_->SetExitingFullScreen(true); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { shell_->RedrawTrafficLights(); } } - (void)windowDidExitFullScreen:(NSNotification*)notification { + shell_->SetFullScreenTransitionState(FullScreenTransitionState::NONE); + shell_->SetResizable(is_resizable_); shell_->NotifyWindowLeaveFullScreen(); - shell_->SetExitingFullScreen(false); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { shell_->RedrawTrafficLights(); } + + shell_->HandlePendingFullscreenTransitions(); } - (void)windowWillClose:(NSNotification*)notification { diff --git a/shell/browser/ui/cocoa/electron_touch_bar.mm b/shell/browser/ui/cocoa/electron_touch_bar.mm index 676cfb2894832..e4b47a29543a7 100644 --- a/shell/browser/ui/cocoa/electron_touch_bar.mm +++ b/shell/browser/ui/cocoa/electron_touch_bar.mm @@ -551,7 +551,7 @@ - (void)updatePopover:(NSPopoverTouchBarItem*)item gin_helper::PersistentDictionary child; std::vector items; - if (settings.Get("child", &child) && child.Get("ordereredItems", &items)) { + if (settings.Get("child", &child) && child.Get("orderedItems", &items)) { item.popoverTouchBar = [self touchBarFromItemIdentifiers:[self identifiersFromSettings:items]]; } @@ -572,7 +572,7 @@ - (NSTouchBarItem*)makeGroupForID:(NSString*)id if (!settings.Get("child", &child)) return nil; std::vector items; - if (!child.Get("ordereredItems", &items)) + if (!child.Get("orderedItems", &items)) return nil; NSMutableArray* generatedItems = [NSMutableArray array]; @@ -601,7 +601,7 @@ - (void)updateGroup:(NSGroupTouchBarItem*)item if (!settings.Get("child", &child)) return; std::vector items; - if (!child.Get("ordereredItems", &items)) + if (!child.Get("orderedItems", &items)) return; item.groupTouchBar = diff --git a/shell/browser/ui/file_dialog_gtk.cc b/shell/browser/ui/file_dialog_gtk.cc index 9a76752273faf..6bc6ca9a15a98 100644 --- a/shell/browser/ui/file_dialog_gtk.cc +++ b/shell/browser/ui/file_dialog_gtk.cc @@ -75,6 +75,7 @@ class FileChooserDialog { gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog_), TRUE); if (!settings.default_path.empty()) { + base::ThreadRestrictions::ScopedAllowIO allow_io; if (base::DirectoryExists(settings.default_path)) { gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog_), settings.default_path.value().c_str()); diff --git a/shell/browser/ui/win/jump_list.cc b/shell/browser/ui/win/jump_list.cc index 25b07b004d5b4..1aaae96806361 100644 --- a/shell/browser/ui/win/jump_list.cc +++ b/shell/browser/ui/win/jump_list.cc @@ -28,6 +28,13 @@ bool AppendTask(const JumpListItem& item, IObjectCollection* collection) { FAILED(link->SetDescription(item.description.c_str()))) return false; + // SetDescription limits the size of the parameter to INFOTIPSIZE (1024), + // which suggests rejection when exceeding that limit, but experimentation + // has shown that descriptions longer than 260 characters cause a silent + // failure, despite SetDescription returning the success code S_OK. + if (item.description.size() > 260) + return false; + if (!item.icon_path.empty() && FAILED(link->SetIconLocation(item.icon_path.value().c_str(), item.icon_index))) diff --git a/shell/browser/ui/win/taskbar_host.cc b/shell/browser/ui/win/taskbar_host.cc index 717937b5855df..56fdc8668ca7b 100644 --- a/shell/browser/ui/win/taskbar_host.cc +++ b/shell/browser/ui/win/taskbar_host.cc @@ -114,11 +114,12 @@ bool TaskbarHost::SetThumbarButtons(HWND window, // Finally add them to taskbar. HRESULT r; - if (thumbar_buttons_added_) + if (thumbar_buttons_added_) { r = taskbar_->ThumbBarUpdateButtons(window, kMaxButtonsCount, thumb_buttons); - else + } else { r = taskbar_->ThumbBarAddButtons(window, kMaxButtonsCount, thumb_buttons); + } thumbar_buttons_added_ = true; last_buttons_ = buttons; diff --git a/shell/browser/ui/win/taskbar_host.h b/shell/browser/ui/win/taskbar_host.h index 886633ff2789e..f97070618944c 100644 --- a/shell/browser/ui/win/taskbar_host.h +++ b/shell/browser/ui/win/taskbar_host.h @@ -60,6 +60,8 @@ class TaskbarHost { // Called by the window that there is a button in thumbar clicked. bool HandleThumbarButtonEvent(int button_id); + void SetThumbarButtonsAdded(bool added) { thumbar_buttons_added_ = added; } + private: // Initialize the taskbar object. bool InitializeTaskbar(); diff --git a/shell/common/api/electron_api_native_image_win.cc b/shell/common/api/electron_api_native_image_win.cc index 4ae0c15103f6b..55c0ae75ef302 100644 --- a/shell/common/api/electron_api_native_image_win.cc +++ b/shell/common/api/electron_api_native_image_win.cc @@ -12,6 +12,7 @@ #include #include +#include "base/win/scoped_com_initializer.h" #include "shell/common/gin_converters/image_converter.h" #include "shell/common/gin_helper/promise.h" #include "shell/common/skia_util.h" @@ -26,6 +27,8 @@ v8::Local NativeImage::CreateThumbnailFromPath( v8::Isolate* isolate, const base::FilePath& path, const gfx::Size& size) { + base::win::ScopedCOMInitializer scoped_com_initializer; + gin_helper::Promise promise(isolate); v8::Local handle = promise.GetHandle(); HRESULT hr; diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 85a3bbe2a0cc9..565b6945752ae 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -128,6 +128,10 @@ void stop_and_close_uv_loop(uv_loop_t* loop) { bool g_is_initialized = false; bool IsPackagedApp() { + auto env = base::Environment::Create(); + if (env->HasVar("ELECTRON_FORCE_IS_PACKAGED")) + return true; + base::FilePath exe_path; base::PathService::Get(base::FILE_EXE, &exe_path); base::FilePath::StringType base_name = @@ -539,15 +543,13 @@ void NodeBindings::LoadEnvironment(node::Environment* env) { void NodeBindings::PrepareMessageLoop() { #if !defined(OS_WIN) int handle = uv_backend_fd(uv_loop_); -#else - HANDLE handle = uv_loop_->iocp; -#endif // If the backend fd hasn't changed, don't proceed. if (handle == handle_) return; handle_ = handle; +#endif // Add dummy handle for libuv, otherwise libuv would quit when there is // nothing to do. @@ -582,8 +584,13 @@ void NodeBindings::UvRunOnce() { // Enter node context while dealing with uv events. v8::Context::Scope context_scope(env->context()); - // Perform microtask checkpoint after running JavaScript. - gin_helper::MicrotasksScope microtasks_scope(env->isolate()); + // Node.js expects `kExplicit` microtasks policy and will run microtasks + // checkpoints after every call into JavaScript. Since we use a different + // policy in the renderer - switch to `kExplicit` and then drop back to the + // previous policy value. + auto old_policy = env->isolate()->GetMicrotasksPolicy(); + DCHECK_EQ(v8::MicrotasksScope::GetCurrentDepth(env->isolate()), 0); + env->isolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); if (browser_env_ != BrowserEnvironment::BROWSER) TRACE_EVENT_BEGIN0("devtools.timeline", "FunctionCall"); @@ -594,6 +601,8 @@ void NodeBindings::UvRunOnce() { if (browser_env_ != BrowserEnvironment::BROWSER) TRACE_EVENT_END0("devtools.timeline", "FunctionCall"); + env->isolate()->SetMicrotasksPolicy(old_policy); + if (r == 0) base::RunLoop().QuitWhenIdle(); // Quit from uv. diff --git a/shell/common/node_bindings.h b/shell/common/node_bindings.h index eba92bda3da4e..4fd8a63bbb56f 100644 --- a/shell/common/node_bindings.h +++ b/shell/common/node_bindings.h @@ -159,9 +159,7 @@ class NodeBindings { // Isolate data used in creating the environment node::IsolateData* isolate_data_ = nullptr; -#if defined(OS_WIN) - HANDLE handle_; -#else +#if !defined(OS_WIN) int handle_ = -1; #endif diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index 37c310380d08e..d3e5599a2031f 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -177,7 +177,6 @@ void ElectronRendererClient::WillReleaseScriptContext( if (command_line->HasSwitch(switches::kNodeIntegrationInSubFrames) || command_line->HasSwitch( switches::kDisableElectronSiteInstanceOverrides)) { - node::RunAtExit(env); node::FreeEnvironment(env); if (env == node_bindings_->uv_env()) node::FreeIsolateData(node_bindings_->isolate_data()); diff --git a/spec-main/api-app-spec.ts b/spec-main/api-app-spec.ts index cb8693b7f08b7..e85168f933123 100644 --- a/spec-main/api-app-spec.ts +++ b/spec-main/api-app-spec.ts @@ -1716,6 +1716,8 @@ describe('default behavior', () => { }); describe('window-all-closed', () => { + afterEach(closeAllWindows); + it('quits when the app does not handle the event', async () => { const result = await runTestApp('window-all-closed'); expect(result).to.equal(false); @@ -1725,6 +1727,17 @@ describe('default behavior', () => { const result = await runTestApp('window-all-closed', '--handle-event'); expect(result).to.equal(true); }); + + it('should omit closed windows from getAllWindows', async () => { + const w = new BrowserWindow({ show: false }); + const len = new Promise(resolve => { + app.on('window-all-closed', () => { + resolve(BrowserWindow.getAllWindows().length); + }); + }); + w.close(); + expect(await len).to.equal(0); + }); }); describe('user agent fallback', () => { diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index cf7aa025dc43b..70746d4c68316 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -8,7 +8,7 @@ import * as http from 'http'; import { AddressInfo } from 'net'; import { app, BrowserWindow, BrowserView, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main'; -import { emittedOnce, emittedUntil } from './events-helpers'; +import { emittedOnce, emittedUntil, emittedNTimes } from './events-helpers'; import { ifit, ifdescribe, defer, delay } from './spec-helpers'; import { closeWindow, closeAllWindows } from './window-helpers'; @@ -4046,6 +4046,42 @@ describe('BrowserWindow module', () => { expect(w.isFullScreen()).to.be.false('isFullScreen'); }); + it('handles several transitions starting with fullscreen', async () => { + const w = new BrowserWindow({ fullscreen: true, show: true }); + + expect(w.isFullScreen()).to.be.true('not fullscreen'); + + w.setFullScreen(false); + w.setFullScreen(true); + + const enterFullScreen = emittedNTimes(w, 'enter-full-screen', 2); + await enterFullScreen; + + expect(w.isFullScreen()).to.be.true('not fullscreen'); + + await delay(); + const leaveFullScreen = emittedOnce(w, 'leave-full-screen'); + w.setFullScreen(false); + await leaveFullScreen; + + expect(w.isFullScreen()).to.be.false('is fullscreen'); + }); + + it('handles several transitions in close proximity', async () => { + const w = new BrowserWindow(); + + expect(w.isFullScreen()).to.be.false('is fullscreen'); + + w.setFullScreen(true); + w.setFullScreen(false); + w.setFullScreen(true); + + const enterFullScreen = emittedNTimes(w, 'enter-full-screen', 2); + await enterFullScreen; + + expect(w.isFullScreen()).to.be.true('not fullscreen'); + }); + it('does not crash when exiting simpleFullScreen (properties)', async () => { const w = new BrowserWindow(); w.setSimpleFullScreen(true); diff --git a/spec-main/api-native-theme-spec.ts b/spec-main/api-native-theme-spec.ts index f4b63851cd90e..3a03eb1ef81a2 100644 --- a/spec-main/api-native-theme-spec.ts +++ b/spec-main/api-native-theme-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { nativeTheme, systemPreferences, BrowserWindow } from 'electron/main'; +import { nativeTheme, systemPreferences, BrowserWindow, ipcMain } from 'electron/main'; import * as os from 'os'; import * as path from 'path'; import * as semver from 'semver'; @@ -75,14 +75,24 @@ describe('nativeTheme module', () => { }; it('should override the result of prefers-color-scheme CSS media query', async () => { - const w = new BrowserWindow({ show: false }); + const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: false, nodeIntegration: true } }); await w.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); + await w.webContents.executeJavaScript(` + window.matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', () => require('electron').ipcRenderer.send('theme-change')) + `); const originalSystemIsDark = await getPrefersColorSchemeIsDark(w); + let changePromise: Promise = emittedOnce(ipcMain, 'theme-change'); nativeTheme.themeSource = 'dark'; + if (!originalSystemIsDark) await changePromise; expect(await getPrefersColorSchemeIsDark(w)).to.equal(true); + changePromise = emittedOnce(ipcMain, 'theme-change'); nativeTheme.themeSource = 'light'; + await changePromise; expect(await getPrefersColorSchemeIsDark(w)).to.equal(false); + changePromise = emittedOnce(ipcMain, 'theme-change'); nativeTheme.themeSource = 'system'; + if (originalSystemIsDark) await changePromise; expect(await getPrefersColorSchemeIsDark(w)).to.equal(originalSystemIsDark); w.close(); }); diff --git a/spec-main/api-protocol-spec.ts b/spec-main/api-protocol-spec.ts index 2f578a979af68..bda949dff9ff9 100644 --- a/spec-main/api-protocol-spec.ts +++ b/spec-main/api-protocol-spec.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { v4 } from 'uuid'; import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main'; import { AddressInfo } from 'net'; import * as ChildProcess from 'child_process'; @@ -703,6 +704,42 @@ describe('protocol module', () => { }); }); + describe('protocol.registerSchemesAsPrivileged allowServiceWorkers', () => { + const { serviceWorkerScheme } = global as any; + protocol.registerStringProtocol(serviceWorkerScheme, (request, cb) => { + if (request.url.endsWith('.js')) { + cb({ + mimeType: 'text/javascript', + charset: 'utf-8', + data: 'console.log("Loaded")' + }); + } else { + cb({ + mimeType: 'text/html', + charset: 'utf-8', + data: '' + }); + } + }); + after(() => protocol.unregisterProtocol(serviceWorkerScheme)); + + it('should fail when registering invalid service worker', async () => { + await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`); + const wait = emittedOnce(contents, 'console-message'); + await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.notjs', {scope: './'}).then(() => console.log('ok')).catch(() => console.log('error'))`); + const [,, msg] = await wait; + expect(msg).to.equal('error'); + }); + + it('should be able to register service worker for custom scheme', async () => { + await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`); + const wait = emittedOnce(contents, 'console-message'); + await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.js', {scope: './'}).then(() => console.log('ok')).catch(() => console.log('error'))`); + const [,, msg] = await wait; + expect(msg).to.equal('ok'); + }); + }); + describe.skip('protocol.registerSchemesAsPrivileged standard', () => { const standardScheme = (global as any).standardScheme; const origin = `${standardScheme}://fake-host`; diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 1fedbb9fb9d3c..a815e98ad6562 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -282,6 +282,77 @@ describe('web security', () => { expect(response).to.equal('passed'); }); + describe('accessing file://', () => { + async function loadFile (w: BrowserWindow) { + const thisFile = url.format({ + pathname: __filename.replace(/\\/g, '/'), + protocol: 'file', + slashes: true + }); + await w.loadURL(`data:text/html,`); + return await w.webContents.executeJavaScript('loadFile()'); + } + + it('is forbidden when web security is enabled', async () => { + const w = new BrowserWindow({ show: false, webPreferences: { webSecurity: true } }); + const result = await loadFile(w); + expect(result).to.equal('failed'); + }); + + it('is allowed when web security is disabled', async () => { + const w = new BrowserWindow({ show: false, webPreferences: { webSecurity: false } }); + const result = await loadFile(w); + expect(result).to.equal('loaded'); + }); + }); + + describe('wasm-eval csp', () => { + async function loadWasm (csp: string) { + const w = new BrowserWindow({ + show: false, + webPreferences: { + sandbox: true, + enableBlinkFeatures: 'WebAssemblyCSP' + } + }); + await w.loadURL(`data:text/html, + + + `); + return await w.webContents.executeJavaScript('loadWasm()'); + } + + it('wasm codegen is disallowed by default', async () => { + const r = await loadWasm(''); + expect(r).to.equal('WebAssembly.instantiate(): Wasm code generation disallowed by embedder'); + }); + + it('wasm codegen is allowed with "wasm-eval" csp', async () => { + const r = await loadWasm("'wasm-eval'"); + expect(r).to.equal('loaded'); + }); + }); + it('does not crash when multiple WebContent are created with web security disabled', () => { const options = { webPreferences: { webSecurity: false } }; const w1 = new BrowserWindow(options); @@ -1431,8 +1502,8 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); afterEach(async () => { - await closeAllWindows() - ;(w as any) = null; + await closeAllWindows(); + (w as any) = null; server.close(); }); diff --git a/spec-main/fixtures/apps/libuv-hang/index.html b/spec-main/fixtures/apps/libuv-hang/index.html new file mode 100644 index 0000000000000..a3534d419a6f4 --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/index.html @@ -0,0 +1,13 @@ + + + + + + + Hello World! + + +

Hello World!

+ + + diff --git a/spec-main/fixtures/apps/libuv-hang/main.js b/spec-main/fixtures/apps/libuv-hang/main.js new file mode 100644 index 0000000000000..4f96b62ba8805 --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/main.js @@ -0,0 +1,37 @@ +const { app, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); + +async function createWindow () { + const mainWindow = new BrowserWindow({ + show: false, + webPreferences: { + contextIsolation: true, + preload: path.join(__dirname, 'preload.js') + } + }); + + await mainWindow.loadFile('index.html'); +} + +app.whenReady().then(() => { + createWindow(); + app.on('activate', function () { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); +}); + +let count = 0; +ipcMain.handle('reload-successful', () => { + if (count === 2) { + app.quit(); + } else { + count++; + return count; + } +}); + +app.on('window-all-closed', function () { + if (process.platform !== 'darwin') app.quit(); +}); diff --git a/spec-main/fixtures/apps/libuv-hang/preload.js b/spec-main/fixtures/apps/libuv-hang/preload.js new file mode 100644 index 0000000000000..a5840f557f562 --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/preload.js @@ -0,0 +1,16 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('api', { + ipcRenderer, + run: async () => { + const { promises: fs } = require('fs'); + for (let i = 0; i < 10; i++) { + const list = await fs.readdir('.', { withFileTypes: true }); + for (const file of list) { + if (file.isFile()) { + await fs.readFile(file.name, 'utf-8'); + } + } + } + } +}); diff --git a/spec-main/fixtures/apps/libuv-hang/renderer.js b/spec-main/fixtures/apps/libuv-hang/renderer.js new file mode 100644 index 0000000000000..5f0a2b58b5145 --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/renderer.js @@ -0,0 +1,8 @@ +const count = localStorage.getItem('count'); + +const { run, ipcRenderer } = window.api; + +run().then(async () => { + const count = await ipcRenderer.invoke('reload-successful'); + if (count < 3) location.reload(); +}).catch(console.log); diff --git a/spec-main/index.js b/spec-main/index.js index 84f025984499a..820e513a3855a 100644 --- a/spec-main/index.js +++ b/spec-main/index.js @@ -33,9 +33,11 @@ app.commandLine.appendSwitch('use-fake-device-for-media-stream'); global.standardScheme = 'app'; global.zoomScheme = 'zoom'; +global.serviceWorkerScheme = 'sw'; protocol.registerSchemesAsPrivileged([ { scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, + { scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } }, { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'no-cors', privileges: { supportFetchAPI: true } }, diff --git a/spec-main/node-spec.ts b/spec-main/node-spec.ts index eb3af602a5796..8f11ec86503c2 100644 --- a/spec-main/node-spec.ts +++ b/spec-main/node-spec.ts @@ -7,6 +7,7 @@ import { ifdescribe, ifit } from './spec-helpers'; import { webContents, WebContents } from 'electron/main'; const features = process._linkedBinding('electron_common_features'); +const mainFixturesPath = path.resolve(__dirname, 'fixtures'); describe('node feature', () => { const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); @@ -22,6 +23,16 @@ describe('node feature', () => { }); }); + it('does not hang when using the fs module in the renderer process', async () => { + const appPath = path.join(mainFixturesPath, 'apps', 'libuv-hang', 'main.js'); + const appProcess = childProcess.spawn(process.execPath, [appPath], { + cwd: path.join(mainFixturesPath, 'apps', 'libuv-hang'), + stdio: 'inherit' + }); + const [code] = await emittedOnce(appProcess, 'close'); + expect(code).to.equal(0); + }); + describe('contexts', () => { describe('setTimeout called under Chromium event loop in browser process', () => { it('Can be scheduled in time', (done) => { @@ -121,6 +132,29 @@ describe('node feature', () => { child.stderr.on('data', listener); child.stdout.on('data', listener); }); + + it('does allow --require in non-packaged apps', async () => { + const appPath = path.join(fixtures, 'module', 'noop.js'); + const env = Object.assign({}, process.env, { + NODE_OPTIONS: `--require=${path.join(fixtures, 'module', 'fail.js')}` + }); + // App should exit with code 1. + const child = childProcess.spawn(process.execPath, [appPath], { env }); + const [code] = await emittedOnce(child, 'exit'); + expect(code).to.equal(1); + }); + + it('does not allow --require in packaged apps', async () => { + const appPath = path.join(fixtures, 'module', 'noop.js'); + const env = Object.assign({}, process.env, { + ELECTRON_FORCE_IS_PACKAGED: 'true', + NODE_OPTIONS: `--require=${path.join(fixtures, 'module', 'fail.js')}` + }); + // App should exit with code 0. + const child = childProcess.spawn(process.execPath, [appPath], { env }); + const [code] = await emittedOnce(child, 'exit'); + expect(code).to.equal(0); + }); }); describe('Node.js cli flags', () => { diff --git a/spec-main/security-warnings-spec.ts b/spec-main/security-warnings-spec.ts index 217cb916262c0..88bb8f9510e3a 100644 --- a/spec-main/security-warnings-spec.ts +++ b/spec-main/security-warnings-spec.ts @@ -228,7 +228,7 @@ describe('security warnings', () => { it('should warn about enabled remote module with remote content', async () => { w = new BrowserWindow({ show: false, - webPreferences + webPreferences: { ...webPreferences, enableRemoteModule: true } }); w.loadURL(`${serverUrl}/base-page-security.html`); @@ -239,7 +239,7 @@ describe('security warnings', () => { it('should not warn about enabled remote module with remote content from localhost', async () => { w = new BrowserWindow({ show: false, - webPreferences + webPreferences: { ...webPreferences, enableRemoteModule: true } }); w.loadURL(`${serverUrl}/base-page-security-onload-message.html`); const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded); diff --git a/spec-main/version-bump-spec.ts b/spec-main/version-bump-spec.ts index aff7cd28b53f6..247f960b06f36 100644 --- a/spec-main/version-bump-spec.ts +++ b/spec-main/version-bump-spec.ts @@ -43,7 +43,7 @@ describe('version-bumper', () => { // On macOS Circle CI we don't have a real git environment due to running // gclient sync on a linux machine. These tests therefore don't run as expected. - ifdescribe(!(process.platform === 'linux' && process.arch === 'arm') && process.platform !== 'darwin')('nextVersion', () => { + ifdescribe(!(process.platform === 'linux' && process.arch.indexOf('arm') === 0) && process.platform !== 'darwin')('nextVersion', () => { const nightlyPattern = /[0-9.]*(-nightly.(\d{4})(\d{2})(\d{2}))$/g; const betaPattern = /[0-9.]*(-beta[0-9.]*)/g; diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index 50217dc364c75..97142ddf19023 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -684,4 +684,49 @@ describe(' tag', function () { generateSpecs('without sandbox', false); generateSpecs('with sandbox', true); }); + + describe('DOM events', () => { + afterEach(closeAllWindows); + it('receives extra properties on DOM events when contextIsolation is enabled', async () => { + const w = new BrowserWindow({ + show: false, + webPreferences: { + webviewTag: true, + contextIsolation: true + } + }); + await w.loadURL('about:blank'); + const message = await w.webContents.executeJavaScript(`new Promise((resolve, reject) => { + const webview = new WebView() + webview.setAttribute('src', 'data:text/html,') + webview.addEventListener('console-message', (e) => { + resolve(e.message) + }) + document.body.appendChild(webview) + })`); + expect(message).to.equal('hi'); + }); + + it('emits focus event when contextIsolation is enabled', async () => { + const w = new BrowserWindow({ + show: false, + webPreferences: { + webviewTag: true, + contextIsolation: true + } + }); + await w.loadURL('about:blank'); + await w.webContents.executeJavaScript(`new Promise((resolve, reject) => { + const webview = new WebView() + webview.setAttribute('src', 'about:blank') + webview.addEventListener('dom-ready', () => { + webview.focus() + }) + webview.addEventListener('focus', () => { + resolve(); + }) + document.body.appendChild(webview) + })`); + }); + }); }); diff --git a/spec/fixtures/module/fail.js b/spec/fixtures/module/fail.js new file mode 100644 index 0000000000000..6cee2e1e79a14 --- /dev/null +++ b/spec/fixtures/module/fail.js @@ -0,0 +1 @@ +process.exit(1); diff --git a/spec/webview-spec.js b/spec/webview-spec.js index d44e91963e9ef..36dc72e92bc4b 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -33,6 +33,28 @@ describe(' tag', function () { return event.message; }; + async function loadFileInWebView (webview, attributes = {}) { + const thisFile = url.format({ + pathname: __filename.replace(/\\/g, '/'), + protocol: 'file', + slashes: true + }); + const src = ``; + attributes.src = `data:text/html;base64,${btoa(unescape(encodeURIComponent(src)))}`; + await startLoadingWebViewAndWaitForMessage(webview, attributes); + return await webview.executeJavaScript('loadFile()'); + } + beforeEach(() => { webview = new WebView(); }); @@ -316,27 +338,13 @@ describe(' tag', function () { describe('disablewebsecurity attribute', () => { it('does not disable web security when not set', async () => { - const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js'); - const src = ` `; - const encoded = btoa(unescape(encodeURIComponent(src))); - - const message = await startLoadingWebViewAndWaitForMessage(webview, { - src: `data:text/html;base64,${encoded}` - }); - expect(message).to.be.a('string'); - expect(message).to.contain('Not allowed to load local resource'); + const result = await loadFileInWebView(webview); + expect(result).to.equal('failed'); }); it('disables web security when set', async () => { - const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js'); - const src = ` `; - const encoded = btoa(unescape(encodeURIComponent(src))); - - const message = await startLoadingWebViewAndWaitForMessage(webview, { - disablewebsecurity: '', - src: `data:text/html;base64,${encoded}` - }); - expect(message).to.equal('ok'); + const result = await loadFileInWebView(webview, { disablewebsecurity: '' }); + expect(result).to.equal('loaded'); }); it('does not break node integration', async () => { @@ -487,16 +495,10 @@ describe(' tag', function () { }); it('can disables web security and enable nodeintegration', async () => { - const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js'); - const src = ` `; - const encoded = btoa(unescape(encodeURIComponent(src))); - - const message = await startLoadingWebViewAndWaitForMessage(webview, { - src: `data:text/html;base64,${encoded}`, - webpreferences: 'webSecurity=no, nodeIntegration=yes' - }); - - expect(message).to.equal('function'); + const result = await loadFileInWebView(webview, { webpreferences: 'webSecurity=no, nodeIntegration=yes, contextIsolation=no' }); + expect(result).to.equal('loaded'); + const type = await webview.executeJavaScript('typeof require'); + expect(type).to.equal('function'); }); }); diff --git a/typings/internal-electron.d.ts b/typings/internal-electron.d.ts index 4dbaaf2118e7a..9732b8cad7957 100644 --- a/typings/internal-electron.d.ts +++ b/typings/internal-electron.d.ts @@ -277,16 +277,6 @@ declare namespace ElectronInternal { allowGuestViewElementDefinition(window: Window, context: any): void; } - interface WebFrameResizeEvent extends Electron.Event { - newWidth: number; - newHeight: number; - } - - interface WebViewEvent extends Event { - url: string; - isMainFrame: boolean; - } - class WebViewElement extends HTMLElement { static observedAttributes: Array; diff --git a/yarn.lock b/yarn.lock index 4d59a175c8f07..d38d6755e23c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4963,7 +4963,7 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^2.2.1, minipass@^2.3.5: +minipass@^2.2.1: version "2.3.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== @@ -4971,6 +4971,14 @@ minipass@^2.2.1, minipass@^2.3.5: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^2.8.6: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + minizlib@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" @@ -7739,13 +7747,13 @@ tar-stream@^1.1.2: xtend "^4.0.0" tar@^4, tar@^4.4.7: - version "4.4.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" - integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== + version "4.4.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" + integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.5" + minipass "^2.8.6" minizlib "^1.2.1" mkdirp "^0.5.0" safe-buffer "^5.1.2"